要证据,不要绿灯 — 逐层验证安装结果
目标
按节点 → 核心 Pod → DNS → 证书 → 留下的文件 → 真实工作负载的顺序,确认用 kubespray 搭建的集群, 并把“接下来什么时候该做什么”写成一页报告。
为什么重要
PLAY RECAP 中的 failed=0 意味着“playbook 一路运行到了结束”,而不是“集群可以使用”。kubespray 在 kubeadm 之上 用自己的方式叠加了一些东西——把 etcd 作为主机服务单独搭建,在 Pod DNS 前面放一个节点本地缓存,并搬动证书目录。 所以如果照着检查亲手用 kubeadm 搭建的集群的习惯来看,就会漏掉 etcd 证书,或者只看 CoreDNS 就判断 DNS 可用。 把每一层都亲自确认一遍的顺序练熟之后,无论是在安装之后,还是在升级之后,都能用同样的顺序检查。
步骤
- 在
/opt/ks/kubespray中运行ansible-playbook -i /root/ks/inventory/lab/inventory.ini cluster.yml,把完整输出保存到/root/ks/logs/cluster-1.log(约 7 分钟)。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(在节点上分别向两个地址查询kubernetes.default.svc.cluster.local得到的 A 记录)。两个答案必须与 kubernetes Service 的 ClusterIP 相同。 - 在
/root/ks/verify/certs.json中,以 UTC2026-01-01T00:00:00Z格式写入apiserver_not_after、ca_not_after(/etc/kubernetes/ssl中的 apiserver.crt、ca.crt)、etcd_member_not_after(/etc/ssl/etcd/ssl/member-node1.pem),并写入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的第三列)、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)。两个 Pod 都变为 Ready 之后,在节点上多次调用curl http://<web 서비스 IP>/hostname(占位符为 web Service 的 IP),确认两个 Pod 的名称都返回了,并把这两个名称排序后,每行一个地写入/root/ks/verify/smoke.txt。 - 在
/root/ks/verify/report.json中写入node_ready(布尔值)、core_apps(第 3 步 apps 的个数,数字)、dns_ok(第 4 步的两个答案是否与 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,inventory 位于/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,把完整输出保存到 /root/ks/logs/cluster-1.log(约 7 分钟)。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 污点。然而这个节点也同时位于 inventory 的 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 搭建的集群的不同之处在这里显现出来。请在 group_vars/all/etcd.yml 中找出 kubespray 默认的 etcd 部署方式(etcd_deployment_type)是什么,并用 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(在节点上分别向两个地址查询 kubernetes.default.svc.cluster.local 得到的 A 记录)。两个答案必须与 kubernetes Service 的 ClusterIP 相同。
kubespray 默认会打开 nodelocaldns(enable_nodelocaldns: true)。每个节点上都在链路本地地址上运行缓存 DNS,kubelet 会把这个地址告知 Pod。CoreDNS Service 的名称可能与 kubeadm 默认值不同,所以请在 Service 列表中通过选择器来查找。dig +short @<주소> <이름>(占位符依次为地址和名称)只返回答案。
证书——两支不同的到期日
在 /root/ks/verify/certs.json 中,以 UTC 2026-01-01T00:00:00Z 格式写入 apiserver_not_after、ca_not_after(/etc/kubernetes/ssl 中的 apiserver.crt、ca.crt)、etcd_member_not_after(/etc/ssl/etcd/ssl/member-node1.pem),并写入 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 的第三列)、releases_mb(/tmp/releases 的大小,MiB 整数——du -sm)。
kubespray 会把调用 kubeadm 时使用的配置文件和 etcd 环境文件留在节点上。它们是日后确认安装用了什么值的第一手资料。/tmp/releases 是 kubespray 下载的二进制文件缓存(local_release_dir),也是重新安装或升级速度很快的原因。
最后的证据是工作负载
在 default 命名空间中创建 Deployment web(镜像 registry.k8s.io/e2e-test-images/agnhost:2.59,参数 netexec --http-port=8080,副本数 2)以及同名的 ClusterIP Service(端口 80 → 8080)。两个 Pod 都变为 Ready 之后,在节点上多次调用 curl http://<web 서비스 IP>/hostname(占位符为 web Service 的 IP),确认两个 Pod 的名称都返回了,并把这两个名称排序后,每行一个地写入 /root/ks/verify/smoke.txt。
这一步通过之后,下载镜像(containerd、注册表出口)、调度(污点)、Pod 网络(calico)、Service(kube-proxy)就一次性得到了确认。这就是为什么要发送真实请求,而不是看绿灯。agnhost 的 netexec 会在 /hostname 上返回 Pod 名称。
验证报告
在 /root/ks/verify/report.json 中写入 node_ready(布尔值)、core_apps(第 3 步 apps 的个数,数字)、dns_ok(第 4 步的两个答案是否与 kubernetes Service 的 IP 相同,布尔值)、first_cert_to_expire(kubeadm 叶子证书和 etcd member 证书中先到期的一方:"kubeadm" 或 "etcd")、days_until_first_expiry(该证书到期之前还剩的天数,整数)、smoke_pods(第 7 步中响应的 Pod 数,数字)。
这些都可以根据前面步骤的记录和当前的集群重新计算。剩余天数,是用从当前时刻到 notAfter 的时间向下取整得到的整数。这份报告的目的,是把“接下来什么时候该做什么”用一行留下来。