1. 静态 Pod:Kubernetes 集群的“基石守护者”

在 Kubernetes 集群的日常运维和搭建过程中,我们接触最多的可能是通过 Deployment、StatefulSet 等控制器动态管理的 Pod。但你是否想过,那些负责管理集群自身核心组件的 Pod,比如 kube-apiserver、kube-scheduler、kube-controller-manager,它们是如何被启动和管理的?它们可不能因为节点重启或调度问题而轻易消失。答案就是 静态 Pod 。静态 Pod 是 K8s 中一个独特且至关重要的概念,它不由 Kubernetes 主控节点上的 API Server 和 Scheduler 管理,而是由特定节点上的 kubelet 守护进程直接监视并维护。简单来说,静态 Pod 是 kubelet 的“自留地”,是保障集群基础设施稳定运行的基石。理解静态 Pod,不仅是深入掌握 K8s 架构的关键,也是进行高可用集群部署、排查核心组件故障的必备技能。无论你是正在搭建自己的第一个 K8s 集群,还是需要维护一个生产环境,搞懂静态 Pod 的工作原理和实操细节,都能让你在面对集群核心服务时更加从容。

2. 核心原理:为什么需要静态 Pod?

要理解静态 Pod 的价值,我们必须先回到 Kubernetes 集群的启动流程这个根本问题上。一个典型的 K8s 集群包含控制平面(Control Plane)和工作节点(Node)。控制平面的核心组件(如 API Server)需要先运行起来,才能接收和处理创建其他 Pod 的请求。这就形成了一个“先有鸡还是先有蛋”的悖论: 谁来自动启动和管理这些控制平面组件本身?

2.1 静态 Pod 的设计哲学与解决的核心问题

Kubernetes 的设计者通过静态 Pod 巧妙地解决了这个引导问题。其核心设计哲学是 “去中心化”和“节点自治” 。静态 Pod 的定义文件(通常是 YAML 或 JSON 格式)不提交给 API Server,而是直接放置在集群节点上由 kubelet 监视的特定目录中(默认为 /etc/kubernetes/manifests )。kubelet 会周期性地扫描这个目录,一旦发现文件,就会根据其内容在本节点上创建并运行对应的 Pod。

这种设计解决了几个关键问题:

  1. 集群引导 :在 API Server 自身尚未启动时,kubelet 就可以根据静态 Pod 定义文件将其启动起来,从而完成了集群控制平面的自举过程。
  2. 高可用与独立性 :即使 API Server 暂时不可用或整个控制平面出现故障,只要节点和 kubelet 正常,静态 Pod 依然可以运行。这对于部署高可用的控制平面组件(如在每个控制平面节点上都运行为静态 Pod 的 API Server)至关重要。
  3. 简化核心服务管理 :将集群核心组件的生命周期管理与普通的业务应用解耦。运维人员可以通过直接操作节点上的文件来管理这些核心 Pod(如更新镜像版本、修改参数),而不需要经过 Kubernetes 的调度和管理流程,在某些场景下更直接、更可靠。

2.2 静态 Pod 与常规 Pod 的核心区别

为了更清晰地理解,我们可以通过一个表格来对比静态 Pod 和由控制器管理的常规 Pod:

特性 静态 Pod 常规 Pod (如通过 Deployment 创建)
管理方式 由节点上的 kubelet 直接管理。 API Server 接收指令,经 Scheduler 调度,由目标节点的 kubelet 执行。
定义来源 节点本地目录中的 静态清单文件 通过 kubectl 或客户端向 API Server 提交的 YAML/JSON。
可见性 在本节点上通过 docker ps crictl ps 可见。在集群中通过 kubectl get pods 可见,但会被加上节点后缀(如 node-name )。 在集群中通过 kubectl get pods 直接可见。
生命周期 依赖于 kubelet 和清单文件。删除清单文件,Pod 会被终止。 依赖于控制器(如 Deployment)。删除 Pod,控制器会重建。
主要用途 部署集群核心控制平面组件 (kube-apiserver, kube-scheduler, kube-controller-manager, etcd),或需要在特定节点上绝对保证运行的 守护进程 部署 业务应用 、微服务、中间件等。
高可用实现 需要在多个节点上分别放置清单文件,由各节点的 kubelet 独立维护,实现 冗余 通过控制器的 replicas 字段和调度器实现 副本集 和跨节点分布。

注意 :一个常见的误解是认为静态 Pod 完全独立于 API Server。实际上,kubelet 在成功创建静态 Pod 后,会尝试通过 API Server 为其创建一个“镜像 Pod”(Mirror Pod)对象。这个镜像 Pod 是只读的,仅用于在集群层面展示静态 Pod 的状态,方便用户通过 kubectl 查看。你无法通过 kubectl delete 删除这个镜像 Pod 来终止静态 Pod,必须去节点上操作清单文件。这解释了为什么你在 kubectl get pods -n kube-system 中能看到控制平面组件,并且它们都带有节点名称后缀。

3. 实战:从零创建与管理一个静态 Pod

理解了原理,我们通过一个完整的实战来巩固。我们将在一个已有的 K8s 工作节点上,部署一个简单的 Nginx 作为静态 Pod,并观察其整个生命周期。

3.1 环境准备与清单文件编写

首先,你需要一个已经安装了 kubelet 并正常加入集群的节点。可以通过 kubectl get nodes 确认节点状态为 Ready

静态 Pod 的清单文件格式与普通的 Pod YAML 完全一致。我们创建一个文件 /etc/kubernetes/manifests/static-nginx.yaml 。如果 /etc/kubernetes/manifests 目录不存在,需要手动创建,并确保 kubelet 有读取权限。

apiVersion: v1
kind: Pod
metadata:
  name: static-web
  namespace: default # 静态Pod通常放在kube-system,这里为演示放在default
  labels:
    app: static-nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.25-alpine # 使用alpine版本,镜像较小
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: "64Mi"
        cpu: "50m"
      limits:
        memory: "128Mi"
        cpu: "100m"
    livenessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 3
      periodSeconds: 10
    readinessProbe:
      httpGet:
        path: /
        port: 80
      initialDelaySeconds: 5
      periodSeconds: 5

关键点解析:

  1. metadata.name :这是 Pod 的名称。注意,当它被 kubelet 创建并同步到 API Server 后,在集群中看到的名称会是 <pod-name>-<node-name> 的格式。
  2. 镜像选择 :在生产环境中,为控制平面组件选择静态 Pod 镜像时,务必使用与集群版本匹配的、来自可信仓库的官方镜像。对于业务演示,我们选择体积小、启动快的 nginx:alpine
  3. 资源限制 强烈建议 为所有静态 Pod(尤其是控制平面组件)设置合理的资源请求( requests )和限制( limits )。这可以防止它们耗尽节点资源,影响节点稳定性。例如,kube-apiserver 在高负载下可能消耗较多内存。
  4. 探针 :添加 livenessProbe readinessProbe 是生产级的最佳实践。kubelet 会根据这些探针来判定容器健康状态,并在失败时重启容器(针对存活探针)。这增强了静态 Pod 的自愈能力。

3.2 部署与验证操作

将编写好的 YAML 文件放入 /etc/kubernetes/manifests/ 目录后,无需执行任何 kubectl 命令。kubelet 默认每 20 秒扫描一次该目录(可通过 --pod-manifest-path --file-check-frequency 参数配置),它会自动检测到新文件并创建 Pod。

我们可以通过以下几种方式验证:

方式一:在节点上直接查看容器

# 使用 crictl (推荐,如果使用 containerd 作为运行时)
sudo crictl ps | grep static-web

# 或使用 docker (如果使用 docker 作为运行时)
sudo docker ps | grep static-web

你应该能看到一个运行中的 nginx 容器。

方式二:通过 Kubernetes API 查看镜像 Pod

kubectl get pods -o wide | grep static-web

输出可能类似于: static-web-node01 1/1 Running 0 2m10s 10.244.1.5 node01 <none> 。注意 Pod 名称自动附加了节点名。

方式三:查看详细信息和日志

# 查看 Pod 详细信息
kubectl describe pod static-web-node01

# 查看 Pod 日志
kubectl logs static-web-node01

3.3 静态 Pod 的生命周期管理

对静态 Pod 的操作本质是对其清单文件的操作:

  • 更新 Pod :直接修改 /etc/kubernetes/manifests/static-nginx.yaml 文件。例如,将 image: nginx:1.25-alpine 改为 image: nginx:1.26-alpine 。kubelet 检测到文件内容变化后,会 先停止旧 Pod,再创建新 Pod 。这个过程会导致服务短暂中断。
  • 删除 Pod :只需将 YAML 文件从 manifests 目录中移走或重命名。kubelet 检测到文件消失后,会终止对应的 Pod。
    sudo mv /etc/kubernetes/manifests/static-nginx.yaml /etc/kubernetes/manifests/static-nginx.yaml.bak
    
  • 重启 kubelet :重启 kubelet 服务 ( sudo systemctl restart kubelet ) 会重新扫描清单目录,并启动其中定义的所有静态 Pod。这是恢复静态 Pod 的常用方法。

实操心得 :在修改生产环境控制平面组件的静态 Pod 清单时,一定要遵循“先备份,再修改”的原则。一个错误的 YAML 格式可能导致 kubelet 无法解析,进而使得核心组件 Pod 消失,引发集群故障。建议在非关键节点上先测试修改后的 YAML 文件是否能正确创建 Pod。

4. 经典应用场景:Kubernetes 控制平面高可用部署

静态 Pod 最经典、最重要的应用场景就是部署高可用(High Availability, HA)的 Kubernetes 控制平面。以 kubeadm 工具搭建的 HA 集群为例,它正是在每个控制平面节点上,使用静态 Pod 来运行 apiserver、scheduler、controller-manager 以及 etcd(如果也是堆叠模式)。

4.1 高可用架构下的静态 Pod 配置

在一个三节点的控制平面中,每个节点(如 cp1 , cp2 , cp3 )的 /etc/kubernetes/manifests/ 目录下都会有类似下面的一组文件:

  • kube-apiserver.yaml
  • kube-controller-manager.yaml
  • kube-scheduler.yaml
  • etcd.yaml (如果 etcd 也是静态 Pod 运行)

每个节点上的清单文件内容基本相同,但会有一些关键参数指向本节点,例如 etcd --initial-advertise-peer-urls --listen-peer-urls --advertise-client-urls 需要指向当前节点的 IP。

kube-apiserver.yaml 片段为例:

apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.1.101 # 当前节点的IP
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --client-ca-file=/etc/kubernetes/pki/ca.crt
    - --enable-admission-plugins=NodeRestriction
    - --enable-bootstrap-token-auth=true
    - --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
    - --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
    - --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
    - --etcd-servers=https://192.168.1.101:2379,https://192.168.1.102:2379,https://192.168.1.103:2379 # 所有etcd节点
    - --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
    - --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
    - --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
    # ... 更多参数
    image: registry.k8s.io/kube-apiserver:v1.29.0
    name: kube-apiserver
    # ... 其他定义

这样,三个节点上的 kubelet 各自独立地维护着一个 kube-apiserver 实例。前端通过一个负载均衡器(如 HAProxy、Nginx 或云厂商的 LB)将流量分发到这三个实例,从而实现 API Server 的高可用。

4.2 使用静态 Pod 部署控制平面组件的优劣分析

优势:

  1. 部署简单 :kubeadm 等工具自动化了清单和证书的生成,一键即可搭建。
  2. 自举能力强 :不依赖已存在的集群控制平面,适合初始搭建。
  3. 各节点独立 :单个节点故障不影响其他节点上的同类组件运行。
  4. 与 kubelet 集成深 :健康检查、崩溃重启直接由 kubelet 负责,响应快。

劣势与注意事项:

  1. 升级繁琐 :升级 Kubernetes 版本时,需要手动或通过工具(如 kubeadm upgrade )更新每个控制平面节点上的静态 Pod 清单文件中的镜像标签和可能变化的参数,然后逐节点滚动重启。这个过程需要谨慎操作,并确保 etcd 等有状态组件的备份。
  2. 配置分散 :配置文件分散在各个节点,批量修改和配置管理不如使用 ConfigMap 集中方便。
  3. 依赖节点本地存储 :清单文件存储在节点本地,需要做好备份,防止节点磁盘损坏导致配置丢失。

注意事项 :对于 etcd 集群,如果也采用静态 Pod 部署,务必确保其数据目录( --data-dir )使用持久化存储(如本地 SSD 或云盘),并建立定期的备份快照机制。etcd 数据的丢失是灾难性的。

5. 进阶:静态 Pod 的配置、调试与安全实践

掌握了基础部署后,我们深入看看如何更好地配置、调试和保障静态 Pod 的安全与稳定。

5.1 自定义 kubelet 的静态 Pod 目录

默认的静态 Pod 路径是 /etc/kubernetes/manifests 。你可以通过修改 kubelet 的启动参数来改变它。这通常在 kubelet 的 systemd 服务文件(如 /etc/systemd/system/kubelet.service.d/10-kubeadm.conf )中配置。

# 查看 kubelet 当前配置
sudo systemctl cat kubelet | grep -i manifest
# 通常能看到类似:--pod-manifest-path=/etc/kubernetes/manifests

# 如果需要修改,编辑对应的 drop-in 文件
sudo vim /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
# 在 `ExecStart=` 行的参数中添加或修改 `--pod-manifest-path=/your/custom/path`
# 然后重启 kubelet
sudo systemctl daemon-reload
sudo systemctl restart kubelet

修改后,记得将原有的静态 Pod 清单文件移动到新目录。

5.2 调试静态 Pod 的常见问题

当静态 Pod 没有按预期运行时,可以按照以下思路排查:

  1. 检查清单文件 :首先使用 kubectl describe pod <pod-name> 查看镜像 Pod 的事件。常见的错误是 Failed to create pod sandbox ErrImagePull 。更直接的是在节点上查看 kubelet 日志:

    sudo journalctl -u kubelet -f --since "5 minutes ago" | grep -A5 -B5 static-web
    

    日志中会明确提示 YAML 解析错误、镜像拉取失败、端口冲突等问题。

  2. 检查 kubelet 配置 :确认 kubelet 是否正常运行,并且 --pod-manifest-path 参数指向了正确的目录。

    ps -ef | grep kubelet | grep manifest
    
  3. 直接检查容器运行时 :绕过 Kubernetes 层面,直接询问容器运行时(如 containerd)。

    # 对于 containerd
    sudo crictl ps -a | grep -v POD # 查看所有容器,包括停止的
    sudo crictl logs <container-id> # 查看特定容器日志
    
  4. 文件权限与 SELinux/AppArmor :确保 kubelet 进程有权限读取清单文件。在启用了 SELinux 或 AppArmor 的系统上,不正确的安全上下文也可能导致 kubelet 无法读取文件或创建容器。检查 /var/log/messages journalctl 中是否有相关的拒绝日志。

5.3 安全与资源管控最佳实践

  1. 使用非 root 用户运行容器 :在 Pod 的 securityContext 中设置 runAsNonRoot: true runAsUser ,避免容器以 root 权限运行,减少攻击面。

    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
      containers:
      - name: myapp
        # ...
    
  2. 设置严格的资源限制 :如前所述,为控制平面静态 Pod 设置合理的 CPU/内存 limits requests ,防止其异常时拖垮节点。可以参考官方文档对组件资源需求的建议。

  3. 镜像拉取策略与凭证 :对于私有仓库的镜像,需要在节点上配置 imagePullSecrets ,或者更常见的是,在 kubelet 层面配置全局的 --registry-auth 或使用 docker config 。对于静态 Pod,你可以在 Pod 规范中定义 imagePullSecrets ,但对应的 Secret 需要提前在集群中创建(这又依赖于 API Server,对于引导阶段的组件可能不适用)。因此,更常见的做法是使用公开镜像或将必要镜像预拉到节点本地。

  4. 清单文件备份与版本控制 :将 /etc/kubernetes/manifests/ 目录纳入版本控制系统(如 Git),或者至少定期备份。任何对生产环境控制平面组件清单的修改,都应在测试环境验证,并有明确的回滚方案。

6. 静态 Pod 的替代方案与选择思考

虽然静态 Pod 是部署控制平面组件的标准方式,但它并非唯一选择。了解替代方案有助于你在不同场景下做出更合适的设计决策。

6.1 DaemonSet:节点级守护进程的现代选择

对于需要在集群 每个节点 部分特定节点 上运行一个副本的守护进程(如日志收集器 Fluentd、网络插件 Calico 的 node 组件、监控代理 Node Exporter), DaemonSet 是比静态 Pod 更主流、更云原生的选择。

DaemonSet 的优势:

  • 集中管理 :通过 API Server 统一管理,使用 kubectl 即可完成部署、升级、删除。
  • 智能调度 :可以基于节点标签( nodeSelector )或污点容忍( tolerations )进行灵活调度。
  • 滚动更新 :支持优雅的滚动更新策略,减少服务中断。
  • 与集群集成更好 :可以方便地使用 ConfigMap、Secret 来管理配置。

何时选择静态 Pod 而非 DaemonSet?

  • 集群引导阶段 :当 API Server 本身尚未运行时。
  • 运行集群核心组件 :如 kube-apiserver,其本身是 DaemonSet 能够运行的前提。
  • 对管理平面有极高独立性要求 :希望核心服务的生命周期完全不受集群主控平面故障的影响。

6.2 托管 Kubernetes 服务中的控制平面

在使用 AWS EKS、Google GKE、Azure AKS 等托管 Kubernetes 服务时,控制平面(Master)由云厂商完全托管,对用户不可见。用户无需关心 apiserver、scheduler 等是如何部署和运行的,无论是通过静态 Pod、虚拟机还是其他更高级的托管服务。这大大降低了运维复杂度,是生产环境的推荐选择。此时,静态 Pod 的知识更多用于深度故障排查和理解底层原理。

6.3 选择决策流程图

面对一个需要在节点上常驻的服务,你可以参考以下思路进行选择:

是否需要该服务来启动或保障 Kubernetes 控制平面本身?
    ├── 是 -> 使用【静态 Pod】(如 kube-apiserver, etcd)
    └── 否 -> 该服务是否需要在(几乎)所有工作节点上运行?
        ├── 是 -> 使用【DaemonSet】(如网络插件、日志代理)
        └── 否 -> 该服务是否是普通的集群应用?
            ├── 是 -> 使用【Deployment】+ 节点选择器/污点容忍
            └── 否 -> 考虑是否有特殊需求(如需要特权的系统级服务),可能仍需评估静态Pod或DaemonSet

7. 常见陷阱、疑难排查与经验实录

即便理解了原理,在实际操作中依然会遇到各种问题。这里记录一些我踩过的坑和总结的排查技巧。

7.1 镜像拉取失败:私有仓库与网络问题

这是最常见的问题之一。对于控制平面组件的镜像(如 registry.k8s.io/kube-apiserver:v1.29.0 ),如果节点无法访问外网或该仓库,kubelet 会报 ErrImagePull

解决方案:

  • 离线环境 :在搭建集群前,使用 kubeadm config images pull 或手动 docker pull / crictl pull 将所有所需镜像下载到本地,并推送到内网私有仓库。然后修改 kubelet 配置或静态 Pod 清单中的镜像地址为内网仓库地址。
  • 配置镜像加速器或代理 :对于可以访问外网但速度慢的环境,在容器运行时(Docker 或 containerd)配置镜像加速器。
  • 清单中指定 imagePullPolicy :如果镜像已预加载到本地,可以设置 imagePullPolicy: IfNotPresent Never ,避免 kubelet 总是尝试拉取。

7.2 端口冲突与主机网络

静态 Pod 默认使用普通的 Pod 网络。但像 kube-apiserver 需要绑定节点的特定端口(6443)以供外部访问,这就需要在清单中声明 hostNetwork: true hostPort ,或者使用 NodePort 类型的 Service。更关键的是,要确保这些端口没有被其他进程占用。

排查命令:

# 检查端口占用
sudo netstat -tlnp | grep :6443
sudo ss -tlnp | grep :6443

# 如果使用 hostNetwork,在 Pod 内看到的网络栈就是主机的

如果端口被占用,需要停止冲突的服务或修改静态 Pod 的监听端口(但通常不推荐修改默认端口)。

7.3 资源不足导致 Pod 处于 Pending 状态

尽管静态 Pod 不经过调度器,但 kubelet 在创建前也会进行本地资源校验。如果节点内存或 CPU 不足,Pod 会卡在 Pending 状态。通过 kubectl describe pod 可以看到类似 Insufficient memory 的事件。

解决与预防:

  • 监控节点资源使用情况。
  • 为静态 Pod 设置合理的 resources.requests ,确保 kubelet 能为其预留资源。
  • 清理节点上无关的进程或容器。

7.4 修改清单后 Pod 没有重建

有时修改了 YAML 文件,但 kubelet 似乎没有反应。可能的原因:

  1. 文件权限或路径错误 :kubelet 无法读取新文件。检查文件权限( ls -l )和路径。
  2. YAML 格式错误 :一个缩进或语法错误会导致整个文件被 kubelet 忽略。使用 yamllint kubectl apply --dry-run=client -f your-file.yaml (用 kubectl 模拟验证)来检查语法。
  3. kubelet 未检测到变化 :检查 kubelet 日志,看是否有相关错误。可以尝试重启 kubelet 来强制重新扫描。

7.5 集群中看到“双份”Pod

如果你在节点上通过 crictl ps 看到了一个容器,同时在 kubectl get pods 里也看到一个同名但带节点后缀的 Pod,这是正常现象。后者是“镜像 Pod”。 切勿尝试删除这个镜像 Pod ,因为删除后 API Server 又会根据 kubelet 上报的信息重新创建它。要删除静态 Pod,必须移除或修改节点上的清单文件。

静态 Pod 是 Kubernetes 架构中一个精妙的设计,它平衡了简单性、独立性和可靠性。从手动编写一个简单的 Nginx 静态 Pod,到理解它如何支撑起整个高可用控制平面,这个过程能让你对 K8s 集群的生命周期和运维有更深刻的把握。记住,对于核心基础设施,变更永远要谨慎,做好备份和回滚计划。当你下次再执行 kubectl get pods -n kube-system 看到那些 -master0 , -master1 后缀的 Pod 时,你就能清晰地知道它们从何而来,由谁管理,以及如何与它们交互了。

更多推荐