Skip to content

Kubernetes从零学习路线与第一个应用 ​

Kubernetes,简称 K8s,是一个管理容器化应用的编排平台。它真正解决的不是“怎么启动一个容器”,而是:当应用有多个副本、机器会故障、版本要持续发布、配置会变化、流量有波动时,怎样让系统持续接近期望状态。

本页不是命令速查表,而是一门 Kubernetes 课程的入口。你会亲手部署一个最小 Web 服务,并沿着 kubectl → API Server → 控制器 → 调度器 → kubelet → 容器运行时 → Service 的链路理解每一步为什么发生、怎样证明它发生了,以及失败后从哪里开始排查。

官方文档:Kubernetes 中文文档。不同集群发行版、CNI、CSI 和 Ingress Controller 的具体实现可能不同,本专栏优先解释 Kubernetes API 的稳定语义,再明确实现边界。

学习目标 ​

完成本页后,你应该能够:

  1. 说清 Docker、容器、Pod、Deployment、Service 和 Ingress 的关系。
  2. 理解声明式 API、期望状态、实际状态与控制循环,而不是只会背 YAML。
  3. 看懂一个 Kubernetes 对象的 apiVersion、kind、metadata、spec 和 status。
  4. 独立创建 Namespace、Deployment 和 Service,并验证应用真的可用。
  5. 解释一次 kubectl apply 后,API Server、etcd、Controller、Scheduler、kubelet、CRI 和 CNI 分别做了什么。
  6. 使用 get、describe、logs、events、exec 和 port-forward 收集证据。
  7. 区分“Pod Running”“Pod Ready”“Service 有 Endpoint”“用户请求成功”这几个不同事实。
  8. 知道下一步应按什么顺序学习工作负载、配置、网络、存储、探针、Helm 和可观测性。

一、先回答:只有Docker为什么还需要Kubernetes ​

Docker 能够把程序及其依赖打进镜像,并在一台主机上启动容器:

bash
docker run -d --name order-api -p 8080:8080 order-api:1.0.0

这条命令适合学习或管理少量容器,但商业系统会继续出现下面的问题:

问题只靠单机手工命令的后果Kubernetes提供的能力
容器进程退出需要人工发现和重启Controller持续维持副本数
机器宕机该机器上的应用全部消失Scheduler可把替代Pod调度到健康节点
多个副本地址变化调用方维护IP列表,容易过期Service提供稳定虚拟入口和服务发现
发布新版本手工逐台替换,容易中断或版本混乱Deployment管理滚动更新和Revision
配置变化重新制作镜像或手工改容器ConfigMap、Secret分离配置和镜像
应用“活着但不能接流量”端口存在也可能返回错误Readiness控制是否进入Service Endpoint
多团队共享机器资源争抢,责任边界不清Namespace、Request/Limit、RBAC和策略治理

关键区别是:Docker 更关注“一个容器怎样被创建和运行”,Kubernetes 更关注“整个应用系统的期望状态怎样被持续维护”。Kubernetes 最终仍需要容器运行时,例如 containerd;它不是容器运行时的替代品。

二、Kubernetes不是传统脚本系统 ​

传统命令式脚本描述“按什么步骤做”:

text
创建容器A
创建容器B
如果A失败就再创建A
把A和B写入负载均衡配置

Kubernetes 的声明式对象描述“最终希望成为什么样”:

yaml
spec:
  replicas: 2

Deployment Controller 会不断比较:

text
期望副本数 - 当前可用副本数 = 需要补充的副本数

如果期望是 2,实际只有 1,它就推动系统补出 1 个;如果实际出现 3 个,它会减少 1 个。这个“观察实际状态、计算差异、采取动作、再次观察”的过程叫控制循环或 Reconcile。

mermaid
flowchart TD
    A["用户提交期望状态"] --> B["API Server校验并持久化对象"]
    B --> C["Controller观察对象与实际资源"]
    C --> D["比较期望状态和实际状态"]
    D --> E{"是否已经一致"}
    E -->|"否"| F["创建、更新或删除下级资源"]
    F --> C
    E -->|"是"| G["继续Watch并等待下一次变化"]
    G --> C

这解释了三个常见现象:

  1. kubectl apply 返回成功,只能证明 API 请求被接受,不能证明 Pod 已经 Ready。
  2. 手工删除 Deployment 管理的某个 Pod,Controller 会再创建一个,因为期望副本数没有变化。
  3. 手工修改由 Deployment 管理的 Pod 不是可靠发布方式,新 Pod 会按照 Deployment 的 Pod Template 重建。

深入原理见:Kubernetes架构、控制循环与Pod创建全过程。

三、先建立对象关系,不要孤立背名词 ​

一个典型无状态 Web 应用的对象关系是:

mermaid
flowchart TD
    A["Deployment:声明版本、副本和发布策略"] --> B["ReplicaSet:维护某一版本的Pod副本"]
    B --> C1["Pod 1:最小调度单元"]
    B --> C2["Pod 2:最小调度单元"]
    C1 --> D1["Container:真正运行应用进程"]
    C2 --> D2["Container:真正运行应用进程"]
    E["Service:根据Label选择Ready Pod"] --> C1
    E --> C2
    F["Ingress或Gateway:处理HTTP域名和路径"] --> E

3.1 Image是什么 ​

镜像是只读模板,里面包含应用文件、运行时依赖和默认启动配置。镜像本身不运行,例如 nginx:1.27-alpine 只是一个可分发制品。

3.2 Container是什么 ​

容器是镜像的一次运行实例,本质上仍是宿主机进程,只是受到 Namespace、cgroup 和文件系统隔离。Kubernetes 通常通过 CRI 调用 containerd 等运行时创建容器。

3.3 Pod是什么 ​

Pod 是 Kubernetes 最小调度单位,而不是容器的别名。一个 Pod 可以有多个容器,这些容器:

  • 总是被调度到同一个 Node。
  • 共享 Pod 网络命名空间,因此共享同一个 Pod IP,容器之间可用 localhost 通信。
  • 可以共享声明在 Pod 中的 Volume。
  • 生命周期紧密绑定,不适合把无关业务服务硬塞进同一个 Pod。

Kubernetes 调度 Pod,不直接调度单个容器。最常见的是一个 Pod 放一个业务容器,额外容器只承担紧密相关的 Sidecar 职责。

3.4 Deployment是什么 ​

Deployment 适合无状态、可替换的应用。它不直接创建和计数 Pod,而是创建 ReplicaSet,再由 ReplicaSet 维持 Pod。多一层 ReplicaSet 是为了保存不同 Pod Template 版本,使滚动更新和回滚可追踪。

3.5 Service是什么 ​

Pod 会重建,IP 也会变化。Service 通过 Label Selector 找到一组 Pod,并以稳定 DNS 名称和虚拟地址提供访问入口。Service 本身不是应用副本,也不是简单把 Pod IP 写死在配置文件中。

Service 能不能转发请求,关键要看 EndpointSlice 中是否存在可用 Endpoint;只有 Service 对象存在并不能证明后端可用。

3.6 Ingress是什么 ​

Ingress 是 HTTP/HTTPS 路由规则对象,例如“api.example.com/orders 转给 order-service”。真正处理流量的是 Ingress Controller。只创建 Ingress 而没有对应 Controller,不会凭空产生反向代理。

四、Control Plane和Node分别做什么 ​

可以先把集群理解成两部分:

mermaid
flowchart TD
    A["控制面Control Plane"] --> A1["API Server:统一API入口"]
    A --> A2["etcd:保存集群对象状态"]
    A --> A3["Controller Manager:持续协调对象"]
    A --> A4["Scheduler:为未调度Pod选择Node"]
    B["工作节点Node"] --> B1["kubelet:维护本节点Pod"]
    B --> B2["Container Runtime:创建容器"]
    B --> B3["CNI插件:建立Pod网络"]
    B --> B4["CSI Node插件:挂载持久卷"]
组件核心职责它通常不负责什么
kube-apiserver认证、授权、准入、校验并提供资源API不直接到节点执行 docker run
etcd保存 Kubernetes API 对象的持久状态不保存业务数据库数据,也不保存容器日志
kube-controller-manager运行Deployment、ReplicaSet、Node等控制器不决定Pod最终调度到哪个Node
kube-scheduler为未绑定Pod选择合适Node并提交Binding不负责下载镜像和启动容器
kubelet观察分配到本节点的Pod并驱动运行时落地不负责全局负载均衡和跨节点调度决策
Container Runtime拉镜像、创建Sandbox和Container不理解Deployment滚动更新语义
CNI插件配置Pod网卡、IP和路由不等于HTTP Ingress Controller

记忆这些组件时,重点不是背名称,而是明确职责边界。排障时边界决定证据从哪里找:Pod 一直 Pending 更关注 Scheduler 和事件,Pod 已调度但镜像拉取失败更关注 kubelet、运行时和镜像仓库。

五、Kubernetes对象的通用结构 ​

几乎所有 YAML 都可以先拆成五部分:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-demo
  namespace: k8s-learning
spec:
  replicas: 2
  selector:
    matchLabels:
      app: web-demo
  template:
    metadata:
      labels:
        app: web-demo
    spec:
      containers:
        - name: web
          image: nginxinc/nginx-unprivileged:1.27.3-alpine
status: {} # 通常由系统写入,不应在清单中手工维护

5.1 apiVersion ​

apiVersion 表示资源属于哪个 API Group 和版本。Deployment 使用 apps/v1,不是因为 Kubernetes 软件版本叫 v1,而是因为 Deployment API 当前稳定版本位于 apps Group 的 v1。

5.2 kind ​

kind 表示对象类型。API Server 根据 apiVersion + kind 找到对应资源结构和校验规则。

5.3 metadata ​

metadata 保存对象身份和管理信息,常见字段包括:

  • name:同一 Namespace、同一资源类型内的名称。
  • namespace:命名空间作用域。
  • labels:用于选择、聚合和组织对象的键值对。
  • annotations:不用于选择的大块元数据或工具配置。
  • uid:API Server 为对象生成的全局唯一身份。
  • resourceVersion:并发控制和 Watch 使用的对象版本。
  • ownerReferences:表达上级对象,用于级联管理和垃圾回收。
  • finalizers:删除前必须完成的清理责任。

5.4 spec ​

spec 是用户声明的期望状态。不同 kind 的 spec 结构不同,例如 Deployment 有副本和发布策略,Service 有 Selector 和端口。

5.5 status ​

status 是控制器和系统组件观察后写回的实际状态。把 spec.replicas: 2 写进去,不等于立刻有两个可用实例;需要检查 status.readyReplicas 等字段。

text
spec = 我希望系统变成什么样
status = 系统目前观察到什么样
controller = 持续缩小二者差距

六、实验前需要什么 ​

你需要一个可用 Kubernetes 集群和 kubectl。本页不绑定特定发行版,可以使用:

  • Docker Desktop 内置 Kubernetes。
  • minikube。
  • kind。
  • 企业测试集群或云厂商托管集群。

首先确认当前上下文,避免误操作生产集群:

bash
kubectl config current-context
kubectl config get-contexts

检查 API Server 是否可访问:

bash
kubectl cluster-info

查看节点:

bash
kubectl get nodes -o wide

期望看到类似:

text
NAME           STATUS   ROLES           AGE   VERSION   INTERNAL-IP
learning-node  Ready    control-plane   10d   v1.31.1   192.168.1.20

只有 STATUS=Ready 才说明节点当前能够接受常规调度。即使 Node Ready,仍不能证明镜像仓库、CNI、CSI 和应用都正常。

生产环境执行任何写命令前,都要再次确认 context、Namespace 和目标对象。不要直接复制学习命令到生产集群。

七、创建独立Namespace ​

为实验创建命名空间,避免把对象散落在 default:

yaml
apiVersion: v1
kind: Namespace
metadata:
  name: k8s-learning
  labels:
    purpose: learning

保存为 00-namespace.yaml,然后执行:

bash
kubectl apply -f 00-namespace.yaml

验证:

bash
kubectl get namespace k8s-learning

Namespace 提供名称和策略边界,但它不是虚拟机,也不会天然提供强网络隔离、资源隔离和权限隔离。完整治理还需要 RBAC、ResourceQuota、LimitRange 和 NetworkPolicy。

为了减少后续命令长度,可以设置当前 context 的默认 Namespace:

bash
kubectl config set-context --current --namespace=k8s-learning

切换后仍建议在生产脚本中显式传 -n,避免上下文漂移:

bash
kubectl get pods -n k8s-learning

八、部署第一个应用 ​

下面使用固定版本的 Nginx 演示对象关系。它不是完整生产配置,但已经包含 Label、资源请求/限制、Readiness、Liveness 和安全上下文,足够观察核心链路。

保存为 10-web-demo.yaml:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-demo
  namespace: k8s-learning
  labels:
    app.kubernetes.io/name: web-demo
    app.kubernetes.io/part-of: learning-system
spec:
  replicas: 2
  revisionHistoryLimit: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app.kubernetes.io/name: web-demo
  template:
    metadata:
      labels:
        app.kubernetes.io/name: web-demo
        app.kubernetes.io/part-of: learning-system
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: web
          image: nginx:1.27-alpine
          imagePullPolicy: IfNotPresent
          ports:
            - name: http
              containerPort: 8080
              protocol: TCP
          resources:
            requests:
              cpu: 50m
              memory: 32Mi
            limits:
              cpu: 500m
              memory: 128Mi
          readinessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 2
            periodSeconds: 5
            timeoutSeconds: 2
            failureThreshold: 3
          livenessProbe:
            httpGet:
              path: /
              port: http
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 2
            failureThreshold: 3
          securityContext:
            runAsNonRoot: true
            runAsUser: 101
            runAsGroup: 101
            allowPrivilegeEscalation: false
            capabilities:
              drop:
                - ALL
            seccompProfile:
              type: RuntimeDefault
---
apiVersion: v1
kind: Service
metadata:
  name: web-demo
  namespace: k8s-learning
  labels:
    app.kubernetes.io/name: web-demo
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: web-demo
  ports:
    - name: http
      port: 80
      targetPort: http
      protocol: TCP

8.1 为什么没有写latest ​

固定镜像版本能明确“当前到底发布了什么”。latest 标签可以被重新指向其他镜像内容,导致相同 YAML 在不同时间拉到不同制品。生产还应保存并校验镜像 Digest。

8.2 selector和Pod label为什么必须匹配 ​

Deployment 的 .spec.selector.matchLabels 决定它管理哪些 Pod;Pod Template 中的 Label 必须与其匹配。Service 也通过 Selector 查找 Pod。它们只是使用了相同 Label,但 Deployment 和 Service 之间不存在直接父子关系。

如果 Service Selector 拼错:

text
Deployment和Pod都可以正常运行
Service对象也可以成功创建
但EndpointSlice没有后端
最终请求无法转发到Pod

8.3 requests和limits是什么 ​

  • requests 是调度和资源保障的重要输入。Scheduler 会判断 Node 是否有足够的可分配资源。
  • limits.memory 通常通过 cgroup 限制容器总内存,超限可能被 OOM Kill。
  • limits.cpu 通常形成 CPU 时间配额,超出后会被节流,而不是像内存一样直接杀进程。

50m CPU 表示 0.05 个 CPU 核的请求,不是 50 个 CPU;32Mi 是二进制容量单位。

8.4 readiness和liveness为什么分开 ​

  • Readiness 失败:容器通常继续运行,但 Pod 不应继续接收 Service 流量。
  • Liveness 连续失败:kubelet 会重启该容器。

如果把数据库、下游服务等所有依赖都放进 Liveness,高峰期一次依赖抖动可能触发大批容器重启,形成重启风暴。详细设计见:Startup、Readiness与Liveness探针全过程。

九、提交清单时究竟发生了什么 ​

执行:

bash
kubectl apply -f 10-web-demo.yaml

看到:

text
deployment.apps/web-demo created
service/web-demo created

这只表示两个 API 对象的提交被接受,不表示应用已经可以访问。内部过程可以拆成下面几段。

mermaid
flowchart TD
    A["kubectl读取YAML并确定目标API"] --> B["向API Server发送认证请求"]
    B --> C["认证、授权、准入和字段校验"]
    C --> D["对象持久化到etcd"]
    D --> E["Deployment Controller观察到新对象"]
    E --> F["创建ReplicaSet"]
    F --> G["ReplicaSet Controller创建两个Pod对象"]
    G --> H["Scheduler为每个Pod选择Node"]
    H --> I["目标Node上的kubelet观察到Pod"]
    I --> J["运行时拉镜像并创建容器"]
    J --> K["CNI配置Pod网络"]
    K --> L["探针成功后Pod变为Ready"]
    L --> M["EndpointSlice Controller发布可用Endpoint"]
    M --> N["Service流量可以到达Pod"]

9.1 kubectl不是直接操作容器 ​

kubectl 是 API 客户端。它读取 kubeconfig,确定 Cluster、User、Context 和 Namespace,然后通过 HTTPS 请求 API Server。关闭本机 kubectl 不会让已经运行的 Pod 停止。

9.2 API Server为什么是唯一入口 ​

统一入口可以集中执行认证、授权、准入、字段默认化、版本转换和并发控制。控制器、Scheduler 和 kubelet 也主要通过 API Server 观察和更新对象,不应绕过它直接修改 etcd。

9.3 为什么创建的是ReplicaSet ​

Deployment 负责发布策略和版本历史,ReplicaSet 负责某一个 Pod Template 哈希版本的副本数量。滚动更新时,新旧 ReplicaSet 可以同时存在并按策略调整副本。

9.4 Scheduler为什么只负责选择Node ​

Scheduler 对未绑定 Pod 执行过滤和评分,选择 Node 后写入 Binding。真正到节点拉镜像、建立 Sandbox 和启动容器的是 kubelet 与容器运行时。

9.5 Ready之后为什么还要等待Endpoint传播 ​

Readiness 结果先更新为 Pod Condition,再由控制器更新 EndpointSlice,节点网络数据面随后根据 Endpoint 变化更新。因此“Pod 刚变 Ready”和“所有请求路径已经使用新 Endpoint”之间可能存在短暂传播时间。

十、不要只看一条get pod ​

10.1 先看Deployment ​

bash
kubectl get deployment web-demo -n k8s-learning

示例:

text
NAME       READY   UP-TO-DATE   AVAILABLE   AGE
web-demo   2/2     2            2           40s

字段含义:

  • READY 2/2:当前 Ready 副本数 / 期望副本数。
  • UP-TO-DATE:使用当前 Pod Template 的副本数。
  • AVAILABLE:满足可用条件的副本数,不应简单等同于 Running。

10.2 查看ReplicaSet ​

bash
kubectl get replicasets -n k8s-learning

名称类似:

text
web-demo-7f8c6d4c8b

后缀来自 Pod Template 哈希,用来区分版本。它不是随机业务 ID。

10.3 查看Pod及其Node和IP ​

bash
kubectl get pods -n k8s-learning -o wide

示例:

text
NAME                        READY   STATUS    RESTARTS   AGE   IP           NODE
web-demo-7f8c6d4c8b-abcde   1/1     Running   0          1m    10.244.1.8   worker-1
web-demo-7f8c6d4c8b-fghij   1/1     Running   0          1m    10.244.2.5   worker-2

不要混淆:

观察结果能证明什么不能证明什么
STATUS=Running至少有容器处于运行状态,Pod未处于终止Phase不证明Readiness成功,不证明业务请求成功
READY=1/1当前Pod中要求Ready的容器都Ready不证明Ingress、DNS和公网链路正常
RESTARTS=0当前容器状态记录中没有重启不证明应用没有业务异常
有Pod IPCNI至少分配了地址不证明跨节点路由和NetworkPolicy允许通信

10.4 查看OwnerReference ​

bash
kubectl get pod -n k8s-learning \
  -l app.kubernetes.io/name=web-demo \
  -o custom-columns='NAME:.metadata.name,OWNER_KIND:.metadata.ownerReferences[0].kind,OWNER_NAME:.metadata.ownerReferences[0].name'

你会看到 Pod 的直接 Owner 是 ReplicaSet,而不是 Deployment。继续查看 ReplicaSet 才会发现它的 Owner 是 Deployment。

10.5 查看Service ​

bash
kubectl get service web-demo -n k8s-learning -o wide

CLUSTER-IP 是集群内部虚拟地址,通常不是某个进程在该 IP 上直接监听。iptables、IPVS 或 eBPF 数据面根据实现完成流量转发。

10.6 查看EndpointSlice ​

bash
kubectl get endpointslices -n k8s-learning \
  -l kubernetes.io/service-name=web-demo -o wide

也可以查看详细内容:

bash
kubectl get endpointslices -n k8s-learning \
  -l kubernetes.io/service-name=web-demo -o yaml

这里应看到两个 Ready Pod 的 IP。如果为空,优先对比:

bash
kubectl get service web-demo -n k8s-learning -o jsonpath='{.spec.selector}'
kubectl get pods -n k8s-learning --show-labels

十一、验证访问链路 ​

11.1 使用port-forward进行本地验证 ​

bash
kubectl port-forward -n k8s-learning service/web-demo 8080:80

另开终端访问:

bash
curl -i http://127.0.0.1:8080/

浏览器也可以打开 http://127.0.0.1:8080/。

port-forward 的用途是临时调试,不是生产暴露方案。命令终止后转发立即失效,它也不验证真实 Ingress、云负载均衡和公网 DNS 链路。

11.2 从集群内部访问Service DNS ​

启动一次性客户端:

bash
kubectl run curl-client \
  -n k8s-learning \
  --image=curlimages/curl:8.10.1 \
  --restart=Never \
  --rm -it \
  -- curl -i http://web-demo.k8s-learning.svc.cluster.local/

这条命令验证:

  1. 临时 Pod 能创建并运行。
  2. CoreDNS 能解析 Service 完整域名。
  3. Service 数据面能把连接转发到后端 Endpoint。
  4. Nginx 能返回 HTTP 响应。

它仍然不证明公网入口正常,因为请求没有经过外部负载均衡和 Ingress。

11.3 为什么不直接依赖Pod IP ​

Pod IP 属于当前 Pod 实例。Pod 重建后 IP 可能变化,调用方不应该把它写死。Service DNS 提供的是稳定服务身份,EndpointSlice 负责跟踪当前后端。

十二、get、describe、logs分别回答什么 ​

12.1 get:快速观察当前状态 ​

bash
kubectl get pods -n k8s-learning -o wide
kubectl get deployment web-demo -n k8s-learning -o yaml

get 适合看对象列表或完整资源字段。-o yaml 中的 spec、status、managedFields 是 API Server 当前保存的状态,不完全等于最初文件内容,因为系统会默认化并由控制器写回字段。

12.2 describe:聚合对象详情和相关事件 ​

先找到 Pod:

bash
kubectl get pods -n k8s-learning \
  -l app.kubernetes.io/name=web-demo

再查看:

bash
kubectl describe pod <pod-name> -n k8s-learning

重点阅读:

  • Node:是否已经调度以及调度到哪里。
  • Status、Conditions:Pod Phase 和各 Condition。
  • Containers:当前 State、Last State、Exit Code 和 Restart Count。
  • Requests/Limits:最终资源配置。
  • Readiness/Liveness:最终探针参数。
  • Events:调度、拉镜像、挂载、探针失败等事件。

describe 中没有事件不代表从未发生错误。Event 有保留期、聚合和限流边界,事故证据需要及时保存。

12.3 logs:读取容器标准输出和标准错误 ​

bash
kubectl logs -n k8s-learning <pod-name> -c web --tail=200 --timestamps

持续跟踪:

bash
kubectl logs -n k8s-learning <pod-name> -c web -f

如果容器重启过,查看上一次实例:

bash
kubectl logs -n k8s-learning <pod-name> -c web --previous --timestamps

kubectl logs 通常读取容器运行时管理的 stdout/stderr 日志。应用只写容器内文件时,它不一定能读取该文件。

12.4 events:建立时间线 ​

bash
kubectl get events -n k8s-learning \
  --sort-by=.metadata.creationTimestamp

新版本可使用:

bash
kubectl events -n k8s-learning --for deployment/web-demo

Event 中的 Reason 和 Message 比只看 STATUS 更接近失败原因,例如:

  • FailedScheduling:调度过滤后没有合适 Node。
  • FailedMount:Volume 挂载失败。
  • ErrImagePull:首次镜像拉取失败。
  • ImagePullBackOff:拉取失败后进入指数退避。
  • Unhealthy:探针失败。
  • BackOff:容器反复失败,kubelet延迟重启。

十三、第一次故障实验:Service Selector写错 ​

学习 Kubernetes 最快的方法不是只看成功案例,而是制造一个可恢复故障,并观察每层证据。

执行 Patch,让 Service 选择一个不存在的 Label:

bash
kubectl patch service web-demo -n k8s-learning \
  --type=merge \
  -p '{"spec":{"selector":{"app.kubernetes.io/name":"wrong-name"}}}'

观察 Pod:

bash
kubectl get pods -n k8s-learning \
  -l app.kubernetes.io/name=web-demo

Pod 仍然 Running 且 Ready,因为修改 Service 不会停止 Pod。

观察 EndpointSlice:

bash
kubectl get endpointslices -n k8s-learning \
  -l kubernetes.io/service-name=web-demo -o wide

后端 Endpoint 会消失。此时再次访问 Service 会失败。完整因果链是:

mermaid
flowchart TD
    A["Service Selector被改错"] --> B["没有Pod Label与Selector匹配"]
    B --> C["EndpointSlice Controller移除后端"]
    C --> D["Service对象仍然存在"]
    D --> E["请求没有可转发的Endpoint"]
    E --> F["客户端超时、拒绝或收到实现相关错误"]

恢复正确 Selector:

bash
kubectl patch service web-demo -n k8s-learning \
  --type=merge \
  -p '{"spec":{"selector":{"app.kubernetes.io/name":"web-demo"}}}'

等待并再次检查 EndpointSlice。这个实验说明:排查 Service 访问失败时,不能因为 Pod 正常就停止,也不能因为 Service 对象存在就认定转发正常。

十四、第二次故障实验:手工删除Pod ​

先记录 Pod:

bash
kubectl get pods -n k8s-learning \
  -l app.kubernetes.io/name=web-demo

删除其中一个:

bash
kubectl delete pod <pod-name> -n k8s-learning

持续观察:

bash
kubectl get pods -n k8s-learning -w

你会看到旧 Pod 进入 Terminating,同时出现一个新 Pod。原因不是“Pod 会自愈”,而是:

mermaid
flowchart TD
    A["删除一个Deployment管理的Pod"] --> B["实际Pod数从2变为1"]
    B --> C["ReplicaSet Controller观察到差异"]
    C --> D["ReplicaSet仍声明replicas等于2"]
    D --> E["Controller创建一个替代Pod对象"]
    E --> F["Scheduler和kubelet完成新Pod落地"]

如果你删除的是一个没有 Controller 的裸 Pod,就不会自动重建。因此准确说法是“控制器根据期望状态补副本”,不是“所有 Pod 天生都会自愈”。

十五、第三次实验:扩容和发布 ​

15.1 扩容 ​

bash
kubectl scale deployment web-demo \
  -n k8s-learning \
  --replicas=3

观察状态:

bash
kubectl rollout status deployment/web-demo -n k8s-learning
kubectl get pods -n k8s-learning -o wide

kubectl scale 修改的仍是 Deployment 的 spec.replicas,不是直接运行一个新容器。ReplicaSet Controller 根据差异创建 Pod。

如果随后重新 apply 仍写着 replicas: 2 的原始文件,副本数可能又回到 2。这是声明源不一致,不是 Kubernetes 随机缩容。生产应明确 Git、Helm、GitOps Controller 或 HPA 中谁拥有该字段。

15.2 更新镜像 ​

为了保持示例可复现,可以只理解命令,不必在生产照搬:

bash
kubectl set image deployment/web-demo \
  -n k8s-learning \
  web=nginxinc/nginx-unprivileged:1.27.4-alpine

查看发布:

bash
kubectl rollout status deployment/web-demo -n k8s-learning --timeout=2m
kubectl rollout history deployment/web-demo -n k8s-learning
kubectl get replicasets -n k8s-learning

滚动更新不是原地修改现有容器。Deployment 创建新 ReplicaSet,逐步扩大新版本并缩小旧版本。maxSurge: 1 允许临时比期望多 1 个 Pod,maxUnavailable: 0 要求更新期间不主动降低可用副本目标。

15.3 回滚 ​

bash
kubectl rollout undo deployment/web-demo -n k8s-learning
kubectl rollout status deployment/web-demo -n k8s-learning

Deployment 回滚主要恢复历史 Pod Template。它不能自动回滚数据库迁移、消息副作用、外部接口调用和不兼容数据。商业发布必须把应用回滚与数据兼容策略一起设计。

完整算法和取整规则见:工作负载、滚动更新与任务调度。

十六、常见状态到底表示什么 ​

状态/原因所在层准确含义第一批证据
PendingPod PhasePod尚未完成运行落地,可能未调度,也可能在等镜像或Volumedescribe pod、Events、.spec.nodeName
ContainerCreatingkubectl展示原因kubelet正在准备Sandbox、网络、挂载或容器Events、kubelet/运行时证据
ErrImagePullContainer Waiting Reason本次镜像拉取失败Events中的仓库、认证、名称错误
ImagePullBackOffContainer Waiting Reason连续拉取失败后处于退避,不是新的根因describe pod中更早的拉取错误
CrashLoopBackOffContainer Waiting Reason容器反复退出,kubelet延迟重启logs --previous、Last State、Exit Code
RunningPod PhasePod已绑定Node,至少一个容器运行/启动/重启中Conditions、容器State、Readiness
Ready=FalsePod ConditionPod当前不应接收常规Service流量Probe事件、EndpointSlice、应用健康
OOMKilledContainer Termination Reason容器受到内存限制相关OOM终止limits、工作集、JVM/native内存、节点日志
Terminatingkubectl展示状态删除时间戳已设置,正在执行终止和清理finalizer、preStop、grace period、Volume卸载

CrashLoopBackOff 不是根因,只是“连续失败后延迟重启”的现象。真正根因可能是参数错误、配置缺失、端口占用、权限错误、依赖不可用、探针误杀或 OOM。

十七、从现象到证据的最小排查顺序 ​

收到“应用在 K8s 上打不开”时,不要一上来重启。先固定时间、集群、Namespace、对象和版本,然后从对象链逐层缩小范围:

mermaid
flowchart TD
    A["确认Context、Namespace和故障时间"] --> B["查看Deployment与Pod状态"]
    B --> C["describe读取Condition、State和Events"]
    C --> D["logs与previous还原应用退出原因"]
    D --> E["检查Service Selector和EndpointSlice"]
    E --> F["从集群内验证DNS、端口和HTTP"]
    F --> G["检查Ingress、TLS和外部负载均衡"]
    G --> H["对齐指标、Trace、节点与变更时间线"]

最小命令集:

bash
kubectl config current-context
kubectl get deployment,pods,service,endpointslices -n k8s-learning -o wide
kubectl describe deployment web-demo -n k8s-learning
kubectl describe pod <pod-name> -n k8s-learning
kubectl logs <pod-name> -n k8s-learning -c web --tail=200 --timestamps
kubectl logs <pod-name> -n k8s-learning -c web --previous --timestamps
kubectl get events -n k8s-learning --sort-by=.metadata.creationTimestamp

生产证据应该包含原始输出和准确时间,不要只记录“Pod 看起来正常”。

十八、配置、网络、存储和入口应该怎样继续学 ​

Kubernetes 的推荐学习顺序不是按命令数量,而是按依赖关系推进:

第1阶段:对象模型和Pod创建 ​

学习:架构、控制循环与Pod创建全过程

必须理解:

  • API 请求如何经过认证、授权、准入和持久化。
  • List-Watch、Informer、Queue 和 Reconcile。
  • Deployment、ReplicaSet、Pod 的 OwnerReference 链。
  • Scheduler Filter/Score/Bind。
  • kubelet、CRI、CNI、CSI 和 Pod Sandbox。

第2阶段:工作负载和发布 ​

学习:工作负载、滚动更新与任务调度

必须理解:

  • Deployment、StatefulSet、DaemonSet、Job 和 CronJob 的选择边界。
  • maxSurge、maxUnavailable 的计算和发布峰值。
  • 优雅终止、PDB、HPA、回滚边界。
  • 有状态身份、PVC 生命周期和任务幂等。

第3阶段:配置和密钥 ​

学习:ConfigMap、Secret与配置轮换全过程

必须理解:

  • 环境变量为什么不会热更新。
  • Projected Volume 的 Atomic Writer 和 ..data 符号链接。
  • subPath 为什么不会跟随更新。
  • Secret Base64 为什么不是加密。
  • 数据库密码和证书怎样零停机轮换。

第4阶段:Service、DNS和网络策略 ​

学习:Pod网络、Service、DNS与NetworkPolicy

必须理解:

  • Pod Network Namespace 和跨节点通信。
  • Service Selector 如何变成 EndpointSlice。
  • ClusterIP、iptables/IPVS/eBPF 和连接级负载均衡。
  • CoreDNS、搜索域、ndots 和 DNS 缓存。
  • NetworkPolicy 的默认拒绝和 DNS 放行。

第5阶段:Ingress和TLS ​

学习:Ingress、TLS与七层流量排障

必须理解:

  • Ingress API、IngressClass 和 Controller 的区别。
  • DNS、SNI、Host、Path、Service 到 Pod 的完整路径。
  • TLS终止、重加密、透传和证书轮换。
  • 404、502、503、504、413 分层排查。
  • Ingress、Gateway API 和业务 API Gateway 的边界。

第6阶段:持久化存储 ​

学习:PV、PVC、StorageClass与CSI存储全过程

必须理解:

  • PV、PVC、StorageClass 的解耦。
  • 动态供应、WaitForFirstConsumer 和 Volume Binding。
  • CSI Attach、Stage、Publish。
  • RWO、RWOP、RWX 的真实语义。
  • Snapshot 为什么不等于备份。

第7阶段:健康检查和流量准入 ​

学习:Startup、Readiness、Liveness探针全过程

必须理解:

  • Startup 对另外两种探针的门控。
  • Readiness 到 EndpointSlice 的异步传播。
  • Liveness 为什么可能制造重启风暴。
  • 探针成功为什么仍不等于用户链路成功。

第8阶段:Helm和生产发布 ​

学习:Helm Chart、模板渲染与生产发布全过程

必须理解:

  • Chart、Release、Revision 和 Values 优先级。
  • Go Template 作用域、渲染和类型陷阱。
  • Install、Upgrade、Hook、CRD 和 Rollback。
  • --atomic 为什么不是分布式事务。
  • Release Secret、凭据泄露和制品供应链。

第9阶段:可观测性和生产排障 ​

学习:Kubernetes可观测性与生产故障排查

必须理解:

  • 对象状态、Event、日志、指标、Trace 和 Profile 的证据边界。
  • Metrics Server、Prometheus 和 kube-state-metrics 的区别。
  • Pending、CrashLoopBackOff、OOMKilled、CPU节流、Ready但503和Node NotReady。
  • 事故证据包和告警设计。

完成原理页后再看:Kubernetes面试题。面试页用于练习短回答和追问,不能替代原理学习。

十九、商业项目从开发到上线的完整位置 ​

Kubernetes 不是软件交付链的全部,它位于制品构建之后、运行治理之中:

mermaid
flowchart TD
    A["开发提交代码"] --> B["CI执行测试与安全扫描"]
    B --> C["构建不可变镜像"]
    C --> D["推送镜像仓库并记录Digest"]
    D --> E["更新Helm Values或部署清单"]
    E --> F["审核配置、权限和发布差异"]
    F --> G["提交到Kubernetes API"]
    G --> H["工作负载滚动更新"]
    H --> I["Readiness控制流量准入"]
    I --> J["指标、日志、Trace和业务验证"]
    J --> K{"发布是否满足SLO"}
    K -->|"是"| L["继续观察并完成发布"]
    K -->|"否"| M["停止扩散、回滚或前向修复"]

商业发布至少还要考虑:

  • 镜像来源、漏洞扫描、签名和不可变 Digest。
  • 最小权限 ServiceAccount、RBAC 和 Pod Security。
  • ConfigMap/Secret 版本和密钥轮换。
  • Request/Limit、HPA、PDB 和节点容量。
  • 数据库变更的向前/向后兼容。
  • Readiness、优雅终止和连接排空。
  • 监控、告警、发布标记和回滚判据。
  • 多可用区、存储、备份、恢复和容灾演练。

二十、哪些事实本页Demo没有证明 ​

完成第一个应用后,不要误认为“已经掌握生产 Kubernetes”。本实验能证明和不能证明的范围如下:

实验结果已证明尚未证明
Deployment 2/2 Ready控制器、调度、节点运行时和基础探针链可用多可用区、高峰容量和故障恢复满足SLO
Service有两个EndpointSelector与Readiness链路当前正常长连接均衡、跨节点性能和NetworkPolicy正确
port-forward可访问API连接、临时隧道和应用HTTP响应正常Ingress、TLS、公网DNS和外部LB正常
删除Pod后补出新PodReplicaSet控制循环工作Node宕机时存储、流量和恢复时间满足要求
rollout成功新Pod Template达到Deployment可用条件业务功能、数据库兼容和外部副作用全部正确

工程能力来自“明确证据边界”,而不是看到一个绿色状态就推断整个系统正常。

二十一、清理学习资源 ​

先确认当前 context:

bash
kubectl config current-context

删除整个实验 Namespace 会级联删除其中大多数命名空间级对象:

bash
kubectl delete namespace k8s-learning

然后把 context 默认 Namespace 改回 default:

bash
kubectl config set-context --current --namespace=default

注意:删除 Namespace 不一定会立即完成。Finalizer、Webhook、APIService 或外部控制器故障可能让它长时间处于 Terminating。不要在不了解清理责任时直接删除 Finalizer。

二十二、常见误区与不这样做的后果 ​

误区为什么错后果
Pod等于容器Pod是调度和共享网络/存储的单位,可包含多个容器无法理解Sidecar、Sandbox和Pod级生命周期
Deployment直接管理Pod中间有ReplicaSet保存版本副本看不懂滚动更新、OwnerReference和回滚
apply成功等于上线成功只证明API请求被接受忽略调度、拉镜像、探针和业务验证失败
Running等于服务可用Running不是Ready,更不是端到端可用用户已经失败但监控仍显示“容器运行”
Service存在就能转发后端来自Selector和EndpointSliceSelector错误时长期没有后端
Pod IP可以写进配置Pod重建后IP可变调用方持有过期地址导致故障
Ingress对象会自动代理必须有匹配的Ingress Controller规则存在但没有数据面处理
Secret就是加密存储data默认只是Base64编码凭据被API权限、日志或清单泄露
Limit越小越节省内存过低会OOM,CPU过低会节流延迟升高、重启和发布失败
重启能修好所有故障重启会销毁现场且未修复根因证据丢失、故障反复、影响扩大
回滚Deployment能回滚数据Deployment只管理Pod Template历史数据库和外部副作用仍停留在新状态

二十三、面试标准回答 ​

Kubernetes解决什么问题 ​

Kubernetes通过声明式API和控制循环管理容器化应用。用户提交期望副本、版本、配置和策略,API Server持久化对象,Controller持续协调实际状态,Scheduler选择节点,kubelet驱动容器运行时、网络和存储落地。它提供副本维持、滚动更新、服务发现、配置、存储和故障恢复能力,但不会自动解决应用幂等、数据库高可用、数据兼容和业务SLO问题。

Pod、Deployment和Service是什么关系 ​

Pod是最小调度单位,里面运行一个或多个共享网络与Volume的容器;Deployment通过ReplicaSet维护一组无状态Pod并管理滚动更新;Service根据Label Selector生成EndpointSlice,为Ready Pod提供稳定DNS和虚拟入口。Deployment与Service通常使用相同Pod Label,但二者不存在直接父子关系。

kubectl apply之后发生什么 ​

kubectl把声明发送给API Server,经过认证、授权、准入和字段校验后持久化。Deployment Controller创建ReplicaSet,ReplicaSet Controller创建Pod,Scheduler选择Node,目标Node的kubelet通过CRI驱动运行时拉镜像和建容器,通过CNI建立网络,探针成功后Pod变Ready,EndpointSlice再把它加入Service后端。apply成功只代表API提交成功,不代表业务已经可用。

Pod Running为什么仍然访问不了 ​

Running只表示Pod已绑定节点且至少有容器正在运行或启动,不代表Readiness成功。还要检查容器端口和监听地址、Readiness Condition、Service Selector、EndpointSlice、targetPort、DNS、NetworkPolicy、Ingress和外部负载均衡,并从集群内到集群外逐层验证,不能只凭Pod状态判断。

二十四、学习验收清单 ​

不要以“看完了”为完成标准。你应该能够实际完成并解释:

  • [ ] 画出 Deployment → ReplicaSet → Pod → Container 的管理链。
  • [ ] 解释 spec、status 和 Reconcile 的关系。
  • [ ] 使用独立 Namespace 部署两个副本的应用。
  • [ ] 通过 OwnerReference 证明 Pod 的直接Owner是ReplicaSet。
  • [ ] 通过 Label、Service Selector 和 EndpointSlice 证明流量后端怎样产生。
  • [ ] 区分 Running、Ready、Available 和端到端业务成功。
  • [ ] 使用 describe、logs --previous 和 Events 收集故障证据。
  • [ ] 制造错误Selector并根据EndpointSlice定位问题。
  • [ ] 删除一个Pod并解释为什么Controller会补副本。
  • [ ] 完成扩容、滚动更新和回滚,并说明它们不能解决的数据副作用。
  • [ ] 说明 port-forward 能证明什么、不能证明什么。
  • [ ] 按本页路线完成各深层原理专栏,而不是只背命令。

关联知识点 ​