Kubespray と Terraform でクラスターを構築する
緑のランプではなく証拠で — インストール結果を層ごとに確かめる
目標
kubesprayで構築したクラスターを、ノード → コアPod → DNS → 証明書 → 残ったファイル → 実際のワークロードの順に確認し、「次に何をいつやるべきか」を1枚のレポートにまとめます。
なぜ重要なのか
PLAY RECAPのfailed=0は、「プレイブックが最後まで動いた」という意味であり、「クラスターが使える」という意味ではありません。kubesprayは、kubeadmの上にいくつかのものを独自の方式で載せます。etcdをホストのサービスとして別に構築し、PodのDNSの手前にノードローカルのキャッシュを置き、証明書のディレクトリを移します。 そのため、kubeadmで自分で構築したクラスターを点検していた習慣のままで見ると、etcdの証明書を見落としたり、CoreDNSだけを見てDNSが動いていると判断したりすることになります。層ごとに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である必要があります。- 次のフィールドを書いてください(書き込み先:
/root/ks/verify/node.json)。ready(Ready条件のstatus文字列)、kubelet_version、container_runtime(nodeInfo.containerRuntimeVersion)、internal_ip、roles(node-role.kubernetes.io/で始まるラベルのロール名をソートした配列)、taints(키:효과の文字列の配列、なければ空の配列。プレースホルダーはキーと効果です)です。 - kube-systemのPodを確認して、次のフィールドを書いてください(書き込み先:
/root/ks/verify/pods.json)。apps(各Podのk8s-appラベル、なければcomponentラベルの値を集めた、重複のないソートした配列)、not_ready(ReadyではないPod名の配列、なければ空の配列)、etcd_is_pod(etcdがPodとして起動しているか、ブール値)、etcd_unit(etcdを起動したsystemdユニット名、.serviceを含む)です。 - 次のフィールドを書いてください(書き込み先:
/root/ks/verify/dns.json)。kubelet_cluster_dns(/var/lib/kubelet/config.yamlのclusterDNSの最初の値)、coredns_service_ip(kube-systemのCoreDNS ServiceのClusterIP)、answer_via_nodelocal、answer_via_coredns(ノードから2つのアドレスにそれぞれkubernetes.default.svc.cluster.localを問い合わせたAレコード)です。2つの答えは、kubernetes ServiceのClusterIPと同じである必要があります。 - 次のフィールドを書いてください(書き込み先:
/root/ks/verify/certs.json)。apiserver_not_after、ca_not_after(/etc/kubernetes/sslのapiserver.crtとca.crt)、etcd_member_not_after(/etc/ssl/etcd/ssl/member-node1.pem)をUTCの2026-01-01T00:00:00Z形式で、apiserver_valid_days、etcd_member_valid_days(各証明書のnotBeforeからnotAfterまでの日数、整数)とkubeadm_lists_etcd(kubeadm certs check-expirationの出力にetcdの証明書の行があるか、ブール値)です。 - 次のフィールドを書いてください(書き込み先:
/root/ks/verify/files.json)。kubeadm_config_version(/etc/kubernetes/kubeadm-config.yamlのClusterConfigurationのkubernetesVersion)、etcd_data_dir(/etc/etcd.envのETCD_DATA_DIR)、kubeconfig_server(/root/.kube/configのserverアドレス)、containerd_version(containerd --versionの3番目の列)、releases_mb(/tmp/releasesのサイズ、MiB単位の整数。du -sm)です。 - defaultネームスペースにDeployment
web(イメージはregistry.k8s.io/e2e-test-images/agnhost:2.59、引数はnetexec --http-port=8080、レプリカ2)と、同じ名前のClusterIP Service(ポート80 → 8080)を作成してください。2つのPodがReadyになったら、ノードからcurl http://<web 서비스 IP>/hostnameを何回か実行して(プレースホルダーはwebサービスのIPです)、2つのPod名がどちらも返ってくることを確認し、その2つの名前をソートして、1行に1つずつ書いてください(書き込み先:/root/ks/verify/smoke.txt)。 - 次のフィールドを書いてください(書き込み先:
/root/ks/verify/report.json)。node_ready(ブール値)、core_apps(ステップ3のappsの個数、数値)、dns_ok(ステップ4の2つの答えがkubernetes ServiceのIPと同じか、ブール値)、first_cert_to_expire(kubeadmのリーフ証明書とetcdのmember証明書のうち、先に期限切れになるほう:"kubeadm"または"etcd")、days_until_first_expiry(その証明書が期限切れになるまでの残りの日数、整数)、smoke_pods(ステップ7で応答したPodの数、数値)です。
参考
- kubespray v2.32.0が
/opt/ks/kubesprayに、インベントリが/root/ks/inventory/lab/inventory.ini(kube_version 1.35.8)に用意されています。インストールは、ステップ1で自分で行います(約7分)。 digはインストールされています。kubectlとkubeadmは、インストールが終わると/usr/local/binにできます。- よくあるミス:
kubeadm certs check-expirationだけを見て、証明書の点検を終わりにすることです。この配置では、etcdの証明書はその一覧にありません。 - よくあるミス: CoreDNSのPodがRunningであることだけを見て、DNSを確認したと言うことです。Podが実際に問い合わせるアドレスは、別にあります。
- ドキュメント: Kubespray: DNS stack・Kubernetes: Certificate Management with kubeadm・Kubernetes: NodeLocal DNSCache
検証するクラスターを構築する
/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である必要があります。
モジュール3で行ったインストールと同じです。コンソールが切れても動き続けるように、systemd-runやtmuxで起動して、HOME=/rootを渡してください。待っている間に、このラボの次のステップを読んでおくとよいです。
ノード: Readyとテイント
次のフィールドを書いてください(書き込み先: /root/ks/verify/node.json)。ready(Ready条件のstatus文字列)、kubelet_version、container_runtime(nodeInfo.containerRuntimeVersion)、internal_ip、roles(node-role.kubernetes.io/で始まるラベルのロール名をソートした配列)、taints(키:효과の文字列の配列、なければ空の配列。プレースホルダーはキーと効果です)です。
kubeadmは、コントロールプレーンのノードにNoScheduleテイントを付けます。ところが、このノードはインベントリのkube_nodeにも入っています。kubesprayがその事実を見て何をしたかを、テイントの一覧で確認してください。ロール名は、ラベルのキーの最後の部分です。
コアPod: 何が動いていて、何がないのか
kube-systemのPodを確認して、次のフィールドを書いてください(書き込み先: /root/ks/verify/pods.json)。apps(各Podのk8s-appラベル、なければcomponentラベルの値を集めた、重複のないソートした配列)、not_ready(ReadyではないPod名の配列、なければ空の配列)、etcd_is_pod(etcdがPodとして起動しているか、ブール値)、etcd_unit(etcdを起動したsystemdユニット名、.serviceを含む)です。
kubeadmで自分で構築したクラスターとの違いが、ここで見えます。kubesprayのデフォルトのetcdの配置(etcd_deployment_type)が何かを、group_vars/all/etcd.ymlで探し、systemctl list-unitsで実際のユニットを確認してください。
DNS: Podの問い合わせ先はCoreDNSではなく別のアドレス
次のフィールドを書いてください(書き込み先: /root/ks/verify/dns.json)。kubelet_cluster_dns(/var/lib/kubelet/config.yamlのclusterDNSの最初の値)、coredns_service_ip(kube-systemのCoreDNS ServiceのClusterIP)、answer_via_nodelocal、answer_via_coredns(ノードから2つのアドレスにそれぞれkubernetes.default.svc.cluster.localを問い合わせたAレコード)です。2つの答えは、kubernetes ServiceのClusterIPと同じである必要があります。
kubesprayはデフォルトでnodelocaldns(enable_nodelocaldns: true)を有効にします。ノードごとにリンクローカルアドレスでキャッシュDNSが動き、kubeletはPodにそのアドレスを知らせます。CoreDNS Serviceの名前は、kubeadmのデフォルトと異なることがあるので、Serviceの一覧からセレクターで探してください。dig +short @<주소> <이름>が、答えだけを返します(プレースホルダーはアドレスと名前です)。
証明書: 2系統の有効期限
次のフィールドを書いてください(書き込み先: /root/ks/verify/certs.json)。apiserver_not_after、ca_not_after(/etc/kubernetes/sslのapiserver.crtとca.crt)、etcd_member_not_after(/etc/ssl/etcd/ssl/member-node1.pem)をUTCの2026-01-01T00:00:00Z形式で、apiserver_valid_days、etcd_member_valid_days(各証明書のnotBeforeからnotAfterまでの日数、整数)とkubeadm_lists_etcd(kubeadm certs check-expirationの出力にetcdの証明書の行があるか、ブール値)です。
kubesprayは、kubeadmの証明書のディレクトリを/etc/kubernetes/sslにします(kubeadmのデフォルトはpki)。etcdをホストのサービスとして起動するデフォルトの配置では、etcdの証明書をkubeadmではなくkubesprayがopensslで作り、その期間はkubespray_defaultsのcertificates_durationです。片方だけを見て「証明書の期限の点検は終わり」と言ってはいけない理由が、ここにあります。
kubesprayが残したもの
次のフィールドを書いてください(書き込み先: /root/ks/verify/files.json)。kubeadm_config_version(/etc/kubernetes/kubeadm-config.yamlのClusterConfigurationのkubernetesVersion)、etcd_data_dir(/etc/etcd.envのETCD_DATA_DIR)、kubeconfig_server(/root/.kube/configのserverアドレス)、containerd_version(containerd --versionの3番目の列)、releases_mb(/tmp/releasesのサイズ、MiB単位の整数。du -sm)です。
kubesprayは、kubeadmを呼び出すときに使った設定ファイルと、etcdの環境ファイルを、ノードに残します。インストールがどの値で行われたかを、後で確認するための一次資料です。/tmp/releasesは、kubesprayがダウンロードしたバイナリのキャッシュ(local_release_dir)で、再インストールやアップグレードが速い理由でもあります。
最後の証拠はワークロード
defaultネームスペースにDeploymentweb(イメージはregistry.k8s.io/e2e-test-images/agnhost:2.59、引数はnetexec --http-port=8080、レプリカ2)と、同じ名前のClusterIP Service(ポート80 → 8080)を作成してください。2つのPodがReadyになったら、ノードからcurl http://<web 서비스 IP>/hostnameを何回か実行して(プレースホルダーはwebサービスのIPです)、2つのPod名がどちらも返ってくることを確認し、その2つの名前をソートして、1行に1つずつ書いてください(書き込み先: /root/ks/verify/smoke.txt)。
このステップが通過すれば、イメージのダウンロード(containerdとレジストリへのエグレス)、スケジューリング(テイント)、Podネットワーク(calico)、サービス(kube-proxy)が、一度に確認できます。緑の表示の代わりに実際のリクエストを送る理由です。agnhostのnetexecは、/hostnameにPod名を返します。
検証のレポート
次のフィールドを書いてください(書き込み先: /root/ks/verify/report.json)。node_ready(ブール値)、core_apps(ステップ3のappsの個数、数値)、dns_ok(ステップ4の2つの答えがkubernetes ServiceのIPと同じか、ブール値)、first_cert_to_expire(kubeadmのリーフ証明書とetcdのmember証明書のうち、先に期限切れになるほう: "kubeadm"または"etcd")、days_until_first_expiry(その証明書が期限切れになるまでの残りの日数、整数)、smoke_pods(ステップ7で応答したPodの数、数値)です。
前のステップの記録と現在のクラスターから、すべて再計算できます。残りの日数は、現在の時刻からnotAfterまでを切り捨てた整数で計算します。このレポートの目的は、「次に何をいつやるべきか」を1行で残すことです。