Ansibleでの環境変数の設定(/etc/environment)が挙動不審
はじめに
AnsibleのPlaybookで/etc/environment
にプロキシ関連の環境変数(http_proxy
とか)を書き込んだ際、書き込んだはずの環境変数が後続のタスクで参照できなかったので調査した。
環境
$ cat /etc/os-release | grep PRETTY_NAME PRETTY_NAME="Ubuntu 20.04.1 LTS" $ ansible --version ansible 2.9.7 config file = None configured module search path = ['/home/redj/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /home/redj/.local/lib/python3.8/site-packages/ansible executable location = /home/redj/.local/bin/ansible python version = 3.8.2 (default, Jul 16 2020, 14:00:26) [GCC 9.3.0]
現象
試行 1 - 環境変数が見えない
以下のPlaybookを実行すると、最後のタスクdebug
でhttp_proxy
などが表示されてほしいが、実際には表示されない。
- hosts: target tasks: - name: /etc/environment にプロキシの環境変数を書き込み become: yes ini_file: path: /etc/environment no_extra_spaces: yes section: null option: "{{ item.key }}" value: "{{ item.value }}" loop: "{{ proxy_envs | dict2items }}" vars: proxy_envs: http_proxy: http://192.168.8.8:3128/ https_proxy: http://192.168.8.8:3128/ no_proxy: 127.0.0.1,localhost - name: 環境変数の取得 shell: env | grep "_proxy" | cat register: result - name: 環境変数の表示 debug: var: result.stdout_lines
実行結果は以下のようになり、プロキシ関連の環境変数が見つからないことがわかる。
$ ansible-playbook -i hosts.yml playbook.yml PLAY [target] ********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************* ok: [192.168.8.16] TASK [/etc/environment にプロキシの環境変数を書き込み] ******************************************************************************************** changed: [192.168.8.16] => (item={'key': 'http_proxy', 'value': 'http://192.168.8.8:3128/'}) changed: [192.168.8.16] => (item={'key': 'https_proxy', 'value': 'http://192.168.8.8:3128/'}) changed: [192.168.8.16] => (item={'key': 'no_proxy', 'value': '127.0.0.1,localhost'}) TASK [環境変数の取得] ********************************************************************************************************************* changed: [192.168.8.16] TASK [環境変数の表示] ********************************************************************************************************************* ok: [192.168.8.16] => { "result.stdout_lines": [] } PLAY RECAP ************************************************************************************************************************* 192.168.8.16 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
試行 2 - 環境変数が見える
試しにもう一度実行してみると、今回は環境変数は見えている。
/etc/environment
の更新がok
になっていることから、初回の実行での環境変数の追加は成功していることが分かる。
なぜ初回の実行では環境変数が見えていないんだろうか…。
$ ansible-playbook -i hosts.yml playbook.yml PLAY [target] ********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************* ok: [192.168.8.16] TASK [/etc/environment にプロキシの環境変数を書き込み] ******************************************************************************************** ok: [192.168.8.16] => (item={'key': 'http_proxy', 'value': 'http://192.168.8.8:3128/'}) ok: [192.168.8.16] => (item={'key': 'https_proxy', 'value': 'http://192.168.8.8:3128/'}) ok: [192.168.8.16] => (item={'key': 'no_proxy', 'value': '127.0.0.1,localhost'}) TASK [環境変数の取得] ********************************************************************************************************************* changed: [192.168.8.16] TASK [環境変数の表示] ********************************************************************************************************************* ok: [192.168.8.16] => { "result.stdout_lines": [ "no_proxy=127.0.0.1,localhost", "https_proxy=http://192.168.8.8:3128/", "http_proxy=http://192.168.8.8:3128/" ] } PLAY RECAP ************************************************************************************************************************* 192.168.8.16 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
試行 3 - 環境変数が見える
今度はターゲットのサーバの/etc/environment
を元に戻してやり直してみる。
さらに、Playbookでbecome: yes
の位置を以下のようにタスク全体にかかるように移動しておく。
- hosts: target become: yes tasks: (以下略)
このPlaybookを実行すると、今度は初回の実行で環境変数が見つかる。
/etc/environment
の更新がchanged
になっていることから、環境変数の追加が成功し、かつ後続のタスクで環境変数が見えていることが分かる。
なぜだ…。
$ ansible-playbook -i hosts.yml playbook.yml PLAY [target] ********************************************************************************************************************** TASK [Gathering Facts] ************************************************************************************************************* ok: [192.168.8.16] TASK [/etc/environment にプロキシの環境変数を書き込み] ******************************************************************************************** changed: [192.168.8.16] => (item={'key': 'http_proxy', 'value': 'http://192.168.8.8:3128/'}) changed: [192.168.8.16] => (item={'key': 'https_proxy', 'value': 'http://192.168.8.8:3128/'}) changed: [192.168.8.16] => (item={'key': 'no_proxy', 'value': '127.0.0.1,localhost'}) TASK [環境変数の取得] ********************************************************************************************************************* changed: [192.168.8.16] TASK [環境変数の表示] ********************************************************************************************************************* ok: [192.168.8.16] => { "result.stdout_lines": [ "no_proxy=127.0.0.1,localhost", "https_proxy=http://192.168.8.8:3128/", "http_proxy=http://192.168.8.8:3128/" ] } PLAY RECAP ************************************************************************************************************************* 192.168.8.16 : ok=4 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
解決方法
結論から言うと、Ansibleが呼び出すsshコマンドのパラメータを調整して、Multiplexing (多重化)を無効化することで解決した。
SSHのMultiplexingとは、ひとつのTCPコネクション上に複数のSSHセッションをのせる仕組みのこと。 言い換えるとコネクションを再利用する仕組みであり、これを利用すると都度接続が不要になため、sshコマンドを連発するような場合に高速化が見込める。
つまりこれはAnsibleに適した高速化の仕組みであり、ANSIBLE_SSH_ARGS
という設定項目でデフォルトで有効化されている。
以下にマニュアルでのANSIBLE_SSH_ARGS
の説明を抜粋する。
項目 | 説明 |
---|---|
Description: | If set, this will override the Ansible default ssh arguments. In particular, users may wish to raise the ControlPersist time to encourage performance. A value of 30 minutes may be appropriate. Be aware that if -o ControlPath is set in ssh_args, the control path setting is not used. |
Default: | -C -o ControlMaster=auto -o ControlPersist=60s |
Ini Section: | ssh_connection |
Ini Key: | ssh_args |
Environment: | ANSIBLE_SSH_ARGS |
Multiplexingを無効化するsshのパラメータは?
sshのMultiplexingに関係するパラメータは、主に以下の3つ。 「簡単な説明」は自分なりにまとめたものなので、正確な定義はOpenSSHのマニュアルを参照のこと。
パラメータ | 簡単な説明 |
---|---|
ControlMaster | yes の場合は自身がMasterであることを、no の場合はMasterではない(=クライアントである)ことを示す。auto の場合はMasterがいない時には自動的に自身がMasterになる。 |
ControlPersist | クライアントがいなくなってからMasterのコネクションを切断するまでのタイムアウト値。 |
ControlPath | コネクションを共有するためのソケットファイルのパス。none を指定した場合はMultiplexingを利用しない。 |
上の表より、sshのコマンドラインパラメータとして-o ControlPath=none
を付け加えれば良い。
注意点として、ググると「Multiplexingをオフにするには-o ControlMaster=no
としておけ」みたいな情報があるが、これは「自分はクライアント」と宣言しているのであり、オフになったわけではない。
Ansibleでの設定方法 1 - ANSIBLE_SSH_ARGS
sshのパラメータの「ベース」を指定するANSIBLE_SSH_ARGS
に対して-C -o ControlPath=none
を設定する。
ANSIBLE_SSH_ARGS
の設定方法は、ansible.cfg
で設定する方法と、環境変数で設定する方法の、2パターンある。
ansible.cfg
で設定する場合は、以下のようにする。
[ssh_connection] ssh_args=-C -o ControlPath=none
また、環境変数で設定するには、Ansibleを以下のように実行するか、あるいは~/.profile
などに当該の環境変数を定義しておく。
$ export ANSIBLE_SSH_ARGS="-C -o ControlPath=none" $ ansible-playbook playbook.yml
Ansibleでの設定方法 2 - ansible_ssh_extra_args
ansible_ssh_extra_args
で指定したsshのパラメータは、ANSIBLE_SSH_ARGS
の内容の後ろに追加されてsshに渡される。
ansible_ssh_extra_args
の設定方法は、Ansibleのコマンドラインパラメータとして設定する方法と、インベントリなどでAnsibleの変数として設定する方法の、2パターンある。
Ansibleのコマンドラインパラメータとして設定する場合は、以下のようにAnsibleを実行する。
$ ansible-playbook --ssh-extra-args="-o ControlPath=none" -i hosts.yml playbook
インベントリでAnsibleの変数として設定するには、インベントリを以下のように記述する。
devpc: hosts: "192.168.8.16": ansible_user: jaybanuan ansible_ssh_extra_args: -o ControlPath=none
Ansibleでの設定方法 - まとめ
Ansibleでのsshのコマンドラインパラメータの指定場所がややこしいので、以下の表にまとめておく。
個人的には、ansible_ssh_extra_args
をAnsibleの変数としてインベントリで定義すると、接続先に柔軟に対応できるためよいと思う。
設定場所 | ANSIBLE_SSH_ARGS | ansible_ssh_extra_args |
---|---|---|
ansible.cfg | レ | |
起動時のパラメータ | レ | |
環境変数 | レ | |
Ansibleの変数 | レ |
結局のところ原因は何?
結局はsshのMultiplexingが影響しているという事以外は分からなかった。
sshのMultiplexingに辿り着いたのは、/etc/environment
はログインしなおせば効くはずなのに効いておらず、Multiplexingはクライアントではsshの認証が省略される(のか?)ので、ログアウトしきれていないのでは、と推測したため。