k8s:pod的生命周期
一准备镜像image
1.1 maven编译程序
root@rocky9-2 tmp]# cd /data
[root@rocky9-2 data]# cd java-app/
[root@rocky9-2 java-app]# mvn clean package -DskipTests
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.example:k8s-demo >------------------------
[INFO] Building k8s-demo 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The artifact mysql:mysql-connector-java:jar:8.0.33 has been relocated to com.mysql:mysql-connector-j:jar:8.0.33
[INFO]
[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) @ k8s-demo ---
[INFO] Deleting /data/java-app/target
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ k8s-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] Copying 1 resource
[INFO] Copying 1 resource
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ k8s-demo ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 3 source files to /data/java-app/target/classes
[INFO]
[INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ k8s-demo ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Using 'UTF-8' encoding to copy filtered properties files.
[INFO] skip non existing resourceDirectory /data/java-app/src/test/resources
[INFO]
[INFO] --- maven-compiler-plugin:3.10.1:testCompile (default-testCompile) @ k8s-demo ---
[INFO] No sources to compile
[INFO]
[INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ k8s-demo ---
[INFO] Tests are skipped.
[INFO]
[INFO] --- maven-jar-plugin:3.2.2:jar (default-jar) @ k8s-demo ---
[INFO] Building jar: /data/java-app/target/k8s-demo-1.0.0.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:2.7.18:repackage (repackage) @ k8s-demo ---
[INFO] Replacing main artifact with repackaged archive
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 5.445 s
[INFO] Finished at: 2026-02-13T12:45:20+08:00
[INFO] ------------------------------------------------------------------------
1.2 docker构建
命令:
docker build -t k8s-demo-app:v2.0 .
root@rocky9-2 java-app]# docker build -t k8s-demo-app:v2.0 .
[+] Building 60.5s (8/8) FINISHED docker:default
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 209B 0.0s
=> [internal] load metadata for docker.io/library/openjdk:11-jre-slim 59.9s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [1/3] FROM docker.io/library/openjdk:11-jre-slim@sha256:93af7df2308c5141a751c4830e6b6c5717db102b3b31f012ea29d842dc4f2b02 0.0s
=> [internal] load build context 0.3s
=> => transferring context: 21.23MB 0.3s
=> CACHED [2/3] WORKDIR /app 0.0s
=> [3/3] COPY target/*.jar app.jar 0.1s
=> exporting to image 0.1s
=> => exporting layers 0.1s
=> => writing image sha256:3584deb1306899a00c7e56c5172c17fd8616eb6617873d5b5fdfba9ae6f2bd8e 0.0s
=> => naming to docker.io/library/k8s-demo-app:v2.0 0.0s
这条命令是 Docker 最核心的操作之一:构建镜像。你看到的日志显示构建已经成功完成了。
简单来说,你刚刚把你的 Java 程序(target/*.jar)打包成了一个能在任何安装了容器环境的地方运行的“背囊”(镜像)。
1. 命令拆解:docker build -t k8s-demo-app:v2.0 .
-
docker build: 告诉 Docker 引擎,“照着 Dockerfile 里的说明书给我做一个镜像”。 -
-t k8s-demo-app:v2.0:-t代表 tag(打标签)。 -
k8s-demo-app是镜像的名字。 -
v2.0是版本号。如果不写,默认会是latest。 -
.: 这个代表 上下文路径 (Context)。它告诉 Docker:“就在当前文件夹找 Dockerfile 和代码文件”。
2. 日志里的“细节”
[internal] load build context(21.23MB): ava jar 包和相关文件大小约为 21MB,已经成功传送给 Docker 守护进程。CACHED [2/3] WORKDIR /app: 这一步显示了 缓存(CACHED)。说明之前构建过 v1.0 或者是类似的镜像,Docker 发现/app这个目录没变,直接用了旧的层,节省了时间。[3/3] COPY target/*.jar app.jar: 这是关键一步,把本地编译好的 Java 包塞进了镜像里。writing image sha256:3584...: 镜像生成了唯一的“身份证号”。
二 手动镜像分发
2.1 验证镜像
docker images | grep k8s-demo-app
检查存在后再导出
docker save plan-system:v1.0 -o /tmp/plan-system-v1.0.tar
检查大小
[root@rocky9-2 target]# ls -lh /tmp/plan-system-v1.0.tar
-rw-------. 1 root root 238M 2月 16 12:44 /tmp/plan-system-v1.0.tar
[root@rocky9-2 target]#
选择导进containd
ctr -n k8s.io images import /tmp/plan-system-v1.0.tar
[root@rocky9-2 target]# ctr -n k8s.io images import /tmp/plan-system-v1.0.tar
unpacking docker.io/library/plan-system:v1.0 (sha256:acec55989f9684280865ce699768c9cfc5fd207d75e8682c4f6625949ba79afe)...done
2.2 同步镜像
K8s 调度 Pod 时,如果调度到了 rocky9-1 或 rocky9-3,它们本地没有这个镜像就会报错 ImagePullBackOff。你有两个选择:
root@rocky9-2 java-app]# ctr -n k8s.io images import /tmp/k8s-demo-app-v2.tar
unpacking docker.io/library/k8s-demo-app:v2.0 (sha256:065cdb9ce9121e4f2e52f625b05c0bec174a66184555d921e8118733a99db74a)...done
[root@rocky9-2 java-app]# scp /tmp/k8s-demo-app-v2.tar root@192.168.80.173:/tmp/
k8s-demo-app-v2.tar 100% 237MB 55.9MB/s 00:04
[root@rocky9-2 java-app]# ssh root@192.168.80.173 "ctr -n k8s.io images import /tmp/k8s-demo-app-v2.tar"
unpacking docker.io/library/k8s-demo-app:v2.0 (sha256:065cdb9ce9121e4f2e52f625b05c0bec174a66184555d921e8118733a99db74a)...done
[root@rocky9-2 java-app]# crictl images | grep k8s-demo
docker.io/library/k8s-demo-app v1.0 c07fedfedf5ed 249MB
docker.io/library/k8s-demo-app v2.0 d0bfc15264084 249MB
1. 为什么用 ctr -n k8s.io
因为 Kubernetes 运行时使用的是 containerd。
ctr是 containerd 的原生命令行工具。-n k8s.io是关键:Kubernetes 只会去名为k8s.io的命名空间(Namespace)下寻找镜像。如果你直接导入到默认空间,crictl和 K8s 是看不见这个镜像的。
2. ctr 与 crictl 的配合
- 用
ctr导入镜像(写操作)。 - 你用
crictl检查镜像(读操作)。crictl是专门为 Kubernetes 设计的容器运行时接口(CRI)交互工具。v2.0出现在crictl images的列表中,意味着 Kubernetes 已经准备好使用这个镜像了。
3. 跨节点分发成功
通过 scp 将 237MB 的镜像包拷贝到 192.168.80.173 (rocky9-3),执行了导入。这样无论 Pod 漂移到哪台机器,都不会出现 ImagePullBackOff 错误。
三 更新
3.1 滚动更新
[root@rocky9-2 java-app]# kubectl set image deployment/k8s-demo-app k8s-demo-app=docker.io/library/k8s-demo-app:v2.0 -n java-web --record
Flag --record has been deprecated, --record will be removed in the future
[root@rocky9-2 java-app]# kubectl rollout status deployment/k8s-demo-app -n java-web
deployment "k8s-demo-app" successfully rolled out
3.2 滚动更新失败的排查
发现running的age还是这个48min,但是我这个pod应该是上一秒推上去更新的,所以说时间是不对的
[root@rocky9-2 java-app]# kubectl get pods -n java-web -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
k8s-demo-app-7666646bd4-fmh8z 1/1 Running 0 48m 10.244.1.9 rocky9-2 <none> <none>
k8s-demo-app-7666646bd4-v8m8w 1/1 Running 0 49m 10.244.2.8 rocky9-3 <none> <none>
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h 10.244.1.7 rocky9-2 <none> <none>
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h 10.244.2.5 rocky9-3 <none> <none>
直接删除pod
[root@rocky9-2 java-app]# kubectl -n java-web delete pod -l app=k8s-demo-app
pod "k8s-demo-app-7666646bd4-fmh8z" deleted
pod "k8s-demo-app-7666646bd4-v8m8w" deleted
[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-7666646bd4-b62lw 0/1 Running 0 8s
k8s-demo-app-7666646bd4-tk4jk 0/1 Running 0 8s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
开始滚动更新了,但是也会重启,说明yaml文件写的有问题
[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-7666646bd4-b62lw 0/1 Running 1 (81s ago) 2m52s
k8s-demo-app-7666646bd4-tk4jk 0/1 Running 1 (81s ago) 2m52s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
删除也解决不了问题,直接分析日志
^C[root@rocky9-2 java-app]# kubectl -n java-web describe pod k8s-demo-app-7666646bd4-b62lw
Name: k8s-demo-app-7666646bd4-b62lw
Namespace: java-web
Priority: 0
Service Account: default
Node: rocky9-2/192.168.80.172
Start Time: Fri, 13 Feb 2026 12:53:29 +0800
Labels: app=k8s-demo-app
pod-template-hash=7666646bd4
version=v2.0
Annotations: <none>
Status: Running
IP: 10.244.1.10
IPs:
IP: 10.244.1.10
Controlled By: ReplicaSet/k8s-demo-app-7666646bd4
Containers:
k8s-demo-app:
Container ID: containerd://144d7f1573c7e1ea72e2eaf271f16a89315f735cf5459f75efb61750d8354252
Image: docker.io/library/k8s-demo-app:v2.0
Image ID: sha256:d0bfc152640842377f449c421b65d6c07fbec712c53f8510651287da5e6af17a
Port: 8080/TCP
Host Port: 0/TCP
State: Running
Started: Fri, 13 Feb 2026 12:55:01 +0800
Last State: Terminated
Reason: Error
Exit Code: 143
Started: Fri, 13 Feb 2026 12:53:31 +0800
Finished: Fri, 13 Feb 2026 12:55:00 +0800
Ready: False
Restart Count: 1
Limits:
cpu: 500m
memory: 512Mi
Requests:
cpu: 250m
memory: 256Mi
Liveness: http-get http://:8080/health delay=60s timeout=1s period=10s #success=1 #failure=3
Readiness: http-get http://:8080/health delay=30s timeout=1s period=5s #success=1 #failure=3
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-p6bpr (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
kube-api-access-p6bpr:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: Burstable
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m default-scheduler Successfully assigned java-web/k8s-demo-app-7666646bd4-b62lw to rocky9-2
Warning Unhealthy 2m13s kubelet Readiness probe failed: Get "http://10.244.1.10:8080/health": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
Normal Pulled 89s (x2 over 2m58s) kubelet Container image "docker.io/library/k8s-demo-app:v2.0" already present on machine
Normal Created 89s (x2 over 2m58s) kubelet Created container k8s-demo-app
Warning Unhealthy 89s (x10 over 2m9s) kubelet Readiness probe failed: HTTP probe failed with statuscode: 404
Warning Unhealthy 89s (x3 over 109s) kubelet Liveness probe failed: HTTP probe failed with statuscode: 404
Normal Killing 89s kubelet Container k8s-demo-app failed liveness probe, will be restarted
Normal Started 88s (x2 over 2m58s) kubelet Started container k8s-demo-app
Warning Unhealthy 49s (x4 over 2m24s) kubelet Readiness probe failed: Get "http://10.244.1.10:8080/health": dial tcp 10.244.1.10:8080: connect: connection refused
从这份 describe 日志中,我们可以像医生看 X 光片一样,一眼看出你的 v2.0 应用出问题了。
最核心的结论是:新镜像 v2.0 虽然启动了,但它的健康检查(Liveness/Readiness Probe)失败了,导致容器不断重启且无法对外提供服务。
以下是详细的“病例分析”:
1. 致命错误:HTTP 404
- 日志证据:
Liveness probe failed: HTTP probe failed with statuscode: 404 - 分析:Kubelet 尝试访问 Pod 内部的
http://:8080/health来检查它是否活着,但你的程序返回了 404 Not Found。 - 原因:
- 接口变了:v2.0 是不是把
/health路径删除了,或者改成了/actuator/health? - 代码没写:v2.0 的代码里可能根本没有定义这个 API 接口。
2. 容器正在经历“无限重启”
- 日志证据:
Restart Count: 1并且Reason: Error,Exit Code: 143。 - 分析:因为 Liveness Probe(存活探针)连续 3 次失败,Kubernetes 认为这个容器已经“坏死”,所以执行了 Killing 操作并尝试重启它。
- Exit Code 143:这是典型的
SIGTERM信号,表示 Kubernetes 正常通知容器退出。
3. 就绪检查失败(流量进不来)
- 日志证据:
Ready: False且Readiness probe failed: ... connect: connection refused - 分析:因为探测失败,Pod 被标记为
NotReady。这意味着即便你外面有 Service,流量也不会转发给这个 Pod,用户访问会报错。
4. 资源限制(Limits)
- 日志证据:
cpu: 500m,memory: 512Mi - 分析:这是你设置的资源上限。如果你的 Java 应用启动瞬间需要消耗大量 CPU 或内存(超过 512Mi),也可能导致启动极慢,进而触发
context deadline exceeded(超时)。
四 更新yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: k8s-demo-app
namespace: java-web
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: k8s-demo-app
template:
metadata:
labels:
app: k8s-demo-app
version: v2.0
spec:
containers:
- name: k8s-demo-app
image: docker.io/library/k8s-demo-app:v2.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# ✅ 改成 TCP 探针
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 60
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 30
periodSeconds: 5
五 更新成功
root@rocky9-2 java-app]# kubectl apply -f k8s-demo-app-v2.yaml
deployment.apps/k8s-demo-app configured
[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-759779f4fb-4gtcc 0/1 Running 0 11s
k8s-demo-app-7666646bd4-b62lw 0/1 Running 5 (12s ago) 7m33s
k8s-demo-app-7666646bd4-tk4jk 0/1 Running 5 (2s ago) 7m33s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
^C[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-759779f4fb-4gtcc 0/1 Running 0 17s
k8s-demo-app-7666646bd4-b62lw 0/1 Running 5 (18s ago) 7m39s
k8s-demo-app-7666646bd4-tk4jk 0/1 Running 5 (8s ago) 7m39s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
^C[root@rocky9-2 java-app]#
[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-759779f4fb-4gtcc 0/1 Running 0 28s
k8s-demo-app-7666646bd4-b62lw 0/1 Running 5 (29s ago) 7m50s
k8s-demo-app-7666646bd4-tk4jk 0/1 Running 5 (19s ago) 7m50s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
^[[A^C[root@rocky9-2 java-app]#
[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-759779f4fb-4gtcc 0/1 Running 0 33s
k8s-demo-app-7666646bd4-b62lw 0/1 Running 5 (34s ago) 7m55s
k8s-demo-app-7666646bd4-tk4jk 0/1 Running 5 (24s ago) 7m55s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
k8s-demo-app-759779f4fb-4gtcc 1/1 Running 0 45s
k8s-demo-app-7666646bd4-tk4jk 0/1 Terminating 5 (36s ago) 8m7s
k8s-demo-app-759779f4fb-hf9k2 0/1 Pending 0 0s
k8s-demo-app-759779f4fb-hf9k2 0/1 Pending 0 0s
k8s-demo-app-759779f4fb-hf9k2 0/1 ContainerCreating 0 0s
k8s-demo-app-759779f4fb-hf9k2 0/1 Running 0 2s
k8s-demo-app-7666646bd4-tk4jk 0/1 Terminating 5 8m16s
k8s-demo-app-7666646bd4-tk4jk 0/1 Terminating 5 8m17s
k8s-demo-app-7666646bd4-tk4jk 0/1 Terminating 5 8m17s
k8s-demo-app-7666646bd4-tk4jk 0/1 Terminating 5 8m17s
k8s-demo-app-759779f4fb-hf9k2 1/1 Running 0 41s
k8s-demo-app-7666646bd4-b62lw 0/1 Terminating 5 (87s ago) 8m48s
k8s-demo-app-7666646bd4-b62lw 0/1 Terminating 5 8m48s
k8s-demo-app-7666646bd4-b62lw 0/1 Terminating 5 8m49s
k8s-demo-app-7666646bd4-b62lw 0/1 Terminating 5 8m49s
k8s-demo-app-7666646bd4-b62lw 0/1 Terminating 5 8m49s
^C[root@rocky9-2 java-app]# kubectl -n java-web get pods -w
NAME READY STATUS RESTARTS AGE
k8s-demo-app-759779f4fb-4gtcc 1/1 Running 0 3m
k8s-demo-app-759779f4fb-hf9k2 1/1 Running 0 2m15s
tomcat-6c48f7bd6-s84xn 1/1 Running 0 12h
tomcat-6c48f7bd6-xvkdq 1/1 Running 0 12h
非常好,这一段 -w 输出已经说明问题被修复了,而且滚动升级是健康的 👍
我们来“复盘”你刚刚看到的全过程:
真实滚动升级过程
1️⃣ 新 ReplicaSet 创建
k8s-demo-app-759779f4fb-4gtcc 0/1 Running
2️⃣ 新 Pod 就绪
k8s-demo-app-759779f4fb-4gtcc 1/1 Running
3️⃣ 老 Pod 被优雅下线
k8s-demo-app-7666646bd4-tk4jk Terminating
k8s-demo-app-7666646bd4-b62lw Terminating
4️⃣ 补齐第 2 个新副本
k8s-demo-app-759779f4fb-hf9k2 1/1 Running
最终状态(几秒后)应该是:
kubectl -n java-web get pods
老的 7666646bd4-* 全部消失。
- 探针误杀 → 定位 → 修复
- 滚动发布不中断服务
- 观察 Pod 生命周期状态流转
更多推荐




所有评论(0)