目次


洗練されたPerl: システム管理用cfengine入門

UNIX戦士たちの朝食

Comments

この記事が一番役に立つのは、初心者および中級者のシステム管理者でしょう。以下の説明や例題は、初心者レベルのシステム管理をほぼ行うことができる方を想定しています。

この記事に出てくる例題を動作させるときは、最近 (2000年以降) のメインストリームのUNIXシステム (Linux、Solaris、BSD) を使用してください。ここで示される例題は、PerlやUNIXの以前のバージョン、あるいは他のオペレーティング・システムでも動くかもしれませんが、うまく動作しない場合には、練習だと思って自分で解決してみてください。

Cfengineは、まだ進化中です。この記事は、安定しているバージョン1.6.3を基にしています。現在アルファ・テスト段階に入っているバージョン2.0では、構文をほとんど変更することなく、アーキテクチャを練り直し、新たに多くのすばらしい機能を追加する予定になっています。cfengineの機能の詳細を見てみたい方、あるいはバージョン2.0の機能拡張について知りたい方は、cfengineのホームページ (稿末の参考文献参照) をご覧ください。

Cfengineの要点

Cfengineは、システム管理のやり方を変えてしまうものです。コマンド1個を実行し、後はシステムが安定な状態に収束していくのを見守るだけとなります。きっと、手品を見ている気分になることと思います。Cfengineは、みなさんがお茶でもすすっている間に、ファイルをいくつか編集し、コマンドをあれこれ実行して、symlinkを作り上げます。

といっても、cfengineがみなさんに代わって思考するわけではありません。構成ファイルは、やはりみなさんが記述し、製品に入れるときにはテストする必要があります。しかし、めったにcfengineが有害なことを行うことはありません。

Cfengineは、システムの収斂を可能にします。でも、なぜ収斂が必要なのでしょうか。まあ、考えてみてください。システム管理者が一番願っていることは心の平和です。それ以外は、二の次のことです。安定性、信頼性、それに予測可能性が心の平和を得るための道です。これは、単純化しすぎのように思われるかもしれませんが、誰か善良なシステム管理者に尋ねてみてください。必ずこの答が返ってきます。収斂は、安定性、信頼性、予測可能性をもたらす助けとなります。収斂が、これらの目標を達成するための唯一の方法だというわけではありませんが、筆者自身の経験から、もっとも苦労の少ない方法だと言うことができます。

安定性とは、意図しない変化に対する抵抗力と定義できます。Cfengineが収斂を実現するときには、一連のルールに基づいて行います。ルールをうまく設計してやると (安心してください、cfengineはうまい設計を簡単にできるようにしてくれます)、システムは理想的な状態 (cfengineのルールで定義される理想的状態) にしか向かいません。たとえば、システムの大切なsymlinkは、cfengineを実行するたびに作成し直すようにすることができます。あるいは、スタートアップ・スクリプトのinit.dは、ローカル・コピーに変更が加えられたときには、確実な保存場所からコピーしてくるようにすることができます。プロセスも、実行していることをcfengineが確認できないときは、リスタートさせるようにすることができます。

信頼性とは、いろいろな問題を切り抜けるマシンの能力のことです。ネットワークの停電やディスクの故障は、信頼性を計る大きな試験だといえます。みなさんのシステムは、こうした問題を乗り越えられるでしょうか。ある理想的な状態に収斂するということは、システムがその理想的な状態にあるか、もしくはそれに近い状態にあるとみなせることになります。信頼性がcfengineだけで得られるわけではないにしろ、収斂は、信頼性を高めます。安定なシステムは、いろいろな問題から影響を受けにくいわけですから、安定性も信頼性にとって重要な資質です。最後に、cfengineの収斂を利用すれば、「ブランク」システムを受け取り、それをお望みの状態にもっていくができますので、重要なシステムを代替システムに素早く切替えることが可能になります。

必要な状態と理想的な状態については、補足が必要です。これまで、理想的な状態は1つだけ存在し、必要なのはそれだけだという前提で説明を進めてきました。しかし実際には、すべてのマシンに共通な理想的な状態というものはありません。マシンは、タスク、場所、接続関係、ユーザー、オペレーティング・システムの種類とバージョンなどによってクラス分けされます。システム管理者は、誰でも、いくつかの方法で、通常は、上記の項目全部あるいはもっと多くの項目も含めて、管理しているマシンをクラス分けします (クラスについては、後でさらに言及します)。したがって、すべてのマシンに共通な理想的な状態というものは存在しないけれども、あるマシン・クラスにとって実現可能な理想的状態というものは存在します。これがcfengineの設計目標です。Cfengineは、理想的な状態を探し求め、その状態に簡単に収斂させることを、現実のものにしているのです。

予測可能性とは、マシンが予期したとおりに動作する能力のことです。収斂は、システムを安定で信頼性の高いものにすることで、予測可能性を実現します。また、新しいマシンは、ある理想的な状態に収斂した時点で、それが代替した古いマシンと同じ動作をするようになっているはずです。システムの追加や入れ替えを行うときのスケジューリング・コストは、マシンがある既知の状態にほどなく収斂する場合、非常に簡単に見積ることができます。最後に、それぞれのシステムに合わせて作成されたソフトウェアは、それらのシステムがほぼ理想的な状態にあるものとみなすことができます。そうであれば、システム・リソースは予測可能な状態にありますので、ソフトウェアは、すべてのシステムを未知の敵地として扱う必要性が減り、本来の機能 (features) に多く集中できることになります。

Cfengineの概要

Cfengineは、いくつかのプログラムからなっています。バージョン1.6.3では、メインのプログラムをcfengine と呼んでいます。Cfengineプログラムは、あるファイルに記述されている一連のルールを解釈し、それが要求しているアクションを実行します。厳密には、cfengineプログラムは、cfengine言語のインタプリターにすぎず、cfengineの各プログラムは、このインタプリターを記述するスクリプトに他なりません。

バージョン1.6.3には、cfd というデーモン・プログラムや、それと組になっているcfrun も用意されています。Cfdは、バージョン2.0で強化されることになっていますが、1.6.3バージョンには、不十分なところが多々ありました。幸い、筆者の場合は、必要な作業 (cfengineのシグナルによる実行やファイルの遠隔コピー) をcfdなしでも成し遂げることができました。筆者は、sshから明示的なスクリプトを使ってcfengineを起動するほうが好きです。cfdよりも少し遅い面もありますが、モニターするのが簡単です。cfdで起動したときは、cfengineから出力されるエラーやリモートで定義されるクラスが、いつも確実に表示されるわけではありませんでした。遠隔ファイル・コピーに関しては、cfdは当てにならず、セキュリティ・ハザードがありましたので、代わりにrsyncを使うようにしています。cfdは、バージョン2.0でかなりの改良を加えるのでrsyncは必要なくなるだろうと、cfengineの作者であるMark Burgessは語っていますが、2.0が出まわるまでは、cfdは避けたほうがよいでしょう。

cfengineを利用するには、まずcfengineをコンパイルしてインストールする必要があります。RPMを使用できるシステムではRPMを利用してインストールできますし、Solarisパッケージも用意されています (参考文献参照)。Tripwireがやっているのと同じように、ファイルのパーマネント・チェックサムを保存したい場合には、Berkeley DBサポート付きでコンパイルする必要があります。その後、構成ファイルの作成を開始します。一番中心となるファイル (ファイル名を指定せずにcfengineを起動したときに実行されるもの) は、/etc/cfengine/cfengine.confです (1.6.3でコンパイルする際にデフォルトの構成ディレクトリーとして別のディレクトリーを指定することもできますが、2.0以上では /etc/cfengineしかチェックされないようになりますので、このディレクトリーを使用するようにしたほうがよいでしょう)。

以下のリストは、cfengineの初期の構成を示したものです。これは、完成品ではありません。実際に使用するときには、まずcfengineのリファレンスやチュートリアル (参考文献参照) に注意深く目を通してください。-v -n オプション (詳細情報を出力し、ファイルは変更しない) を指定してcfengineを実行し、この構成がどんな振る舞いをするのかを確認してみてください。-n (dry run) オプションを指定した場合、システムには何も影響を与えません。

リスト1: cfengineに対する初期構成ファイル
/etc/cfengine/cfengine.conf

# note that only some of the possible sections are used here;
# refer to the cfengine documentation for the full list of sections
# you can have.  Comments, as you can see, are like shell or Perl
# comments.

# see the tutorial and reference for any unexplained phenomena

import:
 any::
  cf.groups
groups:

# all groups are defined in cf.groups, imported above, but you can
# define extras here.  The format is simple:

class = ( machine1 machine2 )

# and then any machine named machine1 or machine2 will have that class
# defined.
# the control section sets up how cfengine will behave
control:
 any::
# you have to state in AddInstallable what classes unknown to cfengine
# by default you will be using.  Run cfengine as "cfengine -v" to see
# the built-in classes you don't have to define.  Here we divide
# machines into the ones that run inetd and the ones that run xinetd,
# as an example.
  AddInstallable = ( inetd xinetd )
  editfilesize   = ( 300000 )
  moduledirectory = ( /etc/cfengine/modules )
  domain	= ( yourdomain.com )
 any::
  LogDirectory	= ( /etc/cfengine/log )
  netmask	= ( 255.255.255.0 )
  Repository	= ( /etc/cfengine/repository )
  sysadm	= ( "tzz@iglou.com" )
  # Bug in cfengine: actionsequence must follow LogDirectory and Repository 
  actionsequence = ( directories files editfiles copy links processes disable 
                               shellcommands )
directories:
# this ensures that these directories will be created when cfengine runs
 /etc/cfengine/log
 /etc/cfengine/repository
 /etc/cfengine/cfcollector
files:
 any::
# set the permissions for these files
  /etc/sudoers mode=0440 owner=root group=root action=fixall
  /etc/hosts.allow mode=0644 owner=root group=root action=fixall
  /etc/hosts.deny mode=0644 owner=root group=root action=fixall
# just warn if this file's permissions are wrong
  /etc/shadow mode=0400 owner=root action=warnall inform=true
# CERT advisory CA-2001-05, for Solaris only
 solaris::
  /usr/lib/dmi/snmpXdmid mode=0000 owner=root group=root action=fixall
# example of setting permissions differently for different OS types
# (not Linux and Linux), and negating classes
 !linux::
  /.ssh mode=0700 owner=root action=fixall inform=true
 linux::
  /root/.ssh mode=0700 owner=root action=fixall inform=true
editfiles:
 any::
# add the rsync service to /etc/services and /etc/inetd.conf
  { /etc/services
    SetLine "rsync	873/tcp	# rsync"
    AppendIfNoLineMatching "rsync.*"
  }
  { /etc/inetd.conf
    # add rsync
    SetLine "rsync stream tcp nowait root /usr/local/bin/rsync rsyncd --daemon"
    AppendIfNoLineMatching "rsync.*"
  }
copy:
# set up sshd startup script, from trusted master distribution in /etc/cfengine
  /etc/cfengine/sshd dest=/etc/init.d/sshd repository=/etc/cfengine/repository
links:
 any::
# link the sshd init.d script to /etc/rc3.d, overwriting existing
# links if they exist
  /etc/rc3.d/S72local_sshd ->! /etc/init.d/sshd
processes:
# invoke cfengine with "cfengine -DHupInetd" to define this class and
# send inetd the HUP signal (the machine has to be in the inetd class
# discussed above, too).  This is an example of compound classes.
 inetd.HupInetd::
  "inetd" signal=hup
disable:
# empty this file (this can also be used to rotate logs, with
# different rotate options)
  /etc/rc3.d/S77dmi rotate=empty
shellcommands:
 any::
# always put the contents of the $domain variable in this file.
# note that all the cfengine variables can be interpolated inside strings.
  "/bin/echo $(domain) > /etc/cfengine/cfdomainname"

簡単な使用法: ファイルの編集とコピー

ファイルを編集するには、editfiles セクションを使います。構文は、かなり複雑ですが、このコラムでは、いくつかのコマンドしか使用しません。コマンド一覧については、cfengineリファレンス (参考文献参照) を参照してください。

リスト2: ファイルの編集
editfiles:
 development::
  { /etc/sudoers
    DeleteLinesContaining "cpa "
  }
 accounting::
  { /etc/sudoers
    SetLine "cpa ALL=(ALL) ALL"
    AppendIfNoLineMatching "cpa .*"
  }

リスト2では、ユーザーcpaが開発部門から経理部門に移動になったときにどんなことが行われたのかがわかります。開発部門のマシンへのこのユーザーからのsudo(「superuser do」の略)アクセスは無効にし、経理部門のマシンにsudoアクセスできるようにしています。それにはcontrol: セクションでAddInstallable() を使って宣言されているはずのマシン・クラスを使用します。マシン・クラスにしたがって、必要なアクションを決めます。aの後にスペースを入れるように求めていますので、これらのルールでcpa1という名前のユーザーが問題となることはありません。

一般的で簡単な使用法としては、ファイルのコピーもあります。リスト1は大枠だけの例となっていますが、sshdをセットアップするためのcopy: セクションとlinks: セクションが示してあります。これらのセクションは、何をするものなのでしょうか。

リスト3: ファイルのコピー
copy:
# set up the sshd startup script
  /etc/cfengine/sshd dest=/etc/init.d/sshd repository=/etc/cfengine/repository
links:
 any::
# link the sshd init.d script to /etc/rc3.d
  /etc/rc3.d/S72local_sshd ->! /etc/init.d/sshd

われわれのところでは、cfengineを実行する前に、中央の信頼のおける場所に /etc/cfengineをrsync するようにしています。そうすることで、/etc/cfengineは、われわれが使用したい信頼のおけるファイルのローカル・コピーとなるわけです。そうしたファイルの1つが、SSHデーモンのためのスタートアップ・スクリプトである /etc/cfengine/sshdです。そこで、copy: セクションでは、信頼のおけるスタートアップ・スクリプトが /etc/init.d中のものと異なる場合に、前者を後者に上書きコピーしています。こうすることで、悪意の攻撃者が改竄を行っても、排除されることになります (われわれは、/etc/init.dのスクリプトのほとんどを、このようにして保守しています) 。また、スタートアップ・スクリプトに対する変更は、このように「シグナルド・プル (signalled pull) 」の手法を使って簡単に伝播させることができます。

copy: に対するリポジトリー・オプションは、sshdスクリプトを上書きしなければならない場合に、古いスクリプトをどこに保存すべきなのかをcfengineに伝えます。このオプションは、事故があったときのための保険として有効です。

links: セクションは、正しいスタートアップ・ディレクトリーへのリンクを設定します。なお、これはSolarisもしくはSystem Vのスタートアップ階層です。Linuxの場合には、solaris::/linux:: クラス・ディビジョンで扱うようにします。上のサンプルでは、簡単のために省略しました。感嘆符 (!) はオプションで、既存のリンクを上書きするかどうかを指定します (既存のファイルを上書きするわけではありません) 。

ディレクトリーの中身を一度にまるごとコピーしたり、リンクすることもできます。たとえば、/etc/cfengine/sbin to /usr/local/sbin中のすべてのファイルを一気にコピーしたり、リンクするといったことができます。必要なものだけコピーやリンクが行われます。Cfengineでは、copy: コマンドに遠隔コピー・オプションを許していますが、遠隔コピーはcfdを通して行われます。cfd自体に問題があるため、製品環境ではちゃんと機能しませんでした。Cfengineの1.6.3およびそれ以前のバージョンでは、事前実行 (pre-running)rsync のほうが有効なオプションです。

高度な使用法: プロセスのリスタート

プロセスは、定期的に実行されるcfengineのサブ構成を使うことで、もっとも効率よく処理され、通常 /etc/cfengine/cf.minuteかそれと同等の場所に格納されています。Cf.minuteには、cfengine.confが使用するものと同じcf.groupsのグループ定義を含め、cfengine -f /etc/cfengine/cf.minute のコマンドで起動ください。

下のサンプルは、プロセスのリスタートおよびプロセスに対するシグナルの仕方を示したものです。

リスト4: プロセスのリスタートおよびシグナルの方法
processes:
 any::
# restart cfd if it's not running already
  "/usr/local/sbin/cfd" restart "/etc/init.d/start-cfd start"
# restart sshd if it's not running already
  "/usr/local/sbin/sshd" restart "/etc/init.d/sshd start"
# HUP inetd if the HupInetd class is defined, see listing 1
 inetd.HupInetd::
  "inetd" signal=hup

Cfdは、cfengineデーモンです。このデーモンは、頻繁にリスタートを実行してみた結果、動作が非常に不安定であることがわかりました。そういうことから、筆者は、バージョン1.6.3段階ではcfengineデーモンを使用しないことにしました。2.0およびそれ以降のデーモンは、大きく改善されることと思います。

どんなコンピューター・ソフトウェアでもそうですが、1つのグループとしてのデーモン・ソフトウェアは、いろいろな形で実装されるという弊害があります。Cfengineからすれば、デーモンには2つの種類があります。すなわち、forkしてexitする種類のもの (inetd、sshd、cfdは、この種類のよい例です) と、そうでないもの (たとえば、qmailスタートアップ・デーモン) です。

筆者個人としては、デーモンは、どちらの動作も選択できるようにすべきだと思います。そうすれば、まったく異なる2つの種類のプログラムをモニターすることなくなります。しかし、現在のほとんどのデーモンでは、そうした選択はできませんので、対応が必要です。

Fork、exit形式のデーモンについては、cfengineは問題なく処理することができます。デーモンがforkする場合、cfengineは軽々と処理を続けます。しかし、forkしない種類のデーモンの場合 (社内用デーモンのほとんどが、こちらの種類)、cfengineには特別な手助けが必要となります。バージョン2.0では、何らかの形で解決されることになっていますが、1.6.3では、ソフトウェア側から "cfengine-die" + キャリッジ・リターンをプリントアウトすることで、cfengineに対し停止しても安全であることを知らせる必要があります。あるいは、Dan Bernsteinによるdaemontoolsのようなソフトウェア (参考文献参照) を使って、非fork型のデーモンをモニターすることもできますが、それ自体が大仕事となります。他に影響を及ぼすことのないその場しのぎとしては、通常、print 文をプログラムに入れるほうが楽です。

結論

本稿では、cfengineでできることをざっと紹介しました。読者は、cfengineのリファレンスに目を通してから、自分自身で試してみて、cfengineの有効性を確認してみてください。

バージョン1.6.3は、筆者の経験から、cfdデーモンを除き、非常に安定しています。構文は、ある程度の拡張はあるにしろ、次のバージョン (2.0) にも受け継がれますので、いま習得に時間を費やしても、決して無駄にはならないでしょう。

Cfengineは、すばらしいシステム管理ツールです。みなさんのサイトで実際に採用しないとしても、その考え方や試しに実行してみた経験は、みなさんの仕事の助けになることと思います。採用することにした場合、cfengineが極めて自由度の高いもので、とてつもなく便利なものであることがおわかりになることでしょう。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Linux, Open source
ArticleID=226920
ArticleTitle=洗練されたPerl: システム管理用cfengine入門
publish-date=02012002