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

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

ファイルを替えることと新しい証明書を使うこと — 更新と初期化

TT Labで続きを見る

一言でいうと

証明書の更新は、ファイルを変更することと、そのファイルを読み取るプロセスを再び起動することの2段階です。kubesprayのタイマーは、期限が近いときにだけ、その2段階を代わりに行います。reset.ymlはクラスターを削除しますが、ダウンロードしたファイルといくつかの痕跡は残します。

なぜ必要なのか

kubeadmの証明書管理のドキュメントによると、kubeadmが作ったクライアント証明書は1年後に期限切れになり、コントロールプレーンをアップグレードするとき、kubeadmがすべて更新します。ドキュメントはそのため、「頻繁にアップグレードするのが最善の方法」と書いています。ところが、現場のクラスターは1年以上バージョンを固定しておくことがよくあり、そのようなクラスターは、インストールから1年になる日に、APIサーバーとコントローラーの間の認証が一斉に失敗します。通知がなければ、原因を探すだけでもかなり時間がかかります。

どう動くのか

kubeadmの証明書は、kubesprayでは/etc/kubernetes/sslにあります。kubeadmのデフォルトは/etc/kubernetes/pkiですが、kubesprayはkube_cert_dirをsslにして、pkiをそちらにリンクします。kubeadm certs check-expirationは、リーフ証明書(apiserver、apiserver-kubelet-client、front-proxy-client、そしてadmin.conf・controller-manager.conf・scheduler.conf・super-admin.conf内のクライアント証明書)とCAを表示し、外部etcdであるこの配置では、etcdの証明書は表示しません(モジュール4)。

更新は2段階です。kubeadm certs renew allは、CAでリーフ証明書を再署名して、ファイルとkubeconfigを変更します。そして「kube-apiserver、kube-controller-manager、kube-scheduler、etcdを再起動して初めて新しい証明書を使う」と出力します。実測すると、APIサーバーのサービング証明書は、ファイルが変わると再起動の前にも、6443で新しいserialで出てきました。kube-apiserverがサービング証明書のファイルを読み直すからです。しかし、controller-managerとschedulerは、kubeconfig内のクライアント証明書を起動時に読み取ります。そのため、kubesprayのk8s-certs-renew.shは、更新後にcrictl rmpで3つの静的Podのサンドボックスを削除し(kubeletがマニフェストを見て再び起動します)、/root/.kube/configを新しいadmin.confに置き換え、6443が再び開くまで待ちます。実測では、サンドボックスを削除してから/readyzが戻るまで6秒でした。

タイマーは、期限が近いときにだけ動きます。auto_renew_certificates: trueにすると、コントロールプレーンのロールがk8s-certs-renew.timerをインストールします。デフォルトのカレンダーはMon *-*-1,2,3,4,5,6,7 03:00:00で、つまり毎月第1月曜日の午前3時です。スクリプトは、「次のタイマーの時刻 + 7日」より先に期限切れになる証明書があるときにだけ更新して再起動します。更新したばかりの証明書でサービスを実行すると、## Skip cert renew and K8S container restart, since all residualTimes are beyond threshold ##で終わります。毎月コントロールプレーンを揺らさずに、期限の1か月前ごろに1回だけ再起動するように設計されているのです。

reset.ymlは元に戻せません。そのため、reset_confirmation=yesを指定しないと、プロンプトで止まります。ロールは、サービス(kubelet・containerd・etcd)を停止し、コンテナとPodを削除し、iptablesとIPVSのルールを空にし、長い一覧のファイルとディレクトリを削除します。/etc/kubernetes、/var/lib/kubelet、etcdのデータ、containerdのストレージ、/etc/cni、~/.kube、kubesprayがインストールしたバイナリ類です。この削除タスクはignore_errorsなので、1つが失敗しても、残りを削除し続けます。逆に、一覧にないものは残ります。実測では、ダウンロードしたファイルのキャッシュ/tmp/releases(543MB)と/usr/local/bin/etcdutl、そしてkubesprayが変更したホスト名が残りました。ダウンロードしたファイルが残るのは意図された便宜で、再インストールが速くなり(実測326秒)、etcdutlは削除の一覧から漏れたものです。

現場での姿

1年目の障害の典型的な順序は、こうです。ある日、kubectlがx509: certificate has expiredで拒否され、少し後にコントローラーが止まって、新しいPodが作られません。急いでkubeadm certs renew allを実行したのに、依然としておかしいです。ファイルは新しいのに、controller-managerは古いkubeconfigで起動していて、運用者の~/.kube/configも、古い証明書のままです。2番目の段階(再起動とkubeconfigの配布)までやって、初めて終わりです。このコースの推奨は単純です。アップグレードを1年に1回はやり、それができないクラスターなら、auto_renew_certificatesを有効にしておき、check-expirationの結果をモニタリングに入れます。

resetは、2つの場合に使います。モジュール3で見たように、最初のインストールがCNIの手前で止まって、再実行しても続きから進まないとき、そして、インストール後に変更しにくい値(ネットワークプラグイン、PodとServiceのCIDR範囲)を変更する必要があるときです。どちらも「削除して、同じインベントリでやり直す」のが最も速い道で、インベントリが宣言として残っているからこそ、可能な選択です。

次のラボですること

クラスターを構築して証明書のserialを記録した後、手で更新して静的Podを再び起動し、新しい証明書が使われているかを確認します。自動更新のタイマーを有効にして、今1回実行し、スクリプトがなぜ更新しないかを見ます。reset.ymlで削除した後に残ったものを書き、同じインベントリで再び構築して、CAとノードが新しいかを確認します。