☰
K3s 实战:部署 Nginx 双副本、排查镜像拉取失败与验证
2026/10/15 11:33:30 网站建设 项目流程

文章目录

    • 一、实践背景
    • 二、实验环境
    • 三、创建 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-lab

Namespace 用于对 Kubernetes 资源进行逻辑隔离。将本次练习放在nginx-lab中,可以避免与其他实验资源混淆。

检查命名空间:

sudok3s kubectl get namespaces

3.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 部署后,还需要进一步验证应用是否可以被访问。

下一步计划如下:

  1. 创建ClusterIP类型的 Service,为 Nginx 提供稳定的集群内访问地址。
  2. 检查 Service 的 Endpoints 或 EndpointSlices,确认两个 Nginx Pod 被正确关联。
  3. 从集群内部发起 HTTP 请求,验证 Nginx 是否返回正常响应。
  4. 删除一个 Pod,观察 Deployment 是否自动创建新的副本。
  5. 学习 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=80Service 对外提供的端口
–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,但不代表应用内部逻辑一定正常,也不代表单节点故障后整个集群仍然可用。

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

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

立即咨询