第 8 章 健康检查与资源:让系统知道应用的状态
Running 不等于能服务
容器进程存在,只能说明它在运行;它可能还在加载数据,也可能已经卡死。Kubernetes 提供三种探针。
readinessProbe(就绪探针)回答“现在能接流量吗”。失败时,Pod 会变为未就绪,通常不再作为 Service 的就绪后端;它本身不会因此被重启。
livenessProbe(存活探针)回答“是否需要重启这个容器”。连续失败达到阈值后,kubelet 会按重启策略处理。错误地把外部数据库偶发故障作为存活失败原因,可能造成大量无效重启。
startupProbe(启动探针)给慢启动应用留时间。配置了启动探针后,在它成功之前,就绪和存活探针不会开始工作。简单 NGINX 静态网页启动很快,本书完整示例只使用就绪和存活探针。
若两种探针都检查同一个路径,删除页面可能同时触发两种失败,不能据此推断“就绪失败导致重启”。分开验证时,应让就绪路径失败、存活路径仍成功,观察 Service 就绪后端移除而 restartCount 不变;再单独破坏存活路径,检查容器重启与恢复。随书自动化实验用另建的故障 Pod 完成这项隔离检查,主线网页清单仍保持易读。
requests 与 limits
requests 是调度时用于计算资源需求的重要依据。limits 约束运行时资源使用。CPU 的 100m 是 0.1 核,内存 64Mi 是 64 × 1024 × 1024 字节。
CPU 超出限制通常会被限流;内存达到限制后,进程可能被内核终止,出现 OOMKilled。节点内存压力也可能导致 Pod 被驱逐。资源不足时应结合事件和实际负载分析,不能见到重启就无限增大上限。
把前面知识合成完整应用
将之前的 deployment.yaml 替换为下面内容。这是完整文件,不是片段。
apiVersion: apps/v1
kind: Deployment
metadata:
name: hello-web
spec:
replicas: 2
revisionHistoryLimit: 3
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: hello-web
template:
metadata:
labels:
app: hello-web
spec:
containers:
- name: web
image: nginx:1.28
ports:
- name: http
containerPort: 80
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
readinessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 2
periodSeconds: 5
livenessProbe:
httpGet:
path: /
port: http
initialDelaySeconds: 10
periodSeconds: 10
volumeMounts:
- name: page
mountPath: /usr/share/nginx/html
readOnly: true
volumes:
- name: page
configMap:
name: hello-page这个清单挂载整个网页目录,所以该目录中原镜像自带的文件会被挂载内容遮住。readOnly 表示容器不能通过这个挂载点改写配置。普通 NGINX 镜像的权限模型适合本地教学,不要据此假定已满足生产环境的非 root 与安全加固要求。
kubectl apply -n k8s-book -f configmap.yaml
kubectl apply -n k8s-book -f deployment.yaml
kubectl apply -n k8s-book -f service.yaml
kubectl rollout status deployment/hello-web -n k8s-book --timeout=120s
kubectl get pods -n k8s-book保持 port-forward 运行,刷新浏览器。如果转发因 Pod 替换退出,重新执行第 6 章的转发命令。你应该看到中文欢迎页。
本章自测:就绪失败是否必然重启容器?100m 是一百核吗?为什么 ConfigMap 缺失可能导致 Pod 一直无法启动?