本文面向有一定 Kubernetes 基础的运维工程师,系统梳理容器编排在生产环境中的进阶实践,涵盖自动伸缩、资源管理、调度策略、RBAC、网络策略、存储进阶、Pod 调度、故障排查、集群运维与生产最佳实践。


一、HPA 自动伸缩

1.1 基于 CPU/内存的 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2
  maxReplicas: 20
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Percent
          value: 100
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Percent
          value: 10
          periodSeconds: 60
# 创建 HPA
kubectl apply -f hpa.yaml

# 查看 HPA 状态
kubectl get hpa web-app-hpa -o wide

# 手动触发扩容测试
kubectl run load-test --image=busybox -- /bin/sh -c "while true; do wget -q -O- http://web-app.default.svc.cluster.local; done"

1.2 自定义指标 HPA

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: web-app-hpa-custom
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  minReplicas: 2
  maxReplicas: 50
  metrics:
    - type: Pods
      pods:
        metric:
          name: http_requests_per_second
        target:
          type: AverageValue
          averageValue: "1000"
    - type: Object
      object:
        metric:
          name: requests_per_second
        describedObject:
          apiVersion: networking.k8s.io/v1
          kind: Ingress
          name: main-ingress
        target:
          type: Value
          value: "10000"

1.3 Prometheus Adapter 配置

# prometheus-adapter ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: prometheus-adapter-config
  namespace: monitoring
data:
  config.yaml: |
    rules:
      - seriesQuery: 'http_requests_total{namespace!="",pod!=""}'
        resources:
          overrides:
            namespace: {resource: "namespace"}
            pod: {resource: "pod"}
        name:
          matches: "^(.*)_total$"
          as: "${1}_per_second"
        metricsQuery: 'sum(rate(<<.Series>>{<<.LabelMatchers>>}[2m])) by (<<.GroupBy>>)'
      - seriesQuery: 'nginx_connections_active{namespace!="",pod!=""}'
        resources:
          overrides:
            namespace: {resource: "namespace"}
            pod: {resource: "pod"}
        name:
          as: "nginx_active_connections"
        metricsQuery: 'sum(<<.Series>>{<<.LabelMatchers>>}) by (<<.GroupBy>>)'
# 安装 Prometheus Adapter
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm install prometheus-adapter prometheus-community/prometheus-adapter \
  --namespace monitoring \
  --values prometheus-adapter-values.yaml

# 验证自定义指标是否可用
kubectl get --raw "/apis/custom.metrics.k8s.io/v1beta1" | jq .

1.4 VPA(Vertical Pod Autoscaler)

apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: web-app-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: web-app
  updatePolicy:
    updateMode: "Auto"  # Off | Initial | Recreate | Auto
  resourcePolicy:
    containerPolicies:
      - containerName: "*"
        minAllowed:
          cpu: 100m
          memory: 128Mi
        maxAllowed:
          cpu: 4
          memory: 8Gi
        controlledResources: ["cpu", "memory"]
# 安装 VPA
git clone https://github.com/kubernetes/autoscaler.git
cd autoscaler/vertical-pod-autoscaler
./hack/vpa-up.sh

# 查看 VPA 推荐
kubectl describe vpa web-app-vpa

二、资源管理

2.1 Requests 与 Limits

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      containers:
        - name: web
          image: nginx:1.25
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          ports:
            - containerPort: 80

2.2 LimitRange

apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: production
spec:
  limits:
    - type: Container
      default:
        cpu: 500m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 128Mi
      max:
        cpu: "4"
        memory: 8Gi
      min:
        cpu: 50m
        memory: 64Mi
    - type: Pod
      max:
        cpu: "8"
        memory: 16Gi
    - type: PersistentVolumeClaim
      max:
        storage: 500Gi
      min:
        storage: 1Gi

2.3 ResourceQuota

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: team-a
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "100"
    services: "20"
    persistentvolumeclaims: "30"
    requests.storage: 2Ti
    configmaps: "50"
    secrets: "50"
  scopes:
    - NotTerminating
# 查看配额使用情况
kubectl describe resourcequota team-quota -n team-a

# 查看所有命名空间的配额
kubectl get resourcequota --all-namespaces

2.4 QoS 类别

QoS 类别

条件

OOM 优先级

调度优先级

Guaranteed

requests == limits(所有容器)

最低(最后被杀)

最高

Burstable

requests < limits

中等

中等

BestEffort

未设置 requests/limits

最高(最先被杀)

最低

# Guaranteed Pod
apiVersion: v1
kind: Pod
metadata:
  name: guaranteed-pod
spec:
  containers:
    - name: app
      image: nginx
      resources:
        requests:
          cpu: "1"
          memory: 1Gi
        limits:
          cpu: "1"
          memory: 1Gi

---
# Burstable Pod
apiVersion: v1
kind: Pod
metadata:
  name: burstable-pod
spec:
  containers:
    - name: app
      image: nginx
      resources:
        requests:
          cpu: 250m
          memory: 256Mi
        limits:
          cpu: "2"
          memory: 2Gi
# 查看 Pod 的 QoS 类别
kubectl get pods -o custom-columns=\
  NAME:.metadata.name,\
  QOS:.status.qosClass,\
  CPU_REQ:.spec.containers[*].resources.requests.cpu,\
  CPU_LIM:.spec.containers[*].resources.limits.cpu

三、调度策略

3.1 NodeSelector

apiVersion: v1
kind: Pod
metadata:
  name: gpu-workload
spec:
  nodeSelector:
    gpu: "nvidia-a100"
    disktype: ssd
  containers:
    - name: ml-training
      image: tensorflow/tensorflow:latest-gpu
      resources:
        limits:
          nvidia.com/gpu: 2
# 给节点打标签
kubectl label nodes node-01 gpu=nvidia-a100 disktype=ssd
kubectl label nodes node-02 gpu=nvidia-v100 disktype=nvme

3.2 NodeAffinity

apiVersion: v1
kind: Pod
metadata:
  name: web-app
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
          - matchExpressions:
              - key: kubernetes.io/os
                operator: In
                values:
                  - linux
              - key: node-role.kubernetes.io/worker
                operator: Exists
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 80
          preference:
            matchExpressions:
              - key: topology.kubernetes.io/zone
                operator: In
                values:
                  - cn-hangzhou-b
                  - cn-hangzhou-c
        - weight: 20
          preference:
            matchExpressions:
              - key: node.kubernetes.io/instance-type
                operator: In
                values:
                  - ecs.g7.2xlarge
                  - ecs.g7.4xlarge
  containers:
    - name: web
      image: nginx:1.25

3.3 PodAffinity 与 PodAntiAffinity

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-frontend
  template:
    metadata:
      labels:
        app: web-frontend
        tier: frontend
    spec:
      affinity:
        # Pod 亲和性:尽量与缓存服务在同一节点
        podAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - redis-cache
                topologyKey: kubernetes.io/hostname
        # Pod 反亲和性:前端副本分散到不同可用区
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
            - labelSelector:
                matchExpressions:
                  - key: app
                    operator: In
                    values:
                      - web-frontend
              topologyKey: topology.kubernetes.io/zone
          preferredDuringSchedulingIgnoredDuringExecution:
            - weight: 100
              podAffinityTerm:
                labelSelector:
                  matchExpressions:
                    - key: app
                      operator: In
                      values:
                        - web-frontend
                topologyKey: kubernetes.io/hostname
      containers:
        - name: web
          image: nginx:1.25

3.4 Taint 与 Toleration

# 给节点设置污点
kubectl taint nodes node-01 dedicated=gpu:NoSchedule
kubectl taint nodes node-02 dedicated=storage:NoSchedule
kubectl taint nodes node-03 special=true:NoExecute

# 查看节点污点
kubectl get nodes -o custom-columns=NAME:.metadata.name,TAINTS:.spec.taints

# 移除污点
kubectl taint nodes node-01 dedicated=gpu:NoSchedule-
apiVersion: v1
kind: Pod
metadata:
  name: gpu-task
spec:
  tolerations:
    - key: "dedicated"
      operator: "Equal"
      value: "gpu"
      effect: "NoSchedule"
    - key: "node.kubernetes.io/not-ready"
      operator: "Exists"
      effect: "NoExecute"
      tolerationSeconds: 300
  containers:
    - name: ml-job
      image: pytorch/pytorch:latest

3.5 TopologySpreadConstraints

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 6
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: web-app
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: ScheduleAnyway
          labelSelector:
            matchLabels:
              app: web-app
      containers:
        - name: web
          image: nginx:1.25
          resources:
            requests:
              cpu: 250m
              memory: 256Mi

四、RBAC 权限管理

4.1 Role 与 RoleBinding

# Role:命名空间级别权限
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: pod-reader
  namespace: production
rules:
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources: ["pods/exec"]
    verbs: ["create"]
  - apiGroups: [""]
    resources: ["services", "endpoints"]
    verbs: ["get", "list"]

---
# RoleBinding:绑定到用户和 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: production
subjects:
  - kind: User
    name: developer@example.com
    apiGroup: rbac.authorization.k8s.io
  - kind: Group
    name: dev-team
    apiGroup: rbac.authorization.k8s.io
  - kind: ServiceAccount
    name: monitoring-sa
    namespace: monitoring
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

4.2 ClusterRole 与 ClusterRoleBinding

# ClusterRole:集群级别权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: cluster-reader
rules:
  - apiGroups: [""]
    resources: ["nodes", "namespaces", "persistentvolumes"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["metrics.k8s.io"]
    resources: ["nodes", "pods"]
    verbs: ["get", "list"]
  - apiGroups: ["storage.k8s.io"]
    resources: ["storageclasses"]
    verbs: ["get", "list", "watch"]

---
# ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cluster-reader-binding
subjects:
  - kind: ServiceAccount
    name: monitoring-sa
    namespace: monitoring
roleRef:
  kind: ClusterRole
  name: cluster-reader
  apiGroup: rbac.authorization.k8s.io

4.3 ServiceAccount 实践

apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-sa
  namespace: production
  annotations:
    eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/app-role
automountServiceAccountToken: true

---
# 使用 ServiceAccount 的 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web-app
  template:
    metadata:
      labels:
        app: web-app
    spec:
      serviceAccountName: app-sa
      automountServiceAccountToken: true
      containers:
        - name: web
          image: nginx:1.25
# 查看 ServiceAccount 的权限
kubectl auth can-i --list --as=system:serviceaccount:production:app-sa

# 验证特定权限
kubectl auth can-i get pods --as=system:serviceaccount:production:app-sa -n production
kubectl auth can-i create deployments --as=system:serviceaccount:production:app-sa -n production

# 创建长期 Token(Kubernetes 1.24+)
kubectl create token app-sa -n production --duration=8760h

五、网络策略

5.1 基本 NetworkPolicy

# 默认拒绝所有入站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress

---
# 默认拒绝所有出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress

---
# 允许特定应用的入站和出站
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: web-app-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web-app
  policyTypes:
    - Ingress
    - Egress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
        - podSelector:
            matchLabels:
              app: api-gateway
      ports:
        - protocol: TCP
          port: 8080
    - from:
        - podSelector:
            matchLabels:
              app: prometheus
      ports:
        - protocol: TCP
          port: 9090
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    - to:  # 允许 DNS
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
    - to:  # 允许访问外部 HTTPS
        - ipBlock:
            cidr: 0.0.0.0/0
            except:
              - 10.0.0.0/8
              - 172.16.0.0/12
              - 192.168.0.0/16
      ports:
        - protocol: TCP
          port: 443

5.2 Calico 网络策略

# Calico GlobalNetworkPolicy(集群级别)
apiVersion: projectcalico.org/v3
kind: GlobalNetworkPolicy
metadata:
  name: deny-external-access
spec:
  order: 100
  selector: tenant == "internal"
  types:
    - Ingress
  ingress:
    - action: Deny
      source:
        nets:
          - 0.0.0.0/0
        notNets:
          - 10.0.0.0/8

---
# Calico NetworkPolicy(支持更多功能)
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: advanced-web-policy
  namespace: production
spec:
  selector: app == "web-app"
  types:
    - Ingress
    - Egress
  ingress:
    - action: Allow
      protocol: TCP
      source:
        namespaceSelector: env == "staging"
        serviceAccounts:
          name: gateway-sa
      destination:
        ports:
          - 8080
          - 8443
  egress:
    - action: Allow
      protocol: TCP
      destination:
        selector: app == "database"
        ports:
          - 5432
    - action: Allow
      protocol: UDP
      destination:
        ports:
          - 53

5.3 Cilium 网络策略

# Cilium CiliumNetworkPolicy
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
  name: l7-web-policy
  namespace: production
spec:
  endpointSelector:
    matchLabels:
      app: web-app
  ingress:
    - fromEndpoints:
        - matchLabels:
            app: api-gateway
      toPorts:
        - ports:
            - port: "8080"
              protocol: TCP
          rules:
            http:
              - method: GET
                path: "/api/v1/.*"
              - method: POST
                path: "/api/v1/submit"
                headers:
                  - 'Content-Type: application/json'
  egress:
    - toEndpoints:
        - matchLabels:
            app: database
      toPorts:
        - ports:
            - port: "5432"
              protocol: TCP
    - toFQDNs:
        - matchName: "api.external.com"
        - matchPattern: "*.s3.amazonaws.com"
      toPorts:
        - ports:
            - port: "443"
              protocol: TCP
# 安装 Cilium
helm repo add cilium https://helm.cilium.io/
helm install cilium cilium/cilium --namespace kube-system \
  --set hubble.enabled=true \
  --set hubble.relay.enabled=true \
  --set hubble.ui.enabled=true \
  --set kubeProxyReplacement=strict

# 查看 Cilium 状态
cilium status

# 查看网络流量
cilium monitor --type drop

六、存储进阶

6.1 CSI 驱动

# StorageClass(CSI)
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-ssd
provisioner: disk.csi.aliyun.com
parameters:
  type: cloud_essd
  performanceLevel: PL1
  fstype: ext4
  encrypted: "true"
reclaimPolicy: Retain
allowVolumeExpansion: true
volumeBindingMode: WaitForFirstConsumer
mountOptions:
  - noatime
  - nodiratime

---
# PVC 使用 CSI StorageClass
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data
  namespace: production
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 100Gi

6.2 VolumeSnapshot

# VolumeSnapshotClass
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshotClass
metadata:
  name: csi-snapclass
driver: disk.csi.aliyun.com
deletionPolicy: Retain
parameters:
  tags: "env=production,managedby=velero"

---
# 创建快照
apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: app-data-snapshot-20260610
  namespace: production
spec:
  volumeSnapshotClassName: csi-snapclass
  source:
    persistentVolumeClaimName: app-data
# 查看快照
kubectl get volumesnapshot -n production

# 从快照恢复
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data-restored
  namespace: production
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 100Gi
  dataSource:
    name: app-data-snapshot-20260610
    kind: VolumeSnapshot
    apiGroup: snapshot.storage.k8s.io
EOF

6.3 Volume Clone

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: app-data-clone
  namespace: production
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 100Gi
  dataSource:
    name: app-data
    kind: PersistentVolumeClaim

6.4 Velero 备份与恢复

# 安装 Velero
velero install \
  --provider alibabacloud \
  --plugins velero/velero-plugin-for-alibaba:v1.9 \
  --bucket velero-backup \
  --secret-file ./credentials-velero \
  --backup-location-config region=cn-hangzhou \
  --snapshot-location-config region=cn-hangzhou

# 创建备份
velero backup create production-backup \
  --include-namespaces production \
  --snapshot-volumes \
  --ttl 720h

# 定时备份(每天凌晨 2 点)
velero schedule create production-daily \
  --include-namespaces production \
  --schedule="0 2 * * *" \
  --ttl 720h

# 查看备份状态
velero backup describe production-backup
velero backup logs production-backup

# 恢复
velero restore create --from-backup production-backup
# Velero Schedule CRD
apiVersion: velero.io/v1
kind: Schedule
metadata:
  name: production-daily
  namespace: velero
spec:
  schedule: "0 2 * * *"
  template:
    includedNamespaces:
      - production
    snapshotVolumes: true
    storageLocation: default
    ttl: 720h
    hooks:
      resources:
        - name: pre-backup-hook
          includedNamespaces:
            - production
          labelSelector:
            matchLabels:
              backup-hook: "true"
          pre:
            - exec:
                container: postgres
                command:
                  - /bin/bash
                  - -c
                  - "PGPASSWORD=$POSTGRES_PASSWORD pg_dump -U postgres app_db > /backup/dump.sql"
                onError: Fail
                timeout: 300s

七、Pod 调度进阶

7.1 DaemonSet

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-exporter
  namespace: monitoring
spec:
  selector:
    matchLabels:
      app: node-exporter
  template:
    metadata:
      labels:
        app: node-exporter
    spec:
      hostNetwork: true
      hostPID: true
      tolerations:
        - operator: Exists
      containers:
        - name: node-exporter
          image: prom/node-exporter:v1.7.0
          ports:
            - containerPort: 9100
              hostPort: 9100
          resources:
            requests:
              cpu: 50m
              memory: 64Mi
            limits:
              cpu: 200m
              memory: 256Mi
          volumeMounts:
            - name: proc
              mountPath: /host/proc
              readOnly: true
            - name: sys
              mountPath: /host/sys
              readOnly: true
      volumes:
        - name: proc
          hostPath:
            path: /proc
        - name: sys
          hostPath:
            path: /sys
  updateStrategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 1

7.2 StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  namespace: production
spec:
  serviceName: postgres
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      terminationGracePeriodSeconds: 30
      containers:
        - name: postgres
          image: postgres:16
          ports:
            - containerPort: 5432
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
          resources:
            requests:
              cpu: 500m
              memory: 1Gi
            limits:
              cpu: "2"
              memory: 4Gi
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
          livenessProbe:
            exec:
              command:
                - pg_isready
                - -U
                - postgres
            initialDelaySeconds: 30
            periodSeconds: 10
          readinessProbe:
            exec:
              command:
                - pg_isready
                - -U
                - postgres
            initialDelaySeconds: 5
            periodSeconds: 5
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes:
          - ReadWriteOnce
        storageClassName: fast-ssd
        resources:
          requests:
            storage: 100Gi
  podManagementPolicy: Parallel
  updateStrategy:
    type: RollingUpdate

7.3 Job 与 CronJob

# Job:一次性任务
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migration
  namespace: production
spec:
  backoffLimit: 3
  activeDeadlineSeconds: 600
  ttlSecondsAfterFinished: 3600
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: migrate
          image: myapp/migrations:v2.5.0
          command: ["python", "manage.py", "migrate"]
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: db-credentials
                  key: url
          resources:
            requests:
              cpu: 250m
              memory: 512Mi

---
# CronJob:定时任务
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-report
  namespace: production
spec:
  schedule: "0 3 * * *"
  concurrencyPolicy: Forbid
  failedJobsHistoryLimit: 3
  successfulJobsHistoryLimit: 5
  startingDeadlineSeconds: 600
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: report
              image: myapp/report-generator:v1.0.0
              command: ["python", "generate_report.py"]
              resources:
                requests:
                  cpu: 500m
                  memory: 1Gi
                limits:
                  cpu: "2"
                  memory: 4Gi

7.4 Init Container

apiVersion: v1
kind: Pod
metadata:
  name: web-app
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command:
        - sh
        - -c
        - |
          until nc -z postgres.production.svc.cluster.local 5432; do
            echo "Waiting for database..."
            sleep 2
          done
    - name: init-schema
      image: myapp/migrations:v2.5.0
      command: ["python", "manage.py", "init-schema"]
      env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
    - name: download-config
      image: alpine:3.19
      command:
        - sh
        - -c
        - |
          wget -O /config/app.conf https://config.internal.com/app.conf
          wget -O /config/tls.crt https://vault.internal.com/certs/tls.crt
      volumeMounts:
        - name: config
          mountPath: /config
  containers:
    - name: web
      image: myapp/web:v2.5.0
      volumeMounts:
        - name: config
          mountPath: /app/config
          readOnly: true
  volumes:
    - name: config
      emptyDir: {}

7.5 Sidecar 模式

apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecars
  labels:
    app: web-app
spec:
  containers:
    # 主容器
    - name: web
      image: nginx:1.25
      ports:
        - containerPort: 8080
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/nginx
    # Sidecar: 日志收集
    - name: log-collector
      image: fluent/fluent-bit:2.2
      volumeMounts:
        - name: shared-logs
          mountPath: /var/log/nginx
          readOnly: true
        - name: fluent-config
          mountPath: /fluent-bit/etc/
      resources:
        requests:
          cpu: 50m
          memory: 64Mi
    # Sidecar: 代理
    - name: envoy-proxy
      image: envoyproxy/envoy:v1.28
      ports:
        - containerPort: 15001
          name: envoy-port
      volumeMounts:
        - name: envoy-config
          mountPath: /etc/envoy
      resources:
        requests:
          cpu: 100m
          memory: 128Mi
  volumes:
    - name: shared-logs
      emptyDir: {}
    - name: fluent-config
      configMap:
        name: fluent-bit-config
    - name: envoy-config
      configMap:
        name: envoy-config

八、故障排查

8.1 Pod 状态诊断

# 查看 Pod 详情(重点关注 Events 和 Conditions)
kubectl describe pod <pod-name> -n <namespace>

# 常见 Pod 状态及排查
# CrashLoopBackOff
kubectl logs <pod-name> -n <namespace> --previous
kubectl logs <pod-name> -n <namespace> -p --tail=100

# ImagePullBackOff
kubectl describe pod <pod-name> | grep -A5 "Events"
# 检查镜像名称、tag、仓库认证
kubectl get secret regcred -o jsonpath='{.data.\.dockerconfigjson}' | base64 -d

# Pending
kubectl describe pod <pod-name> | grep -A10 "Events"
# 常见原因:资源不足、节点选择器不匹配、PVC 未绑定
kubectl get nodes -o custom-columns=NAME:.metadata.name,CPU_ALLOC:.status.allocatable.cpu,MEM_ALLOC:.status.allocatable.memory

# OOMKilled
kubectl get pod <pod-name> -o jsonpath='{.status.containerStatuses[0].lastState.terminated.reason}'
kubectl top pod <pod-name> -n <namespace>

8.2 Events 分析

# 按时间排序查看事件
kubectl get events -n <namespace> --sort-by='.lastTimestamp'

# 查看特定资源的事件
kubectl get events -n <namespace> --field-selector involvedObject.name=<pod-name>

# 集群级别警告事件
kubectl get events --all-namespaces --field-selector type=Warning --sort-by='.lastTimestamp' | tail -20

# 实时监控事件
kubectl get events -n <namespace> -w

8.3 日志排查

# 实时日志
kubectl logs -f <pod-name> -n <namespace>

# 多容器 Pod 日志
kubectl logs <pod-name> -c <container-name> -n <namespace>

# 前一个容器实例的日志(崩溃后查看)
kubectl logs <pod-name> -p -n <namespace>

# 带时间范围的日志
kubectl logs <pod-name> -n <namespace> --since=1h
kubectl logs <pod-name> -n <namespace> --since-time=2026-06-10T06:00:00Z

# 按标签查询多个 Pod 日志
kubectl logs -l app=web-app -n <namespace> --all-containers=true --tail=50

# 日志导出
kubectl logs <pod-name> -n <namespace> > pod.log 2>&1

8.4 Exec 调试

# 进入容器
kubectl exec -it <pod-name> -n <namespace> -- /bin/sh

# 多容器 Pod
kubectl exec -it <pod-name> -c <container-name> -n <namespace> -- /bin/bash

# 网络诊断
kubectl exec -it <pod-name> -n <namespace> -- curl -v http://service-name:8080/health
kubectl exec -it <pod-name> -n <namespace> -- nslookup kubernetes.default
kubectl exec -it <pod-name> -n <namespace> -- nc -zv <service-name> <port>

# 文件系统检查
kubectl exec -it <pod-name> -n <namespace> -- df -h
kubectl exec -it <pod-name> -n <namespace> -- ls -la /app/config/

8.5 网络诊断

# DNS 诊断
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup kubernetes.default
kubectl run dns-test --image=busybox:1.36 --rm -it --restart=Never -- nslookup my-service.production.svc.cluster.local

# 连通性测试
kubectl run curl-test --image=curlimages/curl --rm -it --restart=Never -- \
  curl -v http://web-app.production.svc.cluster.local:8080/health

# 使用临时调试容器(Kubernetes 1.25+)
kubectl debug -it <pod-name> -n <namespace> --image=nicolaka/netshoot --target=<container-name>

# 查看 Service 端点
kubectl get endpoints <service-name> -n <namespace>

# 查看 iptables 规则(在节点上执行)
iptables -t nat -L KUBE-SERVICES -n | grep <service-name>

# 查看 CoreDNS 日志
kubectl logs -l k8s-app=kube-dns -n kube-system --tail=100

九、集群运维

9.1 节点维护

# 标记节点不可调度(新 Pod 不会调度到该节点)
kubectl cordon node-02

# 驱逐节点上的 Pod(自动调度到其他节点)
kubectl drain node-02 \
  --ignore-daemonsets \
  --delete-emptydir-data \
  --grace-period=60 \
  --timeout=300s

# 执行维护操作(升级内核、更换硬件等)
# ... 维护完成 ...

# 恢复节点调度
kubectl uncordon node-02

# 验证节点状态
kubectl get nodes
kubectl describe node node-02 | grep -A5 Conditions

9.2 etcd 备份与恢复

# 备份 etcd
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-$(date +%Y%m%d-%H%M%S).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

# 验证备份
ETCDCTL_API=3 etcdctl snapshot status /backup/etcd-20260610-145800.db --write-table

# 自动备份 CronJob
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: CronJob
metadata:
  name: etcd-backup
  namespace: kube-system
spec:
  schedule: "0 */6 * * *"
  jobTemplate:
    spec:
      template:
        spec:
          hostNetwork: true
          nodeSelector:
            node-role.kubernetes.io/control-plane: ""
          tolerations:
            - operator: Exists
          containers:
            - name: backup
              image: registry.k8s.io/etcd:3.5.12
              command:
                - /bin/sh
                - -c
                - |
                  etcdctl snapshot save /backup/etcd-\$(date +%Y%m%d-%H%M%S).db
                  find /backup -name "etcd-*.db" -mtime +7 -delete
              env:
                - name: ETCDCTL_API
                  value: "3"
                - name: ETCDCTL_ENDPOINTS
                  value: "https://127.0.0.1:2379"
                - name: ETCDCTL_CACERT
                  value: "/etc/kubernetes/pki/etcd/ca.crt"
                - name: ETCDCTL_CERT
                  value: "/etc/kubernetes/pki/etcd/server.crt"
                - name: ETCDCTL_KEY
                  value: "/etc/kubernetes/pki/etcd/server.key"
              volumeMounts:
                - name: etcd-certs
                  mountPath: /etc/kubernetes/pki/etcd
                  readOnly: true
                - name: backup
                  mountPath: /backup
          volumes:
            - name: etcd-certs
              hostPath:
                path: /etc/kubernetes/pki/etcd
            - name: backup
              hostPath:
                path: /var/backups/etcd
          restartPolicy: OnFailure
EOF

# 恢复 etcd(灾难恢复场景)
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-20260610-145800.db \
  --data-dir=/var/lib/etcd-restore \
  --name=etcd-0 \
  --initial-cluster=etcd-0=https://192.168.1.10:2380 \
  --initial-advertise-peer-urls=https://192.168.1.10:2380

9.3 证书轮换

# 查看证书过期时间
kubeadm certs check-expiration

# 使用 kubeadm 轮换所有证书
kubeadm certs renew all

# 轮换特定证书
kubeadm certs renew apiserver
kubeadm certs renew apiserver-kubelet-client
kubeadm certs renew front-proxy-client

# 重启控制面组件
systemctl restart kubelet

# 手动轮换 kubeconfig
kubeadm kubeconfig user --client-name=admin --org=system:masters > /etc/kubernetes/admin.conf
cp /etc/kubernetes/admin.conf ~/.kube/config

# 验证新证书
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates

9.4 集群升级

# 1. 升级 kubeadm
apt-get update && apt-get install -y kubeadm=1.30.0-*

# 2. 检查升级计划
kubeadm upgrade plan

# 3. 升级控制面
kubeadm upgrade apply v1.30.0

# 4. 升级 kubelet 和 kubectl
apt-get install -y kubelet=1.30.0-* kubectl=1.30.0-*
systemctl daemon-reload
systemctl restart kubelet

# 5. 逐个升级 Worker 节点
# 在 Master 上:
kubectl drain worker-01 --ignore-daemonsets --delete-emptydir-data

# 在 Worker 上:
apt-get update && apt-get install -y kubeadm=1.30.0-*
kubeadm upgrade node
apt-get install -y kubelet=1.30.0-* kubectl=1.30.0-*
systemctl daemon-reload
systemctl restart kubelet

# 在 Master 上:
kubectl uncordon worker-01

# 6. 验证升级
kubectl get nodes
kubectl version

十、生产最佳实践

10.1 Namespace 规划

# 标准命名空间结构
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    env: production
    managed-by: platform-team
---
apiVersion: v1
kind: Namespace
metadata:
  name: staging
  labels:
    env: staging
    managed-by: platform-team
---
apiVersion: v1
kind: Namespace
metadata:
  name: monitoring
  labels:
    env: shared
    managed-by: platform-team

10.2 资源配额模板

apiVersion: v1
kind: ResourceQuota
metadata:
  name: team-quota
  namespace: ${TEAM_NS}
spec:
  hard:
    requests.cpu: "20"
    requests.memory: 40Gi
    limits.cpu: "40"
    limits.memory: 80Gi
    pods: "100"
    services: "20"
    persistentvolumeclaims: "30"
    requests.storage: 2Ti

---
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: ${TEAM_NS}
spec:
  limits:
    - type: Container
      default:
        cpu: 500m
        memory: 512Mi
      defaultRequest:
        cpu: 100m
        memory: 128Mi

10.3 镜像策略

# Kyverno 镜像策略
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: image-policy
spec:
  validationFailureAction: Enforce
  background: true
  rules:
    - name: require-image-tag
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "镜像必须指定 tag,禁止使用 latest"
        pattern:
          spec:
            containers:
              - image: "!*:latest & *:*"
    - name: verify-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "registry.example.com/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
                      -----END PUBLIC KEY-----
    - name: restrict-registries
      match:
        any:
          - resources:
              kinds:
                - Pod
      validate:
        message: "只允许从公司内部仓库拉取镜像"
        pattern:
          spec:
            containers:
              - image: "registry.example.com/*"

10.4 安全加固

# Pod Security Standards
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

---
# 安全上下文最佳实践
apiVersion: apps/v1
kind: Deployment
metadata:
  name: secure-app
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: secure-app
  template:
    metadata:
      labels:
        app: secure-app
    spec:
      automountServiceAccountToken: false
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 3000
        fsGroup: 2000
        seccompProfile:
          type: RuntimeDefault
      containers:
        - name: app
          image: registry.example.com/app:v2.5.0
          securityContext:
            allowPrivilegeEscalation: false
            readOnlyRootFilesystem: true
            capabilities:
              drop:
                - ALL
          resources:
            requests:
              cpu: 250m
              memory: 256Mi
            limits:
              cpu: "1"
              memory: 1Gi
          volumeMounts:
            - name: tmp
              mountPath: /tmp
            - name: app-config
              mountPath: /app/config
              readOnly: true
          livenessProbe:
            httpGet:
              path: /healthz
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
      volumes:
        - name: tmp
          emptyDir:
            medium: Memory
            sizeLimit: 100Mi
        - name: app-config
          configMap:
            name: app-config

10.5 GitOps 工作流

# ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-app
  namespace: argocd
spec:
  project: production
  source:
    repoURL: https://git.internal.com/platform/k8s-manifests.git
    targetRevision: main
    path: apps/web-app/overlays/production
  destination:
    server: https://kubernetes.default.svc
    namespace: production
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
      - PrunePropagationPolicy=foreground
      - ServerSideApply=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

---
# ArgoCD AppProject
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argocd
spec:
  description: "生产环境项目"
  sourceRepos:
    - "https://git.internal.com/platform/*"
  destinations:
    - namespace: "production"
      server: "https://kubernetes.default.svc"
    - namespace: "monitoring"
      server: "https://kubernetes.default.svc"
  clusterResourceWhitelist:
    - group: "*"
      kind: Namespace
  namespaceResourceWhitelist:
    - group: "*"
      kind: "*"
  roles:
    - name: developer
      description: "开发者角色"
      policies:
        - p, proj:production:developer, applications, get, production/*, allow
        - p, proj:production:developer, applications, sync, production/*, allow
# 安装 ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml

# 获取初始密码
argocd admin initial-password -n argocd

# 配置 GitOps 仓库
argocd repo add https://git.internal.com/platform/k8s-manifests.git \
  --username deploy-token \
  --password <token>

# 同步应用
argocd app sync web-app
argocd app get web-app

附录:常用命令速查

场景

命令

查看集群信息

kubectl cluster-info

查看所有资源

kubectl get all --all-namespaces

资源使用率

kubectl top nodes / kubectl top pods

端口转发

kubectl port-forward svc/web-app 8080:80

查看 YAML

kubectl get deploy web-app -o yaml

干运行

kubectl apply -f manifest.yaml --dry-run=client

Diff 变更

kubectl diff -f manifest.yaml

强制删除 Pod

kubectl delete pod <pod> --grace-period=0 --force

查看 Ingress

kubectl get ingress -A -o wide

查看 StorageClass

kubectl get sc

查看 CRD

kubectl get crd

API 资源列表

kubectl api-resources


参考文档