Kubernetes从零学习路线与第一个应用
Kubernetes,简称 K8s,是一个管理容器化应用的编排平台。它真正解决的不是“怎么启动一个容器”,而是:当应用有多个副本、机器会故障、版本要持续发布、配置会变化、流量有波动时,怎样让系统持续接近期望状态。
本页不是命令速查表,而是一门 Kubernetes 课程的入口。你会亲手部署一个最小 Web 服务,并沿着 kubectl → API Server → 控制器 → 调度器 → kubelet → 容器运行时 → Service 的链路理解每一步为什么发生、怎样证明它发生了,以及失败后从哪里开始排查。
官方文档:Kubernetes 中文文档。不同集群发行版、CNI、CSI 和 Ingress Controller 的具体实现可能不同,本专栏优先解释 Kubernetes API 的稳定语义,再明确实现边界。
学习目标
完成本页后,你应该能够:
- 说清 Docker、容器、Pod、Deployment、Service 和 Ingress 的关系。
- 理解声明式 API、期望状态、实际状态与控制循环,而不是只会背 YAML。
- 看懂一个 Kubernetes 对象的
apiVersion、kind、metadata、spec和status。 - 独立创建 Namespace、Deployment 和 Service,并验证应用真的可用。
- 解释一次
kubectl apply后,API Server、etcd、Controller、Scheduler、kubelet、CRI 和 CNI 分别做了什么。 - 使用
get、describe、logs、events、exec和port-forward收集证据。 - 区分“Pod Running”“Pod Ready”“Service 有 Endpoint”“用户请求成功”这几个不同事实。
- 知道下一步应按什么顺序学习工作负载、配置、网络、存储、探针、Helm 和可观测性。
一、先回答:只有Docker为什么还需要Kubernetes
Docker 能够把程序及其依赖打进镜像,并在一台主机上启动容器:
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不是传统脚本系统
传统命令式脚本描述“按什么步骤做”:
创建容器A
创建容器B
如果A失败就再创建A
把A和B写入负载均衡配置Kubernetes 的声明式对象描述“最终希望成为什么样”:
spec:
replicas: 2Deployment Controller 会不断比较:
期望副本数 - 当前可用副本数 = 需要补充的副本数如果期望是 2,实际只有 1,它就推动系统补出 1 个;如果实际出现 3 个,它会减少 1 个。这个“观察实际状态、计算差异、采取动作、再次观察”的过程叫控制循环或 Reconcile。
flowchart TD
A["用户提交期望状态"] --> B["API Server校验并持久化对象"]
B --> C["Controller观察对象与实际资源"]
C --> D["比较期望状态和实际状态"]
D --> E{"是否已经一致"}
E -->|"否"| F["创建、更新或删除下级资源"]
F --> C
E -->|"是"| G["继续Watch并等待下一次变化"]
G --> C这解释了三个常见现象:
kubectl apply返回成功,只能证明 API 请求被接受,不能证明 Pod 已经 Ready。- 手工删除 Deployment 管理的某个 Pod,Controller 会再创建一个,因为期望副本数没有变化。
- 手工修改由 Deployment 管理的 Pod 不是可靠发布方式,新 Pod 会按照 Deployment 的 Pod Template 重建。
深入原理见:Kubernetes架构、控制循环与Pod创建全过程。
三、先建立对象关系,不要孤立背名词
一个典型无状态 Web 应用的对象关系是:
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域名和路径"] --> E3.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分别做什么
可以先把集群理解成两部分:
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 都可以先拆成五部分:
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 等字段。
spec = 我希望系统变成什么样
status = 系统目前观察到什么样
controller = 持续缩小二者差距六、实验前需要什么
你需要一个可用 Kubernetes 集群和 kubectl。本页不绑定特定发行版,可以使用:
- Docker Desktop 内置 Kubernetes。
- minikube。
- kind。
- 企业测试集群或云厂商托管集群。
首先确认当前上下文,避免误操作生产集群:
kubectl config current-context
kubectl config get-contexts检查 API Server 是否可访问:
kubectl cluster-info查看节点:
kubectl get nodes -o wide期望看到类似:
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:
apiVersion: v1
kind: Namespace
metadata:
name: k8s-learning
labels:
purpose: learning保存为 00-namespace.yaml,然后执行:
kubectl apply -f 00-namespace.yaml验证:
kubectl get namespace k8s-learningNamespace 提供名称和策略边界,但它不是虚拟机,也不会天然提供强网络隔离、资源隔离和权限隔离。完整治理还需要 RBAC、ResourceQuota、LimitRange 和 NetworkPolicy。
为了减少后续命令长度,可以设置当前 context 的默认 Namespace:
kubectl config set-context --current --namespace=k8s-learning切换后仍建议在生产脚本中显式传 -n,避免上下文漂移:
kubectl get pods -n k8s-learning八、部署第一个应用
下面使用固定版本的 Nginx 演示对象关系。它不是完整生产配置,但已经包含 Label、资源请求/限制、Readiness、Liveness 和安全上下文,足够观察核心链路。
保存为 10-web-demo.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: TCP8.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 拼错:
Deployment和Pod都可以正常运行
Service对象也可以成功创建
但EndpointSlice没有后端
最终请求无法转发到Pod8.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探针全过程。
九、提交清单时究竟发生了什么
执行:
kubectl apply -f 10-web-demo.yaml看到:
deployment.apps/web-demo created
service/web-demo created这只表示两个 API 对象的提交被接受,不表示应用已经可以访问。内部过程可以拆成下面几段。
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
kubectl get deployment web-demo -n k8s-learning示例:
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
kubectl get replicasets -n k8s-learning名称类似:
web-demo-7f8c6d4c8b后缀来自 Pod Template 哈希,用来区分版本。它不是随机业务 ID。
10.3 查看Pod及其Node和IP
kubectl get pods -n k8s-learning -o wide示例:
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 IP | CNI至少分配了地址 | 不证明跨节点路由和NetworkPolicy允许通信 |
10.4 查看OwnerReference
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
kubectl get service web-demo -n k8s-learning -o wideCLUSTER-IP 是集群内部虚拟地址,通常不是某个进程在该 IP 上直接监听。iptables、IPVS 或 eBPF 数据面根据实现完成流量转发。
10.6 查看EndpointSlice
kubectl get endpointslices -n k8s-learning \
-l kubernetes.io/service-name=web-demo -o wide也可以查看详细内容:
kubectl get endpointslices -n k8s-learning \
-l kubernetes.io/service-name=web-demo -o yaml这里应看到两个 Ready Pod 的 IP。如果为空,优先对比:
kubectl get service web-demo -n k8s-learning -o jsonpath='{.spec.selector}'
kubectl get pods -n k8s-learning --show-labels十一、验证访问链路
11.1 使用port-forward进行本地验证
kubectl port-forward -n k8s-learning service/web-demo 8080:80另开终端访问:
curl -i http://127.0.0.1:8080/浏览器也可以打开 http://127.0.0.1:8080/。
port-forward 的用途是临时调试,不是生产暴露方案。命令终止后转发立即失效,它也不验证真实 Ingress、云负载均衡和公网 DNS 链路。
11.2 从集群内部访问Service DNS
启动一次性客户端:
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/这条命令验证:
- 临时 Pod 能创建并运行。
- CoreDNS 能解析 Service 完整域名。
- Service 数据面能把连接转发到后端 Endpoint。
- Nginx 能返回 HTTP 响应。
它仍然不证明公网入口正常,因为请求没有经过外部负载均衡和 Ingress。
11.3 为什么不直接依赖Pod IP
Pod IP 属于当前 Pod 实例。Pod 重建后 IP 可能变化,调用方不应该把它写死。Service DNS 提供的是稳定服务身份,EndpointSlice 负责跟踪当前后端。
十二、get、describe、logs分别回答什么
12.1 get:快速观察当前状态
kubectl get pods -n k8s-learning -o wide
kubectl get deployment web-demo -n k8s-learning -o yamlget 适合看对象列表或完整资源字段。-o yaml 中的 spec、status、managedFields 是 API Server 当前保存的状态,不完全等于最初文件内容,因为系统会默认化并由控制器写回字段。
12.2 describe:聚合对象详情和相关事件
先找到 Pod:
kubectl get pods -n k8s-learning \
-l app.kubernetes.io/name=web-demo再查看:
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:读取容器标准输出和标准错误
kubectl logs -n k8s-learning <pod-name> -c web --tail=200 --timestamps持续跟踪:
kubectl logs -n k8s-learning <pod-name> -c web -f如果容器重启过,查看上一次实例:
kubectl logs -n k8s-learning <pod-name> -c web --previous --timestampskubectl logs 通常读取容器运行时管理的 stdout/stderr 日志。应用只写容器内文件时,它不一定能读取该文件。
12.4 events:建立时间线
kubectl get events -n k8s-learning \
--sort-by=.metadata.creationTimestamp新版本可使用:
kubectl events -n k8s-learning --for deployment/web-demoEvent 中的 Reason 和 Message 比只看 STATUS 更接近失败原因,例如:
FailedScheduling:调度过滤后没有合适 Node。FailedMount:Volume 挂载失败。ErrImagePull:首次镜像拉取失败。ImagePullBackOff:拉取失败后进入指数退避。Unhealthy:探针失败。BackOff:容器反复失败,kubelet延迟重启。
十三、第一次故障实验:Service Selector写错
学习 Kubernetes 最快的方法不是只看成功案例,而是制造一个可恢复故障,并观察每层证据。
执行 Patch,让 Service 选择一个不存在的 Label:
kubectl patch service web-demo -n k8s-learning \
--type=merge \
-p '{"spec":{"selector":{"app.kubernetes.io/name":"wrong-name"}}}'观察 Pod:
kubectl get pods -n k8s-learning \
-l app.kubernetes.io/name=web-demoPod 仍然 Running 且 Ready,因为修改 Service 不会停止 Pod。
观察 EndpointSlice:
kubectl get endpointslices -n k8s-learning \
-l kubernetes.io/service-name=web-demo -o wide后端 Endpoint 会消失。此时再次访问 Service 会失败。完整因果链是:
flowchart TD
A["Service Selector被改错"] --> B["没有Pod Label与Selector匹配"]
B --> C["EndpointSlice Controller移除后端"]
C --> D["Service对象仍然存在"]
D --> E["请求没有可转发的Endpoint"]
E --> F["客户端超时、拒绝或收到实现相关错误"]恢复正确 Selector:
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:
kubectl get pods -n k8s-learning \
-l app.kubernetes.io/name=web-demo删除其中一个:
kubectl delete pod <pod-name> -n k8s-learning持续观察:
kubectl get pods -n k8s-learning -w你会看到旧 Pod 进入 Terminating,同时出现一个新 Pod。原因不是“Pod 会自愈”,而是:
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 扩容
kubectl scale deployment web-demo \
-n k8s-learning \
--replicas=3观察状态:
kubectl rollout status deployment/web-demo -n k8s-learning
kubectl get pods -n k8s-learning -o widekubectl scale 修改的仍是 Deployment 的 spec.replicas,不是直接运行一个新容器。ReplicaSet Controller 根据差异创建 Pod。
如果随后重新 apply 仍写着 replicas: 2 的原始文件,副本数可能又回到 2。这是声明源不一致,不是 Kubernetes 随机缩容。生产应明确 Git、Helm、GitOps Controller 或 HPA 中谁拥有该字段。
15.2 更新镜像
为了保持示例可复现,可以只理解命令,不必在生产照搬:
kubectl set image deployment/web-demo \
-n k8s-learning \
web=nginxinc/nginx-unprivileged:1.27.4-alpine查看发布:
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 回滚
kubectl rollout undo deployment/web-demo -n k8s-learning
kubectl rollout status deployment/web-demo -n k8s-learningDeployment 回滚主要恢复历史 Pod Template。它不能自动回滚数据库迁移、消息副作用、外部接口调用和不兼容数据。商业发布必须把应用回滚与数据兼容策略一起设计。
完整算法和取整规则见:工作负载、滚动更新与任务调度。
十六、常见状态到底表示什么
| 状态/原因 | 所在层 | 准确含义 | 第一批证据 |
|---|---|---|---|
Pending | Pod Phase | Pod尚未完成运行落地,可能未调度,也可能在等镜像或Volume | describe pod、Events、.spec.nodeName |
ContainerCreating | kubectl展示原因 | kubelet正在准备Sandbox、网络、挂载或容器 | Events、kubelet/运行时证据 |
ErrImagePull | Container Waiting Reason | 本次镜像拉取失败 | Events中的仓库、认证、名称错误 |
ImagePullBackOff | Container Waiting Reason | 连续拉取失败后处于退避,不是新的根因 | describe pod中更早的拉取错误 |
CrashLoopBackOff | Container Waiting Reason | 容器反复退出,kubelet延迟重启 | logs --previous、Last State、Exit Code |
Running | Pod Phase | Pod已绑定Node,至少一个容器运行/启动/重启中 | Conditions、容器State、Readiness |
Ready=False | Pod Condition | Pod当前不应接收常规Service流量 | Probe事件、EndpointSlice、应用健康 |
OOMKilled | Container Termination Reason | 容器受到内存限制相关OOM终止 | limits、工作集、JVM/native内存、节点日志 |
Terminating | kubectl展示状态 | 删除时间戳已设置,正在执行终止和清理 | finalizer、preStop、grace period、Volume卸载 |
CrashLoopBackOff 不是根因,只是“连续失败后延迟重启”的现象。真正根因可能是参数错误、配置缺失、端口占用、权限错误、依赖不可用、探针误杀或 OOM。
十七、从现象到证据的最小排查顺序
收到“应用在 K8s 上打不开”时,不要一上来重启。先固定时间、集群、Namespace、对象和版本,然后从对象链逐层缩小范围:
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、节点与变更时间线"]最小命令集:
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创建
必须理解:
- 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阶段:配置和密钥
必须理解:
- 环境变量为什么不会热更新。
- 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 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和生产发布
必须理解:
- Chart、Release、Revision 和 Values 优先级。
- Go Template 作用域、渲染和类型陷阱。
- Install、Upgrade、Hook、CRD 和 Rollback。
--atomic为什么不是分布式事务。- Release Secret、凭据泄露和制品供应链。
第9阶段:可观测性和生产排障
必须理解:
- 对象状态、Event、日志、指标、Trace 和 Profile 的证据边界。
- Metrics Server、Prometheus 和 kube-state-metrics 的区别。
- Pending、CrashLoopBackOff、OOMKilled、CPU节流、Ready但503和Node NotReady。
- 事故证据包和告警设计。
完成原理页后再看:Kubernetes面试题。面试页用于练习短回答和追问,不能替代原理学习。
十九、商业项目从开发到上线的完整位置
Kubernetes 不是软件交付链的全部,它位于制品构建之后、运行治理之中:
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有两个Endpoint | Selector与Readiness链路当前正常 | 长连接均衡、跨节点性能和NetworkPolicy正确 |
| port-forward可访问 | API连接、临时隧道和应用HTTP响应正常 | Ingress、TLS、公网DNS和外部LB正常 |
| 删除Pod后补出新Pod | ReplicaSet控制循环工作 | Node宕机时存储、流量和恢复时间满足要求 |
| rollout成功 | 新Pod Template达到Deployment可用条件 | 业务功能、数据库兼容和外部副作用全部正确 |
工程能力来自“明确证据边界”,而不是看到一个绿色状态就推断整个系统正常。
二十一、清理学习资源
先确认当前 context:
kubectl config current-context删除整个实验 Namespace 会级联删除其中大多数命名空间级对象:
kubectl delete namespace k8s-learning然后把 context 默认 Namespace 改回 default:
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和EndpointSlice | Selector错误时长期没有后端 |
| 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 能证明什么、不能证明什么。
- [ ] 按本页路线完成各深层原理专栏,而不是只背命令。
