4. Kubernetes核心对象部署
在 GKE 上部署第一个无状态应用,真正要掌握的不是“把容器跑起来”这一动作,而是用声明式方式描述“期望状态”,再让 Kubernetes 控制器持续把实际状态拉回期望状态。本节围绕四个核心对象展开:Deployment 负责无状态工作负载的副本与滚动更新,Service 负责为一组 Pod 提供稳定访问入口,ConfigMap 承载非敏感配置,Secret 承载敏感配置。理解它们之间的引用与选择关系,是后续所有部署工作的基础。
一、四个对象各自解决什么问题
Deployment 是最常用的工作负载控制器。它不直接管理 Pod,而是通过 ReplicaSet 间接管理:你声明副本数、容器镜像、端口和更新策略,Deployment 控制器负责创建 ReplicaSet,再由 ReplicaSet 保证 Pod 数量符合期望。当镜像版本变化时,Deployment 会创建新的 ReplicaSet 并按策略逐步替换旧 Pod,这就是滚动更新的来源。
Pod 是临时的,重建后 IP 会变化,因此不能把 Pod IP 当作访问入口。Service 通过标签选择器(selector)选中一组 Pod,并为其分配一个稳定的 ClusterIP 和 DNS 名称。集群内其他应用访问 Service 名称即可,无需关心后端 Pod 的增减。GKE 中常用的类型有 ClusterIP(集群内访问)和 LoadBalancer(通过外部负载均衡暴露)。
ConfigMap 与 Secret 都是键值配置对象,区别在于用途与处理方式:ConfigMap 存放普通配置,如日志级别、功能开关;Secret 存放密码、令牌、证书等敏感数据,其值以 base64 编码存储,并可配合 IAM 与加密能力做更严格的访问控制。二者都可以通过环境变量或卷挂载注入容器。
二、用 kubectl 完成一次声明式部署
声明式管理的核心动作是“写 YAML → apply → 观察收敛”。下面以部署一个名为 web 的 Nginx 无状态应用为例,镜像使用 nginx:1.25,容器端口 80,副本数 3。先准备 ConfigMap 与 Secret,再准备 Deployment 与 Service。
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: web-config
data:
LOG_LEVEL: "info"
APP_MODE: "production"
---
# secret.yaml(值需为 base64 编码)
apiVersion: v1
kind: Secret
metadata:
name: web-secret
type: Opaque
data:
DB_PASSWORD: cGFzc3dvcmQxMjM=
Secret 的 data 字段要求 base64 编码。可以用 echo -n 'password123' | base64 生成,注意 -n 避免把换行符一起编码。若不想手动编码,也可使用 stringData 字段直接写明文,kubectl 会在提交时自动转换。
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.25
ports:
- containerPort: 80
envFrom:
- configMapRef:
name: web-config
- secretRef:
name: web-secret
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "250m"
memory: "256Mi"
这里有两个关键点。第一,selector.matchLabels 必须与 template.metadata.labels 一致,否则 Deployment 无法认领自己创建的 Pod。第二,envFrom 会把 ConfigMap 和 Secret 中的所有键一次性注入为环境变量,适合键较少的场景;若只想注入部分键,应改用 env 配合 valueFrom 精确引用。
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: web-svc
spec:
type: ClusterIP
selector:
app: web
ports:
- port: 80
targetPort: 80
Service 的 selector 同样是 app: web,它据此把流量转发到三个 Pod 的 80 端口。port 是 Service 自身暴露的端口,targetPort 是容器实际监听端口,二者可以不同,但必须与容器配置对应。
三、应用与观察收敛过程
kubectl apply -f configmap.yaml
kubectl apply -f secret.yaml
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl get deploy,rs,pod -l app=web
kubectl rollout status deployment/web
kubectl get svc web-svc
执行后应看到 Deployment 的 READY 为 3/3,ReplicaSet 与三个 Pod 均处于 Running。kubectl rollout status 会阻塞直到滚动更新完成,是判断收敛是否成功的直接依据。若 Pod 长时间处于 Pending 或 CrashLoopBackOff,应先用 kubectl describe pod 查看事件,再用 kubectl logs 查看容器日志。
验证配置注入是否生效,可以进入某个 Pod 打印环境变量:
kubectl exec -it deploy/web -- env | grep -E 'LOG_LEVEL|APP_MODE|DB_PASSWORD'
预期能看到 LOG_LEVEL=info、APP_MODE=production 以及 DB_PASSWORD=password123。注意 Secret 注入到环境变量后是明文,因此要避免在日志中打印敏感变量。
四、更新与回滚
声明式部署的更新只需修改 YAML 中的镜像版本并再次 apply。例如把 nginx:1.25 改为 nginx:1.26,执行 kubectl apply -f deployment.yaml 后,Deployment 会创建新的 ReplicaSet,按 RollingUpdate 策略逐步替换旧 Pod。默认 maxSurge=25%、maxUnavailable=25%,即先多起一部分新 Pod,再逐步下线旧 Pod,过程中服务不中断。
kubectl rollout history deployment/web
kubectl rollout undo deployment/web
kubectl rollout undo deployment/web --to-revision=1
回滚依赖 ReplicaSet 历史版本,因此不要随意清理旧 ReplicaSet。若更新后发现新版本异常,rollout undo 可快速恢复到上一版本。判断更新是否成功,仍以 rollout status 和 Pod 就绪状态为准,而不是仅看 apply 命令是否返回成功。
kubectl rollout restart deployment/web);若以卷方式挂载,文件内容会在一段时间后同步更新,但应用是否重新读取取决于自身实现。五、常见排障方向
- Pod 一直 Pending:多为节点资源不足或调度约束不满足,查看
kubectl describe pod的 Events。 - Pod CrashLoopBackOff:容器启动即退出,优先看
kubectl logs,常见原因是配置缺失或端口冲突。 - Service 访问不通:先确认 selector 与 Pod 标签是否一致,再用
kubectl get endpoints web-svc检查后端是否已挂载。 - Secret 解码异常:检查 base64 是否包含多余换行,或改用
stringData规避手工编码错误。
把上述四个对象放在一起看,声明式部署的闭环就清晰了:YAML 描述期望状态,kubectl apply 提交,控制器负责收敛,rollout 与 endpoints 提供结果判断依据。掌握这条链路后,后续的探针、扩缩容、Ingress 与持久化存储都可以在此基础上逐步叠加。