从零搭建一个单节点 K8S 可观测实验室(七):从一次 Pod 创建过程,看懂 Kubernetes 核心原理

在前面的文章中,我们已经完成了单节点 Kubernetes 集群的搭建,并逐步接入了:

  • Prometheus

  • Grafana

  • Loki

  • Fluent Bit

  • Tempo

  • OpenTelemetry Collector

现在,我们已经可以观察 Kubernetes 的指标、日志和链路追踪。

但还有一个问题:

当我们执行一条 kubectl 命令时,Kubernetes 内部究竟发生了什么?

例如:

复制代码
kubectl create deployment nginx --image=nginx

几秒钟之后,一个 Nginx Pod 就运行起来了。

看起来似乎是 kubectl 创建了 Pod。

但实际上,kubectl 并没有直接创建容器,甚至没有直接创建 Pod。

在这条命令背后,API Server、etcd、Scheduler、Controller Manager、Kubelet、Container Runtime 和 CNI 等多个组件依次参与,最终才让 Nginx 容器真正运行起来。

这一篇,我们就沿着这条命令的执行过程,把 Kubernetes 的核心组件串起来。


一、先看 Kubernetes 的整体分工

在分析 Pod 创建过程之前,可以先把 Kubernetes 集群分成两部分:

复制代码
Kubernetes 集群
├── Control Plane
│   ├── API Server
│   ├── etcd
│   ├── Scheduler
│   └── Controller Manager
│
└── Node
    ├── Kubelet
    ├── Container Runtime
    ├── CNI
    └── Pod

其中,Control Plane 负责做决策:

  • 用户想要什么

  • 集群应该达到什么状态

  • Pod 应该运行在哪个节点

  • 少了 Pod 是否需要补回来

Node 则负责真正执行:

  • 下载镜像

  • 创建容器

  • 配置网络

  • 启动 Pod

  • 汇报运行状态

可以简单理解为:

Control Plane 负责发号施令,Node 负责落地执行。


二、我们执行了什么命令

首先创建一个单独的命名空间:

复制代码
kubectl create namespace production

然后执行:

复制代码
kubectl create deployment nginx \
  --image=nginx \
  -n production

查看资源:

复制代码
kubectl get deployment,pod -n production

可能看到:

复制代码
NAME                    READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx   1/1     1            1           20s

NAME                         READY   STATUS    RESTARTS   AGE
pod/nginx-5869d7778c-xxxxx   1/1     Running   0          20s

表面上,我们只创建了一个 Deployment。

但 Kubernetes 实际上生成了这样一条资源链:

复制代码
Deployment
    ↓
ReplicaSet
    ↓
Pod
    ↓
Container

可以执行下面的命令验证:

复制代码
kubectl get deployment,replicaset,pod -n production

你会看到 Deployment、ReplicaSet 和 Pod 三种资源。

需要注意:

kubectl create deployment 创建的是 Deployment,而不是直接创建 Pod。

真正负责创建 Pod 的,是 Kubernetes 内部的控制器。


三、第一步:kubectl 把请求发送给 API Server

当我们执行:

复制代码
kubectl create deployment nginx \
  --image=nginx \
  -n production

kubectl 会根据本机的 kubeconfig 找到 Kubernetes API Server。

通常使用的配置文件是:

复制代码
~/.kube/config

可以查看当前连接的集群:

复制代码
kubectl config current-context

也可以查看 API Server 地址:

复制代码
kubectl cluster-info

kubectl 会把我们的命令转换成一个 Deployment 对象,大致相当于下面这份 YAML:

复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: production
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx

然后通过 Kubernetes API,把这个对象发送给 API Server。

这里有一个非常重要的概念:

几乎所有 Kubernetes 操作,都要经过 API Server。

无论是:

  • kubectl

  • Scheduler

  • Controller Manager

  • Kubelet

  • 各种 Operator

  • 第三方控制器

它们通常都不会直接修改 etcd,而是通过 API Server 读取或更新集群状态。

因此,API Server 可以看作 Kubernetes 的统一入口。


四、API Server 做了什么

API Server 收到 Deployment 创建请求后,不会立刻创建容器。

它首先要处理一系列检查。

1. 认证

API Server 首先确认:

你是谁?

例如:

  • 客户端证书

  • ServiceAccount Token

  • OIDC Token

  • 其他认证方式

在 kubeadm 安装的集群中,管理员通常通过 kubeconfig 中的客户端证书访问集群。

2. 鉴权

确认身份之后,还要判断:

你有没有权限创建 Deployment?

这通常由 RBAC 控制。

例如可以执行:

复制代码
kubectl auth can-i create deployment -n production

如果返回:

复制代码
yes

说明当前用户有权限。

3. Admission Control

接下来,API Server 还会经过准入控制器。

准入控制器可以:

  • 修改资源

  • 补充默认值

  • 检查资源是否合法

  • 拒绝不符合安全策略的请求

例如:

  • 是否允许使用特权容器

  • 是否满足资源限制要求

  • 是否符合命名空间策略

  • 是否需要自动注入 Sidecar

经过这些步骤后,Deployment 对象才会被接受。


五、第二步:Deployment 被保存到 etcd

请求验证通过后,API Server 会把 Deployment 的状态保存到 etcd。

etcd 是一个分布式键值数据库,用于保存 Kubernetes 集群状态。

其中包括:

  • Deployment

  • ReplicaSet

  • Pod

  • Service

  • ConfigMap

  • Secret

  • Node

  • Namespace

  • 资源状态和元数据

可以把 etcd 理解成:

Kubernetes 的集群数据库。

但需要注意,普通组件一般不会直接访问 etcd。

通常只有 API Server 直接读写 etcd。

整体关系是:

复制代码
kubectl
   ↓
API Server
   ↓
etcd

而不是:

复制代码
kubectl
   ↓
etcd

如果 etcd 中的数据全部丢失,那么 Kubernetes 即使节点上的容器还暂时运行着,也会失去对整个集群状态的管理能力。

所以在生产环境中,etcd 的备份非常重要。


六、Kubernetes 的核心:期望状态与实际状态

Deployment 被写入 etcd 后,里面记录的是用户的期望:

复制代码
replicas: 1

它表达的意思不是:

立即执行一次创建 Pod 的命令。

而是:

我希望集群中始终存在一个符合模板定义的 Pod。

这就是 Kubernetes 最核心的设计思想之一:

复制代码
期望状态:应该有 1 个 Nginx Pod
实际状态:现在有 0 个 Nginx Pod
差异:少了 1 个
操作:创建 1 个 Pod

Kubernetes 会持续比较:

复制代码
期望状态
    ↓
实际状态
    ↓
发现差异
    ↓
执行修正

这个过程通常被称为:

Reconciliation Loop,也就是调谐循环。

Kubernetes 并不是把命令执行一次就结束,而是不断检查实际状态是否符合期望状态。


七、第三步:Controller Manager 创建 ReplicaSet

Deployment 保存成功后,API Server 会向客户端返回创建成功。

但是这时 Pod 可能还没有出现。

Controller Manager 中运行着许多控制器,其中 Deployment Controller 会持续关注 Deployment 资源。

它发现:

复制代码
存在 Deployment nginx
期望副本数为 1
但对应的 ReplicaSet 还不存在

于是 Deployment Controller 创建一个 ReplicaSet。

执行:

复制代码
kubectl get replicaset -n production

可能看到:

复制代码
NAME               DESIRED   CURRENT   READY   AGE
nginx-5869d7778c   1         1         1       1m

ReplicaSet 的名字通常由:

复制代码
Deployment 名称 + Pod 模板哈希

组成。

因此可能是:

复制代码
nginx-5869d7778c

Deployment 本身主要负责:

  • 管理版本

  • 管理滚动升级

  • 管理回滚

  • 创建和切换 ReplicaSet

ReplicaSet 负责:

  • 保证指定数量的 Pod 存在

可以简单理解为:

复制代码
Deployment 管版本
ReplicaSet 管数量
Pod 负责实际运行

八、第四步:ReplicaSet Controller 创建 Pod

ReplicaSet 创建之后,ReplicaSet Controller 会发现:

复制代码
期望 Pod 数量:1
实际 Pod 数量:0

于是它通过 API Server 创建一个 Pod 对象。

这时,etcd 中已经出现了 Pod 的定义。

但是这个 Pod 还不能立即运行。

执行:

复制代码
kubectl get pod -n production -o wide

在创建过程很慢时,可能短暂看到:

复制代码
NAME                     READY   STATUS    NODE
nginx-5869d7778c-xxxxx   0/1     Pending   <none>

这里最值得注意的是:

复制代码
NODE: <none>

这表示 Pod 已经创建出来了,但还没有决定运行在哪个节点。

此时 Pod 只是一个保存在 Kubernetes API 中的对象,还没有真正对应到任何正在运行的容器。


九、第五步:Scheduler 为 Pod 选择节点

Scheduler 持续观察所有还没有绑定节点的 Pod。

它发现这个 Nginx Pod:

复制代码
spec.nodeName 为空

于是开始为它选择合适的节点。

Scheduler 的调度大致分成两个阶段。

1. 过滤

先排除不满足条件的节点,例如:

  • CPU 或内存不足

  • 节点不可调度

  • 不满足 Node Selector

  • 不满足亲和性规则

  • 存在无法容忍的污点

  • 端口冲突

  • 存储卷无法挂载

2. 评分

在剩余节点中进行评分,例如考虑:

  • 资源是否均衡

  • Pod 是否应该分散

  • 亲和性和反亲和性

  • 镜像是否已经存在

  • 自定义调度规则

最后,Scheduler 选择一个分数较高的节点。

在我们的单节点实验室中,通常只有一个可用节点,所以选择过程比较简单。

可以查看节点:

复制代码
kubectl get nodes

例如:

复制代码
NAME                    STATUS   ROLES           AGE   VERSION
vbox-ubuntu24-server    Ready    control-plane   10d   v1.xx.x

Scheduler 选中节点后,会通过 API Server 更新 Pod 的绑定信息。

Pod 中会出现:

复制代码
spec:
  nodeName: vbox-ubuntu24-server

从这一刻开始,这个 Pod 就归该节点负责了。


十、第六步:Kubelet 发现属于自己的 Pod

每个 Kubernetes 节点上都运行着 Kubelet。

Kubelet 可以理解为节点上的 Kubernetes 管理代理。

它会持续通过 API Server 关注:

有没有 Pod 被调度到我这个节点?

当 Kubelet 发现 Nginx Pod 的 nodeName 是自己时,就开始执行 Pod 的实际创建过程。

Kubelet 主要负责:

  • 获取 Pod 定义

  • 请求 Container Runtime 创建容器

  • 挂载 Volume

  • 配置 Secret 和 ConfigMap

  • 执行健康检查

  • 重启失败的容器

  • 向 API Server 汇报状态

但 Kubelet 自己并不直接负责运行容器。

它需要调用 Container Runtime。


十一、第七步:Container Runtime 创建容器

Kubelet 通过 CRI,也就是 Container Runtime Interface,与容器运行时通信。

常见的容器运行时包括:

  • containerd

  • CRI-O

  • Docker 配合 cri-dockerd

我们的实验室采用的是:

复制代码
Kubelet
   ↓ CRI
cri-dockerd
   ↓
Docker Engine

可以查看 Kubelet 的运行时端点:

复制代码
sudo grep containerRuntimeEndpoint \
  /var/lib/kubelet/config.yaml

在我们的环境中,大致是:

复制代码
unix:///var/run/cri-dockerd.sock

Kubelet 会要求容器运行时完成以下工作:

  1. 创建 Pod Sandbox

  2. 准备 Pod 网络命名空间

  3. 拉取 Nginx 镜像

  4. 创建容器

  5. 启动容器

如果本地没有镜像,容器运行时会执行类似:

复制代码
docker pull nginx

的操作。

因此,如果无法访问镜像仓库,Pod 可能进入:

复制代码
ErrImagePull

或者:

复制代码
ImagePullBackOff

可以通过下面的命令查看原因:

复制代码
kubectl describe pod \
  -l app=nginx \
  -n production

十二、Pod 到底是什么

很多初学者会把 Pod 和容器混为一谈。

实际上,Pod 并不等于容器。

Pod 是 Kubernetes 中最小的调度单位。

一个 Pod 中可以包含:

  • 一个业务容器

  • 多个业务容器

  • Sidecar 容器

  • Init Container

同一个 Pod 内的容器共享:

  • 网络命名空间

  • Pod IP

  • 端口空间

  • 部分存储卷

  • 生命周期

例如:

复制代码
Pod
├── Nginx 容器
└── 日志 Sidecar 容器

这两个容器可以通过:

复制代码
localhost

互相访问。

不过大多数简单应用,一个 Pod 中通常只有一个主要容器。

在本例中:

复制代码
Pod
└── Nginx Container

Pod 是 Kubernetes 管理和调度的对象,而容器是真正运行应用程序的地方。


十三、第八步:CNI 为 Pod 配置网络

容器要运行,还需要网络。

Kubernetes 本身定义了网络模型,但通常不会直接实现具体的 Pod 网络。

具体网络配置由 CNI 插件完成。

CNI 的全称是:

复制代码
Container Network Interface

常见的 CNI 插件包括:

  • Flannel

  • Calico

  • Cilium

  • Weave Net

我们的单节点实验室使用 Flannel。

当 Kubelet 和容器运行时创建 Pod Sandbox 时,会调用 CNI 插件,为 Pod 完成网络配置。

通常包括:

  • 创建虚拟网卡

  • 把网卡放入 Pod 网络命名空间

  • 给 Pod 分配 IP 地址

  • 设置路由

  • 设置宿主机网络连接

  • 配置跨节点通信

可以查看 Pod IP:

复制代码
kubectl get pod -n production -o wide

可能看到:

复制代码
NAME                     IP           NODE
nginx-5869d7778c-xxxxx   10.244.0.8   vbox-ubuntu24-server

其中:

复制代码
10.244.0.8

就是由集群网络分配给 Pod 的地址。

需要注意:

Pod IP 通常不是长期稳定的。

Pod 被删除并重新创建后,IP 很可能变化。

因此,业务访问通常不应该直接依赖 Pod IP,而应该通过 Service。


十四、第九步:Pod 进入 Running 状态

容器成功启动后,Kubelet 会不断检查容器状态。

随后通过 API Server 更新 Pod Status。

状态变化可能是:

复制代码
Pending
   ↓
ContainerCreating
   ↓
Running

执行:

复制代码
kubectl get pod -n production -w

可以实时观察 Pod 状态变化。

当看到:

复制代码
READY   STATUS
1/1     Running

表示:

  • Pod 已经被调度到节点

  • Pod 网络已经配置完成

  • Nginx 镜像已经准备好

  • 容器已经启动

  • 容器处于就绪状态

到这里,一次完整的 Deployment 创建流程才基本结束。


十五、把整个创建流程串起来

现在把整个过程连起来:

复制代码
1. 用户执行 kubectl create deployment
                    ↓
2. kubectl 向 API Server 发送请求
                    ↓
3. API Server 完成认证、鉴权和准入检查
                    ↓
4. Deployment 被保存到 etcd
                    ↓
5. Deployment Controller 发现新的 Deployment
                    ↓
6. Deployment Controller 创建 ReplicaSet
                    ↓
7. ReplicaSet Controller 创建 Pod
                    ↓
8. Scheduler 为 Pod 选择节点
                    ↓
9. Kubelet 发现 Pod 被分配到本节点
                    ↓
10. Kubelet 调用 Container Runtime
                    ↓
11. Container Runtime 创建 Pod Sandbox 和容器
                    ↓
12. CNI 为 Pod 配置网络
                    ↓
13. Container Runtime 启动 Nginx
                    ↓
14. Kubelet 将 Pod 状态更新为 Running

这里没有任何一个组件独自完成全部工作。

Kubernetes 是依靠多个组件共同协作,让集群逐渐达到期望状态。


十六、删除 Pod 后,为什么它又回来了

现在可以做一个非常直观的实验。

先查看 Pod:

复制代码
kubectl get pod -n production

然后删除它:

复制代码
kubectl delete pod \
  -l app=nginx \
  -n production

再次查看:

复制代码
kubectl get pod -n production -w

你会发现旧 Pod 被删除后,很快又出现了一个新 Pod。

这是因为我们删除的只是 Pod,而 Deployment 和 ReplicaSet 仍然存在。

ReplicaSet Controller 看到:

复制代码
期望副本数:1
实际副本数:0

于是重新创建一个 Pod。

这就是 Kubernetes 的自愈能力。

但严格来说,并不是旧 Pod"复活"了。

实际发生的是:

复制代码
旧 Pod 被删除
    ↓
ReplicaSet 发现副本数不足
    ↓
创建一个新的 Pod

新的 Pod 通常具有:

  • 新的 Pod 名称

  • 新的 Pod UID

  • 可能不同的 Pod IP


十七、Deployment 为什么不直接管理 Pod

可能有人会问:

Deployment 为什么还要经过 ReplicaSet,不能直接创建 Pod 吗?

主要原因是版本管理和滚动更新。

假设我们修改镜像:

复制代码
kubectl set image deployment/nginx \
  nginx=nginx:1.27-alpine \
  -n production

Deployment 会为新的 Pod 模板创建一个新的 ReplicaSet。

大致过程是:

复制代码
旧 ReplicaSet
├── 旧 Pod
└── 旧 Pod

新 ReplicaSet
├── 新 Pod
└── 新 Pod

在滚动更新过程中,Deployment 会逐步:

  • 增加新 ReplicaSet 的副本

  • 减少旧 ReplicaSet 的副本

  • 保证服务尽量不中断

可以查看滚动更新状态:

复制代码
kubectl rollout status deployment/nginx \
  -n production

也可以查看历史:

复制代码
kubectl rollout history deployment/nginx \
  -n production

如果新版本存在问题,还可以回滚:

复制代码
kubectl rollout undo deployment/nginx \
  -n production

所以:

复制代码
Deployment
   ↓ 管理版本
ReplicaSet
   ↓ 管理副本数量
Pod
   ↓ 运行应用
Container

这样的分层设计,为滚动升级和回滚提供了基础。


十八、Service 什么时候参与

到目前为止,我们执行的命令只创建了 Deployment:

复制代码
kubectl create deployment nginx --image=nginx

它不会自动创建 Service。

虽然 Pod 已经拥有 IP,但 Pod IP 并不稳定。

为了给应用提供一个稳定的访问入口,需要创建 Service:

复制代码
kubectl expose deployment nginx \
  --port=80 \
  --target-port=80 \
  -n production

查看 Service:

复制代码
kubectl get service -n production

可能看到:

复制代码
NAME    TYPE        CLUSTER-IP     PORT(S)
nginx   ClusterIP   10.96.31.20    80/TCP

Service 提供了一个相对稳定的:

  • 名称

  • ClusterIP

  • 端口

Service 并不是一台真正的代理服务器,也不是一个普通 Pod。

它更像是一组访问规则。

Service 会通过 Label Selector 找到后端 Pod。

可以查看:

复制代码
kubectl describe service nginx -n production

其中可能包含:

复制代码
Selector: app=nginx
Endpoints: 10.244.0.8:80

关系如下:

复制代码
Service nginx
    ↓ 根据 Label Selector 查找
Pod app=nginx
    ↓
Nginx Container

只要新的 Pod 仍然带有:

复制代码
app: nginx

这个 Label,Service 就可以把流量发送给它。

即使 Pod 被替换、IP 发生变化,Service 的访问入口仍然可以保持稳定。


十九、Service 如何找到 Pod

Service 本身依靠标签选择器匹配 Pod。

可以查看 Deployment 的标签:

复制代码
kubectl get pod \
  -n production \
  --show-labels

例如:

复制代码
NAME                     LABELS
nginx-5869d7778c-xxxxx   app=nginx,pod-template-hash=5869d7778c

Service 的选择器是:

复制代码
app=nginx

因此它会匹配这个 Pod。

Kubernetes 还会维护对应的 EndpointSlice。

查看:

复制代码
kubectl get endpointslice -n production

它记录了当前可用后端 Pod 的地址。

整体关系是:

复制代码
Service
   ↓
EndpointSlice
   ↓
Pod IP

当 Pod 被替换后,EndpointSlice Controller 会自动更新后端地址。

所以应用不需要关心 Pod IP 是否变化。


二十、CoreDNS 什么时候参与

创建 Pod 本身并不一定需要 CoreDNS。

但是当 Pod 需要通过名称访问 Service 时,CoreDNS 就会参与。

在 Kubernetes 中,Service 通常会获得类似这样的 DNS 名称:

复制代码
nginx.production.svc.cluster.local

其中:

复制代码
nginx

是 Service 名称。

复制代码
production

是 Namespace。

复制代码
svc.cluster.local

是 Kubernetes 集群中的 Service DNS 域。

我们可以创建一个临时 Pod 进行验证:

复制代码
kubectl run test-client \
  --image=busybox:1.36 \
  --restart=Never \
  -n production \
  -- sh -c 'sleep 3600'

查看 DNS 解析:

复制代码
kubectl exec test-client \
  -n production \
  -- nslookup nginx

访问 Nginx:

复制代码
kubectl exec test-client \
  -n production \
  -- wget -qO- http://nginx

这里的:

复制代码
http://nginx

并不是访问某个名为 nginx 的 Pod。

它实际访问的是名为 nginx 的 Service。

大致过程是:

复制代码
test-client Pod
    ↓ 查询 nginx
CoreDNS
    ↓ 返回 Service ClusterIP
nginx Service
    ↓ 转发到后端
nginx Pod

CoreDNS 负责名称解析,但它不负责真正转发业务流量。

它解决的是:

nginx 这个名称对应哪个 IP?


二十一、CNI、CoreDNS 和 Service 的区别

这三个组件经常被混在一起。

可以这样区分。

CNI:负责网络连通

CNI 负责:

  • 给 Pod 分配 IP

  • 配置虚拟网卡

  • 配置路由

  • 让 Pod 之间能够通信

它解决的问题是:

Pod 的网络怎么建立?

CoreDNS:负责名称解析

CoreDNS 负责:

  • 把 Service 名称解析为 ClusterIP

  • 提供集群内部 DNS 服务

它解决的问题是:

nginx 这个名字对应哪个地址?

Service:提供稳定访问入口

Service 负责:

  • 提供稳定的 ClusterIP 和端口

  • 根据 Label 找到后端 Pod

  • 把流量送往可用 Pod

它解决的问题是:

Pod IP 会变化,应用应该通过什么入口访问?

三者串起来就是:

复制代码
应用访问 http://nginx
        ↓
CoreDNS 解析 nginx
        ↓
得到 Service ClusterIP
        ↓
Service 选择后端 Pod
        ↓
通过 CNI 建立的网络到达 Pod

二十二、API Server、Controller 和 Kubelet 都在循环

理解 Kubernetes 时,最重要的一点是:

Kubernetes 不是一条从头执行到尾的脚本。

它更像是一组不断运行的控制循环。

Deployment Controller

不断检查:

复制代码
Deployment 是否有正确的 ReplicaSet

ReplicaSet Controller

不断检查:

复制代码
实际 Pod 数量是否等于期望副本数

Scheduler

不断检查:

复制代码
是否存在尚未分配节点的 Pod

Kubelet

不断检查:

复制代码
分配到本节点的 Pod 是否真的在运行

EndpointSlice Controller

不断检查:

复制代码
Service 后面的 Pod 地址是否发生变化

每个组件只负责自己的一小部分。

它们通过 API Server 观察和更新资源状态,最终让整个集群逐渐达到期望状态。


二十三、控制面并不会直接启动容器

从整个过程可以看到:

  • API Server 不启动容器

  • etcd 不启动容器

  • Scheduler 不启动容器

  • Controller Manager 也不启动容器

真正与容器运行相关的是:

复制代码
Kubelet
   ↓
Container Runtime

控制面主要负责:

  • 保存期望状态

  • 创建资源对象

  • 选择节点

  • 修正状态差异

节点负责:

  • 拉取镜像

  • 创建容器

  • 配置网络

  • 执行健康检查

  • 汇报状态

这也是为什么即使 API Server 暂时不可用,已经运行的容器可能仍然继续运行。

但是控制面不可用期间:

  • 无法创建新资源

  • 无法调度新 Pod

  • 无法完成新的控制操作

  • 集群状态无法正常协调


二十四、Pod 创建失败时,应该从哪里排查

理解了创建流程之后,故障排查就不再只是反复执行 kubectl get pod

可以根据 Pod 停在哪个阶段判断问题。

Pod 一直 Pending

可能原因:

  • 没有可用节点

  • CPU 或内存不足

  • Taint 无法容忍

  • Node Selector 不匹配

  • PVC 无法绑定

  • Scheduler 无法找到合适节点

查看:

复制代码
kubectl describe pod <pod-name> \
  -n production

重点看 Events。

Pod 一直 ContainerCreating

可能原因:

  • 镜像正在下载

  • CNI 配置失败

  • Volume 挂载失败

  • Container Runtime 异常

查看:

复制代码
kubectl describe pod <pod-name> \
  -n production

也可以检查 Kubelet:

复制代码
sudo journalctl -u kubelet -n 100

Pod 出现 ImagePullBackOff

说明镜像下载失败。

常见原因:

  • 镜像名称错误

  • 镜像仓库无法访问

  • 私有仓库未配置凭据

  • 网络或 DNS 异常

Pod 出现 CrashLoopBackOff

说明容器已经启动,但应用反复崩溃。

查看日志:

复制代码
kubectl logs <pod-name> \
  -n production

如果容器已经重启,可以查看上一次日志:

复制代码
kubectl logs <pod-name> \
  -n production \
  --previous

理解 Pod 创建链路后,排查思路可以变成:

复制代码
对象有没有创建?
    ↓
有没有完成调度?
    ↓
Kubelet 有没有收到?
    ↓
镜像有没有拉取成功?
    ↓
CNI 有没有配置成功?
    ↓
容器有没有启动?
    ↓
应用为什么退出?

二十五、通过事件观察 Kubernetes 的动作

Kubernetes 中很多创建、调度和启动过程都会记录为 Event。

可以执行:

复制代码
kubectl get events \
  -n production \
  --sort-by=.metadata.creationTimestamp

可能看到:

复制代码
Scheduled
Pulling
Pulled
Created
Started

它们分别表示:

复制代码
Scheduled
Pod 已经被调度到节点

Pulling
正在拉取容器镜像

Pulled
镜像拉取完成

Created
容器已经创建

Started
容器已经启动

这是观察 Pod 创建过程最直观的方法之一。

也可以重新创建 Deployment,并实时观察:

复制代码
kubectl delete deployment nginx -n production

然后在一个终端中执行:

复制代码
kubectl get pod -n production -w

另一个终端重新创建:

复制代码
kubectl create deployment nginx \
  --image=nginx \
  -n production

同时查看事件:

复制代码
kubectl get events \
  -n production \
  --sort-by=.metadata.creationTimestamp

这样就能看到从创建到运行的完整过程。


二十六、在可观测系统中,这些组件意味着什么

前面我们已经安装了 Prometheus、Grafana、Loki 和 Tempo。

现在再回头看,会更容易理解为什么 Kubernetes 可观测性需要覆盖多个层次。

API Server

可以观察:

  • API 请求数量

  • 请求延迟

  • 错误率

  • 认证和鉴权异常

etcd

可以观察:

  • 请求延迟

  • 数据库大小

  • Leader 状态

  • 磁盘写入性能

Scheduler

可以观察:

  • 调度延迟

  • 调度成功次数

  • 调度失败次数

  • Pending Pod 数量

Controller Manager

可以观察:

  • 控制器调谐次数

  • 调谐失败情况

  • 工作队列长度

Kubelet

可以观察:

  • Pod 启动状态

  • 容器运行状态

  • 节点资源

  • Volume 和网络异常

Container Runtime

可以观察:

  • 镜像拉取

  • 容器启动

  • 容器退出

  • Runtime 错误

应用 Pod

可以观察:

  • CPU

  • 内存

  • 请求量

  • 错误率

  • 日志

  • Trace

因此,Kubernetes 可观测性不是只看一张 CPU 曲线。

真正完整的可观测链路应该覆盖:

复制代码
用户请求
   ↓
API Server
   ↓
控制器与调度器
   ↓
Kubelet
   ↓
Container Runtime
   ↓
Pod
   ↓
应用

二十七、本次实验资源清理

实验完成后,删除临时测试 Pod:

复制代码
kubectl delete pod test-client \
  -n production \
  --ignore-not-found

删除 Nginx Service:

复制代码
kubectl delete service nginx \
  -n production \
  --ignore-not-found

删除 Deployment:

复制代码
kubectl delete deployment nginx \
  -n production \
  --ignore-not-found

确认资源已经删除:

复制代码
kubectl get deployment,replicaset,pod,service \
  -n production

如果 production 命名空间后续不再使用,也可以删除:

复制代码
kubectl delete namespace production

不过如果后续文章还要继续在 production 命名空间中部署应用,可以保留该命名空间。


二十八、总结

执行:

复制代码
kubectl create deployment nginx --image=nginx

看起来只是一条简单命令。

但 Kubernetes 内部实际经历了:

复制代码
kubectl
   ↓
API Server
   ↓
etcd
   ↓
Deployment Controller
   ↓
ReplicaSet Controller
   ↓
Scheduler
   ↓
Kubelet
   ↓
Container Runtime
   ↓
CNI
   ↓
Pod

如果再创建 Service,并通过名称访问应用,还会涉及:

复制代码
CoreDNS
   ↓
Service
   ↓
EndpointSlice
   ↓
Pod

其中:

  • API Server 是统一入口

  • etcd 保存集群状态

  • Controller Manager 维护期望状态

  • Scheduler 选择运行节点

  • Kubelet 管理节点上的 Pod

  • Container Runtime 创建和运行容器

  • CNI 配置 Pod 网络

  • Pod 是 Kubernetes 的最小调度单位

  • Deployment 管理版本和更新

  • ReplicaSet 保证 Pod 副本数量

  • Service 提供稳定访问入口

  • CoreDNS 提供集群内部名称解析

Kubernetes 最核心的并不是"启动容器"。

它真正重要的能力是:

用户只需要声明期望状态,Kubernetes 会通过一组持续运行的控制循环,让实际状态不断接近期望状态。

理解这一点之后,Deployment、滚动发布、自愈、自动扩缩容和声明式管理就不再是彼此独立的功能。

它们本质上都是同一种机制的不同表现。

下一篇,我们将在这个 Kubernetes 实验室中安装 Jenkins,并把代码构建、镜像制作和应用部署流程正式接入集群。

相关推荐
云川之下18 小时前
【k8s】NAD(NetworkAttachmentDefinition)与SR‑IOV‑Operator 关系
云原生·容器·kubernetes
ZzzZZzzzZZZzzzz…21 小时前
K8S实战:NFS+StorageClass+PV/PVC+Deployment
运维·云原生·容器·kubernetes·storageclass·linux运维·持久卷
三言老师1 天前
CentOS7.9 + Kubernetes 1.18.20 -清理所有 docker / containerd 残留
docker·容器·kubernetes
circuitsosk1 天前
从Docker到Kubernetes:AI应用服务化与弹性伸缩的落地实践
人工智能·docker·kubernetes·ai应用部署
AAA@峥1 天前
K8s 配置管理实战:ConfigMap 与 Secret 完整使用指南
kubernetes·云计算
showyoui1 天前
K8s 集群迁移踩坑:Istio 升级后 nginx 403
网络·docker·kubernetes·istio·service_mesh
海兰1 天前
云原生持续交付平台 Zadig 在 Ubuntu 24.04 上的部署及使用指南
linux·ubuntu·云原生
云川之下1 天前
【k8s】sriov‑device‑plugin、sriov‑network‑config‑daemon详解
云原生·容器·kubernetes
张忠琳2 天前
【NPU】Ascend Device Plugin —Part 6 K8s客户端 + 重复检测模块 超深度源码分析之二
云原生·容器·kubernetes·npu·docker device