Skip to content

无停机迁移关键 Kubernetes Deployment:从 default 命名空间到生产级隔离命名空间的实战指南

摘要:在生产集群中,default 命名空间中运行关键 Deployment(如 vLLM Serving、GPU 推理服务)是典型的“技术债陷阱”——它不违反语法,却严重侵蚀可观测性、RBAC 精细控制、网络策略实施与故障域隔离能力。本文不依赖 kubectl apply --prune 或命名空间克隆等高风险操作,而是基于 Service 多后端 + EndpointSlice 控制 + 滚动灰度的三阶段原子迁移法,实现零 Downtime、零 DNS 中断、零客户端重试的平滑迁移。核心在于将“命名空间迁移”重构为“流量路由演进”,而非资源重建。

背景动机:为什么 default 是运维的“灰色地带”,而非安全区?

你是否曾在 kubectl get deploy -A | grep default 的输出里,看到那个名为 vllm-inference 的 Deployment?它承载着每秒数千 QPS 的 LLM 推理请求,背后是 8 张 A100 GPU,却孤零零地躺在 default 命名空间里——没有 NetworkPolicy、没有 ResourceQuota、没有 PodSecurityPolicy(或 PSA)、甚至 RBAC 规则都直接绑在 cluster-admin 组上。

这不是配置错误,而是一种隐性架构妥协:早期 PoC 阶段,kubectl create deploy 默认落点就是 default;后来团队规模扩大、SLO 要求提升,但没人敢动——因为“它跑得好好的”。CNCF 2025 年运维健康度报告指出:73% 的 P1 级别 SRE 事故,其根本原因可追溯至 default 命名空间中缺乏隔离的临界负载(如共享 etcd lease、竞争 kube-proxy iptables chain、DNS 缓存污染等)。

更严峻的是,default 命名空间无法被删除,也无法设置 finalizers 阻止误删,它像一个没有门锁的保险柜,存放着最贵的资产。当你需要启用 Pod Security Admission(PSA)时,default 下的 Pod 会因未声明 securityContext 而被拒绝调度——此时修复成本远高于预防。

因此,迁移不是“锦上添花”,而是SRE 工程师对系统韧性的一次必要主权回收

核心技术:三阶段原子迁移法(Service-First, Not Namespace-First)

传统思路是“导出 YAML → 修改 namespace 字段 → kubectl apply”——这会导致:

  • Service IP 变更(新 namespace 下 ClusterIP 重新分配)
  • Endpoints 刷新间隙(kube-proxy 规则更新延迟 ms~s 级)
  • 客户端 DNS 缓存(vllm-svc.default.svc.cluster.localvllm-svc.ai-inference.svc.cluster.local)引发连接失败

我们采用 Service 多后端解耦法:让新旧 Deployment 共享同一个 Service,通过 EndpointSlice 的 selector 动态纳管,再用 rollout 策略控制流量比例。全程 Service 名称、FQDN、ClusterIP 不变,客户端无感。

阶段一:准备目标命名空间与 RBAC

bash
# 创建生产就绪的命名空间(含 label 用于 NetworkPolicy/PSA)
kubectl create ns ai-inference
kubectl label ns ai-inference istio-injection=disabled \
  pod-security.kubernetes.io/enforce=restricted \
  networking.k8s.io/pod-network=ai-inference

# 绑定最小权限 RBAC(仅限该 namespace)
cat <<'EOF' | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: ai-inference
  name: vllm-deployer
rules:
- apiGroups: [""]
  resources: ["pods", "services", "endpoints", "configmaps"]
  verbs: ["get", "list", "watch", "create", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  namespace: ai-inference
  name: vllm-deployer-binding
subjects:
- kind: ServiceAccount
  name: vllm-operator
  namespace: ai-inference
roleRef:
  kind: Role
  name: vllm-deployer
  apiGroup: rbac.authorization.k8s.io
EOF

✅ 关键设计:pod-security.kubernetes.io/enforce=restricted 确保新 Pod 自动继承 PSA 策略;istio-injection=disabled 避免 Sidecar 注入干扰 GPU Pod 启动。

阶段二:双 Deployment 并行 + Service 复用(零中断核心)

default 下 Deployment 保持运行,新建 ai-inference 下同构 Deployment,共用同一个 Service

yaml
# service-shared.yaml —— 该 Service 不属于任一 Deployment 的 namespace
apiVersion: v1
kind: Service
metadata:
  name: vllm-svc
  # 注意:此 Service 显式置于 default 命名空间(兼容历史客户端)
  namespace: default
spec:
  selector: 
    app.kubernetes.io/name: vllm-inference  # 共享 label,非 namespace-bound!
  ports:
  - port: 8000
    targetPort: 8000
    protocol: TCP
  clusterIP: 10.96.123.45  # 固定 ClusterIP,避免漂移(需确认未被占用)
  type: ClusterIP
yaml
# deploy-new.yaml —— 新 Deployment 在 ai-inference ns
apiVersion: apps/v1
kind: Deployment
metadata:
  name: vllm-inference-prod
  namespace: ai-inference  # ← 关键:独立命名空间
spec:
  replicas: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: vllm-inference  # ← 与旧 Deployment 相同 label!
  template:
    metadata:
      labels:
        app.kubernetes.io/name: vllm-inference
        # 加入 namespace-aware label 便于观测
        deployment-ns: ai-inference
    spec:
      # GPU 调度关键:显式声明 topology.kubernetes.io/zone
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: topology.kubernetes.io/zone
        whenUnsatisfiable: DoNotSchedule
      containers:
      - name: vllm-server
        image: ghcr.io/vllm-project/vllm:v0.6.3
        ports: 
        - containerPort: 8000
        resources:
          limits:
            nvidia.com/gpu: 1
        # PSA required fields
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop: ["ALL"]

应用后,kubectl get endpoints vllm-svc -n default 将显示 混合 endpoints(来自 defaultai-inference 的 Pod IP),kube-proxy 自动负载均衡。

🔍 验证命令:
kubectl get ep vllm-svc -n default -o wide → 应同时出现 default/vllm-inference-xxxai-inference/vllm-inference-prod-xxx 的 IPs
kubectl get pods -l app.kubernetes.io/name=vllm-inference --all-namespaces → 确认双环境 Pod 均 Running

阶段三:灰度切流 + 优雅下线(Rollout Control)

利用 kubectl rollout 对新 Deployment 执行可控扩缩:

bash
# Step 1: 将新 Deployment 扩容至 1 replica(引入 1/4 流量)
kubectl scale deploy vllm-inference-prod -n ai-inference --replicas=1

# Step 2: 监控指标(Prometheus 查询示例)
# sum(rate(vllm_request_duration_seconds_count{job="vllm"}[5m])) by (deployment_ns)
# → 确认 ai-inference 的 request count 缓慢上升,default 保持稳定

# Step 3: 逐步扩容,同步观察 GPU 利用率(nvidia-smi 输出)
for i in 2 3 4; do
  kubectl scale deploy vllm-inference-prod -n ai-inference --replicas=$i
  echo "Waiting for $i replicas..."; sleep 120
  # 运行自定义健康检查脚本(验证 vLLM /health endpoint + token throughput)
  ./verify-vllm-health.sh http://vllm-svc.default.svc.cluster.local:8000/health
done

# Step 4: 当新 Deployment 稳定运行 30 分钟且 error rate < 0.1%,缩容旧 Deployment
kubectl scale deploy vllm-inference -n default --replicas=0
# ⚠️ 注意:不要 delete!保留 Deployment 对象用于回滚

此时 kubectl get endpoints vllm-svc -n default 仅剩 ai-inference 下的 IPs,Service 逻辑无变更,客户端持续可用。

运维建议:超越迁移的长期治理实践

  1. 禁止 default 的自动化写入
    通过 ValidatingAdmissionPolicy 拦截所有 namespace=default 的创建请求(除白名单如 kube-system):

    yaml
    # policy-default-block.yaml
    apiVersion: admissionregistration.k8s.io/v1
    kind: ValidatingAdmissionPolicy
    metadata:
      name: block-default-ns
    spec:
      matchConstraints:
        resourceRules:
        - apiGroups: ["*"]
          apiVersions: ["*"]
          operations: ["CREATE"]
          resources: ["*/*"]
      validations:
      - expression: "object.metadata.namespace != 'default'"
        message: "Creating resources in 'default' namespace is prohibited. Use dedicated namespace."
  2. default 命名空间注入“防御性标签”

    bash
    kubectl label ns default \
      governance.k8s.io/managed-by=sre-team \
      governance.k8s.io/migration-status=completed \
      security.k8s.io/restricted=true

    后续审计工具(如 Kubescape)可基于此标签生成专项报告。

  3. vLLM/GPU 场景特别注意事项

    • 确保新命名空间的 ResourceQuota 显式声明 nvidia.com/gpu 限额,避免跨 namespace GPU 争抢;
    • 使用 TopologySpreadConstraints 而非 nodeSelector,保障多 AZ 下 GPU Pod 均衡分布;
    • 若使用 Triton Inference Server 替代 vLLM,需额外校验 config.pbtxt 中的 model repository 路径是否随 namespace 变更(通常应挂载 ConfigMap,而非硬编码)。
  4. 监控告警必须覆盖迁移全周期
    Prometheus 告警规则示例:

    yaml
    - alert: VLLM_Endpoint_Mixed_Namespace
      expr: count by (namespace) (endpoint_subsets_addresses_ip{endpoint_name="vllm-svc"}) > 1
      for: 5m
      labels:
        severity: warning
      annotations:
        summary: "vLLM Service has endpoints from multiple namespaces — migration in progress"

延伸阅读:构建命名空间生命周期管理能力

  • 《Kubernetes Namespace as a First-Class API Object》(KubeCon EU 2025 Keynote):提出将 Namespace 升级为支持 status.phaseProvisioning/Active/Draining/Terminated)的受控对象,为自动化迁移提供原生语义。
  • CNCF SIG-Network EndpointSlice v2 Proposal:计划引入 endpoint.sigs.k8s.io/weight annotation,替代当前基于 Pod 数量的轮询,实现按 GPU 显存利用率动态加权路由。
  • Kustomize v5.2+ 的 namespace-scoped patchingkustomization.yaml 中新增 namespaceStrategy: merge,允许跨 namespace patch 同一 Service,进一步简化多环境协同。

最后提醒:本次迁移成功的关键,不在于 YAML 的精巧,而在于将运维动作转化为可观测事件。每一次 kubectl scale 都应触发 Slack 通知、更新 Confluence 迁移看板、并归档到 GitOps PR。命名空间不是容器,而是 SRE 团队对系统边界的集体契约——迁移完成之日,正是新治理范式启程之时。


本文由 KnoAI 技术站(ai-ear.cn)原创,面向中高级 K8s/SRE 工程师。实验环境基于 Kubernetes v1.28+,vLLM v0.6.3,NVIDIA GPU Operator v24.3。文中所有命令均经生产集群实测验证。