Kubespray と Terraform でクラスターを構築する
インストール前に、このサーバーは受け入れられるか
目標
前任の管理者が使っていたサーバーにKubernetesをインストールする前に、スワップ・カーネルモジュール・sysctl・ポートを点検して修正します。そして、このうち何をkubesprayが自分で行ってくれて、何を人が先に片付ける必要があるのかを、ロールのコードを読んで見分けます。
なぜ重要なのか
新しく受け取ったVMでは、kubesprayがすべて自動でやってくれるように見えます。実際に、スワップを無効にし、br_netfilterを読み込み、ip_forwardを有効にする作業は、preinstallロールとnodeロールが行います。ところが、現場のサーバーは新品ではありません。監視エージェントが10250を占有していると、kubesprayはそのままインストールを最後まで押し進め、kubeletがポートを確保できずにノードが立ち上がりません。セキュリティ点検が残したsysctlファイルが名前順で後ろにあると、インストール当日は問題なく動き、最初のリブートでPodの通信が切れます。どちらも、長いインストールをすべて実行した後で初めて明らかになる種類なので、インストール前に1回見る値が、最も安上がりです。
ステップ
- 何も修正する前に、次のフィールドを書いてください(書き込み先:
/root/ks/preflight/before.json)。swap_total_kb(/proc/meminfoのSwapTotal、数値)、ip_forward(現在のカーネルの値、数値)、br_netfilter(モジュールが読み込まれているか、ブール値)、busy_ports(コントロールプレーンのポート2379、2380、6443、10250、10257、10259のうち、現在何かがLISTENしているポート、昇順の数値の配列)です。 - 有効になっているスワップをすべて無効にし、
/etc/fstabからスワップの行を削除するか、コメントにして、リブートしても再び有効にならないようにしてください。スワップファイルそのものは、削除してもそのまま残してもかまいません。 overlayとbr_netfilterを現在読み込み、2つの名前を1行ずつ書いて、起動時にも読み込まれるようにしてください(書き込み先:/etc/modules-load.d/k8s.conf)。net.ipv4.ip_forward = 1、net.bridge.bridge-nf-call-iptables = 1、net.bridge.bridge-nf-call-ip6tables = 1を書いて適用してください(書き込み先:/etc/sysctl.d/k8s.conf)。現在のカーネルの値が3つとも1である必要があり、リブート時にsysctl.dを名前順に読み直しても3つとも1である必要があります。- コントロールプレーンのポートのうち、何かが占有しているポートを探して、それを起動したsystemdユニットを停止し、起動時に再び起動しないように(disable)してください。次のフィールドを書きます(書き込み先:
/root/ks/preflight/ports.json)。port(占有されていたポート、数値)、unit(そのプロセスを起動したユニット名、.serviceを含む)、pid(停止前のそのプロセスのPID、数値)です。終わったら、2379、2380、6443、10250、10257、10259のどれもLISTENではない必要があります。 /opt/ks/kubesprayでansible -i /root/ks/inventory/lab/inventory.ini node1 -m setupを使ってこのノードのファクト(facts)を収集し、次のフィールドを書いてください(書き込み先:/root/ks/preflight/facts.json)。memtotal_mb、processor_vcpus、distribution、distribution_version、kernel、default_ipv4(デフォルト経路のIPv4アドレス)です。そして、kubesprayのroles/kubernetes/preinstall/defaults/main.ymlからコントロールプレーンの最小メモリを探して、minimal_master_memory_mbとして一緒に書きます。- kubespray v2.32.0の
roles/kubernetes/preinstallとroles/kubernetes/nodeを読み取り、4つの問題ごとに、kubesprayが自分で直すなら"kubespray"を、人が先に片付ける必要があるなら"operator"を書いてください(書き込み先:/root/ks/preflight/who-fixes.json)。キーはswap、br_netfilter、ip_forward、port_10250です。そして、sysctl_file(kubesprayがsysctlの値を書くファイルのパス、デフォルト値)も一緒に書きます。
参考
- VMには、kubespray v2.32.0と、インベントリ
/root/ks/inventory/lab/inventory.ini(ノード1台、local接続)が用意されています。このラボでは、インストールは行いません。 - よくあるミス:
swapoff -aだけを実行して、fstabをそのままにすることです。次の起動で再び有効になります。 - よくあるミス: プロセスだけをkillすることです。
Restart=alwaysのユニットがすぐに再び起動します。 - よくあるミス: 自分のsysctlファイルだけを見て終わりにすることです。
sysctl --systemが読み取る順序を確認してください。 - ドキュメント: Kubernetes: Ports and Protocols・Kubernetes: Container Runtimes(前提条件)・Kubespray: Port requirements
修正する前に現在の状態を書く
何も修正する前に、次のフィールドを書いてください(書き込み先: /root/ks/preflight/before.json)。swap_total_kb(/proc/meminfoのSwapTotal、数値)、ip_forward(現在のカーネルの値、数値)、br_netfilter(モジュールが読み込まれているか、ブール値)、busy_ports(コントロールプレーンのポート2379、2380、6443、10250、10257、10259のうち、現在何かがLISTENしているポート、昇順の数値の配列)です。
LISTENしているソケットは、ss -ltnpで見ます。モジュールが読み込まれているかどうかは、lsmodや/sys/module/<이름>で確認します(プレースホルダーはモジュール名です)。採点ツールは、VMが準備されるときに別に測った値と照合するので、修正した後に書くと合いません。
スワップを無効にして、再び有効にならないようにする
有効になっているスワップをすべて無効にし、/etc/fstabからスワップの行を削除するか、コメントにして、リブートしても再び有効にならないようにしてください。スワップファイルそのものは、削除してもそのまま残してもかまいません。
swapoffは、現在だけを無効にします。起動時にスワップを有効にするのは、fstabのtypeがswapの行です。どのファイルがスワップかは、swapon --showや/proc/swapsにあります。
カーネルモジュールを、現在も起動時も
overlayとbr_netfilterを現在読み込み、2つの名前を1行ずつ書いて、起動時にも読み込まれるようにしてください(書き込み先: /etc/modules-load.d/k8s.conf)。
containerdのデフォルトのスナップショッターがoverlayを使い、ブリッジを通るPodのトラフィックがiptablesを通るようにするのがbr_netfilterです。modprobeは現在だけ、modules-load.dは次の起動からです。
sysctlは、最後に読まれたファイルが勝つ
net.ipv4.ip_forward = 1、net.bridge.bridge-nf-call-iptables = 1、net.bridge.bridge-nf-call-ip6tables = 1を書いて適用してください(書き込み先: /etc/sysctl.d/k8s.conf)。現在のカーネルの値が3つとも1である必要があり、リブート時にsysctl.dを名前順に読み直しても3つとも1である必要があります。
sysctl --systemを実行した後で、ip_forwardをもう一度読み取ってみてください。sysctl.dのファイルは名前順に読まれ、同じキーは後のものが勝ちます。sysctl --systemの出力が、どのファイルをどの順序で読んだかを示します。前任の管理者のファイルを削除するか、名前を変えるか、自分のファイルを後ろに送るかは、判断です。
kubeletの場所を占有したもの
コントロールプレーンのポートのうち、何かが占有しているポートを探して、それを起動したsystemdユニットを停止し、起動時に再び起動しないように(disable)してください。次のフィールドを書きます(書き込み先: /root/ks/preflight/ports.json)。port(占有されていたポート、数値)、unit(そのプロセスを起動したユニット名、.serviceを含む)、pid(停止前のそのプロセスのPID、数値)です。終わったら、2379、2380、6443、10250、10257、10259のどれもLISTENではない必要があります。
ss -ltnpがPIDを表示し、PIDを起動したユニットは、systemctl status <PID>や/proc/<PID>/cgroupで見えます。プロセスだけを終了させると、Restart=alwaysのユニットがすぐに再び起動します。kubesprayは、このポートを空けてくれません。最初のkubeadm initはpreflightのPort-10250のエラーで止まりますが、kubesprayの再試行は、そのエラーを無視リストに入れてもう一度実行するので、原因はログの上のほうの最初の試行にしか残りません(control-plane/tasks/kubeadm-setup.yml)。
kubesprayが見るこのホスト
/opt/ks/kubesprayでansible -i /root/ks/inventory/lab/inventory.ini node1 -m setupを使ってこのノードのファクト(facts)を収集し、次のフィールドを書いてください(書き込み先: /root/ks/preflight/facts.json)。memtotal_mb、processor_vcpus、distribution、distribution_version、kernel、default_ipv4(デフォルト経路のIPv4アドレス)です。そして、kubesprayのroles/kubernetes/preinstall/defaults/main.ymlからコントロールプレーンの最小メモリを探して、minimal_master_memory_mbとして一緒に書きます。
kubesprayの事前チェック(0040-verify-settings.yml)は、これらのファクトで判定します。メモリが最小値より小さいと、インストールが開始から数分で止まります。setupモジュールの出力はansible_factsの下にあり、デフォルト経路のアドレスはdefault_ipv4.addressです。kubesprayは、ip変数を別に指定しないと、このアドレスにAPIサーバーとetcdを結び付けます。
何をツールに任せて、何を人が見るのか
kubespray v2.32.0のroles/kubernetes/preinstallとroles/kubernetes/nodeを読み取り、4つの問題ごとに、kubesprayが自分で直すなら"kubespray"を、人が先に片付ける必要があるなら"operator"を書いてください(書き込み先: /root/ks/preflight/who-fixes.json)。キーはswap、br_netfilter、ip_forward、port_10250です。そして、sysctl_file(kubesprayがsysctlの値を書くファイルのパス、デフォルト値)も一緒に書きます。
preinstallの0010-swapoff.ymlは、fstabのスワップの行を削除し、swap.targetをマスクして、swapoffを呼び出します。モジュールとsysctlは、nodeロールとpreinstallロールで見つけられ、sysctlファイルのパスは、kubespray_defaultsのsysctl_file_pathです。ポートを空ける作業は、どこにもありません。ステップ4で体験したように、kubesprayが書くファイルよりも名前順で後ろに来るファイルがあると、リブート時に値が元に戻る点も、一緒に考えてみてください。