昨日のこの「playまたぎの変数保持」についての記事、記事への補足訂正は追記済みですが、致命的に認識誤りしてました💦
記事作成当時の認識の「playをまたいで変数を持ち越せない」は、
- masterノードでkubeadm token list等を使ってトークンを取得
- 追加したいworkerノード上で↑の情報を引数にkubeadm join実行
という処理を実現したいのがベースにあり、
- 「masterノード」をターゲットノードにしたplayでトークン取得処理を作成
- 「追加したいworkerノード」をターゲットノードにしたplayで
kubeadm join
を実行
と、二つのplayでターゲットノードが異なるため 1の結果をregister
使って保持しても2のplay実行時に持ち越せないのが本当の原因だったのですが、これを「play間で変数を持ち越せない」と問題を誤認していました。
コメントくださったみなさんありがとうございました!
delegate_* なにして、localhsot 側のPlayの debug の msg で
— よこち(yokochi) (@akira6592) 2020年4月15日
"{{ hostvars[master[0]].kubeadm_token }}"
とやったときに表示できる気がします!
簡単なサンプルなのですが、この画像の例だと、localhost から他のホストが前のPlayでregisterした値が表示できました。 pic.twitter.com/Nnc5iqooHd
曖昧な記憶では内部的には Ansible は各ホスト毎にコンテキスト (変数など) 情報を保持、変数の lifetime は playbook の中の - host ... {roles/tasks} 単位 (play) ではなく実行コンテキストずっと (つまり play をまたぐ) だった気がします。このあたりだったか: https://t.co/Bwn4bZM2Fs
— ssato (@satoru_satoh) 2020年4月15日
ちなみに変数管理周りはVariableManagerなので、この辺から攻めるとよいとのこと。勉強になるなぁ。
ansible が内部的に変数まわりを管理しているのは VariableManager (https://t.co/rlFQqd4kIT) だった気がするので、ここから追っていくと変数まわりの振舞いや仕組みなどについては大体わかったかと。
— ssato (@satoru_satoh) 2020年4月15日
というわけで、動作確認してみました。
Ansibleの実装を追って行った方が確実だし面白そうなんだけど、ひとまず動きを確認してみたい。
検証: registerの値をplayをまたいで参照してみる
パターン1: ターゲットノードが同じ
--- - hosts: master[0] gather_facts: false become: true tasks: - name: exec ls shell: ls /root register: shell_result # ↑の結果を # ↓で参照する - hosts: master[0] gather_facts: false tasks: - name: echo "ls /root" debug: msg: "{{shell_result.stdout}}"
結果
[zaki@manager-dev test-ansible]$ ansible-playbook -i inventory.ini other_host.yaml -K BECOME password: PLAY [master[0]] *************************************************************** TASK [exec ls] ***************************************************************** changed: [master01] PLAY [master[0]] *************************************************************** TASK [echo "ls /root"] ********************************************************* ok: [master01] => msg: |- anaconda-ks.cfg original-ks.cfg PLAY RECAP ********************************************************************* master01 : ok=2 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [zaki@manager-dev test-ansible]$
なるほど、問題無い。
(ちなみに1つ目のplayはbecome: true
でroot実行して、2つ目のplayは通常ユーザー権限で動かしてるので、特権動作の結果を通常権限で参照するという動作でも問題無い)
パターン2: ターゲットノードが異なる場合
--- - hosts: master[0] gather_facts: false become: true tasks: - name: exec ls shell: ls /root register: shell_result # ↑の結果を # ↓で参照する - hosts: master[1] gather_facts: false tasks: - name: echo "ls /root" debug: msg: "{{shell_result.stdout}}"
結果
[zaki@manager-dev test-ansible]$ ansible-playbook -i inventory.ini other_host.yaml -K BECOME password: PLAY [master[0]] *************************************************************** TASK [exec ls] ***************************************************************** changed: [master01] PLAY [master[1]] *************************************************************** TASK [echo "ls /root"] ********************************************************* fatal: [master02]: FAILED! => msg: |- The task includes an option with an undefined variable. The error was: 'shell_result' is undefined The error appears to be in '/home/zaki/ansible/test-ansible/other_host.yaml': line 16, column 7, but may be elsewhere in the file depending on the exact syntax problem. The offending line appears to be: tasks: - name: echo "ls /root" ^ here PLAY RECAP ********************************************************************* master01 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 master02 : ok=0 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 [zaki@manager-dev test-ansible]$
register
使ってセットしたshell_result
は未定義とエラーになった。
パターン3: ターゲットノードは異なるがhostvarsでregisterを使ったノードを指定
hostvars
は、対象ノードのfact変数や定義済み変数などにアクセスする際に使用します。
--- - hosts: master[0] gather_facts: false become: true tasks: - name: exec ls shell: ls /root register: shell_result # ↑の結果を # ↓で参照する - hosts: master[1] gather_facts: false tasks: - name: echo "ls /root" debug: msg: "{{ hostvars[groups.master[0]] }}"
結果
[zaki@manager-dev test-ansible]$ ansible-playbook -i inventory.ini other_host.yaml -K BECOME password: PLAY [master[0]] *************************************************************** TASK [exec ls] ***************************************************************** changed: [master01] PLAY [master[1]] *************************************************************** TASK [echo "ls /root"] ********************************************************* ok: [master02] => msg: ansible_check_mode: false ansible_diff_mode: false ansible_facts: discovered_interpreter_python: /usr/bin/python ansible_forks: 5 ansible_host: 192.168.0.121 ansible_inventory_sources: - /home/zaki/ansible/test-ansible/inventory.ini ansible_playbook_python: /usr/bin/python2 ansible_run_tags: - all ansible_skip_tags: [] ansible_verbosity: 0 ansible_version: full: 2.9.6 major: 2 minor: 9 revision: 6 string: 2.9.6 discovered_interpreter_python: /usr/bin/python group_names: - master groups: all: - worker01 - worker02 - worker03 - master01 - master02 master: - master01 - master02 ungrouped: [] worker: - worker01 - worker02 - worker03 inventory_dir: /home/zaki/ansible/test-ansible inventory_file: /home/zaki/ansible/test-ansible/inventory.ini inventory_hostname: master01 inventory_hostname_short: master01 playbook_dir: /home/zaki/ansible/test-ansible shell_result: ansible_facts: discovered_interpreter_python: /usr/bin/python changed: true cmd: ls /root delta: '0:00:00.003252' end: '2020-04-16 19:50:33.655705' failed: false rc: 0 start: '2020-04-16 19:50:33.652453' stderr: '' stderr_lines: [] stdout: |- anaconda-ks.cfg original-ks.cfg stdout_lines: - anaconda-ks.cfg - original-ks.cfg PLAY RECAP ********************************************************************* master01 : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 master02 : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [zaki@manager-dev test-ansible]$
gather_fact: false
にしているのでfact変数はセットされていないですが、Ansible環境情報やインベントリ変数などに加えて、register
でセットしたshell_result
が含まれているのが確認できます。
追試
パターン4: あらかじめall:varsでホスト変数をセットしておきplay内で1ノードだけ変数更新してみる
インベントリファイル
[master] master01 ansible_host=192.168.0.121 master02 ansible_host=192.168.0.122 master03 ansible_host=192.168.0.123 [all:vars] sample_host_val = "curry"
プレイブック
--- - hosts: master gather_facts: false tasks: - name: print host value debug: msg: "{{ sample_host_val }}" - hosts: master[2] gather_facts: false tasks: - shell: echo "tabetai" register: sample_host_val - name: print host value debug: msg: "{{ sample_host_val.stdout }}" - hosts: master gather_facts: false tasks: - name: print host value debug: msg: "{{ sample_host_val }}"
実行結果
[zaki@manager-dev test-ansible]$ ansible-playbook -i inventory.ini host_vars.yaml PLAY [master] ****************************************************************** TASK [print host value] ******************************************************** ok: [master01] => msg: curry ok: [master02] => msg: curry ok: [master03] => msg: curry PLAY [master[2]] *************************************************************** TASK [shell] ******************************************************************* changed: [master03] TASK [print host value] ******************************************************** ok: [master03] => msg: tabetai PLAY [master] ****************************************************************** TASK [print host value] ******************************************************** ok: [master03] => msg: ansible_facts: discovered_interpreter_python: /usr/bin/python changed: true cmd: echo "tabetai" delta: '0:00:00.002291' end: '2020-04-16 20:09:34.678726' failed: false rc: 0 start: '2020-04-16 20:09:34.676435' stderr: '' stderr_lines: [] stdout: tabetai stdout_lines: - tabetai ok: [master01] => msg: curry ok: [master02] => msg: curry PLAY RECAP ********************************************************************* master01 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 master02 : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 master03 : ok=4 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 [zaki@manager-dev test-ansible]$
master03(master[2]
)でのみregister
を使ってshell
の実行結果をセットしたみたけど、その次のplayではmaster03のみ値が更新されている。
なので、register
を使うと、ホスト変数を上書きするみたい。
ちなみに
- hosts: master[0] gather_facts: false vars: sample_host_val: "tabetai" tasks: - name: print host value debug: msg: "{{ sample_host_val }}"
というplayの場合は、vars
で値をセットしたplay内ではsample_host_val
はtabetai
で上書きされて実行されるけど、次のplayではリセットされる。
というわけで、hostvars使った版のworkerノード追加のplaybookはこの通り。(set_fact
してたtaskが1個減った)
結果
[zaki@k8s-master01 ~]$ kubectl get node NAME STATUS ROLES AGE VERSION k8s-master01.esxi.jp-z.jp Ready master 9d v1.18.0 k8s-master02.esxi.jp-z.jp Ready master 9d v1.18.0 k8s-master03.esxi.jp-z.jp Ready master 9d v1.18.0 k8s-worker01.esxi.jp-z.jp Ready <none> 9d v1.18.0 k8s-worker02.esxi.jp-z.jp Ready <none> 38h v1.18.0 k8s-worker03.esxi.jp-z.jp Ready <none> 33s v1.18.0
うむ。
よこちさん( id:akira6592 )が関連記事を作成してくださいました。ありがとうございます!