文章目录
- 一、实践背景
- 二、实验环境
- 三、创建 Namespace 和 Deployment
- 3.1 创建独立命名空间
- 3.2 创建 Nginx Deployment
- 3.3 检查部署结果
- 四、排查 ImagePullBackOff
- 4.1 查看 Pod 详细信息
- 4.2 验证镜像代理的 API
- 4.3 使用 crictl 验证替代镜像地址
- 五、更新 Deployment 并恢复运行
- 5.1 修改 Deployment 的镜像地址
- 5.2 等待滚动更新完成
- 5.3 检查 Deployment
- 5.4 检查 Pod
- 六、复盘:这次实践解决了什么问题?
- 七、下一步实践计划
- 八、面试时如何介绍这次实践?
- 九、Service 创建与集群内访问验证
- 第一步:创建 ClusterIP Service
- 第二步:检查 Service 和后端 Pod
- 第三步:从集群内部访问 Nginx
- 第四步:验证 Kubernetes 自愈能力
- 实验结果复盘
通过一次真实的 Kubernetes 部署实践,掌握 Deployment、Pod 的基本使用,理解
ImagePullBackOff的排查方法,并解决镜像代理返回502 Bad Gateway导致的镜像拉取失败问题。
一、实践背景
在个人项目「逐浪笔记」的 DevOps 实践中,我已经搭建了 Jenkins、Docker、MySQL 和博客前后端服务。为了进一步学习 Kubernetes 容器编排,我在现有 Ubuntu 云服务器上部署了 K3s,并尝试在集群中运行 Nginx。
本次实践的目标不是单纯启动一个 Nginx 容器,而是完整经历一次 Kubernetes 工作负载的部署、故障排查和结果验收。
主要完成以下任务:
- 创建独立的 Kubernetes Namespace,隔离实验资源。
- 创建 Nginx Deployment,运行两个 Pod 副本。
- 排查镜像拉取失败问题,定位镜像代理返回的 HTTP 502。
- 使用
crictl验证替代镜像地址,并更新 Deployment。 - 验证 Deployment 和 Pod 的运行状态。
- 为后续学习 Service、集群内访问和负载均衡打下基础。
二、实验环境
| 项目 | 环境信息 |
|---|---|
| 操作系统 | Ubuntu 24.04.4 LTS |
| Kubernetes 发行版 | K3s |
| K3s 版本 | v1.36.5+k3s1 |
| 节点数量 | 1 个 |
| 容器运行时 | containerd |
| 实验应用 | Nginx |
| Deployment 副本数 | 2 |
| 实验命名空间 | nginx-lab |
服务器上原本运行着个人博客、Jenkins 和 MySQL。本次实验使用独立的nginx-lab命名空间,不直接修改这些已有业务。
需要注意:两个 Nginx 副本运行在同一个节点上,可以用于练习副本管理,但不能据此认为系统已经实现跨节点高可用。
三、创建 Namespace 和 Deployment
3.1 创建独立命名空间
执行:
sudok3s kubectl create namespace nginx-labNamespace 用于对 Kubernetes 资源进行逻辑隔离。将本次练习放在nginx-lab中,可以避免与其他实验资源混淆。
检查命名空间:
sudok3s kubectl get namespaces3.2 创建 Nginx Deployment
最初使用的镜像地址为:
docker.m.daocloud.io/library/nginx:stable创建两个副本:
sudok3s kubectl create deployment nginx\--image=docker.m.daocloud.io/library/nginx:stable\--replicas=2\--namespace=nginx-lab这里使用的是 Deployment,而不是直接创建两个独立的 Pod。
Deployment 通过 ReplicaSet 管理 Pod,并持续协调实际运行的副本数量,使其尽可能符合声明的目标状态。
例如,设置--replicas=2,就是告诉 Kubernetes 希望始终维持两个应用副本。
3.3 检查部署结果
sudok3s kubectl get deployments-nnginx-labsudok3s kubectl get pods-nnginx-lab-owide实际第一次检查时,Deployment 没有成功就绪:
NAME READY UP-TO-DATE AVAILABLE AGE nginx 0/2 2 0 ...Pod 状态如下:
NAME READY STATUS nginx-545f5c79dd-r4t8z 0/1 ImagePullBackOff nginx-545f5c79dd-r7j28 0/1 ContainerCreating这说明 Deployment 已经创建,但两个副本尚未就绪。其中一个 Pod 明确出现了ImagePullBackOff。
四、排查 ImagePullBackOff
4.1 查看 Pod 详细信息
遇到 Pod 无法启动时,不能只看get pods的状态就直接重建资源。首先需要查看详细信息和事件。
执行:
sudok3s kubectl describe pod\nginx-545f5c79dd-r4t8z\-nnginx-lab重点查看输出中的Events部分。
实际错误信息表明:
Failed to pull image "docker.m.daocloud.io/library/nginx:stable" unexpected status from GET request https://docker.m.daocloud.io/v2/library/nginx/manifests/sha256:... 502 Bad Gateway后续事件中也出现了:
unexpected status from HEAD request https://docker.m.daocloud.io/v2/library/nginx/manifests/stable: 502 Bad Gateway由此可以确定,当前问题与镜像代理返回的 HTTP 502 有直接关系。
ImagePullBackOff表示 Kubernetes 在镜像拉取失败后进入退避重试状态。它是故障表现,而不是具体的根因。
4.2 验证镜像代理的 API
为了进一步判断代理入口是否能够响应请求,执行:
curl-sS-o/dev/null-w'HTTP %{http_code}\n'\--connect-timeout5--max-time15\https://docker.m.daocloud.io/v2/实际返回:
HTTP 401接着测试另一个入口:
curl-sS-o/dev/null-w'HTTP %{http_code}\n'\--connect-timeout5--max-time15\https://m.daocloud.io/v2/实际返回:
HTTP 502这两个结果说明:
- 第一个 Registry API 能够响应请求。HTTP 401 在容器镜像仓库中通常是正常的认证挑战,不代表服务不可用。
- 第二个入口当时返回 HTTP 502,不适合作为本次测试的可用入口。
- 即使
/v2/返回 200 或 401,也不能保证具体镜像的 Manifest 和镜像层下载正常,因此还需要实际拉取镜像进行验证。
4.3 使用 crictl 验证替代镜像地址
这里有一个重要的 Kubernetes 知识点。
服务器虽然安装了 Docker,但 K3s 使用的是 containerd 作为容器运行时。Docker 的镜像加速配置不会自动应用到 K3s 的 containerd。
因此,本次使用 K3s 自带的crictl测试替代镜像地址:
sudok3s crictl pull\m.daocloud.io/docker.io/library/nginx:stable实际结果:
Image is up to date for sha256:3a8edbda21b6357f9d8e8d441c00c44673e89af4c157e76f0f117f2429570e60这表明 containerd 能够通过新的镜像地址获取 Nginx 镜像。
需要区分两个概念:
crictl pull:验证并拉取镜像到节点的容器运行时。- 更新 Deployment:修改 Kubernetes 工作负载声明的镜像地址,让 Kubernetes 按新配置执行工作负载更新。
只执行crictl pull,不会自动修改 Deployment 中原有的镜像地址。
五、更新 Deployment 并恢复运行
5.1 修改 Deployment 的镜像地址
确认替代镜像可以拉取后,更新 Deployment:
sudok3s kubectlsetimage deployment/nginx\nginx=m.daocloud.io/docker.io/library/nginx:stable\-nnginx-lab这条命令修改 Deployment 中名为nginx的容器所使用的镜像。
镜像配置发生变化后,Deployment 会通过滚动更新机制创建使用新配置的 Pod,并逐步替换旧副本。
5.2 等待滚动更新完成
sudok3s kubectl rollout status deployment/nginx\-nnginx-lab--timeout=120s这条命令用于等待 Deployment 更新完成。
如果等待超时,应继续检查 Pod 状态、事件和容器日志,而不是直接认定部署成功。
5.3 检查 Deployment
执行:
sudok3s kubectl get deployment nginx-nnginx-lab实际验收结果:
NAME READY UP-TO-DATE AVAILABLE AGE nginx 2/2 2 2 ...各列的含义:
READY:当前就绪的副本数。UP-TO-DATE:使用最新 Deployment 配置创建的副本数。AVAILABLE:当前可用的副本数。
这里三个指标均表明两个副本已完成更新并进入可用状态。
5.4 检查 Pod
执行:
sudok3s kubectl get pods-nnginx-lab-owide实际验收结果:
NAME READY STATUS RESTARTS AGE IP nginx-68bfbd8bb7-l4kdr 1/1 Running 0 ... 10.42.0.173 nginx-68bfbd8bb7-vv5qd 1/1 Running 0 ... 10.42.0.172两个 Pod 都处于Running状态,容器就绪状态为1/1,重启次数为 0。
至此,本次 Nginx Deployment 部署与故障恢复通过验收。
六、复盘:这次实践解决了什么问题?
本次故障的处理过程可以概括为:
创建 Nginx Deployment | v 发现 Pod 为 ImagePullBackOff | v 使用 describe 查看 Pod 事件 | v 定位到镜像代理返回 HTTP 502 | v 使用 curl 验证代理入口 | v 使用 crictl pull 测试替代镜像 | v 更新 Deployment 镜像地址 | v 检查 Deployment 和 Pod 状态 | v 两个副本均为 Running,READY 2/2这次排查实践带来的几个收获:
1. 从现象定位原因,而不是盲目重启。
ImagePullBackOff只是状态提示。通过describe pod查看事件,才能进一步确认是镜像拉取失败,并从错误信息中找到 HTTP 502。
2. 区分镜像代理问题与 Kubernetes 工作负载问题。
Deployment 已经创建,Pod 也成功调度到节点,但镜像无法拉取,因此容器无法正常启动。此时应优先排查镜像获取链路,而不是修改应用配置。
3. 理解 Docker 与 containerd 的区别。
同一台服务器上可以同时运行 Docker 和 K3s,但它们的镜像存储与配置并不天然共享。排查时必须确认实际使用的容器运行时。
4. 通过验收指标判断是否真正恢复。
不能仅凭set image命令没有报错就认定部署成功。需要结合rollout status、Deployment 的READY指标和 Pod 状态完成验收。
七、下一步实践计划
完成 Deployment 部署后,还需要进一步验证应用是否可以被访问。
下一步计划如下:
- 创建
ClusterIP类型的 Service,为 Nginx 提供稳定的集群内访问地址。 - 检查 Service 的 Endpoints 或 EndpointSlices,确认两个 Nginx Pod 被正确关联。
- 从集群内部发起 HTTP 请求,验证 Nginx 是否返回正常响应。
- 删除一个 Pod,观察 Deployment 是否自动创建新的副本。
- 学习 ConfigMap、探针、资源限制以及 Ingress 等常见 Kubernetes 功能。
这些内容将逐步形成从容器运行、工作负载管理到服务发现与应用访问的完整实践链路。
八、面试时如何介绍这次实践?
面试官问到 Kubernetes 部署或故障排查经验时,可以这样介绍:
我在自己的 DevOps 实践环境中使用 K3s 部署过 Nginx,并设置了两个 Deployment 副本。部署过程中遇到了
ImagePullBackOff,我先通过kubectl describe pod查看事件,发现镜像代理返回 HTTP 502,导致镜像 Manifest 获取失败。随后我通过 curl 验证了镜像代理入口,并使用 K3s 自带的 crictl 测试替代镜像地址。确认镜像可以成功拉取后,我更新了 Deployment 中的镜像配置,并通过 rollout status、Deployment 的 READY 指标和 Pod 状态完成验收,最终两个副本都正常运行。
这次实践让我更清楚地理解了 Deployment、Pod、containerd 和镜像仓库之间的关系,也熟悉了从故障现象、事件日志到根因定位和恢复验证的基本排查流程。
这段经历属于单节点 K3s 实践,重点是展示实际操作和故障分析能力,不应将其描述成生产级 Kubernetes 集群建设或跨节点高可用项目。
实践总结:Kubernetes 部署不仅是创建资源,更重要的是理解控制器、容器运行时、镜像仓库之间的协作关系,并建立可复现、可验证的故障排查流程。对于 DevOps 工程师来说,能够解释“为什么失败、如何定位、怎样恢复、如何验收”,往往比单纯记住命令更有价值。
九、Service 创建与集群内访问验证
第一步:创建 ClusterIP Service
现在的 Nginx Deployment 已经是 2/2 Ready,所以直接执行:
sudok3s kubectl expose deployment nginx\--name=nginx-service\--port=80\--target-port=80\--type=ClusterIP\-nnginx-lab这里几个参数要理解清楚:
| 参数 | 含义 |
|---|---|
| –name=nginx-service | 创建的 Service 名称 |
| –port=80 | Service 对外提供的端口 |
| –target-port=80 | 转发到 Pod 内 Nginx 的 80 端口 |
| –type=ClusterIP | 仅提供集群内部访问入口 |
ClusterIP 不会占用宿主机的 80 端口,因此不会与现有博客前端的 Docker 端口映射冲突。
第二步:检查 Service 和后端 Pod
# 查看 Service 的集群虚拟 IPsudok3s kubectl getservicenginx-service-nnginx-lab# 查看 Service 关联的后端地址sudok3s kubectl get endpoints nginx-service-nnginx-lab# 查看 Service 的标签选择器sudok3s kubectl describeservicenginx-service-nnginx-lab
这一步已经通过验收。现在你不只是有两个运行中的 Nginx Pod,还为它们建立了一个稳定的集群内访问入口。
验收结果
第三步:从集群内部访问 Nginx
我们使用一个临时的 curl Pod 发起请求,不需要安装软件,也不会占用宿主机端口。
先执行
sudok3s kubectl run nginx-curl\--image=m.daocloud.io/docker.io/library/nginx:stable\--restart=Never\--rm-i\--namespace=nginx-lab\--command--sh-c\'curl -sS -o /dev/null -w "HTTP %{http_code}\n" http://nginx-service'
这次不仅是 Pod 运行成功,而是 Nginx 已经通过 Kubernetes Service 完成了真实的 HTTP 访问,返回 HTTP 200。🎉
其中 http://nginx-service 使用的是 Kubernetes Service 名称,而不是 Pod IP。这说明集群内客户端可以通过 Service 名称访问 Nginx。
你看到的 All commands and output … recorded in container logs 是 kubectl 对临时容器日志记录的提示,不是访问失败。以后在测试命令中不要直接传入密码、Token 等敏感信息即可。
这一步在面试中怎么解释?
你可以这样说:
我在 K3s 中部署了两个 Nginx 副本,并通过 ClusterIP 类型的 Service 暴露集群内访问入口。Service 使用 app=nginx 标签选择器发现后端 Pod,Endpoints 中可以看到两个 Pod 的 IP 和 80 端口。接下来我通过集群内部的临时客户端访问 Service,验证 DNS 解析和服务转发是否正常。
第四步:验证 Kubernetes 自愈能力
这是很适合面试展示的实践。
Kubernetes 不仅能启动应用,还能在受管理的 Pod 被删除后,自动创建替代副本,以维持 Deployment 声明的目标数量。
第一步:查看当前两个 Pod
sudok3s kubectl get pods-nnginx-lab-owide记下当前两个 Nginx Pod 的名称。
第二步:手动删除其中一个 Pod
从上一步的输出中选择一个名称,替换下面的 POD_NAME:
sudok3s kubectl delete pod POD_NAME-nnginx-lab这里只删除一个 Pod,不要删除 Deployment 或 Service,也不会影响现有的博客、Jenkins 和 MySQL。
第三步:观察副本是否自动恢复
sudok3s kubectl get pods-nnginx-lab-owide-w-w 表示持续观察资源变化。你可能会看到旧 Pod 被删除、新 Pod 被创建,随后新 Pod 进入 Running。
再检查:
sudok3s kubectl get deployment nginx-nnginx-labsudok3s kubectl get pods-nnginx-labsudok3s kubectl get endpoints nginx-service-nnginx-lab
达到了预期的结果:
Deployment 最终仍为 2/2,新 Pod 的名称和 IP 与被删除的 Pod 不同,Service 后端地址也会随之更新。
这一步能够直观验证:
- Deployment 通过 ReplicaSet 维持期望副本数。
- Pod 是可替换的工作负载实例,不应依赖固定 Pod IP。
- Service 会根据匹配的后端 Pod 更新可转发的地址。
这次实验也通过了。已经验证了 Kubernetes 一个非常核心的能力:当受 Deployment 管理的 Pod 被删除后,Kubernetes 会自动创建新的 Pod,维持期望副本数。
实验结果复盘
从结果可以确认:
- Deployment 仍然维持 2/2 副本。
- 当前两个 Pod 都处于 Running,且容器就绪。
- Service 的后端地址已经更新为新的 Pod IP。
结合前面的 HTTP 200 测试,我们已经完成了从部署到访问、再到副本自愈的基础闭环。
应该真正理解的原理
这里有一个面试中很容易被追问的细节:
删除 Pod 不等于删除应用。 你删除的是一个 Pod 实例;Deployment 管理的 ReplicaSet 发现实际副本数少于期望值后,会创建新的 Pod。
另外,Kubernetes 的这种自愈能力有边界:它可以根据控制器声明重建 Pod,但不代表应用内部逻辑一定正常,也不代表单节点故障后整个集群仍然可用。