久しぶりにansibleをやっているわけですが、.bash_profileに書いたPATHが反映されないなあ、と思って色々調べてみました。
どういう事が起きたか
今回rbenvのplaybookを書いていて気づきました。
大まかにrbenvのインストールは以下の流れになります。
- rbenvをgit cloneする。
- ruby-buildをgit cloneする。
- ~/.bash_profileにrbenvのバイナリへのパスとrbenvの初期化コマンドを追記する。
- rbenv install 2.2.2する。
- rbenv global 2.2.2する。
という感じで、playbookには以下のように記述しました。
- name: install ruby shell: rbenv install {{ ruby_version }} sudo: no
すると、以下のエラーが起きました。
TASK: [rbenv | install ruby] ************************************************** failed: [192.168.33.21] => {"changed": true, "cmd": "rbenv install 2.2.2", "delta": "0:00:00.002410", "end": "2015-05-16 06:23:09.890315", "rc": 127, "start": "2015-05-16 06:23:09.887905", "warnings": []} stderr: /bin/sh: rbenv: command not found FATAL: all hosts have already failed -- aborting
rbenv: command not foundだそうです。
rbenvバイナリへのパスは、.bash_profileに以下のように記述しています。
export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)"
どうやらshellモジュール実行時に.bash_profileの設定が読み込まれない事が解りました。
ansibleのドキュメントを見てみる
The shell module takes the command name followed by a list of space-delimited arguments. It is almost exactly like the command module but runs the command through a shell (/bin/sh) on the remote node.
http://docs.ansible.com/shell_module.html#synopsis
こう書いてあるのとエラーログの内容を加味すると、shellモジュール実行時のシェルは「/bin/sh」であり、ログインシェルは実行していない(.bash_profile等を読み込まない)事が解ります。
2通りの解決策
もっといい方法があるかもしれませんが、私が色々試して思いついた解決策は以下の2種類でした。
shellモジュールを使う時は必ずログインシェルとして実行する
- name: install ruby shell: bash -lc "rbenv install {{ ruby_version }}"
このように「-l」オプションを付けてbashコマンドを実行する事で、ログインシェルとして実行する事で、.bash_profile等を読み込む事ができ、rbenvコマンドが参照できるようになります。
メリット
必要な時だけログインシェルとして実行して各種設定ファイルを読み込むので、shellモジュール実行時のオーバーヘッドが最小限になる。
デメリット
.bash_profile等に記述した環境変数上にあるバイナリを実行する際は、毎回「/bin/bash -l」等と書かなくてはならず、面倒だし冗長になる。
また、ダブルクォートで挟むので、sed等のコマンドでシングル・ダブルクォートが、壊れないよう注意する必要がある。
ansible.cfgにexecutableを追加してしまう
ansible.cfgに以下のように記述してしまう事で、shellコマンド実行時は必ずログインシェルとして実行するようにしてしまいます。
[defaults] executable = /bin/bash -l
Configuring Ansible — Ansible Documentation
メリット
shellモジュール実行毎に「/bin/bash -l」を書かなくて済むのでplaybookがシンプルになる。
デメリット
shellモジュール実行時はログインシェルを都度読み込んでしまい、オーバーヘッドが発生する。(shellだけでなくexecutables属性があるモジュール全てにいえるかもしれません)
雑感
個人的には ansible.cfgにexecutableに書いてしまってもいいかなあと思っています。パフォーマンス面等で考えると都度/bin/bash -lした方がいいんでしょうが、そもそもパフォーマンス面を考えるとansibleではなくchef serverの方がいいでしょうから、割りきってansible.cfgにexecutableを書く方が楽だし冗長化も防げるかと思います。

Ansible実践ガイド 第2版 (impress top gear)
- 作者: 北山晋吾,塚本正隆,畠中幸司
- 出版社/メーカー: インプレス
- 発売日: 2018/03/01
- メディア: 単行本(ソフトカバー)
- この商品を含むブログを見る