最近なにかと見かけることの多くなった Ansible。色々と動きを調べてみた。
とはいっても、本家に良ドキュメントもあるし、日本語のリソースもあちこちで見かけるようになったので、ここでは「シェルスクリプトの代わりに使ってみたら」という切り口で調べたことをまとめてみる。
バージョンに関して
動作確認には、CentOS 6.5 を使用した。Ansible は、この文章を書いている時点(2014年9月3日)で CentOS 6.5 向けにパッケージ提供されている最新バージョン “1.7.1.el6” を導入して検証した。
1. Ansibleって何?
Ansibleは構成管理ツール(CMS)とかデプロイツールとか色々な呼び方がされているけれど、ここでは即物的に次のようなツールだと捉えている。
- ソフトウェアのインストールや設定などのマシン操作手順を
- スクリプト化 して
- 多数のマシン に
- 一括で流し込める ツール
シンプルさを大切にしており、わずかな準備で即利用でき、その一方で適用範囲はかなり広い汎用ツールだと思う。
その強力さは、私の感覚的にはBashやAwkに近い。
1.1. 適用例
以下は本家で挙げられている例。もっと色々な使い方もできるかも?
- 構成管理
マシンとソフトウェアの対応を、グループやロールで、一覧形式にわかりやすく管理 - ローリングアップデート
多数のマシンのソフトウェアを、例えばマシン10台づつ、順次アップデートしていく - ソフトウェア配布の自動化
継続的インテグレーションでの環境構築、テスト、ステージング、本番投入まで一本化 - Orchestration
AWS上の仮想リソースから、サービスに必要なマシン構成・インベントリを自動管理 - Dockerの構築・配布
軽量コンテナ Docker のイメージ構築から配布管理まで、簡単にスクリプト化
1.2. 人気度 - Googleトレンド
Googleトレンドを見ると、2012年春頃から、順調に検索数が伸びている。
個人的には、今後、「クラウド時代のシェルスクリプト」と呼べるような、システムエンジニアの必須スキルになっていくのかな、という気もする。
1.3. 開発モデル、ビジネスモデル
開発モデルとして GitHubでのOSS開発を行っており、だれでも無料で利用できる。現在のGitHubのライセンス表示はGPLv3。
ビジネスモデルとして、UIツールやコンサルを有償で提供している。
2. 事前準備
2.1. インストール
とっても簡単。
制御マシン(マシン管理サーバ)にだけ、Ansibleをインストールする。それで終わり。
ターゲットマシンにはAnsibleのインストールが不要。
例えば Cent OS 系なら、EPELにrpmパッケージがあるから、yum で持ってくればよい。
こんなかんじ。
# rpm -ivh http://dl.fedoraproject.org/pub/epel/6/i386/epel-release-6-8.noarch.rpm
# yum install ansible
試しに、CentOS 6.5 を Basic Server タイプでインストールし、上記コマンドを実行したら、依存関係含めて 9個のパッケージがインストールされた。
# yum install ansible
... 途中省略 ...
Dependencies Resolved
================================================================================
Package Arch Version Repository Size
================================================================================
Installing:
ansible noarch 1.7-1.el6 epel 874 k
Installing for dependencies:
PyYAML x86_64 3.10-3.el6 epel 157 k
libyaml x86_64 0.1.6-1.el6 epel 52 k
python-babel noarch 0.9.4-5.1.el6 base 1.4 M
python-crypto2.6 x86_64 2.6.1-1.el6 epel 530 k
python-httplib2 noarch 0.7.7-1.el6 epel 70 k
python-jinja2 x86_64 2.2.1-2.el6_5 updates 466 k
python-keyczar noarch 0.71c-1.el6 epel 219 k
python-pyasn1 noarch 0.0.12a-1.el6 base 70 k
Transaction Summary
================================================================================
Install 9 Package(s)
Total download size: 3.8 M
Installed size: 15 M
Is this ok [y/N]: y
見ての通り、 本体は874kbと小さい!
Support環境
Ansibleのドキュメント Installation に情報がある。表にするとこんな感じに。
制御マシン(管理サーバ) | ターゲットマシン | |
---|---|---|
対応OS | Red Hat, Debian, CentOS, OSX, BSD(Windowsは不可) | 基本なんでもOK |
必須PKG | Python2.6 | Python2.4以降 |
Pythonベースのツールなので、制御マシン(管理サーバ)とターゲットマシンの両方にPythonが必要。
裏を返すと Pythonされあれば良い という話でもある。
細かな注意点
- ターゲットマシンのPythonが2.4の場合、追加で
python-simplejson
も必要。Ansibleのrawモードを使って、Ansible自身で最初にpython-simple.json
を送り込めば良い。 - ターゲットマシンでSELinuxを有効にしている場合、一部機能(ディレクトリ作成などのファイル系機能)を使うのに
libselinux-python
も必要。それら機能を利用する前に、Ansibleを使いインストールすればよい。 - 現時点で Python3.x には未対応。
2.2. SSHログインの設定
Ansibleでの制御マシンとターゲットマシンの間の通信は、基本、SSH(pluggableな仕組みで切り替えも可)。
Ansibleでターゲットマシンにログインする度にパスワード入力しないで済むよう、公開鍵認証を設定しておく(代わりにパスワード入力を促す
--ask-pass
オプションも利用可)。
例えば、制御マシン controller 上の ansibleman というユーザから、ターゲットマシン 192.168.170.64 の root ユーザに直ログインできるよう、公開鍵を設定してみる。
最初に鍵を生成。今回はパスフレーズを省略。プロンプトに対して改行で先に進む。
[ansibleman@controller ~]$ ssh-keygen -t rsa
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ansibleman/.ssh/id_rsa):
Created directory '/home/ansibleman/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/ansibleman/.ssh/id_rsa.
Your public key has been saved in /home/ansibleman/.ssh/id_rsa.pub.
The key fingerprint is:
... 途中省略 ...
ターゲットマシンのrootユーザに公開鍵をコピー。この1回だけは、rootのログインパスワードで認証してログインする。
[ansibleman@controller ~]$ ssh-copy-id root@192.168.170.64
The authenticity of host '192.168.170.64 (192.168.170.64)' can't be established.
RSA key fingerprint is xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.170.64' (RSA) to the list of known hosts.
root@192.168.170.64's password:
Now try logging into the machine, with "ssh 'root@192.168.170.64'", and check in:
.ssh/authorized_keys
to make sure we haven't added extra keys that you weren't expecting.
パスワードなしでログインできることを確認。
[ansibleman@controller ~]$ ssh root@192.168.170.64 whoami
root
OpenSSH vs. Paramiko
Ansibleは、内部にSSHクライアントのPython実装 Paramikoを抱えており、OS環境にSSHが無くても動作できる。
しかし、もし最近のOpenSSHを利用できる環境なら、Ansibleは自動的にOpenSSHを利用し、より高速動作する。
例えば、最近のOpenSSHでは ControlPersist 機能があり、同一経路のSSHコネクションを束ねたり、コネクションを張りっぱなしにするなど可能。大規模環境では効果大の模様。
ここでは制御マシンに CentOS 6.5 を想定しているので、OpenSSHのバージョンは 5.3p1 と古く(この記事を書いている時点の最新は6.6)、AnsibleのSSH通信にはParamikoが使われている。
Root権限について
Ansible自体の動作に、Root権限は不要。
これからやろうとしているタスクに、ターゲットマシン上のRoot権限が不要なら、ターゲットマシンの一般ユーザに公開鍵を設定すれば良い。そういう意味でも気楽に使える。
また、ターゲットマシン上の一般ユーザに sudo 設定していれば、Ansible で必要なときだけRoot権限に昇格することは簡単にできる。リモートコマンド実行時にsudoを指示できるし、sudoパスワードを入力するオプション
--ask-sudo-pass
もある。2.3. Inventory ファイル
Ansibleでは、ターゲットマシンを列挙した Inventory というデータベースだけは、事前に準備しなければならない。
といっても、お気楽に使う分には簡単なテキストファイルひとつでOK。
例えば、ターゲットマシン 192.168.170.64 に root でSSHログインしたいなら、Inventoryファイルは1行だけでOK。
[ansibleman@controller ~]$ cat inventory
192.168.170.64 ansible_ssh_user=root
ansible_ssh_user
は省略可能なパラメータ。Inventoryファイルに書く代わりに、Ansibleのコマンド実行時に、引数 “-u root” で渡しても良い。
もう一台ターゲットマシン 192.168.170.65 があり、こちらのマシンには testuser1 でログインするなら、Inventoryファイルは2行でOK。
[ansibleman@controller ~]$ cat inventory
192.168.170.64 ansible_ssh_user=root
192.168.170.65 ansible_ssh_user=testuser1
Inventoryファイルの Parameter 一覧
Inventoryに書けるパラメータは、
ansible_ssh_user
以外にも色々ある。
例えば、localhost に対して、SSHを使わずAnsibleでコマンド実行したいなら、
connection=local
を渡す。SSHを経由しないので公開鍵の設定も不要だし、AnsibleはSSHの設定を見に行かない。[ansibleman@controller ~]$ cat inventory
localhost ansible_connection=local
高度なInventory - Group定義など
Inventoryファイルでは、ターゲットマシンのグループを定義して、名前をつけられる。
Ansibleのグループでは、グループ間に包含関係をもたせたり、一つのマシンを複数グループに所属させる定義も書ける。また、マシンやグループに対して変数を定義し、その値をリモートコマンドに渡すことができる。
もし、外部DBに既にマシン一覧を持っているなら、新たにInventoryファイルを作成しなくても、その外部DBを直接参照することができる。
3. 使ってみる
前置きは以上なので、早速、使ってみる。
3.1. SSHリモートコマンドの代わりに使ってみる
以下のSSHリモートコマンドを、Ansibleでやってみる。
[ansibleman@controller ~]$ ssh root@192.168.170.64 date
Tue Sep 2 04:09:19 JST 2014
こんな感じになる。
[ansibleman@controller ~]$ ansible -i ./inventory all -m command -a date
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:15:53 JST 2014
コマンドラインオプションの説明
オプション | 説明 | 省略時 |
---|---|---|
-i ./inventory | Inventoryファイルを指定 | /etc/ansible/hostsを読む |
all | Inventoryからターゲットマシンを抽出する host-pattern を指定:”all” は全マシンの意味 | (省略不可) |
-m command | モジュールを指定 | -m command |
-a date | モジュールへの引数 | 引数なし |
Ansibleは
-i
で指定しなければ、Inventoryファイルとして/etc/ansible/hosts を参照する。/etc/ansible/hosts に 192.168.170.64 ansible_ssh_user=root
の1行が書いてあれば、-i
を省略してもよかった。[ansibleman@controller ~]$ cat /etc/ansible/hosts
192.168.170.64 ansible_ssh_user=root
[ansibleman@controller ~]$ ansible all -m command -a date
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:23:10 JST 2014
しかし、普通は /etc/ansible/hosts に書き込むにはroot権限が必要なので、敷居は少し高いかもしれない。
host-pattern の説明
host-pattern は、Inventoryファイルからターゲットマシンを選別するためのサーチキーワードの役割を果たす。
今回は、Inventoryファイルにマシン1台(192.168.170.64)しかないなら、allと書いても、192.168.170.64 と書いても、192.168.170.* と書いても同じ結果となる。
[ansibleman@controller ~]$ ansible 192.168.170.64 -a date
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:30:31 JST 2014
[ansibleman@controller ~]$ ansible 192.168.170.* -a date
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:30:42 JST 2014
[ansibleman@controller ~]$ ansible *64 -a date
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:30:57 JST 2014
host-pattern で選ばれるターゲットマシンが意図したものになっているかは、実際にターゲットマシン上でコマンドを実行せずとも確認できる。
[ansibleman@controller ~]$ ansible all --list-hosts
192.168.170.64
host-pattern は、ansibleのコマンドラインオプション
--limit
でも指定でき、2重にターゲットマシンを絞り込むことができる。
host-pattern は、globみたいなワイルドカード “*” だけでなく、正規表現や、簡単な集合演算みたいな、ターゲットマシンの高度な選別もできる。
詳しい説明はこちら。
host-pattern は、柔軟にマシン選別できるので、わりとゆるい感じでInventoryを作っても運用に困らないかもしれない。運用より、論理的なグループ分けやホスト名の命名の方が大切かもしれない。DBかWebか、どのデータセンタにあるのか等。
本番サイトとテストサイトみたいに、絶対に取り違えてはいけないようなターゲットマシンの選別には、host-pattern を使うのではなく、Inventory自体を分ける方がBest Practiceとのこと。例えば、こんな感じに。
[ansibleman@controller ~]$ ansible -i ./test-site webservers -a start-app1
[ansibleman@controller ~]$ ansible -i ./production-site webservers -a start-app1
Ansibleモジュール: -m commandなど
Ansibleモジュールは、プラグインの形になっており、Ansibleと外界であるOS環境をつなぐ ”糊の役目” を果たす。
例えば、commandモジュールでは、内部でPythonのsubprocess.Popen を利用し、ターゲットマシン上の外部コマンドを1つ fork&exec する。この機能のおかげで、Ansibleをリモートシェル(RSHやSSH)として利用することができる。
予め組み込まれているAnsibleモジュールは、Linuxでは普通 /usr/share/ansible に格納されている。場所は設定ファイル /etc/ansible/ansible.cfg の library に指定がある。
大概の事はcommandモジュールだけでもできてしまうけれど、典型的な作業については250個近い組み込みモジュールが用意されているので、そちらを利用したほうがよい。そうすることで、異常系が簡単になり本来やりたいことに集中できるし、移植性や冪等性(何度実行しても同じ結果となる)もよくなる。
例えば、こんな作業には組み込みのAnsibleモジュールが用意されている。
- 制御マシンとターゲットマシン間でファイルをコピー:scpやrsyncのように
- ターゲットマシンで設定ファイルを編集:sedやシェルのhere documentのように
- ターゲットマシンでCron設定
- ターゲットマシンでサービスの起動設定:serviceコマンドのように
- メールを送る:sendmailのように
- パッケージのインストール: apt, yum, pip, gem など
- リモートレポジトリからファイル取得: gitやsubversionなど
- ファイルシステムの作成やマウント
一方、自分で作った独自サービスのAPIを、Ansibleから直接制御したいのであれば、比較的簡単に、新たなAnsibleモジュールを書き下すこともできる。
モジュール指定のない場合は、command モジュールが既定値となっている。Ansibleを単にリモートコマンドとして使いたいだけなら、モジュール指定は省略してよい。
[ansibleman@controller ~]$ ansible all -a date
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:23:10 JST 2014
モジュールのより詳しい説明はこちらから。
Output
AnsibleコマンドのOutputは、単なるSSHと比べると若干情報が多い。その理由は、元々、多数マシン上での同時実行を前提としているため。
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:23:10 JST 2014
一行目は、a. Ansibleが処理を実行したマシン(host-patternで選ばれたマシンの内の1台)、b. モジュールの実行成否、c. Return Code(0: 正常)。
二行目以降が、commandモジュールが実行したコマンドの出力。
結果はjson形式でも得ることもできる。
-t <directory>
で出力ディレクトリを指定してこんな感じに。[ansibleman@controller ~]$ ansible -i ./inventory all -a date -t output
192.168.170.64 | success | rc=0 >>
Fri Sep 5 23:08:29 JST 2014
生成されたjsonファイルはこちら。
[ansibleman@controller ~]$ cat output/192.168.170.64
{
"changed": true,
"cmd": [
"date"
],
"delta": "0:00:00.002909",
"end": "2014-09-05 23:08:29.950988",
"rc": 0,
"start": "2014-09-05 23:08:29.948079",
"stderr": "",
"stdout": "Fri Sep 5 23:08:29 JST 2014"
}
デフォルトでは、syslogにも出力がある。
Sep 5 23:08:29 controller ansible-command: Invoked with executable=None shell=False args=date removes=None creates=None chdir=None
3.2. Behind the scene: 裏で何をやっているの?
Ansibleの引数に -v を3つ付けると、裏でやっていることを観察できる。
導通確認にも便利かも?
導通確認にも便利かも?
[ansibleman@controller ~]$ ansible -i ./inventory all -a date -vvv
<192.168.170.64> ESTABLISH CONNECTION FOR USER: root on PORT 22 TO 192.168.170.64
<192.168.170.64> REMOTE_MODULE command date
<192.168.170.64> EXEC /bin/sh -c 'mkdir -p $HOME/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732 && chmod a+rx $HOME/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732 && echo $HOME/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732'
<192.168.170.64> PUT /tmp/tmpJXAAw2 TO /root/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732/command
<192.168.170.64> EXEC /bin/sh -c 'LANG=C LC_CTYPE=C /usr/bin/python /root/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732/command; rm -rf /root/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732/ >/dev/null 2>&1'
192.168.170.64 | success | rc=0 >>
Tue Sep 2 04:37:05 JST 2014
上のログを詳しく見ると、ターゲットマシン192.168.170.64にSSHでログイン後、作業ディレクトリを準備(mkdir, chmod)し、設定ファイルのremote_tmpなどから生成したパス
$HOME/.ansible/tmp/ansible-tmp-1409600225.49-71777331256732/command
へ Python スクリプトを送り込み(PUT)、実行している様子(LANG=C LC_CTYPE=C /usr/bin/python)が見えている。
送り込んでいるスクリプトは、-m で指定したモジュール
command
をテンプレートとして生成している。テンプレートの実体は、制御マシンの/usr/share/ansible/commands/command
にある。ansible -i ./inventory all -a "sleep 60"
などとして、Ansibleの実行中にターゲットマシンの中を覗くと、さらに色々と詳しい動きを見ることも可能。
Ansibleは、このように「ターゲットマシンにAnsibleのインストールも不要、Pythonさえあれば良い」という構造を作り出している。
3.3. scpの代わりに使ってみる
ファイル配布
「scpで制御マシンからターゲットマシンにファイルをコピーする」という処理を単純に行うと、こんな感じになる。
[ansibleman@controller ~]$ echo hello > testdata.txt
[ansibleman@controller ~]$ scp testdata.txt root@192.168.170.64:
testdata.txt 100% 6 0.0KB/s 00:00
一方、Ansibleでやると、こんな感じに。
-m copy
でcopyモジュールを利用している。[ansibleman@controller ~]$ echo hello > testdata.txt
[ansibleman@controller ~]$ ansible -i ./inventory all -m copy -a "src=testdata.txt dest=/root/testdata.txt"
192.168.170.64 | success >> {
"changed": true,
"dest": "/root/testdata.txt",
"gid": 0,
"group": "root",
"md5sum": "b1946ac92492d2347c6235b4d2611184",
"mode": "0644",
"owner": "root",
"secontext": "system_u:object_r:admin_home_t:s0",
"size": 6,
"src": "/root/.ansible/tmp/ansible-tmp-1410014817.94-232935578383127/source",
"state": "file",
"uid": 0
}
“changed”: や “md5sum”: が示唆する通り、同じファイルを複数回コピーすると、2回目以降は実際のコピーは発生しない。しかるに、不運にもコピーの最中にターゲットマシンがフリーズ/クラッシュした場合もコピー先ファイルの中身は壊れにくく、安全性が高い。
コピー先に既にファイルがあれば上書きしないというオプション(force=no)もある。
Ansibleの処理を、大体等価なシェルで書き表すと以下の様になる。
[ansibleman@controller ~]$ scp testdata.txt root@192.168.170.64:tmpfile
testdata.txt 100% 6 0.0KB/s 00:00
[ansibleman@controller ~]$ scp copy.sh root@192.168.170.64:
copy.sh 100% 1012 1.0KB/s 00:00
[ansibleman@controller ~]$ ssh root@192.168.170.64 ./copy.sh tmpfile testdata.txt
"changed": true
"dest": testdata.txt
"md5sum": b1946ac92492d2347c6235b4d2611184
"src": tmpfile
[ansibleman@controller ~]$ ssh root@192.168.170.64 rm -f tmpfile copy.sh
ここでのシェルスクリプト copy.sh が、Ansibleのcopyモジュールに対応しており、次のような処理をしている(実際はPythonスクリプト)。
#!/bin/bash
# Usage: copy.sh <srcfile> <destfile> [force]
# srcfile must be copied from the remote side to the same partition of destfile.
srcfile="$1"
destfile="$2"
force="${3:-yes}"
[ -n "$srcfile" -a -n "$destfile" ] || { echo "missing args"; exit 1; }
[ -r "$srcfile" ] || { echo "missing srcfile"; exit 1; }
md5sum_src=`md5sum ${srcfile}|awk '{print $1}'`
md5sum_dest=""
creating="yes"
if [ -r "$destfile" ]; then
if [ "$force" != "yes" ]; then
echo "dest file already exists"
exit
fi
md5sum_dest=`md5sum ${destfile}|awk '{print $1}'`
creating="no"
fi
if [ "$md5sum_src" != "$md5sum_dest" ]; then
# Need to update the destfile
if [ -e "$destfile" ]; then
chmod --reference "$destfile" "$srcfile"
chown --reference "$destfile" "$srcfile"
fi
mv -f "$srcfile" "$destfile"
if [ "$creating" = "yes" ]; then
chmod 0666 "$destfile"
fi
changed="true"
else
changed="false"
fi
echo '"changed": '${changed}
echo '"dest": '${destfile}
echo '"md5sum": '${md5sum_src}
echo '"src": '${srcfile}
Copyモジュールには、その他にも、Permission(mode)や所有者(owner)を一緒に設定する、事前にバックアップファイルを作る(backup=yes)、コピー先ファイルを更新する前に任意のValidationコマンドを流して文法チェック等行う、ディレクトリ全体をコピーするなど、色々と便利な機能が揃っている。
ちょっと変わったところでは、Here documentのようにファイル内容を埋め込んで、ターゲットマシンに直接ファイルを作成できる。
[ansibleman@controller ~]$ ansible -i ./inventory all -m copy -a 'content="hello" dest=/root/testdata.txt'
ファイル収集
先ほどとは逆向きに、ターゲットマシン上のファイルを制御マシンに転送、収集する。
例えば…
例えば…
[ansibleman@controller ~]$ scp root@192.168.170.64:testdata.txt collectedfile.txt
testdata.txt 100% 12 0.0KB/s 00:00
[ansibleman@controller ~]$ cat collectedfile.txt
hello world!
同じことをAnsibleでやると、こんな感じに。fetch モジュールを利用している。
[ansibleman@controller ~]$ ansible -i ./inventory all -m fetch -a 'src=./testdata.txt dest=newfile.txt flat=yes fail_on_missing=yes'
192.168.170.64 | success >> {
"changed": true,
"dest": "/home/ansibleman/newfile.txt",
"md5sum": "b1946ac92492d2347c6235b4d2611184",
"remote_md5sum": "b1946ac92492d2347c6235b4d2611184"
}
fetchモジュールは元々はログファイルの収集向けに開発されたそうで、ターゲットマシン上にファイルがなくてコピーに失敗しても、既定ではエラーとならない。そのため、
fail_on_missing=yes
を指定している。
Ansibleは、同じ処理を多数のターゲットマシン上で並列動作させる想定なので、ファイルを制御マシンに集める時も
収集ディレクトリ(dest)/ターゲットマシン名/ファイルパス(src)
という形で、整理して格納する。今回は1つのファイルしか収集しなかったので flat=yes
により、パスをdestで直接指定するようにした。
本来の姿では、こんな風になる。この例はちょっと縁起が悪いかも知れない。
[ansibleman@controller ~]$ cat inventory
192.168.170.64 ansible_ssh_user=root
192.168.170.65 ansible_ssh_user=root
192.168.170.66 ansible_ssh_user=root
192.168.170.67 ansible_ssh_user=root
192.168.170.68 ansible_ssh_user=root
192.168.170.69 ansible_ssh_user=root
[ansibleman@controller ~]$ ansible -i ./inventory all -m fetch -a 'src=./tombstone.txt dest=graveyard fail_on_missing=yes'
192.168.170.68 | success >> {
"changed": true,
"dest": "/home/ansibleman/graveyard/192.168.170.68/./tombstone.txt",
"md5sum": "b1946ac92492d2347c6235b4d2611184",
"remote_md5sum": "b1946ac92492d2347c6235b4d2611184"
}
192.168.170.64 | success >> {
"changed": true,
"dest": "/home/ansibleman/graveyard/192.168.170.64/./tombstone.txt",
"md5sum": "b1946ac92492d2347c6235b4d2611184",
"remote_md5sum": "b1946ac92492d2347c6235b4d2611184"
}
... 途中省略 ...
192.168.170.69 | success >> {
"changed": true,
"dest": "/home/ansibleman/graveyard/192.168.170.69/./tombstone.txt",
"md5sum": "b1946ac92492d2347c6235b4d2611184",
"remote_md5sum": "b1946ac92492d2347c6235b4d2611184"
}
fetchでは、パーミッションや所有者は保存されない。こんな感じにansibleを実行した制御マシンのユーザで、デフォルトのパーミッションとなる。
[ansibleman@controller ~]$ find graveyard/ -type f -printf "%p %m %u:%g\n"
graveyard/192.168.170.65/tombstone.txt 664 ansibleman:ansibleman
graveyard/192.168.170.68/tombstone.txt 664 ansibleman:ansibleman
graveyard/192.168.170.67/tombstone.txt 664 ansibleman:ansibleman
graveyard/192.168.170.66/tombstone.txt 664 ansibleman:ansibleman
graveyard/192.168.170.69/tombstone.txt 664 ansibleman:ansibleman
graveyard/192.168.170.64/tombstone.txt 664 ansibleman:ansibleman
スクリプトのリモート実行
ターゲットマシンへファイルコピーする理由が、手元のスクリプトのリモート実行なら、scriptモジュールを利用することができる。
例えば以下のスクリプトをターゲットマシンに送り込んで実行してみる。
[ansibleman@controller ~]$ cat sample-script.sh
#!/bin/bash
echo "hello! I'm `whoami`"
echo "watch out!" >&2
exit 2
シェルでやると、こんな感じ。
[ansibleman@controller ~]$ scp sample-script.sh root@192.168.170.64:
sample-script.sh 100% 68 0.1KB/s 00:00
[ansibleman@controller ~]$ ssh root@192.168.170.64 ./sample-script.sh
hello! I'm root
watch out!
[ansibleman@controller ~]$ echo $?
2
[ansibleman@controller ~]$ ssh root@192.168.170.64 rm ./sample-script.sh
Ansibleで書くと、こんな感じに。
[ansibleman@controller ~]$ ansible -i ./inventory all -m script -a "./sample-script.sh"
192.168.170.64 | FAILED >> {
"changed": true,
"rc": 2,
"stderr": "watch out!\n",
"stdout": "hello! I'm root\n"
}
[ansibleman@controller ~]$ echo $?
2
scriptの異常終了(exit 2)も、ちゃんと呼び元に伝わっている。
異常終了させたので、ステータスは”FAILED” になっている。
異常終了させたので、ステータスは”FAILED” になっている。
rsyncによる多数ファイルの高速転送 - synchronize
copyモジュールは、多数のファイルの転送では、あまり効率がよくない。
主な理由は、ファイルのコピーを1つずつ個別に繰り返すからで、ネットワーク経由でデータを転送している時間(RTT)と、ターゲットマシンでファイルを構築している時間が交互に発生し、片方を実行中にもう片方のリソースが空いてしまうから。ファイルが多数あるなら、本来は工場の組み立てラインの様に、データ転送とファイル構築を流れ作業で同時に動かすことができ、全体のコピー時間を短くできる(Pipelining)。
この辺に気を配っているのが rsync で、ターゲットマシン上に rsync があれば、Ansible からも気軽に利用できる。
SSHを使った以下のコマンドを…
[ansibleman@controller ~]$ rsync --rsh ssh -az --delete /etc/ansible root@192.168.170.64:
ansibleでやるとこんな感じに。rsyncのラッパーとして用意されている synchronizeモジュールを利用。
[ansibleman@controller ~]$ ansible -i ./inventory all -m synchronize -a "src=/etc/ansible dest=./ delete=yes"
192.168.170.64 | success >> {
"changed": true,
"cmd": "rsync --delay-updates -FF --compress --delete-after --archive --rsh 'ssh -o StrictHostKeyChecking=no' --out-format='<<CHANGED>>%i %n%L' \"/etc/ansible\" \"root@192.168.170.64:./\"",
"msg": "cd+++++++++ ansible/\n<f+++++++++ ansible/ansible.cfg\n",
"rc": 0,
"stdout_lines": [
"cd+++++++++ ansible/",
"<f+++++++++ ansible/ansible.cfg"
]
}
一見すると、commandモジュールからrsyncコマンドを直接コールする場合と比べて、何が良いか分かりにくい。しかし、synchronizeを使えば自動でrsyncの引数にターゲットマシンを渡してくれたり、”—delay-updates” など安全性を高めるオプションを指定してくれる。”src” や “dest” といった引数も他のモジュールと統一され、覚えやすい。こういう配慮ができるのも、Ansibleモジュールを書く一つの理由、良さかもしれない。
fetchのようにターゲットマシンから制御サーバにファイルを転送する場合(=逆方向)、
mode=pull
を渡す。[ansibleman@controller ~]$ ansible -i ./inventory all -m synchronize -a "src=/etc/ansible dest=./ mode=pull"
192.168.170.64 | success >> {
"changed": true,
"cmd": "rsync --delay-updates -FF --compress --archive --rsh 'ssh -o StrictHostKeyChecking=no' --out-format='<<CHANGED>>%i %n%L' \"root@192.168.170.64:/etc/ansible\" \"./\"",
"msg": "cd+++++++++ ansible/\n>f+++++++++ ansible/ansible.cfg\n",
"rc": 0,
"stdout_lines": [
"cd+++++++++ ansible/",
">f+++++++++ ansible/ansible.cfg"
]
}
ただし、fetchのようにファイルをマシン名で整理して格納する訳ではないので、多数のターゲットマシンからpullするときには注意が必要。synchronizeのpullモードは、Ansibleのスクリプト(Playbook)で利用する方が良いかもしれない。
つづく。
次回:並列実行の動作制御、他
1 コメント:
Ansibleとシェルの対比1 >>>>> Download Now
Reply>>>>> Download Full
Ansibleとシェルの対比1 >>>>> Download LINK
>>>>> Download Now
Ansibleとシェルの対比1 >>>>> Download Full
>>>>> Download LINK
コメントを投稿