Raspberry Piが増えてきたのでAnsibleを使ってみる
Ansibleを使ってみたくなったので作業記録を残します。
asa-taka@tailmoon pi-ansible % sw_vers
ProductName: macOS
ProductVersion: 11.3.1
BuildVersion: 20E241
asa-taka@tailmoon pi-ansible % ansible --version
ansible [core 2.11.0]
config file = /Users/asa-taka/src/local/pi-ansible/ansible.cfg
configured module search path = ['/Users/asa-taka/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/4.0.0/libexec/lib/python3.9/site-packages/ansible
ansible collection location = /Users/asa-taka/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.9.5 (default, May 4 2021, 03:36:27) [Clang 12.0.0 (clang-1200.0.32.29)]
jinja version = 3.0.1
libyaml = True
以下のまとめにはinventory(対象ホスト)をファイルで定義している場合とそうでない場合が混じっているので注意。完全にアドホックに実行したい場合はinventoryをオプションで指定し、その中のallという指定の仕方をする(allでなくても可)。
ansible --inventory host1,host2 all -m ping
注意点としてホストが1台の場合は --inventory host1,
のように末尾に ,
をつけなければinventoryの設定ファイルのパスだと解釈されてしまう。
ansible.cfgで設定したhostsでinventoryを設定している場合は
ansible host1 -m ping
で実行できる。
とりあえずAnsibleをインストールして使ってみる。ここではplaybookは書かず、ちょっとした用途で前準備なく実行したい場合に向けて、Ansibleを単なるssh runnerとして使ってみたい。コマンド一斉実行、同時再起動などが行えれば十分に役に立つと思う。
Homebrew でインストールする。
asa-taka@tailmoon ~ % brew install ansible
asa-taka@tailmoon ~ % ansible --version
ansible [core 2.11.0]
config file = None
configured module search path = ['/Users/asa-taka/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
ansible python module location = /usr/local/Cellar/ansible/4.0.0/libexec/lib/python3.9/site-packages/ansible
ansible collection location = /Users/asa-taka/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/local/bin/ansible
python version = 3.9.5 (default, May 4 2021, 03:36:27) [Clang 12.0.0 (clang-1200.0.32.29)]
jinja version = 3.0.1
libyaml = True
とりあえず動作確認をする。--inventory
でホストのリストを渡し、全てに対して応答確認する。
asa-taka@tailmoon ~ % ansible --inventory 'pi4a,pi4b,pi4c,pi4d' all -m ping
pi4a | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
pi4b | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
...
pi4a
は ~/.ssh/config
で pi4a.local
にパスフレーズ付き鍵でログインするように設定してあり、パスフレーズは事前に ssh-add
してある。つまり事前に ssh pi4a
で pi4a.local
にログインできるようにしてある状態。
単一ホストを指定する場合は --inventory pi4a,
のように ,
を末尾につける。--inventory
はファイル指定にも対応しているので ,
が無いとファイルを探そうとして失敗する。
複数台再起動やシャットダウンをさせてみる。
https://docs.ansible.com/ansible/latest/collections/ansible/builtin/reboot_module.html を利用してみたかったが、playbook を書かずに実行する方法がわからず、今回は意地でもplaybookを使いたくないという謎のモチベーションがあったので諦めた。
代わりに https://www.jeffgeerling.com/blog/2018/reboot-and-wait-reboot-complete-ansible-playbook で紹介されている方法を利用して再起動を実施した。
ansible -i 'pi4a,pi4b,pi4c,pi4d' all --become --background 1 --poll 0 -m shell -a "sleep 3 && reboot"
ansible コマンドを詳しく見る。
ansible [-h] [--version] [-v] [-b] [--become-method BECOME_METHOD]
[--become-user BECOME_USER] [-K] [-i INVENTORY] [--list-hosts] [-l SUBSET] [-P POLL_INTERVAL] [-B SECONDS] [-o] [-t TREE] [-k]
[--private-key PRIVATE_KEY_FILE] [-u REMOTE_USER] [-c CONNECTION] [-T TIMEOUT] [--ssh-common-args SSH_COMMON_ARGS]
[--sftp-extra-args SFTP_EXTRA_ARGS] [--scp-extra-args SCP_EXTRA_ARGS] [--ssh-extra-args SSH_EXTRA_ARGS] [-C] [--syntax-check]
[-D] [-e EXTRA_VARS] [--vault-id VAULT_IDS] [--ask-vault-password | --vault-password-file VAULT_PASSWORD_FILES] [-f FORKS]
[-M MODULE_PATH] [--playbook-dir BASEDIR] [--task-timeout TASK_TIMEOUT] [-a MODULE_ARGS] [-m MODULE_NAME]
pattern
で、pattern には all
などが入るので、基本形はこれになる。
ansible --inventory 'pi4a,pi4b' all
ただこれだと ERROR! No argument passed to command module
というメッセージが出力される。デフォルトだと上のコマンドは -m command
を指定したのと同じになり、引数が足りないと言われている。なので適当なコマンドを渡してやると正しくそれぞれのホストで実行される。
asa-taka@tailmoon ~ % ansible -i 'pi4a,pi4b' all -a hostname
pi4a | CHANGED | rc=0 >>
pi4a
pi4b | CHANGED | rc=0 >>
pi4b
変数も展開してくれる。
asa-taka@tailmoon ~ % ansible -i 'pi4a,pi4b' all -a 'echo $PWD'
pi4a | CHANGED | rc=0 >>
/home/pi
pi4b | CHANGED | rc=0 >>
/home/pi
ただし &&
などはそのままただの引数扱いになる
asa-taka@tailmoon ~ % ansible -i 'pi4a,pi4b' all -a 'echo $PWD && echo $PWD'
pi4a | CHANGED | rc=0 >>
/home/pi && echo /home/pi
pi4b | CHANGED | rc=0 >>
/home/pi && echo /home/pi
なのでその場合は -m shell
を利用する。
asa-taka@tailmoon ~ % ansible -i 'pi4a,pi4b' all -m shell -a 'echo $PWD && echo $PWD'
pi4b | CHANGED | rc=0 >>
/home/pi
/home/pi
pi4a | CHANGED | rc=0 >>
/home/pi
/home/pi
-m command
を利用して再起動することもできるが、途中で接続が切れてコマンドは失敗扱いになる。
asa-taka@tailmoon ~ % ansible -i 'pi4a,pi4b' all -a 'sudo reboot'
pi4a | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: Could not resolve host \"pi4a\"",
"unreachable": true
}
pi4b | UNREACHABLE! => {
"changed": false,
"msg": "Failed to connect to the host via ssh: ssh: Could not resolve host \"pi4b\"",
"unreachable": true
}
丁寧に行いたい場合は、先に示した方法を利用した方がいい。
asa-taka@tailmoon ~ % ansible -i 'pi4a,' all --background 1 --poll 0 -m shell -a "sleep 3 && sudo reboot"
pi4a | CHANGED => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"ansible_job_id": "314420265886.5008",
"changed": true,
"finished": 0,
"results_file": "/home/pi/.ansible_async/314420265886.5008",
"started": 1
}
ただやはりオプションが多く面倒なので -m command -a 'sudo reboot'
で済ませてしまっている。他のスクリプトに組み込まない限りはこちらでもそう問題はないと思う。
DEPRECATION WARNINGが邪魔な場合は、ユーザホームの設定ファイルで無効化しておくことができる。
cat << EOF >> ~/.ansible.cfg
[defaults]
deprecation_warnings=False
EOF
https://docs.ansible.com/ansible/latest/user_guide/intro_adhoc.html#intro-adhoc に書かれている方法でrebootモジュールも動かせそうなのでやってみた。
asa-taka@tailmoon pi-ansible % ansible pi4b.local -m ansible.builtin.reboot --become
pi4b.local | CHANGED => {
"changed": true,
"elapsed": 34,
"rebooted": true
}
綺麗に成功した。ただここまでして、やっていることは ssh pi4b.local sudo reboot
と同じ。ホストが多くなれば多少恩恵はありそうだけれど。ただ、再起動の完了を同期的に実行できるのは他のコマンドと組み合わせるときにメリットになるかもしれない。例えば
ansible pi4b.local -m ansible.builtin.reboot --become
ssh pi4b.local <command after reboot>
みたいなスクリプトを組む時など。実験してみたところsshが問題なく動くタイミングでansibleコマンドが終了することが確認できた。
終了させたい場合は community.general.shutdown が利用できる。
ansible pi4b.local -m community.general.shutdown --become
[DEPRECATION WARNING]: Distribution debian 10.9 on host pi4d.local should use /usr/bin/python3, but is using /usr/bin/python for backward compatibility with prior Ansible releases. A future Ansible release will default to using the discovered platform python
for this host. See https://docs.ansible.com/ansible/2.11/reference_appendices/interpreter_discovery.html for more information. This feature will be removed in version 2.12. Deprecation warnings can be disabled by setting deprecation_warnings=False in ansible.cfg.
これをどうにかしたい。
- リモートホスト上で python3 を使うべきだけど広報互換性のために python を使っている
- 今後のバージョンではこの挙動はなくなり自動で選択されるようになる
という意味かな。そこまで使い込んでいないので多少挙動が変わっても問題なさそうと判断して warning を disable することにする。
ansible の設定ファイルの場所は man ansible
に書かれている。
/etc/ansible/ansible.cfg -- Config file, used if present
~/.ansible.cfg -- User config file, overrides the default config if present
./ansible.cfg -- Local config file (in current working directory) assumed to be 'project specific' and overrides
the rest if present.
今回はプロジェクトルートの ./ansible.cfg に書くことにした。セクションが未指定だと怒られたので https://docs.ansible.com/ansible/latest/reference_appendices/config.html で確認。deprecation_warnings
は defaults
セクションらしい。
cat << EOF > ./ansible.cfg
[defaults]
deprecation_warnings=False
EOF
無事WARNINGが消えた。
Ansibleの設定について調べる。
ansible の設定ファイルの場所は man ansible
に書かれている。
/etc/ansible/ansible.cfg -- Config file, used if present
~/.ansible.cfg -- User config file, overrides the default config if present
./ansible.cfg -- Local config file (in current working directory) assumed to be 'project specific' and overrides
the rest if present.
エイリアスを指定してあるプロジェクトの設定をローカル上の場所を選ばず実行できるようにする。例えばこんな感じで設定を行なったディレクトリを用意する。
asa-taka@tailmoon ~ % tree ~/src/local/pi-ansible
/Users/asa-taka/src/local/pi-ansible
├── ansible.cfg
├── hosts
└── playbook
└── reboot.yaml
asa-taka@tailmoon ~ % cat ~/src/local/pi-ansible/ansible.cfg
[defaults]
inventory=./hosts
deprecation_warnings=False
inventory を相対パスにしているのがひとつの検証どころ。これに対して環境変数で ANSIBLE_CONFIG
で設定ファイルの場所を指定した ansible
のエイリアスを指定して実行してみる。
alias pi-ansible="ANSIBLE_CONFIG=~/src/local/pi-ansible/ansible.cfg ansible"
関係ないディレクトリから実行しても問題なく動いた。
asa-taka@tailmoon ~ % pi-ansible kubemaster -m ping
pi4a.local | SUCCESS => {
"ansible_facts": {
"discovered_interpreter_python": "/usr/bin/python"
},
"changed": false,
"ping": "pong"
}
ただしホストグループの補完は効かなかったのでどうにかできるならしてみたい。一応ホスト一覧は
pi-ansible --list-hosts all
で表示できたが、グループ一覧の出し方はわからなかった。