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 对象的 apiVersionkindmetadataspecstatus
  4. 独立创建 Namespace、Deployment 和 Service,并验证应用真的可用。
  5. 解释一次 kubectl apply 后,API Server、etcd、Controller、Scheduler、kubelet、CRI 和 CNI 分别做了什么。
  6. 使用 getdescribelogseventsexecport-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 中的 specstatusmanagedFields 是 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:是否已经调度以及调度到哪里。
  • StatusConditions: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 中的 ReasonMessage 比只看 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 仍然 RunningReady,因为修改 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 的选择边界。
  • maxSurgemaxUnavailable 的计算和发布峰值。
  • 优雅终止、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 的管理链。
  • [ ] 解释 specstatus 和 Reconcile 的关系。
  • [ ] 使用独立 Namespace 部署两个副本的应用。
  • [ ] 通过 OwnerReference 证明 Pod 的直接Owner是ReplicaSet。
  • [ ] 通过 Label、Service Selector 和 EndpointSlice 证明流量后端怎样产生。
  • [ ] 区分 Running、Ready、Available 和端到端业务成功。
  • [ ] 使用 describelogs --previous 和 Events 收集故障证据。
  • [ ] 制造错误Selector并根据EndpointSlice定位问题。
  • [ ] 删除一个Pod并解释为什么Controller会补副本。
  • [ ] 完成扩容、滚动更新和回滚,并说明它们不能解决的数据副作用。
  • [ ] 说明 port-forward 能证明什么、不能证明什么。
  • [ ] 按本页路线完成各深层原理专栏,而不是只背命令。

关联知识点