- 文档
- 教程
- 云原生
【免费下载链接】k8s-tutorials
k8s tutorials | k8s 教程
在生产环境中,我们几乎不会直接管理 Pod,而是通过 Kubernetes 的 Deployment 资源以声明式方式管理 Pod 的副本数量、版本升级与故障自愈。本指南以 k8s-tutorials 仓库中的hellok8s示例应用为主线,从零编写 Deployment 资源文件,完整演示水平扩容、v1 到 v2 的版本升级、滚动更新(Rolling Update)策略、回滚操作,以及存活探针(livenessProbe)与就绪探针(readinessProbe)的配置与观测方法。读完本文,你将能够独立编写一份生产可用的 Deployment 定义,并掌握滚动更新与探针机制的核心原理。
为什么需要 Deployment:从手动管理 Pod 到声明式运维
设想一个生产环境:我们手动部署了 10 个hellok8s:v1版本的 Pod,此时需要升级到hellok8s:v2。如果逐个手工删除、重建 Pod,不仅繁琐低效,还极易出错。这正是 Deployment 存在的意义——它是 Kubernetes 中负责管理 Pod 的核心工作负载资源,帮助我们自动完成以下工作:
- 自愈能力:Pod 异常退出或被删除时,自动创建新 Pod 补齐副本;
- 水平扩缩容:通过修改
replicas字段,一键增减 Pod 副本数; - 版本升级与回滚:以滚动更新的方式平滑升级镜像版本,失败时可快速回滚;
- 发布策略控制:通过
strategy字段精细化控制升级过程中的可用性边界。
仓库中的 deployment/v1/deployment.yaml 与 deployment/v2/deployment.yaml 是本文全部实操所对应的最终资源文件,建议边读边对照。
Deployment 资源定义详解
首先创建deployment.yaml文件,用于管理hellok8sPod。以replicas: 1的单副本起步,完整定义如下:
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: replicas: 1 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:v1 name: hellok8s-container各字段的作用如下:
apiVersion: apps/v1:Deployment 所属的 API 版本。Deployment 属于 apps 组,当前稳定版本为apps/v1;kind: Deployment:声明要创建的资源类型是 Deployment;metadata.name:Deployment 的名字,在集群内需要唯一,此处为hellok8s-deployment;spec.replicas:期望的 Pod 副本数量,此处为 1;spec.selector.matchLabels:Deployment 与 Pod 的关联方式。这里表示 Deployment 会管理(select)所有带labels: app: hellok8s标签的 Pod;spec.template:定义 Pod 的模板,其结构与独立创建 Pod 时的定义几乎一致。与 docs/pod.md 中手动创建 Pod 的资源定义相比,唯一的区别是需要通过metadata.labels声明app: hellok8s,与上面的selector.matchLabels对应起来,表明该 Pod 归 Deployment 管理。不需要在 template 中写metadata.name,因为 Deployment 会主动为 Pod 生成唯一的name(通常是deployment名-随机串的形式)。
创建 Deployment 并体验 Pod 自愈
使用kubectl apply创建资源,并通过get、delete pod命令初步感受 Deployment 的魅力。注意:每次创建的 Pod 名称都会变化,某些命令记得替换成你实际的 Pod 名称。
kubectl apply -f deployment.yaml kubectl get deployments #NAME READY UP-TO-DATE AVAILABLE AGE #hellok8s-deployment 1/1 1 1 39s kubectl get pods #NAME READY STATUS RESTARTS AGE #hellok8s-deployment-77bffb88c5-qlxss 1/1 Running 0 119s kubectl delete pod hellok8s-deployment-77bffb88c5-qlxss #pod "hellok8s-deployment-77bffb88c5-qlxss" deleted kubectl get pods #NAME READY STATUS RESTARTS AGE #hellok8s-deployment-77bffb88c5-xp8f7 1/1 Running 0 18s这里出现了一个有趣的现象:手动删除 Pod 后,Deployment 立即自动创建了一个全新的 Pod。这与手动创建 Pod 有本质区别——Pod 名称前缀中的77bffb88c5保持不变,说明新 Pod 仍由同一个 Deployment 的 ReplicaSet 管控。当生产环境管理着成千上万个 Pod 时,我们不需要关心具体每一个 Pod 的状态,只需要维护好这份deployment.yaml的资源定义,Kubernetes 会持续把实际状态收敛到期望状态(desired state),这即是 Kubernetes 声明式管理的核心思想。
水平扩缩容:修改 replicas 并观察
接着通过自动扩容加深理解:想要把hellok8s:v1扩容到 3 个副本时,只需把replicas的值改为 3,再执行kubectl apply -f deployment.yaml即可:
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: replicas: 3 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:v1 name: hellok8s-container建议在kubectl apply之前,另开一个终端窗口执行kubectl get pods --watch来实时观察 Pod 的创建与删除记录;想要减少副本数时也很简单,你可以尝试把副本数随意增大或缩小,再通过--watch观察状态变化。Kubernetes 会根据replicas的期望值,自动创建或终止多余的 Pod,整个过程无需人工干预。
升级版本:从 v1 升级到 v2
接下来尝试把全部v1版本的 Pod 升级到v2版本。首先构建一份hellok8s:v2的镜像,与 v1 的唯一区别是响应字符串替换成了[v2] Hello, Kubernetes!。仓库中的 deployment/v2/main.go 就是 v2 版本的完整源码:
package main import ( "io" "net/http" ) func hello(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "[v2] Hello, Kubernetes!") } func main() { http.HandleFunc("/", hello) http.ListenAndServe(":3000", nil) }将hellok8s:v2构建并推送到镜像仓库(本文以 DockerHub 为例):
docker build . -t guangzhengli/hellok8s:v2 docker push guangzhengli/hellok8s:v2接着编写 v2 版本的 Deployment 资源文件,唯一的变化是image字段:
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: replicas: 3 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:v2 name: hellok8s-container执行升级并验证:
kubectl apply -f deployment.yaml # deployment.apps/hellok8s-deployment configured kubectl get pods # NAME READY STATUS RESTARTS AGE # hellok8s-deployment-66799848c4-kpc6q 1/1 Running 0 3s # hellok8s-deployment-66799848c4-pllj6 1/1 Running 0 3s # hellok8s-deployment-66799848c4-r7qtg 1/1 Running 0 3s kubectl port-forward hellok8s-deployment-66799848c4-kpc6q 3000:3000 # Forwarding from 127.0.0.1:3000 -> 3000 # Forwarding from [::1]:3000 -> 3000 # open another terminal curl http://localhost:3000 # [v2] Hello, Kubernetes!注意到 Pod 名称前缀已从77bffb88c5变为66799848c4,说明这次升级创建了一个全新的 ReplicaSet。你也可以执行kubectl describe pod hellok8s-deployment-66799848c4-kpc6q确认 Pod 当前使用的是否为 v2 版本的镜像。
Rolling Update 滚动更新:平滑升级的秘密
如果生产环境管理着多个hellok8s:v1副本,像上面那样直接修改image升级会带来一个隐患:所有副本在同一时间更新,Pod 在拉取新镜像、重建期间无法提供服务,导致hellok8s服务在短时间内整体不可用。
滚动更新(Rolling Update)正是为此设计:在保证新版本v2的 Pod 尚未就绪(ready)之前,先不删除v1版本的 Pod,从而实现"边增边减、平滑切换"。
strategy 的两种类型
在 Deployment 的资源定义中,spec.strategy.type有两种选择:
- RollingUpdate(默认):逐渐增加新版本的 Pod,同时逐渐减少旧版本的 Pod,保证服务不中断;
- Recreate:先删除所有旧版本 Pod,再创建新版本 Pod,适用于允许短暂停机的场景。
大多数情况下采用滚动更新(RollingUpdate)。滚动更新又可以通过maxSurge和maxUnavailable两个字段控制升级速率:
- maxSurge:最大峰值,指定可以创建的超出期望 Pod 个数的 Pod 数量(可以是绝对数值或百分比);
- maxUnavailable:最大不可用,指定更新过程中不可用的 Pod 个数上限(可以是绝对数值或百分比)。
回滚操作
先输入命令回滚 Deployment,再执行kubectl describe pod会发现 Deployment 已把v2的 Pod 回滚到v1版本:
kubectl rollout undo deployment hellok8s-deployment kubectl get pods # NAME READY STATUS RESTARTS AGE # hellok8s-deployment-77bffb88c5-cvm5c 1/1 Running 0 39s # hellok8s-deployment-77bffb88c5-lktbl 1/1 Running 0 41s # hellok8s-deployment-77bffb88c5-nh82z 1/1 Running 0 37s kubectl describe pod hellok8s-deployment-77bffb88c5-cvm5c # Image: guangzhengli/hellok8s:v1除了直接undo,还可以用history查看历史版本,并通过--to-revision回滚到指定版本:
kubectl rollout history deployment hellok8s-deployment kubectl rollout undo deployment/hellok8s-deployment --to-revision=2配置滚动更新参数
接着把strategy=rollingUpdate、maxSurge=1、maxUnavailable=1与replicas=3写入 deployment.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 replicas: 3 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:v2 name: hellok8s-container这个参数配置意味着:升级过程中最大可能同时存在 4 个 hellok8s Pod(replicas + maxSurge),最小保证 2 个 hellok8s Pod 存活(replicas - maxUnavailable)。换句话说,最多允许 1 个新 Pod 提前创建,同时最多允许 1 个旧 Pod 暂时不可用,从而在"升级速度"与"服务可用性"之间取得平衡。
存活探针(livenessProbe):自动检测并重启故障容器
存活探测器用来确定什么时候要重启容器。例如,存活探测器可以探测到应用死锁(应用程序在运行,但是无法继续执行后面的步骤)情况。重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。——Kubernetes LivenessProbe 文档
生产中,有时会因 bug 导致应用死锁或线程耗尽,应用进程还在运行,却已无法继续提供服务。若没有自动监控与处理手段,故障可能长时间无人发现。此时 kubelet 使用存活探测器(livenessProbe)来确定什么时候要重启容器。
编写带 /healthz 接口的应用
我们编写一个/healthz接口来演示 livenessProbe 的用法:该接口在应用启动成功的 15 秒内正常返回 200 状态码,15 秒后一直返回 500。仓库中的 deployment/liveness/main.go 是完整实现:
package main import ( "fmt" "io" "net/http" "time" ) func hello(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "[v2] Hello, Kubernetes!") } func main() { started := time.Now() http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { duration := time.Since(started) if duration.Seconds() > 15 { w.WriteHeader(500) w.Write([]byte(fmt.Sprintf("error: %v", duration.Seconds()))) } else { w.WriteHeader(200) w.Write([]byte("ok")) } }) http.HandleFunc("/", hello) http.ListenAndServe(":3000", nil) }Dockerfile 构建
Dockerfile 的编写与之前保持一致,仓库中的 deployment/liveness/Dockerfile 使用多阶段构建:第一阶段用golang:1.16-buster编译,第二阶段基于精简的distroless/base-debian10运行,最终镜像只包含编译产物:
# Dockerfile FROM golang:1.16-buster AS builder RUN mkdir /src ADD . /src WORKDIR /src RUN go env -w GO111MODULE=auto RUN go build -o main . FROM gcr.io/distroless/base-debian10 WORKDIR / COPY --from=builder /src/main /main EXPOSE 3000 ENTRYPOINT ["/main"]把 tag 修改为liveness并推送到远程仓库:
docker build . -t guangzhengli/hellok8s:liveness docker push guangzhengli/hellok8s:liveness配置 livenessProbe 并观察重启
最后编写 Deployment 定义。这里使用 HTTP GET 方式的存活探测,请求刚才定义的/healthz接口:periodSeconds指定 kubelet 每隔 3 秒执行一次存活探测;initialDelaySeconds告诉 kubelet 在执行第一次探测前等待 3 秒。如果/healthz返回成功代码,kubelet 认为容器健康存活;如果返回失败代码,kubelet 会杀死这个容器并将其重启。
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 replicas: 3 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:liveness name: hellok8s-container livenessProbe: httpGet: path: /healthz port: 3000 initialDelaySeconds: 3 periodSeconds: 3仓库中的 deployment/liveness/deployment.yaml 与该配置一致。执行kubectl apply后,通过get或describe命令可以观察到 Pod 一直处于重启当中:
kubectl apply -f deployment.yaml kubectl get pods # NAME READY STATUS RESTARTS AGE # hellok8s-deployment-5995ff9447-d5fbz 1/1 Running 4 (6s ago) 102s # hellok8s-deployment-5995ff9447-gz2cx 1/1 Running 4 (5s ago) 101s # hellok8s-deployment-5995ff9447-rh29x 1/1 Running 4 (6s ago) 102s kubectl describe pod hellok8s-68f47f657c-zwn6g # ... # ... # ... # Events: # Type Reason Age From Message # ---- ------ ---- ---- ------- # Normal Scheduled 12m default-scheduler Successfully assigned default/hellok8s-deployment-5995ff9447-rh29x to minikube # Normal Pulled 11m (x4 over 12m) kubelet Container image "guangzhengli/hellok8s:liveness" already present on machine # Normal Created 11m (x4 over 12m) kubelet Created container hellok8s-container # Normal Started 11m (x4 over 12m) kubelet Started container hellok8s-container # Normal Killing 11m (x3 over 12m) kubelet Container hellok8s-container failed liveness probe, will be restarted # Warning Unhealthy 11m (x10 over 12m) kubelet Liveness probe failed: HTTP probe failed with statuscode: 500 # Warning BackOff 2m41s (x36 over 10m) kubelet Back-off restarting failed container从事件中可以看到完整链路:启动 15 秒后/healthz返回 500 → kubelet 发出Liveness probe failed告警 → 容器被杀死(will be restarted)→ 重启后再次探测失败 → 进入Back-off restarting failed container退避重启循环。这就是存活探针自动保障应用可用性的过程。
就绪探针(readinessProbe):控制流量接入
就绪探测器可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。——Kubernetes ReadinessProbe 文档
生产环境升级服务版本是日常需求,此时需要考虑一种场景:当发布的版本存在问题,就不应该让它升级成功。kubelet 使用就绪探测器(readinessProbe)判断容器何时准备好接收请求流量。当一个 Pod 升级后不能就绪,就不应让流量进入该 Pod;配合滚动更新(rollingUpdate),也不能允许升级继续推进,否则可能出现全部 Pod 升级完成、但所有服务均不可用的情况。
构造一个"坏版本"镜像
先把服务回滚到hellok8s:v2版本(可用上一节学到的回滚方法):
kubectl rollout undo deployment hellok8s-deployment --to-revision=2这里把应用的/healthz接口直接设置成返回 500 状态码,代表这是一个有问题的版本。仓库中的 deployment/readiness/main.go 是完整源码:
package main import ( "io" "net/http" ) func hello(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "[v2] Hello, Kubernetes!") } func main() { http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(500) }) http.HandleFunc("/", hello) http.ListenAndServe(":3000", nil) }在 build 阶段将 tag 设置为bad,打包后 push 到远程仓库:
docker build . -t guangzhengli/hellok8s:bad docker push guangzhengli/hellok8s:badProbe 的完整配置字段
Probe 有很多配置字段,可以用这些字段精确控制就绪检测的行为:
initialDelaySeconds:容器启动后要等待多少秒后才启动存活和就绪探测器,默认是 0 秒,最小值是 0;periodSeconds:执行探测的时间间隔(单位是秒),默认是 10 秒,最小值是 1;timeoutSeconds:探测的超时等待时间(秒),默认值是 1 秒,最小值是 1;successThreshold:探测器在失败后,被视为成功的最小连续成功次数,默认值是 1;存活和启动探测的这个值必须是 1,最小值是 1;failureThreshold:探测失败时 Kubernetes 的重试次数。对存活探测而言,放弃就意味着重新启动容器;对就绪探测而言,放弃意味着 Pod 会被打上未就绪的标签。默认值是 3,最小值是 1。
配置 readinessProbe 并观察未就绪状态
接着编写 Deployment 资源文件,配置就绪探针请求/healthz接口:
apiVersion: apps/v1 kind: Deployment metadata: name: hellok8s-deployment spec: strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 1 replicas: 3 selector: matchLabels: app: hellok8s template: metadata: labels: app: hellok8s spec: containers: - image: guangzhengli/hellok8s:bad name: hellok8s-container readinessProbe: httpGet: path: /healthz port: 3000 initialDelaySeconds: 1 successThreshold: 5仓库中的 deployment/readiness/deployment.yaml 与该配置一致。执行 apply 后观察:
kubectl apply -f deployment.yaml kubectl get pods # NAME READY STATUS RESTARTS AGE # hellok8s-deployment-66799848c4-8xzsz 1/1 Running 0 102s # hellok8s-deployment-66799848c4-m9dl5 1/1 Running 0 102s # hellok8s-deployment-9c57c7f56-rww7k 0/1 Running 0 26s # hellok8s-deployment-9c57c7f56-xt9tw 0/1 Running 0 26s kubectl describe pod hellok8s-deployment-9c57c7f56-rww7k # Events: # Type Reason Age From Message # ---- ------ ---- ---- ------- # Normal Scheduled 74s default-scheduler Successfully assigned default/hellok8s-deployment-9c57c7f56-rww7k to minikube # Normal Pulled 73s kubelet Container image "guangzhengli/hellok8s:bad" already present on machine # Normal Created 73s kubelet Created container hellok8s-container # Normal Started 73s kubelet Started container hellok8s-container # Warning Unhealthy 0s (x10 over 72s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 500通过get命令可以看到,两个新 Pod 一直处于0/1的未就绪状态,describe显示原因正是Readiness probe failed: HTTP probe failed with statuscode: 500。又因为设置了最大不可用数量为maxUnavailable=1,滚动更新被阻塞在中间状态,从而保证剩下两个v2版本的hellok8sPod 能继续对外提供服务。这正是"发布坏版本时不让流量进入、不让升级继续"的机制所在。
小结与延伸阅读
通过本指南,我们完成了从"手动管理 Pod"到"Deployment 声明式管理"的完整演进:
| 能力 | 核心机制 | 关键配置/命令 |
|---|---|---|
| 自愈 | 期望状态收敛 | kubectl delete pod后自动重建 |
| 水平扩缩容 | replicas 声明式变更 | kubectl apply -f deployment.yaml |
| 版本升级 | 镜像替换 + ReplicaSet 切换 | kubectl apply、kubectl port-forward |
| 滚动更新 | strategy + maxSurge/maxUnavailable | kubectl rollout undo/history |
| 故障自愈 | livenessProbe 杀死并重启容器 | httpGet + initialDelaySeconds/periodSeconds |
| 流量控制 | readinessProbe 剔除未就绪 Pod | httpGet + successThreshold/failureThreshold |
仓库中配套的源码与配置均可直接参考:v1/v2 版本定义见 deployment/v1/deployment.yaml 与 deployment/v2/deployment.yaml;存活探针示例见 deployment/liveness/main.go 与 deployment/liveness/deployment.yaml;就绪探针示例见 deployment/readiness/main.go 与 deployment/readiness/deployment.yaml。Deployment 管理好 Pod 之后,下一步是如何把服务稳定暴露给外部流量,可继续阅读仓库中的 Service 教程;在此之前,也可以先回顾 Pod 的基础定义 巩固 Pod 层面的知识。
- 文档
- 教程
- 云原生
【免费下载链接】k8s-tutorials
k8s tutorials | k8s 教程
相关推荐
Kubernetes 容器编排实战:从 Pod、Deployment 到 HPA 与健康探针的云原生入门指南
Kubernetes 容器编排实战:从 Pod、Deployment 到 HPA 与健康探针的云原生入门指南 本文基于 easy vibe 课程体系 docs/
教程文档人工智能Vibe CodingKubeVela k8s-update-strategy Trait 实战指南:为 Deployment / StatefulSet / DaemonSet 声明式配置 Kubernetes 更新策略
KubeVela k8s update strategy Trait 实战指南:为 Deployment / StatefulSet / DaemonSet 声
云原生DevOps运维微服务Kubernetes Python客户端终极指南:掌握Deployment滚动更新与回滚技巧
Kubernetes Python客户端终极指南:掌握Deployment滚动更新与回滚技巧 Kubernetes Python客户端是管理Kubernetes
后端云原生容器编排
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考