Kubespray と Terraform でクラスターを構築する
インベントリ 1 枚がクラスターの形を決める
目標
kubespray v2.32.0のサンプルインベントリで、ノード1台のクラスターのインベントリを設計し、バージョンをgroup_varsに固定して、変数がどの層で勝つかと、グループ名を間違えたときに何が壊れるかを、自分で確認します。クラスターはまだ構築しません。
なぜ重要なのか
kubesprayでクラスターを構築するとき、人が直接書くのは、事実上インベントリとgroup_varsだけです。プレイブックはそのままにして、この2つだけを変えて、クラスターの形(誰がコントロールプレーンで、誰がetcdか)と内容(どのバージョン、どのCNI、どのランタイムか)を決めます。 そのため、インストールがうまくいかない原因も、たいていここにあります。グループ名を1つ間違えると、インストールの1分後に見当違いのエラーで止まり、バージョンを書かないと、同じインベントリがkubesprayを上げるたびに異なるKubernetesをインストールします。長いインストールを実行する前に、インベントリツールで展開された値を確認する習慣が、このモジュールの目標です。
ステップ
/opt/ks/kubespray/inventory/sampleを丸ごとコピーしてください(コピー先:/root/ks/inventory/lab)。group_vars/all/all.ymlとgroup_vars/k8s_cluster/k8s-cluster.ymlが、その下にある必要があります。- ノード
node1をkube_control_planeとkube_nodeのグループに入れ、etcdはkube_control_planeを子グループとして受け取る([etcd:children])ように書いてください(書き込み先:/root/ks/inventory/lab/inventory.ini)。k8s_clusterも、kube_control_planeとkube_nodeを子として受け取る([k8s_cluster:children])ように書きます。接続方式は、インベントリの行ではなく、ansible_connection: localとして置いてください(置き場所:/root/ks/inventory/lab/host_vars/node1.yml)。inventory.iniにはansible_connectionがない必要があります。 /opt/ks/kubesprayでansible-inventory --listを2回実行して、パースされたホスト数を数え、次のフィールドを書いてください(書き込み先:/root/ks/parse.json)。dir_hosts(-i /root/ks/inventory/labでディレクトリを渡したとき)、file_hosts(-i /root/ks/inventory/lab/inventory.ini)、ignored_ext(kubesprayのansible.cfgがインベントリのディレクトリでスキップする拡張子のうち、inventory.iniを隠すもの、ドットを含む文字列)です。/root/ks/inventory/lab/group_vars/k8s_cluster/k8s-cluster.ymlにkube_version: 1.35.8を追加してください(サンプルにはありません)。そのあとansible-inventory --host node1で実際に展開された値を読み取り、kube_version、container_manager、kube_network_plugin、kube_proxy_mode、kube_service_addresses、kube_pods_subnetの6つを文字列として書いてください(書き込み先:/root/ks/vars.json)。/root/ks/inventory/lab/group_vars/all/all.ymlの末尾にkube_version: 1.34.11を一時的に追加してから、ansible -i /root/ks/inventory/lab/inventory.ini node1 -m debug -a var=kube_versionで実際の値を確認し、同じコマンドに-e kube_version=1.36.4を付けてもう一度確認してください。次のフィールドを書いてください(書き込み先:/root/ks/precedence.json)。all_yml(all.ymlに入れた値)、effective(-eなしで勝った値)、extra_vars(-eを指定したときの値)、winner_file(-eなしで勝った値が書かれたファイルの、インベントリを基準にした相対パス)です。そのあと、all.ymlに入れた行は削除します。/root/ks/broken/inventory.iniは、誰かが[masters]と書いたインベントリです。直さずに、/opt/ks/kubesprayでansible-playbook -i /root/ks/broken/inventory.ini playbooks/boilerplate.ymlを実行してみてください。次のフィールドを書いてください(書き込み先:/root/ks/broken.json)。failed_task(失敗したタスクの名前、ロールの接頭辞なし)、node1_groups(このインベントリでnode1が属するグループ名のソートした配列、allとungroupedを除く)、missing_group(空であるはずがないのに空になっているkubesprayのグループ名)です。/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini playbooks/boilerplate.ymlを実行して、出力の全体を保存してください(保存先:/root/ks/logs/boilerplate.log)。PLAY RECAPでfailed=0である必要があります。- 次のフィールドを書いてください(書き込み先:
/root/ks/report.json)。kubespray_tag(git -C /opt/ks/kubespray describe --tags)、ansible_core(ansible --versionの1行目のバージョン番号。例: 2.19.0)、kube_version_default(このバージョンのデフォルトのkube_version)、kube_version_min(このバージョンが受け入れる最も低いバージョン)です。
参考
- VMには、kubespray v2.32.0が
/opt/ks/kubesprayに、Ansibleが/opt/ks/venvの仮想環境にあります。ansible・ansible-playbook・ansible-inventoryは、PATHにつないであります。 - プレイブックとインベントリツールは、次のディレクトリで実行してください:
/opt/ks/kubespray。そのディレクトリのansible.cfgが、rolesとlibraryのパスを教えます。 - よくあるミス:
-i /root/ks/inventory/labのようにディレクトリを渡すこと。このリポジトリの設定では、inventory.iniが無視されます。 - よくあるミス:
cp -rのコピー先がすでにあるとき、1階層深くコピーされること。 - ドキュメント: Kubespray: Inventory・Kubespray: Ansible変数の層・Ansible: 変数の優先順位
サンプルインベントリを自分の場所にコピーする
/opt/ks/kubespray/inventory/sampleを丸ごとコピーしてください(コピー先: /root/ks/inventory/lab)。group_vars/all/all.ymlとgroup_vars/k8s_cluster/k8s-cluster.ymlが、その下にある必要があります。
kubesprayは、インベントリのディレクトリの隣にあるgroup_varsを読み取ります。サンプルを直さずにコピーする理由は、次のバージョンに上げるときにサンプルが変わるからです。コピー先のディレクトリがすでにあると、cp -rはその中にsampleというサブディレクトリをもう1つ作ります。
ノード1台を3つのグループに入れる
ノードnode1をkube_control_planeとkube_nodeのグループに入れ、etcdはkube_control_planeを子グループとして受け取る([etcd:children])ように書いてください(書き込み先: /root/ks/inventory/lab/inventory.ini)。k8s_clusterも、kube_control_planeとkube_nodeを子として受け取る([k8s_cluster:children])ように書きます。接続方式は、インベントリの行ではなく、ansible_connection: localとして置いてください(置き場所: /root/ks/inventory/lab/host_vars/node1.yml)。inventory.iniにはansible_connectionがない必要があります。
このVMはコントロールノードであり対象ノードでもあるので、sshの代わりにlocal接続を使います。その事実をhost_varsに置いておけば、ノードが増えるとき、インベントリには名前だけを追加して、接続方式はノードごとのファイルで変えればよくなります。グループ名は、kubesprayが決めたつづりのままである必要があります。サンプルのinventory.iniにはk8s_clusterがありません。kubesprayがプレイブックの中で作るためですが、そうするとプレイブックの外のツール(ansible-inventory、ansibleのアドホック)は、group_vars/k8s_clusterを読み取れません。
ディレクトリを渡すとホストが0個になる
/opt/ks/kubesprayでansible-inventory --listを2回実行して、パースされたホスト数を数え、次のフィールドを書いてください(書き込み先: /root/ks/parse.json)。dir_hosts(-i /root/ks/inventory/labでディレクトリを渡したとき)、file_hosts(-i /root/ks/inventory/lab/inventory.ini)、ignored_ext(kubesprayのansible.cfgがインベントリのディレクトリでスキップする拡張子のうち、inventory.iniを隠すもの、ドットを含む文字列)です。
ホスト数は、--listの結果の_meta.hostvarsのキーの個数で数えられます。ansible-inventoryがどの設定ファイルを読み取るかはansible-config dump --only-changedで見られ、そのファイルは現在のディレクトリから探します。
バージョンをgroup_varsに固定する
/root/ks/inventory/lab/group_vars/k8s_cluster/k8s-cluster.ymlにkube_version: 1.35.8を追加してください(サンプルにはありません)。そのあとansible-inventory --host node1で実際に展開された値を読み取り、kube_version、container_manager、kube_network_plugin、kube_proxy_mode、kube_service_addresses、kube_pods_subnetの6つを文字列として書いてください(書き込み先: /root/ks/vars.json)。
kube_versionを書かないと、kubesprayのバージョンごとにデフォルト値が変わり、同じインベントリで別の日に別のKubernetesがインストールされます。ファイルに書いたものと、Ansibleが実際に展開した値が同じかどうかは、インベントリツールで確認します。YAMLでは1.35のようにドット1つの数字は実数として読まれますが、3つの区切りのバージョン番号は文字列です。
同じ変数が3か所にあるとき
/root/ks/inventory/lab/group_vars/all/all.ymlの末尾にkube_version: 1.34.11を一時的に追加してから、ansible -i /root/ks/inventory/lab/inventory.ini node1 -m debug -a var=kube_versionで実際の値を確認し、同じコマンドに-e kube_version=1.36.4を付けてもう一度確認してください。次のフィールドを書いてください(書き込み先: /root/ks/precedence.json)。all_yml(all.ymlに入れた値)、effective(-eなしで勝った値)、extra_vars(-eを指定したときの値)、winner_file(-eなしで勝った値が書かれたファイルの、インベントリを基準にした相対パス)です。そのあと、all.ymlに入れた行は削除します。
Ansibleでは、より具体的なグループのgroup_varsがallに勝ちます。k8s_clusterはallの子です。-eで渡したextra varsは、何よりも勝ちます。そのため、kubesprayのドキュメントは、-eを「内部変数を上書きするとき」に限って使うよう書いています。コマンドは、kubesprayのディレクトリで実行してください。
グループ名を間違えると何が壊れるのか
/root/ks/broken/inventory.iniは、誰かが[masters]と書いたインベントリです。直さずに、/opt/ks/kubesprayでansible-playbook -i /root/ks/broken/inventory.ini playbooks/boilerplate.ymlを実行してみてください。次のフィールドを書いてください(書き込み先: /root/ks/broken.json)。failed_task(失敗したタスクの名前、ロールの接頭辞なし)、node1_groups(このインベントリでnode1が属するグループ名のソートした配列、allとungroupedを除く)、missing_group(空であるはずがないのに空になっているkubesprayのグループ名)です。
エラーの文言は、グループ名が間違っているとは教えてくれません。失敗した条件式が、どのグループを探して失敗したかを読み取ってください。古い名前のkube-masterはkubesprayが自動で移してくれますが、mastersは違います。ノードが属するグループ(子グループを経由したものも含む)は、ansible ... -m debug -a var=group_namesで見られます。
自分のインベントリは検査を通過するか
/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini playbooks/boilerplate.ymlを実行して、出力の全体を保存してください(保存先: /root/ks/logs/boilerplate.log)。PLAY RECAPでfailed=0である必要があります。
boilerplate.ymlは、cluster.yml・upgrade-cluster.yml・reset.ymlのすべてが、先頭で呼び出す検査のまとまりです。ここで止まるのは、インストールを始める前に止まるということなので、インストールの前に別に実行しておけば、長いプレイブックを実行して1分で止まることを避けられます。
このバージョンのkubesprayが受け入れる範囲
次のフィールドを書いてください(書き込み先: /root/ks/report.json)。kubespray_tag(git -C /opt/ks/kubespray describe --tags)、ansible_core(ansible --versionの1行目のバージョン番号。例: 2.19.0)、kube_version_default(このバージョンのデフォルトのkube_version)、kube_version_min(このバージョンが受け入れる最も低いバージョン)です。
kubesprayは、デフォルトのバージョンと最小のバージョンを別に書いておらず、roles/kubespray_defaults/vars/main/checksums.ymlのkubeletのチェックサム一覧から、最初のキーと最後のキーで計算します(roles/kubespray_defaults/defaults/main/main.yml)。チェックサムがないバージョンはインストールできないという意味です。