Kubespray と Terraform でクラスターを構築する
cluster.yml を 2 回実行しても同じクラスター
目標
用意されたインベントリでcluster.ymlを実行し、VM 1台(4 vCPU・4 GiB)にKubernetes 1.35.8を構築します。同じプレイブックをもう一度実行して、何が再び変わるかを確認し、値を1つ変更して、タグでその部分だけを再適用します。
なぜ重要なのか
kubesprayの運用方式は、「宣言を直して、同じプレイブックを再実行する」です。ノードを追加するときも、設定を変えるときも、同じcluster.ymlを実行します。 そのため、2回目の実行が何を変えるかを知っている必要があります。冪等という言葉はchangedが0という意味ではなく、毎回変わるタスクが何かを知っていて初めて、「今回の実行が意図したものだけを変えたか」を判断できます。長いプレイブックのログを末尾から読む方法(PLAY RECAPとTASKS RECAP)も、ここで身につけます。 このラボでは、インストール2回と部分実行1回で、合計約15分待ちます。セッションは60分なので、余裕が足りなければ延長してください。VMが終了すると、クラスターも消えます。
ステップ
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml --list-tasksを実行してplayの一覧を見て、次のフィールドを数値で書いてください(書き込み先:/root/ks/install/plan.json)。play_count(playの個数)、etcd_play(「Install etcd」のplayの番号)、cni_play(「Invoke kubeadm and install a CNI」の番号)、apps_play(「Install Kubernetes apps」の番号)です。/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.ymlを実行して、出力の全体を残してください(| teeまたはリダイレクト、保存先:/root/ks/logs/cluster-1.log)。約7分かかります。終わったら、PLAY RECAPのnode1がfailed=0で、kubectl get node node1がReadyである必要があります。- 最初の実行について、次のフィールドを書いてください(書き込み先:
/root/ks/install/run1.json)。ok、changed、failed(PLAY RECAPのnode1の値)、elapsed_sec(TASKS RECAPの見出しに出力された累積時間を秒で、整数)、slowest_task(TASKS RECAPの一覧の一番上のタスク名、ロールの接頭辞を含めてそのまま)、node_uid(kubectl get node node1のmetadata.uid)です。 - 同じコマンドをもう一度実行して、出力の全体を残してください(保存先:
/root/ks/logs/cluster-2.log)。2回目もfailed=0である必要があり、ノードのUIDがステップ3で書いた値と同じである必要があります(クラスターを新しく作り直していないという意味です)。 - 2回目の実行で
changed: [node1]を出したタスクの名前を、ログのTASK [...](ハンドラーはRUNNING HANDLER [...])の括弧の中のまま(ロールの接頭辞を含む)、1行に1つずつ書いてください(書き込み先:/root/ks/install/changed.txt)。そして、2回目の実行のok、changed、failed、elapsed_secを書きます(書き込み先:/root/ks/install/run2.json)。 containerd_max_container_log_line_size: 32768を追加し(追加先:/root/ks/inventory/lab/group_vars/all/containerd.yml)、cluster.yml --tags containerdでコンテナランタイムの部分だけをもう一度実行して、出力の全体を残してください(保存先:/root/ks/logs/containerd.log)。終わったら、/etc/containerd/config.tomlのmax_container_log_line_sizeが32768で、containerdが新しい設定で再び起動している必要があり、ノードは引き続きReadyである必要があります。- 次のフィールドを書いてください(書き込み先:
/root/ks/install/report.json)。kubespray(タグ)、server_version(APIサーバーのgitVersion)、first_run_sec、second_run_sec(2回の実行の累積時間、整数)、first_changed、second_changed、tags_run_changed(ステップ6の実行のchanged)、containerd_restarted(ステップ6でcontainerdが再び起動したか、ブール値)です。
参考
- kubespray v2.32.0が
/opt/ks/kubesprayに、インベントリが/root/ks/inventory/lab/inventory.iniに用意されています(ノード1台、kube_version 1.35.8)。プレイブックは/opt/ks/kubesprayで実行します。 - インストール中にダウンロードしたファイルは
/tmp/releasesに、イメージはcontainerdのストレージに溜まります。2回目の実行が速い理由です。 - よくあるミス: ログの
fatal:を見て失敗と判断することです。...ignoringが付いたfatalは、kubesprayがあえて受け流す確認タスクです。判定はPLAY RECAPのfailedで行います。 - よくあるミス: 最初のインストールが途中で止まった後、cluster.ymlだけを再実行することです。CNIの手前で止まった場合は、2回目の実行がコントロールプレーンのReadyを待って失敗します。その場合は、reset.ymlで削除して最初から構築します(モジュール7)。
- ドキュメント: Kubespray: Getting started・Kubespray: Ansible tags
実行する前に順序を見る
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml --list-tasksを実行してplayの一覧を見て、次のフィールドを数値で書いてください(書き込み先: /root/ks/install/plan.json)。play_count(playの個数)、etcd_play(「Install etcd」のplayの番号)、cni_play(「Invoke kubeadm and install a CNI」の番号)、apps_play(「Install Kubernetes apps」の番号)です。
--list-tasksは、何も変更せずにプレイブックを展開して見せてくれます。出力のplay #N (호스트 패턴): 이름の行を見てください(プレースホルダーは番号、ホストパターン、名前です)。etcdがコントロールプレーンより、CNIがアドオンより先に来る理由を考えてみると、後で失敗した地点を読み取りやすくなります。
cluster.ymlを実行する
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.ymlを実行して、出力の全体を残してください(| teeまたはリダイレクト、保存先: /root/ks/logs/cluster-1.log)。約7分かかります。終わったら、PLAY RECAPのnode1がfailed=0で、kubectl get node node1がReadyである必要があります。
コンソールが切れると、プレイブックも一緒に終了することがあります。systemd-run --unit=<이름> --setenv=HOME=/root ...やtmuxで起動すれば(プレースホルダーはユニット名です)、ターミナルと無関係に動き、tail -fで見守れます。HOMEが空だと、kubesprayのkubeモジュールがkubeconfigを見つけられず、localhost:8080に向かいます。ログの途中の...ignoringが付いたfatalは、正常です。
ログの末尾で読み取るもの
最初の実行について、次のフィールドを書いてください(書き込み先: /root/ks/install/run1.json)。ok、changed、failed(PLAY RECAPのnode1の値)、elapsed_sec(TASKS RECAPの見出しに出力された累積時間を秒で、整数)、slowest_task(TASKS RECAPの一覧の一番上のタスク名、ロールの接頭辞を含めてそのまま)、node_uid(kubectl get node node1のmetadata.uid)です。
ansible.cfgが有効にしているprofile_tasksコールバックが、ログの末尾にTASKS RECAPを付けます。最初の行の最後の時間が全体の累積時間で、下の一覧が時間のかかった順です。ノードのUIDは、次のステップで「再実行しても同じノードか」を確認する基準になります。
もう一度実行する
同じコマンドをもう一度実行して、出力の全体を残してください(保存先: /root/ks/logs/cluster-2.log)。2回目もfailed=0である必要があり、ノードのUIDがステップ3で書いた値と同じである必要があります(クラスターを新しく作り直していないという意味です)。
kubesprayは、すでに構築したクラスターにcluster.ymlを再実行しても問題ないように作られています。ノードを追加するときや設定を変えるときに、同じプレイブックを再実行するのが、基本の運用方法です。2回目の実行は、ダウンロードするものがキャッシュにあるので、1回目より速いです。
冪等なのにchangedが0ではない
2回目の実行でchanged: [node1]を出したタスクの名前を、ログのTASK [...](ハンドラーはRUNNING HANDLER [...])の括弧の中のまま(ロールの接頭辞を含む)、1行に1つずつ書いてください(書き込み先: /root/ks/install/changed.txt)。そして、2回目の実行のok、changed、failed、elapsed_secを書きます(書き込み先: /root/ks/install/run2.json)。
冪等(idempotent)は「何回実行しても結果の状態が同じ」という意味であり、「どのタスクもchangedを出さない」という意味ではありません。毎回何かを実行するcommandやshellのタスクや、値を比較せずに書き直すタスクが、changedを出します。ログでchanged: [node1]の行のすぐ上にある、最も近いTASK(またはRUNNING HANDLER)の見出しが、そのタスクです。
値を1つ変えて、その部分だけをもう一度
containerd_max_container_log_line_size: 32768を追加し(追加先: /root/ks/inventory/lab/group_vars/all/containerd.yml)、cluster.yml --tags containerdでコンテナランタイムの部分だけをもう一度実行して、出力の全体を残してください(保存先: /root/ks/logs/containerd.log)。終わったら、/etc/containerd/config.tomlのmax_container_log_line_sizeが32768で、containerdが新しい設定で再び起動している必要があり、ノードは引き続きReadyである必要があります。
タグを指定すると、そのタグが付いたタスク(とalwaysタグの準備タスク)だけが動きます。どんなタグがあるかは、kubesprayのドキュメントのタグの表や--list-tagsで見ます。設定ファイルだけが変わってデーモンが再び起動しないと、古い値のまま動き続けます。ロールのハンドラーが再起動を担当します。ドキュメントが警告するように、タグは、何が動くかを確実に知っているときにだけ使います。
インストールのレポート
次のフィールドを書いてください(書き込み先: /root/ks/install/report.json)。kubespray(タグ)、server_version(APIサーバーのgitVersion)、first_run_sec、second_run_sec(2回の実行の累積時間、整数)、first_changed、second_changed、tags_run_changed(ステップ6の実行のchanged)、containerd_restarted(ステップ6でcontainerdが再び起動したか、ブール値)です。
前のステップの記録と3つのログ、そして現在のクラスターから、すべて再計算できる値です。採点ツールも、同じところから再計算して照合します。containerdが再び起動したかどうかは、containerd.logで再起動ハンドラーがchangedと出力されたかどうかで判断します。