TT Lab
はじめる
学ぶ 学習パス コース

Kubespray と Terraform でクラスターを構築する

1 年の証明書を更新し、消してから建て直す

TT Labで続きを見る

目標

kubeadmの証明書を手で更新して、コントロールプレーンが新しい証明書を使うようにした後、kubesprayの自動更新タイマーを有効にして、それがいつ実際に更新するかを確認します。最後にreset.ymlでクラスターを削除して何が残るかを見たうえで、同じインベントリで最初から構築し直します。

なぜ重要なのか

kubeadmが作ったリーフ証明書は1年ものです。1年以内にアップグレードを1回でも行えば、kubeadmが一緒に更新しますが、そうしなかったクラスターは、ある日、コンポーネント同士の認証が一斉に拒否されます。更新は、ファイルを変更して終わりではありません。そのファイルを読み取るプロセスが、新しい証明書を使っている必要があります。 そして、中途半端に壊れたクラスターを復活させるよりも、削除して構築し直すほうが速い日があります。そのときに、reset.ymlが何を削除して何を残すかを知っていて初めて、「きれいな再インストール」という言葉を信じられます。インストール2回と初期化1回で、約15分待つので、必要ならセッションを延長してください。

ステップ

  1. /opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.ymlを実行して、出力の全体を残してください(約7分、保存先: /root/ks/logs/cluster-1.log)。PLAY RECAPのnode1がfailed=0である必要があります。
  2. 次のフィールドを書いてください(書き込み先: /root/ks/certs/before.json)。apiserver_serial(/etc/kubernetes/ssl/apiserver.crtのserial、opensslが出力する16進数のまま)、apiserver_not_after(opensslのnotAfterの文字列のまま)、admin_client_serial(/etc/kubernetes/admin.conf内のclient-certificate-dataのserial)、ca_serial(/etc/kubernetes/ssl/ca.crtのserial)、node_uidです。
  3. kubeadm certs renew allでkubeadmの証明書を更新した後、コントロールプレーンが新しい証明書を使うように、kube-apiserver・kube-controller-manager・kube-schedulerの静的Podを再び起動し、/etc/kubernetes/admin.confを/root/.kube/configにコピーしてください。終わったら、apiserver.crtのserialが変わっていて、6443が返す証明書がそのファイルと同じで、controller-managerとschedulerのコンテナが、それぞれのkubeconfigが変わった後に起動したものである必要があります。
  4. auto_renew_certificatesをtrueに変更し(対象: /root/ks/inventory/lab/group_vars/k8s_cluster/k8s-cluster.yml)、cluster.yml --tags control-planeでその部分だけをもう一度実行して、出力の全体を残してください(保存先: /root/ks/logs/cp-tags.log)。終わったら、k8s-certs-renew.timerがenabledでactiveである必要があります。
  5. systemctl start k8s-certs-renew.serviceで更新サービスを今1回実行して、journalctl -u k8s-certs-renew.serviceで結果を見てください。次のフィールドを書いてください(書き込み先: /root/ks/certs/timer.json)。oncalendar(タイマーのOnCalendarの値)、renewed(今回の実行が証明書を更新したか、ブール値)、decision_line(更新するかどうかを述べている、## で始まる行をそのまま)です。
  6. /opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini reset.yml -e reset_confirmation=yesを実行して、出力の全体を残してください(約1分半、保存先: /root/ks/logs/reset.log)。そのあと次のフィールドを書いてください(書き込み先: /root/ks/certs/remnants.json)。hostname(現在のホスト名)、releases_kept(/tmp/releasesが残っているか、ブール値)、left_bins(/usr/local/binに残った通常ファイルのうち、labhub-agentを除いた名前のソートした配列。シンボリックリンクは除外)です。
  7. 同じインベントリでcluster.ymlをもう一度実行して、出力の全体を残してください(保存先: /root/ks/logs/cluster-2.log)。終わったら、node1がReadyで、新しいクラスターである証拠として、CAのserialとノードのUIDが、ステップ2の記録と異なる必要があります。

参考

更新して削除するクラスターを構築する

/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.ymlを実行して、出力の全体を残してください(約7分、保存先: /root/ks/logs/cluster-1.log)。PLAY RECAPのnode1がfailed=0である必要があります。

前のモジュールと同じインストールです。コンソールが切れても動き続けるように、systemd-runやtmuxで起動して、HOME=/rootを渡してください。

更新前のserial

次のフィールドを書いてください(書き込み先: /root/ks/certs/before.json)。apiserver_serial(/etc/kubernetes/ssl/apiserver.crtのserial、opensslが出力する16進数のまま)、apiserver_not_after(opensslのnotAfterの文字列のまま)、admin_client_serial(/etc/kubernetes/admin.conf内のclient-certificate-dataのserial)、ca_serial(/etc/kubernetes/ssl/ca.crtのserial)、node_uidです。

serialは、openssl x509 -noout -serialがserial=...として出力します。kubeconfig内の証明書はbase64で入っているので、デコードしてopensslに渡します。この値が、後で「本当に新しい証明書か」「本当に新しいクラスターか」を見分ける基準になります。

更新して、新しい証明書を使わせる

kubeadm certs renew allでkubeadmの証明書を更新した後、コントロールプレーンが新しい証明書を使うように、kube-apiserver・kube-controller-manager・kube-schedulerの静的Podを再び起動し、/etc/kubernetes/admin.confを/root/.kube/configにコピーしてください。終わったら、apiserver.crtのserialが変わっていて、6443が返す証明書がそのファイルと同じで、controller-managerとschedulerのコンテナが、それぞれのkubeconfigが変わった後に起動したものである必要があります。

kubeadmは、更新が終わると「再起動するように」と出力します。静的Podはkubeletが起動するので、Podのサンドボックスを削除すると(crictl pods --name ... -q | xargs crictl rmp -f)、kubeletがマニフェストを見て再び起動します。kubesprayの/usr/local/bin/k8s-certs-renew.shが同じ順序を使っているので、読んでみてください。削除してから数秒間APIが応答しないのは、正常です。

更新をカレンダーに任せる

auto_renew_certificatesをtrueに変更し(対象: /root/ks/inventory/lab/group_vars/k8s_cluster/k8s-cluster.yml)、cluster.yml --tags control-planeでその部分だけをもう一度実行して、出力の全体を残してください(保存先: /root/ks/logs/cp-tags.log)。終わったら、k8s-certs-renew.timerがenabledでactiveである必要があります。

このスイッチは、コントロールプレーンのロールに、systemdのタイマーとサービスをインストールさせます。いつ動くかはauto_renew_certificates_systemd_calendarが決め、systemctl list-timersが次の実行時刻を表示します。デフォルトは、毎月第1月曜日です。

タイマーはいつ実際に更新するのか

systemctl start k8s-certs-renew.serviceで更新サービスを今1回実行して、journalctl -u k8s-certs-renew.serviceで結果を見てください。次のフィールドを書いてください(書き込み先: /root/ks/certs/timer.json)。oncalendar(タイマーのOnCalendarの値)、renewed(今回の実行が証明書を更新したか、ブール値)、decision_line(更新するかどうかを述べている、## で始まる行をそのまま)です。

スクリプトは、次のタイマーの時刻に7日の余裕を加えた時点より先に期限切れになる証明書があるときにだけ更新して、コントロールプレーンを再び起動します。更新したばかりの証明書は1年が残っているので、どうなるかを先に予想してから、確認してください。スクリプトの本文は、/usr/local/bin/k8s-certs-renew.shにあります。

reset.ymlで削除する: 何が残るのか

/opt/ks/kubesprayでansible-playbook -i /root/ks/inventory/lab/inventory.ini reset.yml -e reset_confirmation=yesを実行して、出力の全体を残してください(約1分半、保存先: /root/ks/logs/reset.log)。そのあと次のフィールドを書いてください(書き込み先: /root/ks/certs/remnants.json)。hostname(現在のホスト名)、releases_kept(/tmp/releasesが残っているか、ブール値)、left_bins(/usr/local/binに残った通常ファイルのうち、labhub-agentを除いた名前のソートした配列。シンボリックリンクは除外)です。

reset.ymlは元に戻せない作業なので、確認用の変数を要求します。何を削除するかは、roles/reset/tasks/main.ymlの一覧にあります。その一覧にないものは残ります。残ったものを知っておけば、「きれいに削除した」という言葉を、どこまで信じるかを決められます。

最初から構築し直す

同じインベントリでcluster.ymlをもう一度実行して、出力の全体を残してください(保存先: /root/ks/logs/cluster-2.log)。終わったら、node1がReadyで、新しいクラスターである証拠として、CAのserialとノードのUIDが、ステップ2の記録と異なる必要があります。

resetは/etc/kubernetesを丸ごと削除するので、CAもなくなります。再び構築すると新しいCAが作られ、そのCAで署名された古いkubeconfigは、もう使えません。ダウンロードしたファイルのキャッシュ(/tmp/releases)が残っているので、最初のインストールより少し速いです。