ansibleを使ってみる

Chefが猛烈な勢いで流行り始めている今日このごろですが、似たようなものは 世の中にいくつもあります。今日はその中の一つ、 Ansible を使ってみます。

書いていたらやたらと長くなったので何回か続きます。

軽くご紹介

インストールの前にどのようなツールなのかを軽く説明します。マシンの設定 を自動で行なってくれる、というツールなのはChefと同じです。

そのポリシーは githubのページ に書かれています。

  • シンプルな設定

  • 最初から超速くて並列

  • サーバーやデーモンとかいらない。今あるsshdだけあればいい

  • クライアント側になにもいらない

  • モジュールは「どんな」言語でも書ける

  • 超強力な分散スクリプトを書くためのイケてるAPI

  • rootじゃなくても便利に使える

  • 今までで一番使える設定管理システム

さて、では見て行きましょう。

インストール

ansibleはpythonで書かれています。実行する側には2.6以上が、実行される側 には2.4以上が必要です。

他にも

  • paramiko

  • PyYAML

  • jinja2

が必要ですが、そのあたりはpipがめんどうみてくれるので、

% pip install ansible

でOKです。

使ってみる

% echo "127.0.0.1" > ~/ansible_hosts
% export ANSIBLE_HOSTS=~/ansible_hosts
% ansible all -m ping --ask-pass
SSH password:
127.0.0.1 | success >> {
    "changed": false,
    "ping": "pong"
}

と出ればOKです。これは、

  1. ansbileの対象となるホストを設定

  2. -m pingを実行

という感じです。

対象となるホストを設定

/etc/ansible/hosts を次のように書きます。これはiniファイル形式です。グ ループ化しておくといいですね。/etcに書けなくても、さっきのように ANSBLE_HOSTS 環境変数を設定したり -i で指定できるので問題ないです。

mail.example.com

[servers]
foo.example.com
bar.example.com

[webservers]
web[01:50].example.com    <-- web01から50まで指定

[databases]
db-[a:f].example.com  <-- こういうのもあり

実際にはホストやグループごとに個別に変数を設定できたりと、結構柔軟に書けます。

使い方

以上のように設定して、実際の実行は以下のようにします。

% ansible <対象> -m <モジュール名> -a <引数>

例
% ansible webservers -m service -a "name=httpd state=restarted"

さて、では、これから実際の使い方の例を並べてみます。

# webサーバーを10並列でrebootかける
% ansible webservers -a "/sbin/reboot" -f 10
# sudoで実行
% ansible atlanta -a "/usr/bin/foo" -u username --sudo
# shellモジュールを経由して実行
% ansible raleigh -m shell -a 'echo $TERM'

モジュールについてはあとで説明します。

ファイルを扱う

# 手元の/etc/hostsを各サーバーの/tmp/hostsにコピー
% ansible webservers -m copy -a "src=/etc/hosts dest=/tmp/hosts"
# 各サーバーのa.txtのownerを変更
% ansible webservers -m file -a "dest=/srv/foo/a.txt mode=600 owner=mdehaan"

パッケージ

# yumでinstall
% ansible webservers -m yum -a "name=acme state=installed"
# version指定
% ansible webservers -m yum -a "name=acme-1.5 state=installed"
# 最新指定
% ansible webservers -m yum -a "name=acme state=latest"
# インストールされていないこと
% ansible webservers -m yum -a "name=acme state=removed"

モジュール

ここまでで、ansbileの基礎的な使い方はわかったと思います。そして、 -m で 指定するモジュールというものがあることも。

モジュールはchefで言うところの「Resource」に当たるものです。結構いっぱ い用意されており、 ここ にまと まっています。

モジュールは自分で作成することもできます。

  • 標準入力でオプションを受け取る

  • 標準出力で実行結果を返す(出力形式は基本JSONで。ただし、key=value を空白でつなげたものでも)

ということさえ守っていれば、言語はなんでも大丈夫です。ただし、「冪等性」 は守ったほうがいいでしょう。既存のモジュールは冪等性があります。

詳しくは こちら をご覧ください。

playbook

モジュールは単一の機能だけを提供するものですので、これだけでは自動構成 できません。モジュールを組み合わせて、一連の動作を設定する必要がありま す。これを書くのが playbook と呼ばれるものです。

以下にapacheを設定する例を書きます。

---
- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  user: root
  tasks:
  - name: apacheを最新に
    action: yum pkg=httpd state=latest
  - name: apacheの設定ファイルを書き出す
    action: template src=/srv/httpd.j2 dest=/etc/httpd.conf
    notify:
    - apache再起動
  - name: apacheが起動していることを確認
    action: service name=httpd state=started
  handlers:
    - name: apache再起動
      action: service name=httpd state=restarted

これを実行するのには、 apache.yamlというファイル名で保存したとすると、

% ansible-playbook playbook.yml -f 10

とします。この例では10並列で実行しています。

感じがつかめたところで、順に説明していきます。

playbookの設定

- hosts: webservers
  vars:
    http_port: 80
    max_clients: 200
  user: root

hostsは実行するホストですね。先ほど ansible コマンドでも指定しましたね。 (ここが分離されてると共有しやすくなるんだけどな)

varsは使用する変数です。playbookの中で $http_port と指定したり、 templateモジュールのjinja2で使われたりします。

tasks設定

tasks:
- name: apacheを最新に
  action: yum pkg=httpd state=latest
- name: apacheの設定ファイルを書き出す
  action: template src=/srv/httpd.j2 dest=/etc/httpd.conf
  notify:
  - apache再起動

tasksは実際に実行する動作を指定します。nameはその名前を、actionでモジュー ルと引数を指定します。

実行順序は、書いてある順番で実行されます。

handlers

handlers:
  - name: apache再起動
    action: service name=httpd state=restarted

tasksの設定中で notify というのがありましたが、notifyで指定した動作を 受けるのがhandlerです。これにより、

  1. 設定ファイル書き出し

  2. apache再起動

という流れになります。chefのnotify/subscribeと一緒ですね。

なお、handlerは必ず最後に実行されます。つまり、tasksで「apache再起動」 が何回も指定されていたとしても、最後に一回だけしか実行されません。

includeでtaskを再利用

一度書いたtaskは include で再利用できます。

---
- name: apache再起動
  action: service name=apache state=restarted

とだけ書かれたファイルを用意しておけば、

tasks:
 - include: tasks/foo.yml
 - include: tasks/bar.yml user=shirou

と書けます。2つ目のincludeではuserという変数に別の値を入力しています。これで、 {{ user }} となっているところを置き換えられます。

また、handlerでもincludeできます。

playbookの再利用

- name: this is a play at the top level of a file
  hosts: all
  user: root
  tasks:
  - name: say hi
    tags: foo
    action: shell echo "hi..."

- include: load_balancers.yml
- include: webservers.yml
- include: dbservers.yml

tasksやhandlerと同じように、playbookそのものもincludeできます。 上記例では、load_balancersなどを読み込み、さらに実行するhostsを上書き しています。

role

これら再利用をまとめたのがroleです。

site.yml
webservers.yml
fooservers.yml
roles/
   common/
     files/
     templates/
     tasks/
     handlers/
     vars/
   webservers/
     files/
     templates/
     tasks/
     handlers/
     vars/

というようなファイル構成を作り、以下のようなplaybookを書きます。

---
- hosts: webservers
  roles:
     - common
     - webservers

このplaybookを実行すれば、roles/webserversの中身が設定されます。

rolesは以下のような動作をします。xは上記"webservers"などと読み替えてください。

  • roles/x/tasks/main.yml があればそこに書かれているtaskが自動でinclude

  • roles/x/handlers/main.yml があればそこに書かれているhandlersが自動でinclude

  • roles/x/vars/main.yml があればそこに書かれているvarsが自動でinclude

  • copy taskは、roles/x/files/以下のファイルをパス指定なしで参照できる

  • template taskaは、roles/x/templates以下のファイルをパス指定なしで参照できる

chefで言うところのcookbookが近いですね。

変数を別ファイルに書き出したい

---
- hosts: all
  user: root
  vars:
    favcolor: blue
  vars_files:
    - /vars/external_vars.yml

var_filesとしておけば、変数を外だし出来ます。

OSごとに挙動を変えたい

ansible_os_family という変数がセットされており、それを使います。

例えば、Debian系とCentOSとではapacheのパッケージ名が違いますが、

---
- hosts: all
  user: root
  vars_files:
    - "vars/common.yml"
    - [ "vars/{{ ansible_os_family }}.yml", "vars/os_defaults.yml" ]
  tasks:
  - name: make sure apache is running
    action: service name={{ apache }} state=running

というように書いておき、 vars/CentOS.yml にこう書いておくと、

---
# for vars/CentOS.yml
apache: httpd

CentOSの場合はhttpdが使われることになります。なにも当てはまらなければ os_defaults.ymlが使われます。仮にos_defaults.ymlがない場合はエラーとなります。

なお、この機能を使うためにはfacterかohaiを入れる必要があります。

状態に応じて動作を変える

tasks:
  - name: "Debian Familyだったらshutdownする"
    action: command /sbin/shutdown -t now
    when: ansible_os_family == "Debian"

whenを使うとできます。さらに、registerというものがあり、これはactionの 出力を受け取ります。この2つを併用すると、こういうこともできます。

tasks:
    - action: template src=/templates/foo.j2 dest=/etc/foo.conf
      register: last_result
    - action: command echo 'ファイルが変更されました'
      when: last_result.changed

この例ではactionのtemplateで変更があったら、echoが実行されます。

FAQ

naiveのsshを使うには

  • “–connection=ssh を引数につけるか

  • export ANSIBLE_TRANSPORT=ssh とする

実行時間が超長いコマンドがあるんだけど

# timeoutを3600秒にしてバックグラウンドで実行
% ansible all -B 3600 -a "/usr/bin/long_running_operation"

127.0.0.1 | success >> {
 "ansible_job_id": "27789765441",
 "results_file": "/home/shirou/.ansible_async/27789765441",
 "started": 1
}

# 得られたjob idを使って状態を得る。jobidは全ホスト同じ
% ansible all -m async_status -a "jid=123456789"

# 60秒ごとにポーリング
% ansible all -B 1800 -P 60 -a "/usr/bin/long_running_operation --do-stuff"

まとめ

chefでserverを使うまでもなくchef-soloで十分なレベル(20台ぐらいまで?)で あれば、このansibleを試してみる価値はあるかもしれません。

ただし、ansibleは並列実行をサポートしていますので、かなりの台数までまか なえるかもしれません。特に、実行先のホストにはなにも必要ない点は chef-soloの比べてアドバンテージと言えるでしょう。

ということで、実はいままではドキュメントを読んだことしか書いていません ですが、実際にplaybookを使ってみようと思います。

ということで、まて次号!

参考文献

Comments

comments powered by Disqus