环境隔离与发布顺序
本文承接 20 篇 K8s 配置与密钥,从「单环境内的配置注入」扩展到「多套环境同时运行」,使用 Kustomize overlay + 镜像 digest 晋级。
本课沿用上一课的 k8s/base,使用 kubectl 内置 Kustomize。练习采用单集群三个 namespace;生产是否独立集群,要根据故障影响范围、权限与合规需求选择。namespace 提供资源作用域,不等于完整网络或节点隔离。
“多个 Deployment”和“多个 namespace”也不是二选一:本例每个 namespace 内各有一个 Deployment,由同一个 base 生成。
建立目录与 overlay
在 k8s/base/kustomization.yaml 写入:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- ingress.yaml
- configmap.yaml用以下脚本生成环境目录,并将 CI 输出的相同 digest 写入每个 overlay。首次初始化可以相同;后续晋级时逐环境修改、验证,不一次更新生产。
export IMAGE_REF="$(cat image.txt)"
python3 - <<'CODE'
import os, pathlib, re
ref = os.environ['IMAGE_REF']
assert re.fullmatch(r'ghcr.io/[a-z0-9._/-]+@sha256:[a-f0-9]{64}', ref)
name, digest = ref.split('@')
for env in ('dev', 'staging', 'prod'):
p = pathlib.Path('k8s/overlays') / env
p.mkdir(parents=True, exist_ok=True)
(p / 'kustomization.yaml').write_text(f"""apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: {env}
resources:
- ../../base
images:
- name: ghcr.io/replace-me/delivery-demo
newName: {name}
newTag: ""
digest: {digest}
patches:
- target:
kind: ConfigMap
name: delivery-config
patch: |-
- op: replace
path: /data/APP_ENV
value: {env}
- target:
kind: Ingress
name: delivery-demo
patch: |-
- op: replace
path: /spec/rules/0/host
value: {env}.demo.local
""")
CODE
kubectl kustomize k8s/overlays/dev检查渲染结果:namespace、域名、APP_ENV 应对应环境,镜像必须是 digest 引用。base 的名、selector 保持一致,避免复制三套 YAML 后各自漂移。
预置环境与权限
for env in dev staging prod; do
kubectl create namespace "$env" --dry-run=client -o yaml | kubectl apply -f -
done按第 20 篇方式为每个 namespace 创建各自的 delivery-secret,私有镜像还需 ghcr-read。不要跨环境复用生产凭据。
生产发布身份的最小 Role 示例(由管理员安装,并按工作流实际资源收敛):
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: delivery-deployer
namespace: prod
rules:
- apiGroups: [apps]
resources: [deployments]
verbs: [get, list, watch, create, update, patch]
- apiGroups: [""]
resources: [services, configmaps]
verbs: [get, list, watch, create, update, patch]
- apiGroups: [networking.k8s.io]
resources: [ingresses]
verbs: [get, list, watch, create, update, patch]
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: delivery-deployer
namespace: prod
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: delivery-deployer
namespace: prod
subjects:
- kind: ServiceAccount
name: delivery-deployer
namespace: prod
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: delivery-deployer该身份不直接读取 Secret,但能修改 Pod 模板的身份仍可能间接使用该 namespace 的凭据,因此不能把这个 Role 当作不可信租户隔离。还应限制运行身份、镜像来源和 Pod 权限。生产还需独立的 NetworkPolicy、ResourceQuota 和 LimitRange;未配置 NetworkPolicy 时,不能宣称跨 namespace 网络已隔离。
先验证再晋级
kubectl apply -k k8s/overlays/dev
kubectl -n dev rollout status deployment/delivery-demo --timeout=120s
curl --fail -H 'Host: dev.demo.local' http://127.0.0.1/healthz
curl --fail -H 'Host: dev.demo.local' http://127.0.0.1/environment两个端点应分别返回 ok 和 dev,证明配置已改变应用行为。dev 通过后再执行 staging 的相同命令,环境端点应返回 staging。prod 由受保护的发布流程执行,不能因为目录已经生成就自动发布。检查实际版本:
for env in dev staging prod; do
kubectl -n "$env" get deployment delivery-demo \
-o jsonpath='{.metadata.namespace}{" "}{.spec.template.spec.containers[0].image}{"\n"}'
done晋级完成时三个 digest 应相同。环境标签便于检索,但不能替代 digest 比较;生产阶段不重新构建镜像。各环境允许保留不同的配置与资源规格。
最小验收与边界
用集群管理员身份检查 kubectl auth can-i get secrets --as=system:serviceaccount:prod:delivery-deployer -n prod,预期为 no;核对三个 Host 不串环境。每次发布保留目标环境和 digest 的记录。
环境分离处理配置、权限和故障范围;灰度发布处理同一环境内哪些请求使用新版本,两者不能互相替代。下一课给每个环境配独立数据库并验证迁移顺序。