Ansible

Ansible 入門

More than 3 years have passed since last update.


Ansible 入門

社内勉強会で Ansible の初歩的な発表をしたのでその資料を公開します。

間違い等あるとおもいますが、指摘して頂ければ幸いです。


Ansible とは?

構成管理ツールです。Chef とか Puppet とかああいうののお仲間です。

Puppet は知りませんが、Chef は少し使って嫌気が差して止めました。

理由は


  • 特殊なことをしようとすると Ruby の知識が必要になる

  • 構成管理「される側」にも Chef が必要(エージェント)

が主です。あとなんだかんだ言って結構複雑です。

その点 Ansible は下記の特徴があります。


  • 標準入出力さえできればプラグインの言語は何でも構わない

  • 一つ Ansible が入っているサーバと sshd が動いている対象サーバがあればそれでOK(要するに準備が要らない。もちろん自分自身だけでもOK)

  • 標準(組込みモジュール?)で結構なんでもできる(ただし、 Chef と比較して、ではない。Chef は詳しくないので)

構成管理ツールに必須の冪等性も確保されています。


Ansible の構成

Ansible は端的に言えば


  • hosts ファイル(インベントリ)

  • playbook

  • モジュール

の3つの要素で成り立ちます。


hosts ファイル

作業対象となるホストを記述します。

一般的には下記のように記述されます。


/etc/ansible/hosts

[lbserver]

192.168.1.1:1022
192.168.1.2:1022
[apserver]
192.168.2.1:1022
192.168.2.2:1022
[dbserver]
192.168.3.1:1022
192.168.3.2:1022

見ての通り ini 形式です。[セクション]でグループ化出来ます。このグループは後述する playbook で指定する際に使用しますが別に無くても構いません。

このファイルを/etc/ansible/hostsとして保存すると ansible 実行時に対象ホストが定まり、各ホストで ssh 経由で色々やってくれるということです。

※ hosts の場所は/etc/ansible/hostsじゃなくても構いません。環境変数や-iオプションで指定できたりします。

1022 はポート番号ですが、標準の 22 であれば指定はいりません。また、ssh_config も見てくれるので、ポートや鍵ファイルの指定は本来そちらで行うべきでしょう。


playbook

簡単にいえば後述のモジュールを組み合わせて、「このホストたちにはこのモジュールをこういうオプションで実行してよ」という命令書です。

yaml 形式で記述されます。


playbook例

- hosts: dbserver

remote_user: hoge
sudo: yes
gather_facts: no
vars:
port: 3306
socket: "/var/lib/mysql/mysql.sock"
datadir: "/var/lib/mysql"
handlers:
- name: restart mysqld
service: name=mysqld state=restarted
tasks:
- name: yum install mysql
yum: name="{{item.name}}" enablerepo="{{item.repo}}" state=present
with_items:
- {name: mysql, repo: remi}
- {name: mysql-server, repo: remi}
- {name: mysql-devel, repo: remi}
- name: template my.cnf
template: src=etc/my.j2 dest=/etc/my.cnf mode=0644
notify: restart mysqld

- name: permit bin files
file: path={{item}} mode=0744
with_fileglob:
- /root/bin/*

- name: register
shell: touch /tmp/hoge creates=/tmp/hoge
register: result
- name: print result
debug: msg="rc:{{result.rc}} stdout:{{result.stdout}} stdout:{{result.stderr}}"
when: result.stdout == ""


これは割といろいろ使ってる playbook です。

キー個別に説明します。


hosts

前述の hosts ファイルで指定したグループ名を指定します。

all で全サーバ対象になるようです。


remote_user

リモート側の実効ユーザを指定します。


sudo

sudo するなら yes を指定します。

上に記述はないですが sudo_user で sudo のユーザを指定することもできるようです。


gather_facts

ホスト情報を掻き集めるかのフラグです。

ホスト情報とはCPUのコア数や搭載メモリなどです。

これらは変数に代入されるので後述の template モジュールなどで威力を発揮します。

使用しない場合、無駄なので no にした方が実行速度が早くなります。


vars

変数を使用する場合はここで宣言します。

上の例のように宣言しておくと {{port}}${socket}$datadir などで参照することが出来ます。


handlers

例えば apache の設定ファイルをいじった場合に httpd を restart したい場合などがあると思います。

この handlers を指定すると、変更があった場合に指定タスクを実行してくれます。

通知は tasks の notify で行います。複数のタスクで notify され、かつそれら全てで変更があった場合でも handlers が実行されるのは1回限りです。


tasks

playbook のキモとなる項目です。実行するモジュールを列挙していきます。

name 指定すると実行時に出力が為されます。また、name は必須ではないようです。書かなかった場合、モジュール部分の文字列がそのまま出力されるようです。


with_items

タスク内で使用する変数を宣言します。

モジュールは共通で、オプションのみ異なる場合などに便利です。個人的に使用頻度は非常に高いです。

上の例で言うと mysql と mysql-server をともに remi リポジトリを使って yum install します。


with_fileglob

glob した結果を変数に代入し、その数分モジュールを実行します。

上の例で言うと /root/bin 以下にあるファイル全てのパーミッションを0744にします。


notify

handlers へ通知を行います。値には handlers の name キーを指定します。

上の例で言えば /etc/my.cnf に変更があった場合に mysqld を再起動します。


register

現在のモジュールの諸々の情報を指定した変数名へ代入します。

「諸々の情報」はモジュールによって異なるようです。

上記のように shell モジュールであれば rc, stdout, stderr が取れますし、まっとうな?モジュールであれば changedfailed が取得でき、下記 when と組み合わせることで「前回のタスク結果に依存した」処理が書けるようになります。

※ どんなモジュールがどんなキーを扱えるかはどこかにドキュメントがあるはず…


when

指定した条件が成り立つときのみタスクを実行します。

上記の例だと register タスクの標準出力が空のときのみ実行されるようになります。

自信ないですがおそらく初回実行時のみ実行されます。


changed_when

changed 結果を明示的に指定できます。

changed_when: False などとすると ステータスが changed になることがなくなります。


failed_when

基本的に changed_when と同じです。


モジュール

ansible 自体の主な仕事は、前述の playbook の解釈やホストへの接続、変数の展開などのメタ的な仕事であって、「このファイルをコピーしてくれ」とか「このサービスを起動してくれ」とかそういう具体的な実務は一切行いません。

それら実務はすべてプラグインとして提供・実装され、「モジュール」と呼ばれています。

代表的な(今回簡単に解説する)モジュールを下記に挙げます。

モジュール名
役目
冪等性

shell
シェル上でコマンドを実行
creates

file
ファイルやディレクトリの作成、パーミッション
自動

lineinfile
指定ファイルの行単位の書き換え
半自動

copy
ファイルの丸コピー
自動

template
テンプレートを利用したファイルのコピー
自動

synchronize
要するに rsync
自動

get_url
指定URLからダウンロード
自動

yum
yum コマンド。enablerepo やdisablerepo はもちろん rpm から直接インストールも可能
自動

service
service コマンド。chkconfig も兼ねる
自動

上記は全て標準です。

さらに前述のとおり、なにか凝ったことをしようと思ったときは自分であらゆる言語でモジュールを作成することが出来ます。

ただしモジュールはリモートに転送されて実行されます。

よって php で自前でモジュールを作成してもリモート側に php が入っていないと実行できません。

下記はよく使いそうなオプションのみ記載しています。

結構便利なオプションがあったりするので調べてみるといいです。

モジュールの詳細が知りたい場合はansible-doc モジュール名で調べることが出来ます。


shell

シェル上でコマンドを実行します。似たようなものに command モジュールがありますが、パイプやリダイレクトなどが使えるので基本的には shell の方がいいです。

# composer のインストール

shell: >
curl -sS https://getcomposer.org/installer | php && mv composer.phar composer
chdir=/usr/local/bin
creates=/usr/local/bin/composer

※ 見やすいように改行いれてます(folded スタイル)。一番上が実行コマンドでその他がオプションになります。

chdir オプションでカレントディレクトリを指定、

creates オプションでそのファイルが既にあるなら実行しないようになります。

なんでもできるので便利ですが、playbook が shell ばっかりになると単に手順書を複雑に書いているだけな気分になり、やる気が削がれます。気をつけましょう。

また、ansible は各タスクごとに プロセスを生成するようなので cd だけのタスクとかをして無駄です。chdir オプションを使います。


file

ファイルやディレクトリの作成・削除、パーミッションの設定などを行います。

# /var/log/php ディレクトリを作成して 0644 にする

file: path=/var/log/php mode=0644 state=directory owner=apache group=apache

# /tmp/hoge を削除

file: path=/tmp/hoge state=absent

path で対象ファイルを指定します。

mode でパーミッションを指定します。

state で対象の種類を指定します。ファイルだったりディレクトリだったり作成だったり削除だったり。

owner でオーナーを指定します。

group でグループを指定します。

ファイルの作成は基本的にやらないほうがいいです

state=touch を指定すれば作成されますが、毎回「changed」になるので精神衛生上良くありません。

changed_when: False で抑止できますが、これだと初回実行時も抑止されます。

そういうものだと割りきって shell モジュールで shell: touch /tmp/hoge creates=/tmp/hoge しちゃうのが良いんじゃないでしょうか。


lineinfile

ファイル内の特定の行を書き換えます。個人的に一番良く使います。

# selinux を無効にする

lineinfile: dest=/etc/sysconfig/selinux regexp="^SELINUX=.*" line="SELINUX=disabled"

# ssh のパスワード認証を禁止する

lineinfile: >
dest=/etc/ssh/sshd_config
regexp="^PasswordAuthentication"
line="PasswordAuthentication no"
insertafter="#PasswordAuthentication"

# php.ini に include_path を追加する

lineinfile: >
dest=/etc/php.ini
backrefs=yes
regexp='^include_path = "((?!.*add_path.*)(?(1)|.*))"'
line='include_path "\1:add_path"'

dest で対象ファイルを指定します。

regexp で対象行を指定します(マッチした行が置換される)。

line で置換する文字列自体を指定します。

insertafter でマッチしなかった場合にここで指定した正規表現のにマッチする次の行へ挿入されます。

backrefs でキャプチャ文字が使えるようになりますが、insertafter/before が使用できなくなります。

backrefs を使用して既存文字列に文字列を加えるような場合、上記のような小細工が必要です。

※ ただし、説明は省く


copy

ファイルをローカルからリモートへコピーします。

# php 設定ファイルを転送する

copy: src=etc/php.d/default.ini dest=/etc/php.d/default.ini mode=0644

src でローカルの対象ファイルパスを指定します。絶対パス、あるいは yaml ファイルからの相対パスです。

dest でリモートのファイルパスを指定します。

mode でパーミッションを指定します。


template

テンプレートファイルを元にして、変数置換したファイルをローカルからリモートへコピーします。

# nginx 設定ファイルを転送する

template: src=etc/nginx/nginx.j2 dest=/etc/nginx/nginx.conf mode=0644

src でローカルの対象ファイルパスを指定します。テンプレートエンジンは jinja という python のテンプレートです。

dest でリモートのファイルパスを指定します。

mode でパーミッションを指定します。

etc/nginx/nginx.j2 の冒頭は下記のようになっています。

user              nginx;

worker_processes {{ ansible_processor_vcpus }};
・・・

こういう環境依存のファイルを設置するときは template が便利です。

nginx のプロセス数は CPU のコア数を指定するのが望ましいですが、copy モジュールだと丸コピーなので細かな設定ができません。

copy したあと lineinfile してもいいですが、たくさんあると面倒です。

前述のとおり、変数は自分で宣言できますし、上記のように ansible が用意した変数を使うことも出来ます。ただし、上記の変数は gather_fact:yesしていないと使用できません。


synchronize

rsync 的なことをします。

オプションが大量にある上、各オプションが rsync のオプションに対応しているので例は省きます。

1点注意として、src オプションに相対パスを記述すると実効ユーザのホームディレクトリからの相対パスになるようです。

なんかとても微妙…。


get_url

指定 URL からファイルをダウンロードします。

# fluentd インストールスクリプトをダウンロードする

get_url: url=http://toolbelt.treasuredata.com/sh/install-redhat.sh dest=/root/fluentd-install.sh

url で URL を指定します。

dest で保存場所を指定します。

dest が既に存在する場合は実行されません。


yum

yum コマンドに対応します。

# yum で remi リポジトリから php5.5 をインストールする

yum: name=php state=present enablerepo=remi-php55

# yum で mysql をインストールする

yum: name=http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm state=present
yum: name=mysql-community-server state=present
yum: name=mysql-devel state=present

name でパッケージ名を指定します。

state でどういう状態にするかを指定します。[present|latest|absent] = [install|update|erase]

enablerepo で有効にするリポジトリを指定します。

disablerepo で無効にするリポジトリを指定します。


service

service コマンドに対応します。

# httpd サービスを起動して、自動起動をオンにする

service: name=httpd state=started enabled=yes

# ip6tables サービスを停止して、自動起動をオフにする

service: name=ip6tables state=stopped enabled=no

name でサービス名を指定します。

state でどういう状態にするかを指定します。[started|stopped|restarted|reloaded] = [start|stop|restart|reload]

enabled で自動起動の ON/OFF を指定します(=chkconfig)。

graceful がない!


make install

以下、ソースファイルを download してきて解凍して make して make install する playbook です。

※ そういうモジュールがあるかと思ったけどなかった

    - name: download hoge source file

get_url: url=https://hostname/hoge_source.tar.gz dest=/usr/local/src/hoge_source.tar.gz

- name: extract hoge source file
unarchive: src=/usr/local/src/hoge_source.tar.gz dest=/usr/local/src/ creates=/usr/local/src/hoge_source

- name: install hoge
shell: ./configure && make && make install chdir=/usr/local/src/hoge_source creates=/usr/bin/hoge

冪等性がいい感じに効いてくれるので2回目以降はすべて skip されます。

※ ただし creates=/usr/bin/hoge だけは注意。インストールした結果、何かが出来上がるならそれを指定する


role

依存関係とかいろいろ設定できる role ってのがあるらしいがまだ触ってません。