python製の構成管理ソフト ansible を使ってみたのでまとめ

python製 構成管理ソフト

chef(まともに使ったことないので良く知らないけど)は各nodeが管理サーバへアクセスする方式で、ansibleはサーバから各nodeへアクセスするpush方式。なので一部のmoduleを利用しない限り、各nodeへ何かをinstallする必要はない。管理サーバからsshでアクセスができれば良い。

ファイルをコピーする、コマンドを実行する、などの処理を定義しているものがmodule。
それらをまとめたものがplaybook。

まとめって書いているけど後半疲れてきてただ列挙しているだけなのであまりまとまってない。

(ほんとに初期設定の導入部分をかじった程度だけど)chefの仕組みは理解し辛かったけのに対して、ansible はわかりやすいと思います。個人的に。

install

githubから取ってくるかepelからyumで入れる

# yum install -y --enablerepo=epel ansible
  • 設定ファイル (yumでinstallした場合)
    • /etc/ansible/ansible.cfg
    • /etc/ansible/hosts
    • /usr/share/ansible/module_name

hosts

yumでinstallするとdefaultのファイルは/etc/ansible/hostsになる。

単にhosts(ip, hostname) を書いていくだけでも良いし、ini形式でグループにすることも可能。また、www-[1:5]、www-[01:09]、app-[a:e] のように[]を使うと複数のホストに展開してくれる。

実行

> # ansible [-f forks] [-m module_name] [-a args]
ping module で各hostsへの疎通を確認しておく。

# ansible all -m ping
sv01 | success >> {
    "changed": false, 
    "ping": "pong"
}

sv02 | success >> {
    "changed": false, 
    "ping": "pong"
}

moduleを指定しない場合はdefaultのcommand moduleになる(-a で指定した引数を実行する)。
commandと似たshell moduleもあって、名前の通りshellの環境変数($PATH, $HOMEなど)を使ったり'||'、'&&'などを引数で使う、リダイレクトを使いたい場合はshell module を使う。他にもmoduleは盛りだくさん。yumの場合は/usr/share/ansible 以下に入ってる。

# ansible all -a 'uptime'
sv01 | success | rc=0 >>
 14:47:58 up 15 days,  8:23,  0 users,  load average: 0.00, 0.00, 0.00

sv02 | success | rc=0 >>
 14:47:58 up 39 days, 12:09,  0 users,  load average: 0.00, 0.00, 0.00	

module

cliでもdocument見れる。

# ansible-doc module_name

ファイルをコピーする、コマンドを実行する、などの処理を定義しているものがmodule。

公式のドキュメントざっと斜め読みしてとりあえず使ってみたmoduleメモ。

System
  • ping
    • 確認用
  • user
    • user追加、削除。group, shell なども指定可能
    • passwordはcryptedな文字列で指定
    • python -c 'import crypt; print crypt.crypt("This is my Password", "$1$SomeSalt$")' などで生成できる
  • service
    • service(init.d のscript) の起動、停止。chkconfig などのboot時の起動設定など
    • action: service name=nginx state=started enabled=yes
Commands
  • command
    • 引数をcommandとして実行
  • shell
    • shellを使って引数をcommandとして実行
    • shellなので'>>'、$HOME などが使える
  • script
    • localのscriptをremoteに転送してremoteで実行する
    • ~/.ansible/tmp/以下に一時ファイルが作られて、実行後に削除されるみたい
    • "action: script /tmp/test.pl hoge fuga" を実行して、remoteでps見てたら
    • "bash -c chmod +x /root/.ansible/tmp/ansible-1371288988.93-24846179799534/test.pl; /root/.ansible/tmp/ansible-1371288988.93-24846179799534/test.pl hoge fuga" とあった
File
  • copyやtemplateでbackupオプションをyesにすると、ファイルに変更があった場合に日付付きでbackupしてくれる
  • copy
    • localのファイルをremoteにcopy
    • copy src=/path/to/src dest=/path/to/dest
    • copy時にowner、mode(権限) も指定可能
  • fetch
    • remotenのファイルをlocalのdirectoryにcopy
    • fetch src=/path/to/src dest=/path/to/dest
    • destdirの下に各remoteのhostごとに /path/to/dest/hostname/path/to/src のようにディレクトリが作られる
  • file
    • 既存のfileの権限、オーナを変更したり、symlinkを作成したりする
    • file path=/path/to/file owner=foo mode=0644
    • file src=/path/to/src dest=/path/to/dest state=link
  • template
    • localのtemplateファイルをremoteにcopy
    • varsで設定した変数を定義しておけばcopyに展開してくれる
Network
  • get_url
  • uri
    • 指定したURLにアクセスする
    • "各node"にpyhonのurlparse httplib2 moduleが必要
      • pip などでinstallしておく
    • installされていないと"msg: httplib2 is not installed"というエラーメッセージが出る
    • httpでアクセスをするのはclientなので、client側にhttplib2 moduleがいる。
    • 当初、ansible = clientにインストール不要というのを思い込んでいたけど、それはansible自体が不要なのであってその他のpython module等は用途によっては必要
Notification

mail, irc, jabber などを使って通知するmodule

  • jabber
    • to "user ID or name of the room, when using room use a slash to indicate your nick."
    • とあるけど、roomを使ってない場合でも末尾に"/"を入れないとエラーになる
invalid output was: Traceback (most recent call last):
  File "/root/.ansible/tmp/ansible-1371788441.16-271641304974135/jabber", line 1052, in <module>
    main()
  File "/root/.ansible/tmp/ansible-1371788441.16-271641304974135/jabber", line 103, in main
    to, nick = re.split( r'/', module.params['to'])
ValueError: need more than 1 value to unpack
# grep -n 'to, nick' ../library/notification/jabber
103:    to, nick = re.split( r'/', module.params['to'])

ここで、"/"でsplitしてるけど、toに"/"を含んでない場合に、配列の数が一致しないからエラーになる?

playbook

moduleを組み合わせた一連の処理を定義する。定義したyamlはansible-playbook で使う。

# ansible-playbook filename.yml


↑のdocのsample

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  user: root
  tasks:
  - name: ensure apache is at the latest version
    action: yum pkg=httpd state=latest
  - name: write the apache config file
    action: template src=/srv/httpd.j2 dest=/etc/httpd/conf/httpd.conf
    notify:
    - restart apache
  - name: ensure apache is running
    action: service name=httpd state=started
  handlers:
    - name: restart apache
      action: service name=httpd state=restarted

playbookの主な内容

  • Basics

hosts, user などを設定。userにroot以外を指定する場合、sudoできるよう設定しておいて"sudo: yes" と書ける。sudo実行時にパスワードが必要な場合はansible-playbook に--ask-sudo-pass (-K) を指定する。

vers には変数を設定する。配列も設定可能。playbookの中や、template (jinja2)で利用する。

These variables can be used later in the playbook like this:

$varname or ${varname} or {{ varname }}

とあるが、templateの中では全て使えるが、playbookのyaml内では$varname, ${varname} しか使えなかった。pathは引数で{{ varname }} を使うと、そのまま"{{ varname }}"という文字で扱われて、想定している動作にならなずに失敗した。

---
- hosts: webservers
  user: root
  vars:
     http_port: 80
     van_halen_port: 5150
     other: 'magic'
       resolvers:
       - 192.168.1.10
       - 192.168.1.20

  • Tasks List

ansibleで実行させたい処理を記述する。
name: 標準出力に表示されるdescription。
action: moduleと引数を指定する
actionの省略した書き方で、moduleをそのままyamlのkeyにすることも可能。

tasks:
  - name: install latest nginx
    action: yum name=nginx state=latest enablerepo=epel
  - name: make sure nginx is running
    action: service name=nginx state=started

これは↓でもOK。

tasks:
  - name: install latest nginx
    yum: name=nginx state=latest enablerepo=epel
  - name: make sure nginx is running
    service: name=nginx state=started
エラーを無視する

失敗したときにエラーを無視させる場合はtasks内のリストの項目で"ignore_errors: True" としておく。
例えばatdサービスをOS起動時に起動しないようにしたい(chkconfig atd off)時は、
service: name=atd enabled=no などと書けばいいわけだけど、atd がinstallされていない場合はエラーになって処理が止まる。起動しないようにする設定なので、installされていない場合はそのままエラーを無視して処理を進めて欲しいといった場合はtasksを次のように書く。

tasks:
    - name: make atd not running
      service: name=atd state=stopped enabled=no
      ignore_errors: True


追記

ansible variables

ansible_os_family => "Redhat" などansibleが定義している変数。setup モジュールで確認可能。

# ansible hostname -m setup
(snip)
        "ansible_lsb": {
            "codename": "Final", 
            "description": "CentOS release 5.9 (Final)", 
            "id": "CentOS", 
            "major_release": "5", 
            "release": "5.9"
        }, 	
(snip)