☰
Kubernetes Deployment 实战指南:用 k8s-tutorials 掌握声明式 Pod 管理、滚动更新与健康探针
2026/10/10 1:39:21 网站建设 项目流程
  • 文档
  • 教程
  • 云原生

【免费下载链接】k8s-tutorials

k8s tutorials | k8s 教程

项目地址:https://gitcode.com/gh_mirrors/k8s/k8s-tutorials
点击查看免费下载

在生产环境中,我们几乎不会直接管理 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:bad

Probe 的完整配置字段

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/maxUnavailablekubectl rollout undo/history
故障自愈livenessProbe 杀死并重启容器httpGet + initialDelaySeconds/periodSeconds
流量控制readinessProbe 剔除未就绪 PodhttpGet + 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 教程

项目地址:https://gitcode.com/gh_mirrors/k8s/k8s-tutorials
点击查看免费下载
上一篇:vim-airline代码质量实时工具:提升代码规范性的实用指南
下一篇:WebGL-Fluid-Simulation中的流体粘度温度依赖:模拟温度对粘度影响

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询