第六章:Kubernetes 容器编排

最后更新: 2024-01-01 作者: DevOps Team
页面目录

第六章:Kubernetes 容器编排

Kubernetes (K8s) 是容器编排的事实标准,本章将深入讲解 K8s 的架构、核心资源对象以及在生产环境中的部署和管理实践。


6.1 Kubernetes 架构

6.1.1 集群架构

┌─────────────────────────────────────────────────────────────────┐
│                        Kubernetes 集群                           │
│                                                                 │
│  ┌───────────────────────────────────────────────────────────┐  │
│  │                      Control Plane                        │  │
│  │                                                            │  │
│  │   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌────────┐ │  │
│  │   │ kube-    │  │ kube-    │  │ etcd     │  │ cloud- │ │  │
│  │   │ apiserver│  │ scheduler│  │          │  │ controller│ │  │
│  │   └──────────┘  └──────────┘  └──────────┘  └────────┘ │  │
│  │                                                            │  │
│  └───────────────────────────────────────────────────────────┘  │
│                              │                                  │
│                              │                                  │
│  ┌───────────────────────────┼───────────────────────────────┐ │
│  │                    Worker Nodes                             │ │
│  │                                                            │ │
│  │  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐      │ │
│  │  │   Node 1    │  │   Node 2    │  │   Node 3    │      │ │
│  │  │  ┌───────┐  │  │  ┌───────┐  │  │  ┌───────┐  │      │ │
│  │  │  │  Pod  │  │  │  │  Pod  │  │  │  │  Pod  │  │      │ │
│  │  │  │  (Nginx) │ │  │  │ (App) │  │  │  │ (DB)  │  │      │ │
│  │  │  └───────┘  │  │  └───────┘  │  │  └───────┘  │      │ │
│  │  │ ┌─────────┐│  │ ┌─────────┐│  │ ┌─────────┐│      │ │
│  │  │ │ kubelet ││  │ │ kubelet ││  │ │ kubelet ││      │ │
│  │  │ │ kube-   ││  │ │ kube-   ││  │ │ kube-   ││      │ │
│  │  │ │ proxy   ││  │ │ proxy   ││  │ │ proxy   ││      │ │
│  │  │ │ containerd││ │ │ containerd││ │ │ containerd││ │ │
│  │  │ └─────────┘│  │ └─────────┘│  │ └─────────┘│      │ │
│  │  └─────────────┘  └─────────────┘  └─────────────┘      │ │
│  └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

6.1.2 Control Plane 组件

组件 功能
kube-apiserver 集群 API 网关,处理所有 REST 操作
etcd 分布式键值存储,保存集群所有数据
kube-scheduler 调度 Pod 到合适的 Node
kube-controller-manager 运行控制器进程 (Node、Replica、Endpoint 等)
cloud-controller-manager 与云提供商交互的控制器

6.1.3 Node 组件

组件 功能
kubelet 代理,负责在 Node 上创建/删除 Pod
kube-proxy 网络代理,维护网络规则
container runtime 容器运行时 (containerd, CRI-O)

6.2 核心资源对象

6.2.1 Pod 详解

# basic-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    version: v1
  namespace: default
spec:
  # 容器配置
  containers:
    - name: myapp
      image: myapp:v1.0.0
      imagePullPolicy: Always  # Always/IfNotPresent/Never
      
      ports:
        - name: http
          containerPort: 8080
          protocol: TCP
      
      env:
        - name: NODE_ENV
          value: production
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: db-credentials
              key: url
      
      resources:
        requests:
          memory: "128Mi"
          cpu: "100m"
        limits:
          memory: "256Mi"
          cpu: "500m"
      
      livenessProbe:
        httpGet:
          path: /health
          port: 8080
        initialDelaySeconds: 10
        periodSeconds: 10
        timeoutSeconds: 5
        failureThreshold: 3
      
      readinessProbe:
        httpGet:
          path: /ready
          port: 8080
        initialDelaySeconds: 5
        periodSeconds: 5
        successThreshold: 1
        failureThreshold: 3
      
      startupProbe:
        httpGet:
          path: /health
          port: 8080
        failureThreshold: 30
        periodSeconds: 10
      
      lifecycle:
        preStop:
          exec:
            command: ["/bin/sh", "-c", "sleep 10"]
      
      volumeMounts:
        - name: app-logs
          mountPath: /app/logs
        - name: config
          mountPath: /app/config
          readOnly: true
      
  volumes:
    - name: app-logs
      emptyDir: {}
    - name: config
      configMap:
        name: app-config
  
  # 调度策略
  nodeSelector:
    disktype: ssd
    region: us-east-1
  
  affinity:
    nodeAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
        - weight: 100
          preference:
            matchExpressions:
              - key: node-type
                operator: In
                values:
                  - compute-optimized
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        - labelSelector:
            matchLabels:
              app: myapp
          topologyKey: kubernetes.io/hostname
  
  tolerations:
    - key: "node-type"
      operator: "Equal"
      value: "gpu"
      effect: "NoSchedule"
  
  # DNS 配置
  dnsPolicy: ClusterFirst
  hostname: myapp
  subdomain: myapp.ns
  restartPolicy: Always

6.2.2 Deployment 详解

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
  labels:
    app: myapp
spec:
  # 副本数
  replicas: 3
  
  # 选择器
  selector:
    matchLabels:
      app: myapp
  
  # 更新策略
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
  
  # Pod 模板
  template:
    metadata:
      labels:
        app: myapp
        version: v1
    spec:
      containers:
        - name: myapp
          image: myapp:v1.0.0
          ports:
            - containerPort: 8080
              name: http
          resources:
            requests:
              memory: "128Mi"
              cpu: "100m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
      # 优雅终止
      terminationGracePeriodSeconds: 60
      # 终止前等待
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 10"]

6.2.3 Service 详解

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  labels:
    app: myapp
spec:
  # Service 类型
  type: ClusterIP  # ClusterIP/NodePort/LoadBalancer/ExternalName
  
  # 服务端口
  ports:
    - name: http
      port: 80
      targetPort: 8080
      protocol: TCP
    - name: grpc
      port: 50051
      targetPort: 50051
      protocol: TCP
  
  # 选择后端 Pod
  selector:
    app: myapp
  
  # 流量策略
  sessionAffinity: ClientIP
  sessionAffinityConfig:
    clientIP:
      timeoutSeconds: 10800

---
# Headless Service (无头服务)
apiVersion: v1
kind: Service
metadata:
  name: myapp-headless
spec:
  type: ClusterIP
  clusterIP: None
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: myapp

---
# NodePort Service
apiVersion: v1
kind: Service
metadata:
  name: myapp-nodeport
spec:
  type: NodePort
  ports:
    - port: 80
      targetPort: 8080
      nodePort: 30080
  selector:
    app: myapp

---
# LoadBalancer Service
apiVersion: v1
kind: Service
metadata:
  name: myapp-lb
spec:
  type: LoadBalancer
  loadBalancerIP: 1.2.3.4
  loadBalancerSourceRanges:
    - 10.0.0.0/8
  ports:
    - port: 80
      targetPort: 8080
  selector:
    app: myapp

6.2.4 Ingress 配置

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    nginx.ingress.kubernetes.io/proxy-connect-timeout: "30"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - myapp.example.com
      secretName: myapp-tls
  rules:
    - host: myapp.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-service
                port:
                  number: 80
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080
            annotations:
              nginx.ingress.kubernetes.io/rewrite-target: /api/$2

6.3 ConfigMap 和 Secret

6.3.1 ConfigMap

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  # 键值对
  DATABASE_HOST: "postgres.default.svc"
  REDIS_HOST: "redis.default.svc"
  LOG_LEVEL: "info"
  
  # 配置文件
  nginx.conf: |
    server {
      listen 80;
      server_name localhost;
      location / {
        root /usr/share/nginx/html;
        index index.html;
      }
    }
  
  # JSON 配置
  config.json: |
    {
      "database": {
        "host": "postgres",
        "port": 5432
      }
    }

---
# 使用方式
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: app
      envFrom:
        - configMapRef:
            name: app-config
      env:
        - name: SECRET_KEY
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: secret.key

6.3.2 Secret

# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
stringData:
  DATABASE_PASSWORD: "supersecretpassword"
  API_KEY: "api-key-value"
  TLS_CERT: |
    -----BEGIN CERTIFICATE-----
    ...
    -----END CERTIFICATE-----
  TLS_KEY: |
    -----BEGIN PRIVATE KEY-----
    ...
    -----END PRIVATE KEY-----

---
# Docker Registry Secret
kubectl create secret docker-registry my-registry \
  --docker-server=registry.example.com \
  --docker-username=user \
  --docker-password=pass \
  --docker-email=user@example.com

---
# TLS Secret
kubectl create secret tls my-tls \
  --cert=cert.pem \
  --key=key.pem

6.4 存储管理

6.4.1 PersistentVolume 和 PersistentVolumeClaim

# persistentvolume.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-volume
  labels:
    type: local
spec:
  capacity:
    storage: 10Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: standard
  hostPath:
    path: "/mnt/data"

---
# persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-storage
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: standard
  selector:
    matchLabels:
      type: local

---
# 使用 PVC
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: app
      volumeMounts:
        - name: persistent-storage
          mountPath: /app/data
  volumes:
    - name: persistent-storage
      persistentVolumeClaim:
        claimName: pvc-storage

6.4.2 StatefulSet

# statefulset.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
spec:
  serviceName: postgres
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - name: postgres
          image: postgres:15
          env:
            - name: POSTGRES_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: password
          volumeMounts:
            - name: data
              mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
    - metadata:
        name: data
      spec:
        accessModes: ["ReadWriteOnce"]
        storageClassName: "standard"
        resources:
          requests:
            storage: 10Gi

6.5 Helm 图表管理

6.5.1 Helm 基础

# 安装 Helm
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash

# 添加仓库
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# 搜索图表
helm search repo nginx
helm search hub nginx

# 安装图表
helm install my-release bitnami/nginx
helm install my-release bitnami/nginx --namespace mynamespace --create-namespace

# 升级和回滚
helm upgrade my-release bitnami/nginx
helm rollback my-release 1

# 释放
helm uninstall my-release

6.5.2 自定义 Helm 图表

# 创建图表
helm create mychart

# 图表结构
# mychart/
# ├── Chart.yaml
# ├── values.yaml
# ├── charts/
# ├── templates/
# │   ├── NOTES.txt
# │   ├── _helpers.tpl
# │   ├── deployment.yaml
# │   ├── service.yaml
# │   └── ingress.yaml
# └── .helmignore
# Chart.yaml
apiVersion: v2
name: mychart
description: A Helm chart for My Application
type: application
version: 1.0.0
appVersion: "1.0.0"
keywords:
  - myapp
  - web
home: https://myapp.example.com
sources:
  - https://github.com/myorg/myapp
maintainers:
  - name: DevOps Team
    email: devops@example.com
dependencies:
  - name: postgresql
    version: "12.x.x"
    repository: "https://charts.bitnami.com/bitnami"
    condition: postgresql.enabled
# values.yaml
replicaCount: 3

image:
  repository: myapp
  tag: "latest"
  pullPolicy: IfNotPresent

service:
  type: ClusterIP
  port: 80

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: myapp.example.com
      paths:
        - path: /
          pathType: Prefix
  tls:
    - secretName: myapp-tls
      hosts:
        - myapp.example.com

resources:
  limits:
    cpu: 1000m
    memory: 1Gi
  requests:
    cpu: 100m
    memory: 256Mi

autoscaling:
  enabled: true
  minReplicas: 2
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

postgresql:
  enabled: true
  auth:
    database: myapp
    username: myapp
# templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "mychart.fullname" . }}
  labels:
    {{- include "mychart.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "mychart.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "mychart.selectorLabels" . | nindent 8 }}
    spec:
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: http
              containerPort: {{ .Values.service.port }}
              protocol: TCP
          livenessProbe:
            httpGet:
              path: /health
              port: http
          readinessProbe:
            httpGet:
              path: /ready
              port: http
          resources:
            {{- toYaml .Values.resources | nindent 12 }}

6.6 本章小结

资源对象 用途
Pod 最小部署单元,包含一个或多个容器
Deployment 管理 Pod 副本数和更新策略
Service 为 Pod 提供稳定的访问入口
Ingress HTTP/HTTPS 路由和负载均衡
ConfigMap/Secret 配置管理和敏感信息存储
PersistentVolume 持久化存储
StatefulSet 有状态应用管理
Helm 应用打包和版本管理

📌 下一章预告

下一章我们将学习 基础设施即代码 (IaC),包括:

  • Terraform 基础和使用
  • Ansible 配置管理
  • Infrastructure as Code 最佳实践

💡 提示:使用 Helm 打包应用时,应将 values.yaml 中的默认值设置为生产环境友好,并在 CI/CD 中使用自定义 values 文件覆盖。