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 与加密能力做更严格的访问控制。二者都可以通过环境变量或卷挂载注入容器。

无状态应用声明式部署对象关系 声明文件 (YAML) deployment.yaml service.yaml configmap.yaml / secret.yaml kubectl apply -f Deployment replicas: 3 image: web:v1 strategy: RollingUpdate 管理 ReplicaSet → Pod Service type: ClusterIP / LoadBalancer port: 80 → targetPort: 8080 selector: app=web 稳定 ClusterIP + DNS ConfigMap LOG_LEVEL=info APP_MODE=production 非敏感配置 Secret DB_PASSWORD (base64) API_TOKEN (base64) 敏感数据,受访问控制 Pod 副本(label: app=web) web-xxxx-1 :8080 web-xxxx-2 :8080 web-xxxx-3 :8080 selector 匹配 envFrom / volume envFrom / volume

二、用 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 命令是否返回成功。

ConfigMap 或 Secret 更新后,已注入为环境变量的 Pod 不会自动刷新,需要触发一次滚动重启(如 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 与持久化存储都可以在此基础上逐步叠加。