🏗️ 第21课:微服务部署

📌 课程阶段:实战项目(1/5)|预计时间:60分钟|难度:⭐⭐⭐⭐☆

一、微服务架构设计

在线商城为例,实践完整的微服务部署:API Gateway + 用户服务 + 商品服务 + 订单服务 + 支付服务,使用K8s原生能力实现服务发现、配置管理、弹性伸缩。

┌────────── 在线商城微服务架构 ──────────┐
│                                          │
│  ┌──────────────────────────────────┐    │
│  │         Ingress (Nginx)          │    │
│  │  shop.example.com → Gateway     │    │
│  └─────────────┬────────────────────┘    │
│                │                          │
│  ┌─────────────▼────────────────────┐    │
│  │       API Gateway (Kong/Nginx)   │    │
│  │  /api/users  → user-svc:8080    │    │
│  │  /api/products → product-svc:80 │    │
│  │  /api/orders → order-svc:8080   │    │
│  │  /api/payments → payment-svc:80 │    │
│  └──┬──────┬──────┬──────┬─────────┘    │
│     │      │      │      │                │
│  ┌──▼──┐┌──▼──┐┌──▼──┐┌──▼──┐           │
│  │User ││Prod ││Order││Pay  │           │
│  │ Svc ││ Svc ││ Svc ││ Svc │           │
│  └──┬──┘└──┬──┘└──┬──┘└──┬──┘           │
│     │      │      │      │                │
│  ┌──▼──┐┌──▼──┐┌──▼──┐┌──▼──┐           │
│  │MySQL││Mongo││MySQL││Redis│           │
│  │ PVC ││ PVC ││ PVC ││ PVC │           │
│  └─────┘└─────┘└─────┘└─────┘           │
│                                          │
│  共享基础设施:                            │
│  Redis Cluster | Kafka | Prometheus      │
└──────────────────────────────────────────┘

二、命名空间规划

# namespace-structure.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: shop-system
  labels:
    env: production
    team: platform
---
apiVersion: v1
kind: Namespace
metadata:
  name: shop-monitoring
  labels:
    env: production
    team: sre

# 每个微服务独立部署,共享shop-system命名空间
# 或每个服务独立命名空间(更严格隔离)

三、用户服务部署

# user-service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: user-service
  namespace: shop-system
  labels:
    app: user-service
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: user-service
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  template:
    metadata:
      labels:
        app: user-service
        version: v1
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "8080"
    spec:
      serviceAccountName: user-service-sa
      containers:
      - name: user-service
        image: registry.example.com/shop/user-service:v1.5.0
        ports:
        - name: http
          containerPort: 8080
        - name: metrics
          containerPort: 9090
        env:
        - name: DB_HOST
          valueFrom:
            configMapKeyRef:
              name: user-config
              key: DB_HOST
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: user-secrets
              key: password
        - name: REDIS_HOST
          valueFrom:
            configMapKeyRef:
              name: shared-config
              key: REDIS_HOST
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        resources:
          requests:
            cpu: "100m"
            memory: "256Mi"
          limits:
            cpu: "500m"
            memory: "512Mi"
        livenessProbe:
          httpGet:
            path: /healthz
            port: http
          initialDelaySeconds: 15
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: http
          initialDelaySeconds: 5
          periodSeconds: 5
        volumeMounts:
        - name: tmp
          mountPath: /tmp
      volumes:
      - name: tmp
        emptyDir: {}
      topologySpreadConstraints:
      - maxSkew: 1
        topologyKey: kubernetes.io/hostname
        whenUnsatisfiable: DoNotSchedule
        labelSelector:
          matchLabels:
            app: user-service

---
apiVersion: v1
kind: Service
metadata:
  name: user-service
  namespace: shop-system
spec:
  selector:
    app: user-service
  ports:
  - name: http
    port: 8080
    targetPort: http
  - name: metrics
    port: 9090
    targetPort: metrics

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: user-config
  namespace: shop-system
data:
  DB_HOST: "user-mysql.shop-system.svc.cluster.local"
  DB_PORT: "3306"
  DB_NAME: "userdb"
  LOG_LEVEL: "info"
  JWT_EXPIRY: "24h"

---
apiVersion: v1
kind: Secret
metadata:
  name: user-secrets
  namespace: shop-system
type: Opaque
stringData:
  password: "changeme-in-production"

# ✅ 验证通过
kubectl apply -f user-service.yaml
kubectl get pods -n shop-system -l app=user-service
# NAME                            READY   STATUS    RESTARTS   AGE
# user-service-6c8f9d7b5e-abc12   1/1     Running   0          30s
# user-service-6c8f9d7b5e-def34   1/1     Running   0          30s
# user-service-6c8f9d7b5e-ghi56   1/1     Running   0          30s

四、商品服务+MySQL StatefulSet

# product-service.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: product-mysql
  namespace: shop-system
spec:
  serviceName: product-mysql
  replicas: 1
  selector:
    matchLabels:
      app: product-mysql
  template:
    metadata:
      labels:
        app: product-mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: product-secrets
              key: mysql-root-password
        - name: MYSQL_DATABASE
          value: "productdb"
        ports:
        - containerPort: 3306
        volumeMounts:
        - name: mysql-data
          mountPath: /var/lib/mysql
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "1"
            memory: "1Gi"
  volumeClaimTemplates:
  - metadata:
      name: mysql-data
    spec:
      accessModes: [ReadWriteOnce]
      storageClassName: standard
      resources:
        requests:
          storage: 20Gi

---
# 商品服务Deployment + HPA
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: product-service-hpa
  namespace: shop-system
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: product-service
  minReplicas: 2
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 60
  - type: Pods
    pods:
      metric:
        name: http_requests_per_second
      target:
        type: AverageValue
        averageValue: "500"

五、API Gateway

# api-gateway.yaml - Nginx Ingress作为API Gateway
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop-gateway
  namespace: shop-system
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$2
    nginx.ingress.kubernetes.io/use-regex: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    nginx.ingress.kubernetes.io/rate-limit: "100"
    nginx.ingress.kubernetes.io/limit-rps: "50"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  ingressClassName: nginx
  tls:
  - hosts: [shop.example.com]
    secretName: shop-tls
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /api/users(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: user-service
            port: {number: 8080}
      - path: /api/products(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: product-service
            port: {number: 8080}
      - path: /api/orders(/|$)(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: order-service
            port: {number: 8080}
      - path: /()(.*)
        pathType: ImplementationSpecific
        backend:
          service:
            name: frontend
            port: {number: 80}

# ✅ 验证通过
kubectl get ingress -n shop-system
# NAME            CLASS   HOSTS                ADDRESS       PORTS     AGE
# shop-gateway    nginx   shop.example.com     1.2.3.4       80,443    30s

六、NetworkPolicy微隔离

# shop-networkpolicy.yaml
# 默认拒绝
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
  namespace: shop-system
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
---
# 允许DNS
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: shop-system
spec:
  podSelector: {}
  policyTypes: [Egress]
  egress:
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - {port: 53, protocol: UDP}
---
# 服务间通信
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: order-to-payment
  namespace: shop-system
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes: [Ingress]
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: order-service
    ports:
    - {port: 8080, protocol: TCP}

七、练习

  1. 部署完整的在线商城微服务(至少3个服务+数据库)
  2. 配置Ingress API Gateway路由规则
  3. 为每个服务配置HPA和PDB
  4. 部署NetworkPolicy实现微隔离
  5. 使用Helm Chart打包整个微服务应用

🏆 第21课成就解锁

下一课预告:第22课深入CI/CD流水线——从代码到K8s的自动化。

📌 补充知识

21-微服务部署补充要点K8s生产实践扩展

🔹 资源配额(ResourceQuota):限制命名空间总资源
  apiVersion: v1
  kind: ResourceQuota
  metadata:
    name: compute-quota
    namespace: production
  spec:
    hard:
      requests.cpu: "20"
      requests.memory: 40Gi
      limits.cpu: "40"
      limits.memory: 80Gi
      pods: "50"
      services: "10"

🔹 LimitRange:设置默认资源限制
  apiVersion: v1
  kind: LimitRange
  metadata:
    name: default-limits
  spec:
    limits:
    - type: Container
      default:
        cpu: "200m"
        memory: 256Mi
      defaultRequest:
        cpu: "100m"
        memory: 128Mi
      max:
        cpu: "2"
        memory: 4Gi

🔹 Pod优先级与抢占
  apiVersion: scheduling.k8s.io/v1
  kind: PriorityClass
  metadata:
    name: high-priority
  value: 1000000
  globalDefault: false
  ---
  spec:
    preemptionPolicy: PreemptLowerPriority

🔹 优雅处理Pod中断
  • PDB保证最小可用副本
  • preStop钩子处理连接排空
  • terminationGracePeriodSeconds充足
  • 应用必须处理SIGTERM信号

🔹 生产环境Checklist
  ✅ 设置resources requests/limits
  ✅ 配置liveness/readiness探针
  ✅ 使用PDB保护关键服务
  ✅ 实现优雅关闭(SIGTERM)
  ✅ 配置HPA自动伸缩
  ✅ 使用NetworkPolicy隔离
  ✅ 开启RBAC最小权限
  ✅ 日志结构化输出
  ✅ 指标暴露/metrics端点
  ✅ 配置PVC数据备份