zaki work log

作業ログやら生活ログやらなんやら

[Ansible] playをまたいだ変数保持をdelegate_toとfact変数で行う (Ansibleでkubeadmのworkerノード追加)

task(1)の実行結果を保持してtask(2)の入力にする、であればregisterを使って可能だけど、play(A)の実行結果を保持してplay(B)の入力にするには、、、registerで保持できる変数のスコープはplay内(←この認識は誤り)で、環境変数なんかはAnsibleから更新する方法がよくわからなかったけど、処理を行うホストを変更するdelegate_toとfact変数を使って結果を持ち越せたので、そのやり方について。

※ 4/16 訂正記事作成: [Ansible] registerでセットされる値は実行時のターゲットノードに紐づき寿命はplaybook全体 (play終了で消えない) - zaki work log
※ 4/15 22:30 delegate_toとfact変数を使わない方法について追記

コントロールノード環境(CentOS 7)

[zaki@manager-dev initialize-kubeadm-ansible]$ ansible --version
ansible 2.9.6
  config file = /home/zaki/ansible/initialize-kubeadm-ansible/ansible.cfg
  configured module search path = [u'/home/zaki/.ansible/plugins/modules', u'/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python2.7/site-packages/ansible
  executable location = /usr/bin/ansible
  python version = 2.7.5 (default, Aug  7 2019, 00:51:29) [GCC 4.8.5 20150623 (Red Hat 4.8.5-39)]

やりたいこと

kubeadmを使ったKubernetes環境でworkerノード追加するとき

  1. masterノードでkubeadm token list等を使ってトークンを取得
  2. 追加したいworkerノード上で↑の情報を引数にkubeadm join実行

というのを、Ansibleを使ってやりたい。

うまくいかなかったやり方

1の処理はmasterノード1台でやるので、master[0]hostsに指定したplayを作成。
2の処理は追加したいworkerノード上で行うのでworker(追加対象のworkerを指定したホストグループ)をhostsに指定したplayを作成。

すると、playをまたがった変数保持が必要になるんだけど、単純に1のplay内でregisterを使っても、2のplayに変数を引き継げない。
環境変数も、1のplay内で更新してもやっぱり2には引き継げない。(←追記: 異なるターゲットノード間で引き継げない、が"おそらく"正解)
fact変数にセットしても、1のplay内のfact変数セットはターゲットノードであるmaster[0]ノードが対象となり、2のplayのターゲットノードであるworkerのfact変数には影響しない。

shellssh使うのはさすがに却下。笑

playをまたいだ変数保持の仕方

ベストプラクティスはあるのかもしれないけどよくわからなかった。。
トライアルアンドエラーと検索の末たどり着いたのがdelegate_to

delegate_toを使うことで、一時的に処理対象をターゲットホストでなくdelegate_toで指定したホストに変更することができる。

概要としてはこんな感じ

---
- hosts: master[0]
  gather_facts: false
  become: true
  tasks:

    - name: check exists token
      shell: kubeadm token list
      check_mode: false
      changed_when: false
      register: token_exists

    - name: create token
      shell: kubeadm token create --print-join-command
      when: token_exists.stdout == ''
      register: create_token_result

    - set_fact:
        kubeadm_token: "{{create_token_result.stdout}}"
      delegate_to: localhost   # set_factの実行ホストをlocalhostに移譲
      delegate_facts: true     # delegate_toで実行ホストを変更したfact更新を許可

# ↑の結果を
# ↓に持ち越すのが今回のテーマ

- hosts: localhost
  gather_facts: false
  tasks:

    - name: test
      debug:
        msg: "{{kubeadm_token}}"

まず出力を取り出したい最初のplayの以下のtask

    - name: create token
      shell: kubeadm token create --print-join-command
      when: token_exists.stdout == ''
      register: create_token_result

shellを使ってkubeadm token create --print-join-commandというコマンドを実行し、その結果をcreate_token_resultという変数にセット。
ここまでは普通。

そして次のtask。

    - set_fact:
        kubeadm_token: "{{create_token_result.stdout}}"
      delegate_to: localhost   # set_factの実行ホストをlocalhostに移譲
      delegate_facts: true     # delegate_toで実行ホストを変更したfact更新を許可

delegate_to: localhostを指定して、処理の対象(この場合set_factモジュールを実行するターゲット)をlocalhostに変更している。
その際、前のtaskでregisterを使って保持したcreate_token_resultへのスコープは生きているので、「localhostのfact変数kubeadm_tokencreate_token_result.stdoutをセット」ができる。
なお、ターゲットノードを変更するだけであればdelegate_toで対象指定するだけで良いが、fact変数の更新は更にdelegate_factstrueを指定する必要もある。

これで、以降localhostにfact変数としてkubeadm_tokenを参照できるようになる。

そして次のplay、前のplayとtaskでセットしたkubeadm_tokenに参照している。

- hosts: localhost
  gather_facts: false
  tasks:

    - name: test
      debug:
        msg: "{{kubeadm_token}}"

最終的に(完成はしてない)こんな感じ。
(上の例ではlocalhostset_factしてるけど、実際にやりたいのは全workerノードなので、loopで繰り返し処理してる)

github.com

手順そのものはこちら

zaki-hmkc.hatenablog.com


kubeadmでworkerノードの追加

ということで、うまくいきました。

[zaki@k8s-master01 ~]$ kubectl get node
NAME                        STATUS   ROLES    AGE   VERSION
k8s-master01.esxi.jp-z.jp   Ready    master   8d    v1.18.0
k8s-master02.esxi.jp-z.jp   Ready    master   8d    v1.18.0
k8s-master03.esxi.jp-z.jp   Ready    master   8d    v1.18.0
k8s-worker01.esxi.jp-z.jp   Ready    <none>   8d    v1.18.0
k8s-worker02.esxi.jp-z.jp   Ready    <none>   37s   v1.18.0

参考

github.com

thinkit.co.jp


「そういう処理なら〇〇使う方がいいよー」とか「delegate_toは□□みたいなとき使えないよー」とかいうのがあったら教えてください。。


(追記) delegate_*とfactを使わないやり方

よこちさん( id:akira6592 )が検証してくださった結果、「registerでセットした変数は"playまたぎできない"ではなく、(set_factの通常動作と同様に)セットしたときのホストに紐づく」という動作だったらしい。

言い換えると、Ansible実行中の変数は、ホストという名前空間で区切られており、ホスト名省略時は自ホストで持っている変数しか参照できないが、(FQDN的に)ホスト名を指定すれば別ホストでセットした値も見える、という感じかな?

というわけで「やりたいこと」についてはfact変数を使わなくてもクリアできそう。感謝!!