跳到主要内容

2 篇博文 含有标签「Ingress」

Kubernetes Ingress 七层路由入口

查看所有标签

Kubernetes 全景解析 (6):生产级微服务架构实战

· 阅读需 38 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

"纸上得来终觉浅,绝知此事要躬行。"

在前面的系列文章中,我们系统学习了 K8s 的架构设计、工作负载管理、网络模型、存储体系与配置管理。现在是时候将这些知识串联起来,完成一次从零到生产的完整实战演练。

本文将以一个电商微服务系统为蓝本,手把手带你完成以下全流程:

  • 多服务编排与部署
  • Ingress 七层路由与 TLS 终止
  • HPA 弹性伸缩与高可用保障
  • 健康检查与优雅停机
  • Prometheus + Grafana 监控体系
  • ArgoCD GitOps 持续交付

所有 YAML 配置均基于 Kubernetes v1.34(Of Wind & Will)官方 API 验证,可直接应用于你的集群。

API 版本说明

本文所有 YAML 配置使用的 API 版本均经过 Kubernetes v1.34 官方文档校验:

资源类型apiVersion官方文档
Deployment / StatefulSetapps/v1Workload Resources
Servicev1Service Resources
Ingress / IngressClassnetworking.k8s.io/v1Ingress
HorizontalPodAutoscalerautoscaling/v2HPA v2
PodDisruptionBudgetpolicy/v1PDB
ResourceQuota / LimitRangev1Config and Storage Resources
RBACrbac.authorization.k8s.io/v1Authorization Resources
ServiceMonitormonitoring.coreos.com/v1Prometheus Operator
ArgoCD Applicationargoproj.io/v1alpha1ArgoCD CRDs
StorageClassstorage.k8s.io/v1Storage Resources

一、实战场景概述

1.1 电商微服务架构设计

我们以一个典型的电商系统为例,将其拆分为以下五个核心微服务:

服务职责技术栈端口
API Gateway统一入口、路由转发、限流熔断APISIX / Nginx80/443
用户服务注册、登录、用户信息管理Go (Gin)8080
商品服务商品 CRUD、分类管理、搜索Java (Spring Boot)8081
订单服务下单、支付回调、订单查询Node.js (Express)8082
支付服务支付对接、退款、对账Go (Gin)8083

底层依赖两个有状态服务:

服务职责端口
PostgreSQL关系型数据库(用户、订单)5432
Redis缓存、会话管理、分布式锁6379

1.2 微服务架构拓扑

1.3 技术栈选择

层级技术选型选型理由
网关层APISIX高性能、支持 gRPC 转发、插件生态丰富
服务层Go + Java + Node.js模拟真实多语言微服务环境
数据层PostgreSQL 16 + Redis 7成熟稳定、社区活跃
监控层Prometheus + Grafana云原生监控事实标准
日志层Fluent Bit轻量级、资源占用低
部署层ArgoCDGitOps 声明式持续交付

二、基础设施准备

2.1 命名空间与资源配额

生产环境中,不同团队的服务应该隔离在不同的命名空间中,并通过 ResourceQuota 限制资源使用。

k8s/00-namespace/namespace.yaml
---
apiVersion: v1
kind: Namespace
metadata:
name: microservices
labels:
app.kubernetes.io/part-of: ecommerce
app.kubernetes.io/managed-by: argocd
---
apiVersion: v1
kind: Namespace
metadata:
name: data
labels:
app.kubernetes.io/part-of: ecommerce
app.kubernetes.io/managed-by: argocd
---
apiVersion: v1
kind: Namespace
metadata:
name: gateway
labels:
app.kubernetes.io/part-of: ecommerce
app.kubernetes.io/managed-by: argocd
k8s/00-namespace/resource-quota.yaml
---
apiVersion: v1
kind: ResourceQuota
metadata:
name: microservices-quota
namespace: microservices
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
services: "20"
persistentvolumeclaims: "10"
---
apiVersion: v1
kind: LimitRange
metadata:
name: default-limits
namespace: microservices
spec:
limits:
- default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "100m"
memory: "128Mi"
max:
cpu: "2"
memory: "2Gi"
min:
cpu: "50m"
memory: "64Mi"
type: Container
生产环境注意事项

ResourceQuota 的值应根据集群总容量和业务优先级进行合理分配。建议预留 20% 的资源缓冲,避免某个命名空间的突发流量影响其他业务。LimitRange 确保即使开发者忘记设置资源限制,容器也不会无限制地消耗节点资源。

2.2 ConfigMap 与 Secret 配置管理

将配置从镜像中分离出来,是 12-Factor App 的核心原则之一。

k8s/01-config/configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: app-config
namespace: microservices
data:
# 数据库连接配置
DB_HOST: "postgresql.data.svc.cluster.local"
DB_PORT: "5432"
DB_NAME: "ecommerce"
DB_POOL_SIZE: "20"
DB_CONNECTION_TIMEOUT: "30"

# Redis 连接配置
REDIS_HOST: "redis.data.svc.cluster.local"
REDIS_PORT: "6379"
REDIS_DB: "0"
REDIS_POOL_SIZE: "50"

# 日志配置
LOG_LEVEL: "info"
LOG_FORMAT: "json"

# 服务间调用超时
SERVICE_TIMEOUT: "10s"
SERVICE_RETRY: "3"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: gateway-config
namespace: gateway
data:
# 网关路由配置
UPSTREAM_USER: "user-service.microservices.svc.cluster.local:8080"
UPSTREAM_PRODUCT: "product-service.microservices.svc.cluster.local:8081"
UPSTREAM_ORDER: "order-service.microservices.svc.cluster.local:8082"
UPSTREAM_PAYMENT: "payment-service.microservices.svc.cluster.local:8083"
k8s/01-config/secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: microservices
type: Opaque
stringData:
DB_USERNAME: "ecommerce_app"
DB_PASSWORD: "S3cureP@ssw0rd!2026"
---
apiVersion: v1
kind: Secret
metadata:
name: redis-credentials
namespace: microservices
type: Opaque
stringData:
REDIS_PASSWORD: "R3disS3cret!2026"
---
apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: gateway
type: kubernetes.io/tls
stringData:
tls.crt: |
-----BEGIN CERTIFICATE-----
# 此处替换为你的 TLS 证书
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
# 此处替换为你的 TLS 私钥
-----END PRIVATE KEY-----
安全警告

切勿将 Secret 明文提交到 Git 仓库! 生产环境应使用以下方案之一:

  • Sealed Secrets(Bitn Labs):加密后可安全提交 Git
  • External Secrets Operator:从 AWS Secrets Manager / HashiCorp Vault 同步
  • SOPS(Mozilla):基于 GPG/KMS 的加密工具

本文示例中的 stringData 仅用于演示,生产环境请务必使用加密方案。

2.3 PV/PVC 持久化存储规划

k8s/02-storage/storageclass.yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: fast-ssd
provisioner: kubernetes.io/aws-ebs # 根据云厂商调整
parameters:
type: gp3
fsType: ext4
iopsPerGB: "50"
allowVolumeExpansion: true
reclaimPolicy: Retain
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: standard-hdd
provisioner: kubernetes.io/aws-ebs
parameters:
type: gp3
fsType: ext4
allowVolumeExpansion: true
reclaimPolicy: Delete
volumeBindingMode: Immediate
k8s/02-storage/pvc.yaml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: postgresql-data
namespace: data
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: redis-data
namespace: data
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi

三、核心服务部署

3.1 API Gateway 部署

网关是整个系统的统一入口,负责路由转发、限流、熔断和认证。

k8s/03-services/gateway-deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: api-gateway
namespace: gateway
labels:
app: api-gateway
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: api-gateway
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: api-gateway
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9090"
prometheus.io/path: "/metrics"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: api-gateway
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 60
containers:
- name: apisix
image: apache/apisix:3.9.0-debian
ports:
- name: http
containerPort: 9080
protocol: TCP
- name: https
containerPort: 9443
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
envFrom:
- configMapRef:
name: gateway-config
resources:
requests:
cpu: 250m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 9090
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /healthz
port: 9090
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
---
apiVersion: v1
kind: Service
metadata:
name: api-gateway
namespace: gateway
labels:
app: api-gateway
spec:
type: ClusterIP
ports:
- name: http
port: 80
targetPort: 9080
protocol: TCP
- name: https
port: 443
targetPort: 9443
protocol: TCP
selector:
app: api-gateway

3.2 用户服务部署

k8s/03-services/user-service.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: user-service
namespace: microservices
labels:
app: user-service
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: user-service
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: user-service
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: user-service
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 30
containers:
- name: user-service
image: registry.example.com/ecommerce/user-service:v1.0.0
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: grpc
containerPort: 9090
protocol: TCP
env:
- name: SERVICE_NAME
value: "user-service"
- name: SERVICE_PORT
value: "8080"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: app-config
key: DB_NAME
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: REDIS_HOST
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-credentials
key: REDIS_PASSWORD
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
---
apiVersion: v1
kind: Service
metadata:
name: user-service
namespace: microservices
labels:
app: user-service
spec:
type: ClusterIP
ports:
- name: http
port: 8080
targetPort: 8080
protocol: TCP
- name: grpc
port: 9090
targetPort: 9090
protocol: TCP
selector:
app: user-service

3.3 订单服务部署

k8s/03-services/order-service.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: microservices
labels:
app: order-service
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: order-service
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: order-service
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8082"
prometheus.io/path: "/metrics"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: order-service
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 60
containers:
- name: order-service
image: registry.example.com/ecommerce/order-service:v1.0.0
ports:
- name: http
containerPort: 8082
protocol: TCP
env:
- name: SERVICE_NAME
value: "order-service"
- name: SERVICE_PORT
value: "8082"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: app-config
key: DB_NAME
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: REDIS_HOST
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-credentials
key: REDIS_PASSWORD
- name: USER_SERVICE_URL
value: "http://user-service.microservices.svc.cluster.local:8080"
- name: PRODUCT_SERVICE_URL
value: "http://product-service.microservices.svc.cluster.local:8081"
- name: PAYMENT_SERVICE_URL
value: "http://payment-service.microservices.svc.cluster.local:8083"
resources:
requests:
cpu: 300m
memory: 384Mi
limits:
cpu: "1"
memory: 768Mi
livenessProbe:
httpGet:
path: /healthz
port: 8082
initialDelaySeconds: 20
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8082
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
---
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: microservices
labels:
app: order-service
spec:
type: ClusterIP
ports:
- name: http
port: 8082
targetPort: 8082
protocol: TCP
selector:
app: order-service

3.4 商品服务部署

k8s/03-services/product-service.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: product-service
namespace: microservices
labels:
app: product-service
version: v1
spec:
replicas: 3
selector:
matchLabels:
app: product-service
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: product-service
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8081"
prometheus.io/path: "/metrics"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: product-service
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 30
containers:
- name: product-service
image: registry.example.com/ecommerce/product-service:v1.0.0
ports:
- name: http
containerPort: 8081
protocol: TCP
env:
- name: SERVICE_NAME
value: "product-service"
- name: SERVICE_PORT
value: "8081"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: app-config
key: DB_NAME
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: REDIS_HOST
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-credentials
key: REDIS_PASSWORD
resources:
requests:
cpu: 300m
memory: 512Mi
limits:
cpu: "2"
memory: 1Gi
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8081
initialDelaySeconds: 30
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8081
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 10"]
---
apiVersion: v1
kind: Service
metadata:
name: product-service
namespace: microservices
labels:
app: product-service
spec:
type: ClusterIP
ports:
- name: http
port: 8081
targetPort: 8081
protocol: TCP
selector:
app: product-service

3.5 支付服务部署

k8s/03-services/payment-service.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: payment-service
namespace: microservices
labels:
app: payment-service
version: v1
spec:
replicas: 2
selector:
matchLabels:
app: payment-service
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: payment-service
version: v1
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8083"
prometheus.io/path: "/metrics"
spec:
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchLabels:
app: payment-service
topologyKey: kubernetes.io/hostname
terminationGracePeriodSeconds: 60
containers:
- name: payment-service
image: registry.example.com/ecommerce/payment-service:v1.0.0
ports:
- name: http
containerPort: 8083
protocol: TCP
env:
- name: SERVICE_NAME
value: "payment-service"
- name: SERVICE_PORT
value: "8083"
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: DB_HOST
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: app-config
key: DB_PORT
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: app-config
key: DB_NAME
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
- name: PAYMENT_GATEWAY_KEY
valueFrom:
secretKeyRef:
name: payment-secret
key: GATEWAY_API_KEY
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 8083
initialDelaySeconds: 15
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /readyz
port: 8083
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "sleep 15"]
---
apiVersion: v1
kind: Service
metadata:
name: payment-service
namespace: microservices
labels:
app: payment-service
spec:
type: ClusterIP
ports:
- name: http
port: 8083
targetPort: 8083
protocol: TCP
selector:
app: payment-service

3.6 数据库部署(StatefulSet)

有状态服务使用 StatefulSet 部署,确保稳定的网络标识和持久化存储。

k8s/03-services/postgresql-statefulset.yaml
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql
namespace: data
labels:
app: postgresql
spec:
serviceName: postgresql-headless
replicas: 1
selector:
matchLabels:
app: postgresql
template:
metadata:
labels:
app: postgresql
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9187"
prometheus.io/path: "/metrics"
spec:
terminationGracePeriodSeconds: 60
containers:
- name: postgresql
image: postgres:16-alpine
ports:
- name: postgresql
containerPort: 5432
protocol: TCP
env:
- name: POSTGRES_DB
value: "ecommerce"
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_USERNAME
optional: false
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: db-credentials
key: DB_PASSWORD
optional: false
- name: PGDATA
value: "/var/lib/postgresql/data/pgdata"
resources:
requests:
cpu: 500m
memory: 1Gi
limits:
cpu: "2"
memory: 4Gi
volumeMounts:
- name: postgresql-data
mountPath: /var/lib/postgresql/data
livenessProbe:
exec:
command:
- pg_isready
- -U
- ecommerce_app
- -d
- ecommerce
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
exec:
command:
- pg_isready
- -U
- ecommerce_app
- -d
- ecommerce
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "pg_ctl stop -m fast"]
- name: postgres-exporter
image: prometheuscommunity/postgres-exporter:v0.15.0
ports:
- name: metrics
containerPort: 9187
protocol: TCP
env:
- name: DATA_SOURCE_NAME
value: "postgresql://ecommerce_app:S3cureP@ssw0rd!2026@localhost:5432/ecommerce?sslmode=disable"
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 200m
memory: 128Mi
volumeClaimTemplates:
- metadata:
name: postgresql-data
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100Gi
---
apiVersion: v1
kind: Service
metadata:
name: postgresql-headless
namespace: data
labels:
app: postgresql
spec:
type: ClusterIP
clusterIP: None
ports:
- name: postgresql
port: 5432
targetPort: 5432
protocol: TCP
selector:
app: postgresql
---
apiVersion: v1
kind: Service
metadata:
name: postgresql
namespace: data
labels:
app: postgresql
spec:
type: ClusterIP
ports:
- name: postgresql
port: 5432
targetPort: 5432
protocol: TCP
selector:
app: postgresql
k8s/03-services/redis-statefulset.yaml
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: data
labels:
app: redis
spec:
serviceName: redis-headless
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "9121"
prometheus.io/path: "/metrics"
spec:
terminationGracePeriodSeconds: 30
containers:
- name: redis
image: redis:7-alpine
command:
- redis-server
- --requirepass
- $(REDIS_PASSWORD)
- --maxmemory
- 1gb
- --maxmemory-policy
- allkeys-lru
- --appendonly
- "yes"
ports:
- name: redis
containerPort: 6379
protocol: TCP
env:
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-credentials
key: REDIS_PASSWORD
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
volumeMounts:
- name: redis-data
mountPath: /data
livenessProbe:
exec:
command:
- redis-cli
- -a
- $(REDIS_PASSWORD)
- ping
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
exec:
command:
- redis-cli
- -a
- $(REDIS_PASSWORD)
- ping
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 3
failureThreshold: 3
lifecycle:
preStop:
exec:
command: ["/bin/sh", "-c", "redis-cli -a $(REDIS_PASSWORD) SHUTDOWN NOSAVE"]
- name: redis-exporter
image: oliver006/redis_exporter:v1.58.0
ports:
- name: metrics
containerPort: 9121
protocol: TCP
env:
- name: REDIS_ADDR
value: "redis://localhost:6379"
- name: REDIS_PASSWORD
valueFrom:
secretKeyRef:
name: redis-credentials
key: REDIS_PASSWORD
resources:
requests:
cpu: 50m
memory: 64Mi
limits:
cpu: 100m
memory: 128Mi
volumeClaimTemplates:
- metadata:
name: redis-data
spec:
storageClassName: fast-ssd
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
---
apiVersion: v1
kind: Service
metadata:
name: redis-headless
namespace: data
labels:
app: redis
spec:
type: ClusterIP
clusterIP: None
ports:
- name: redis
port: 6379
targetPort: 6379
protocol: TCP
selector:
app: redis
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: data
labels:
app: redis
spec:
type: ClusterIP
ports:
- name: redis
port: 6379
targetPort: 6379
protocol: TCP
selector:
app: redis
StatefulSet vs Deployment

对于数据库和缓存等有状态服务,务必使用 StatefulSet 而非 Deployment。StatefulSet 提供了以下保证:

  • 稳定的网络标识:每个 Pod 有固定的 DNS 名称(如 postgresql-0.postgresql-headless.data.svc.cluster.local
  • 有序的部署和扩缩容:Pod 按序号顺序创建和删除
  • 稳定的持久化存储:通过 volumeClaimTemplates,每个 Pod 绑定独立的 PVC,Pod 重建后自动重新挂载

四、服务间通信与配置

4.1 CoreDNS 服务发现

Kubernetes 内置的 CoreDNS 为每个 Service 自动创建 DNS 记录,服务间可以通过标准的 FQDN 进行互相访问:

记录格式示例作用域
<service>user-service同命名空间
<service>.<namespace>user-service.microservices跨命名空间
<service>.<namespace>.svc.cluster.localuser-service.microservices.svc.cluster.local全集群
最佳实践

始终使用完整的跨命名空间 FQDN(如 user-service.microservices.svc.cluster.local),即使服务在同一个命名空间中。这样在后续调整命名空间划分时,不需要修改代码和配置。

4.2 健康检查设计原则

健康检查是保障服务可用性的关键机制。K8s 提供了三种探针:

探针类型用途失败后果
Liveness Probe检测容器是否存活重启容器
Readiness Probe检测是否可以接收流量从 Service Endpoints 中移除
Startup Probe检测应用是否启动完成启动完成前禁用其他探针

以下是健康检查的配置要点:

k8s/04-communication/health-check-example.yaml
# 以订单服务为例,展示完整的健康检查配置
spec:
containers:
- name: order-service
# ...其他配置...
startupProbe:
httpGet:
path: /healthz
port: 8082
failureThreshold: 30 # 最多等待 30 * 10s = 300s
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: 8082
initialDelaySeconds: 0 # startupProbe 完成后才开始
periodSeconds: 15
timeoutSeconds: 5
failureThreshold: 3 # 连续 3 次失败则重启
successThreshold: 1
readinessProbe:
httpGet:
path: /readyz
port: 8082
initialDelaySeconds: 0
periodSeconds: 10
timeoutSeconds: 3
failureThreshold: 3 # 连续 3 次失败则摘除流量
successThreshold: 1
生产环境注意事项
  1. Liveness 和 Readiness 必须使用不同的端点。Liveness 检测"进程是否活着",Readiness 检测"是否准备好接收请求"。如果两者使用同一端点,可能导致级联故障——例如数据库短暂不可用时,所有 Pod 同时被 Liveness 重启。
  2. 设置合理的 timeoutSeconds。过短的超时时间会导致误判,建议设置为 P99 响应时间的 2-3 倍。
  3. 对于 Java 等启动较慢的服务,务必配置 Startup Probe,否则 Liveness Probe 可能在应用启动期间就触发重启。

4.3 优雅停机

优雅停机确保 Pod 在被终止时,能够完成正在处理的请求并安全释放资源。

v1.34 新特性:原生 Sleep Lifecycle Hook

Kubernetes v1.34 引入了原生的 sleep action 用于 PreStop 和 PostStart lifecycle hooks,无需再通过 exec 执行 sleep 命令。这提供了更简洁和可靠的优雅停机方式。

详见官方文档:Container Lifecycle Hooks

推荐方式(v1.34+):使用原生 Sleep Hook

k8s/04-communication/graceful-shutdown-v134.yaml
spec:
terminationGracePeriodSeconds: 60 # 给予 60 秒的优雅停机时间
containers:
- name: order-service
lifecycle:
preStop:
sleep:
seconds: 15 # 原生 sleep action
# ...其他配置...

传统方式(v1.34 之前):使用 Exec Hook

k8s/04-communication/graceful-shutdown-legacy.yaml
spec:
terminationGracePeriodSeconds: 60 # 给予 60 秒的优雅停机时间
containers:
- name: order-service
lifecycle:
preStop:
exec:
# 先等待 15 秒,让 Service Endpoints 更新
# 然后发送 SIGTERM 信号,应用开始优雅关闭
command: ["/bin/sh", "-c", "sleep 15"]
# ...其他配置...

优雅停机的完整流程如下:

  1. K8s 向 Pod 发送 SIGTERM 信号
  2. preStop Hook 执行 sleep 15(或原生 sleep action),等待 15 秒(让 Ingress/Service 感知到 Pod 不可用)
  3. 应用收到 SIGTERM 后,停止接收新请求,处理完正在进行的请求
  4. 应用关闭数据库连接池、释放资源
  5. 如果超过 terminationGracePeriodSeconds(60s)仍未退出,K8s 发送 SIGKILL 强制终止
为什么 preStop 需要 sleep?

K8s 在执行 preStop Hook 的同时,会从 Service Endpoints 中移除该 Pod。但 Ingress Controller 和上游代理可能还有缓存,需要一定时间才能感知到变化。sleep 15 确保在应用真正开始关闭之前,所有上游组件都已经停止向该 Pod 转发流量。


五、Ingress 七层路由配置

5.1 Ingress Controller 部署

我们使用 APISIX Ingress Controller 作为七层路由组件。

API 版本说明:Ingress 和 IngressClass 使用 networking.k8s.io/v1,这是 Kubernetes v1.34 中的稳定版本。networking.k8s.io/v1beta1 已在 v1.22 中移除,请确保使用 v1。详见官方文档:Ingress v1

k8s/05-ingress/ingress-controller.yaml
---
# RBAC 配置
# apiVersion: rbac.authorization.k8s.io/v1 是 Kubernetes v1.34 中的稳定版本
# 详见官方文档:https://kubernetes.io/docs/reference/kubernetes-api/authorization-resources/role-v1/
apiVersion: v1
kind: ServiceAccount
metadata:
name: apisix-ingress-controller
namespace: gateway
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: apisix-ingress-controller
rules:
- apiGroups: [""]
resources: ["secrets", "services", "endpoints"]
verbs: ["get", "list", "watch"]
- apiGroups: ["networking.k8s.io"]
resources: ["ingresses", "ingressclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: ["apisix.apache.org"]
resources: ["apisixroutes", "apisixupstreams", "apisixtlsconfigs", "apisixclusters", "apisixpluginconfigs"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: apisix-ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: apisix-ingress-controller
subjects:
- kind: ServiceAccount
name: apisix-ingress-controller
namespace: gateway
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: apisix-ingress-controller
namespace: gateway
labels:
app: apisix-ingress-controller
spec:
replicas: 2
selector:
matchLabels:
app: apisix-ingress-controller
template:
metadata:
labels:
app: apisix-ingress-controller
spec:
serviceAccountName: apisix-ingress-controller
containers:
- name: apisix-ingress-controller
image: apache/apisix-ingress-controller:1.8.0
args:
- --ingress-class
- apisix
- --apisix-admin-api-version
- v3
- --log-level
- info
- --http-port
- "8080"
env:
- name: APISIX_ADMIN_API_URL
value: "http://apisix-admin.gateway.svc.cluster.local:9180/apisix/admin"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /readyz
port: 8080
initialDelaySeconds: 5
periodSeconds: 5

5.2 Ingress 路由规则

k8s/05-ingress/ingress-routes.yaml
---
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: apisix
spec:
controller: apache.org/apisix-ingress-controller
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ecommerce-ingress
namespace: gateway
annotations:
kubernetes.io/ingress.class: apisix
# 启用 CORS
apisix.apache.org/enable-cors: "true"
apisix.apache.org/cors-allow-origin: "https://shop.example.com"
apisix.apache.org/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS"
apisix.apache.org/cors-allow-headers: "Authorization,Content-Type"
# 全局限流
apisix.apache.org/plugin-limit-count: |
{
"count": 1000,
"time_window": 1,
"rejected_code": 429,
"key": "remote_addr"
}
spec:
ingressClassName: apisix
tls:
- hosts:
- api.example.com
secretName: tls-secret
rules:
- host: api.example.com
http:
paths:
# 用户服务路由
- path: /api/v1/users
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 80
# 商品服务路由
- path: /api/v1/products
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 80
# 订单服务路由
- path: /api/v1/orders
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 80
# 支付服务路由
- path: /api/v1/payments
pathType: Prefix
backend:
service:
name: api-gateway
port:
number: 80
路由设计说明

在上述配置中,所有业务路由都指向 API Gateway,由 Gateway 负责将请求转发到具体的后端服务。这种设计的好处是:

  • Gateway 统一处理认证、限流、熔断等横切关注点
  • 后端服务不需要暴露到 Ingress 层
  • 路由规则变更只需修改 Gateway 配置,无需修改 Ingress

六、弹性伸缩与高可用

6.1 HPA 弹性伸缩

Horizontal Pod Autoscaler 根据监控指标自动调整 Pod 副本数,是应对流量波动的核心机制。

API 版本说明:HPA 使用 autoscaling/v2,这是 Kubernetes v1.34 中的稳定版本,支持资源指标(CPU/内存)、Pods 指标和外部指标,以及 behavior 字段精细控制扩缩容行为。详见官方文档:HorizontalPodAutoscaler v2

k8s/06-scalability/hpa.yaml
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: user-service-hpa
namespace: microservices
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: user-service
minReplicas: 3
maxReplicas: 20
metrics:
# CPU 利用率目标
- 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: Pods
value: 4 # 每次最多扩容 4 个 Pod
periodSeconds: 60
- type: Percent
value: 100 # 或每次扩容当前副本数的 100%
periodSeconds: 60
selectPolicy: Max # 取两个策略中更激进的
scaleDown:
stabilizationWindowSeconds: 300 # 缩容稳定窗口 5 分钟
policies:
- type: Pods
value: 2 # 每次最多缩容 2 个 Pod
periodSeconds: 120
selectPolicy: Min # 取两个策略中更保守的
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
namespace: microservices
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3
maxReplicas: 30
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 65
# 自定义指标:HTTP 请求延迟 P99
- type: Pods
pods:
metric:
name: http_request_duration_seconds_p99
target:
type: AverageValue
averageValue: "500m" # 500ms
behavior:
scaleUp:
stabilizationWindowSeconds: 30
policies:
- type: Pods
value: 6
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Pods
value: 2
periodSeconds: 120
生产环境注意事项
  1. 扩容要快,缩容要慢scaleUp.stabilizationWindowSeconds 应设置较小值(30-60s),scaleDown.stabilizationWindowSeconds 应设置较大值(300-600s),避免因流量短暂下降导致频繁缩容。
  2. 设置合理的 minReplicas:最小副本数不应低于 2,且应通过 Pod 反亲和性分布在不同节点上,确保单节点故障不影响服务可用性。
  3. 自定义指标需要安装 Metrics Server 和 Prometheus Adapter,否则 HPA 无法获取自定义指标。

6.2 Pod 反亲和性与 PDB

API 版本说明:PodDisruptionBudget 使用 policy/v1,这是 Kubernetes v1.34 中的稳定版本。PDB 新增了 unhealthyPodEvictionPolicy 字段,支持 IfHealthyBudgetAlwaysAllow 两种策略,更灵活地控制不健康 Pod 的驱逐行为。详见官方文档:PodDisruptionBudget v1

k8s/06-scalability/pdb.yaml
---
# 确保用户服务至少有 2 个可用 Pod
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: user-service-pdb
namespace: microservices
spec:
minAvailable: 2
selector:
matchLabels:
app: user-service
---
# 确保订单服务至少有 50% 的 Pod 可用
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: order-service-pdb
namespace: microservices
spec:
maxUnavailable: "50%"
selector:
matchLabels:
app: order-service
---
# 确保支付服务至少有 1 个可用 Pod
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: payment-service-pdb
namespace: microservices
spec:
minAvailable: 1
selector:
matchLabels:
app: payment-service
PDB 与节点维护

当需要对集群节点进行维护(如升级 K8s 版本、更换硬件)时,PDB 确保驱逐操作不会导致服务可用副本数低于阈值。如果没有 PDB,kubectl drain 可能会一次性驱逐所有 Pod,导致服务中断。


七、监控与日志集成

7.1 Prometheus ServiceMonitor

API 版本说明:ServiceMonitor 使用 monitoring.coreos.com/v1,这是 Prometheus Operator 提供的 CRD,用于定义 Service 的监控目标。详见官方文档:ServiceMonitor CRD

k8s/07-monitoring/servicemonitor.yaml
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: microservices-monitor
namespace: microservices
labels:
release: prometheus # 匹配 Prometheus Operator 的 serviceMonitorSelector
spec:
namespaceSelector:
matchNames:
- microservices
- gateway
selector:
matchLabels:
app.kubernetes.io/part-of: ecommerce
endpoints:
- port: http
path: /metrics
interval: 15s
scrapeTimeout: 10s
honorLabels: true
relabelings:
- sourceLabels: [__meta_kubernetes_pod_name]
targetLabel: pod
- sourceLabels: [__meta_kubernetes_namespace]
targetLabel: namespace
---
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: data-services-monitor
namespace: data
labels:
release: prometheus
spec:
namespaceSelector:
matchNames:
- data
selector:
matchLabels:
app.kubernetes.io/part-of: ecommerce
endpoints:
- port: metrics
path: /metrics
interval: 30s
scrapeTimeout: 10s

7.2 Grafana Dashboard 配置

k8s/07-monitoring/grafana-dashboard.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ecommerce-grafana-dashboards
namespace: monitoring
labels:
grafana_dashboard: "1"
data:
microservices-overview.json: |
{
"dashboard": {
"title": "电商微服务总览",
"panels": [
{
"title": "请求 QPS",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(http_requests_total{namespace=\"microservices\"}[5m])) by (app)"
}
]
},
{
"title": "P99 延迟",
"type": "timeseries",
"targets": [
{
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{namespace=\"microservices\"}[5m])) by (le, app))"
}
]
},
{
"title": "错误率",
"type": "timeseries",
"targets": [
{
"expr": "sum(rate(http_requests_total{namespace=\"microservices\",status=~\"5..\"}[5m])) by (app) / sum(rate(http_requests_total{namespace=\"microservices\"}[5m])) by (app) * 100"
}
]
},
{
"title": "Pod 副本数",
"type": "stat",
"targets": [
{
"expr": "sum(kube_deployment_status_replicas_available{namespace=\"microservices\"}) by (deployment)"
}
]
}
]
}
}

7.3 告警规则

k8s/07-monitoring/alerting-rules.yaml
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: ecommerce-alerts
namespace: microservices
labels:
release: prometheus
spec:
groups:
- name: microservices.alerts
rules:
# 服务不可用告警
- alert: ServiceDown
expr: up{namespace="microservices"} == 0
for: 2m
labels:
severity: critical
annotations:
summary: "服务 {{ $labels.app }} 不可用"
description: "{{ $labels.namespace }} 命名空间中的 {{ $labels.instance }} 已下线超过 2 分钟"

# 高错误率告警
- alert: HighErrorRate
expr: |
sum(rate(http_requests_total{namespace="microservices",status=~"5.."}[5m])) by (app)
/ sum(rate(http_requests_total{namespace="microservices"}[5m])) by (app) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "服务 {{ $labels.app }} 错误率过高"
description: "{{ $labels.app }} 的 5xx 错误率已超过 5%,当前值:{{ $value | humanizePercentage }}"

# P99 延迟告警
- alert: HighLatencyP99
expr: |
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{namespace="microservices"}[5m])) by (le, app)) > 1
for: 5m
labels:
severity: warning
annotations:
summary: "服务 {{ $labels.app }} P99 延迟过高"
description: "{{ $labels.app }} 的 P99 延迟已超过 1 秒,当前值:{{ $value }}s"

# Pod 重启告警
- alert: PodRestarting
expr: increase(kube_pod_container_status_restarts_total{namespace="microservices"}[1h]) > 3
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} 频繁重启"
description: "{{ $labels.namespace }} 中的 {{ $labels.pod }} 在过去 1 小时内重启了 {{ $value }} 次"

# HPA 达到上限告警
- alert: HPAAtMaxReplicas
expr: kube_hpa_status_current_replicas == kube_hpa_status_max_replicas
for: 15m
labels:
severity: warning
annotations:
summary: "HPA {{ $labels.hpa }} 已达到最大副本数"
description: "{{ $labels.namespace }} 中的 {{ $labels.hpa }} 已达到最大副本数 {{ $value }},可能需要调整上限"

7.4 Fluent Bit 日志收集

说明:Fluent Bit 以 DaemonSet 方式部署,确保每个节点运行一个日志采集代理。RBAC 配置使用 rbac.authorization.k8s.io/v1,DaemonSet 使用 apps/v1。详见官方文档:Fluent Bit Kubernetes Filter

k8s/07-monitoring/fluent-bit.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: fluent-bit
namespace: monitoring
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: fluent-bit
rules:
- apiGroups: [""]
resources: ["pods", "namespaces"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: fluent-bit
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: fluent-bit
subjects:
- kind: ServiceAccount
name: fluent-bit
namespace: monitoring
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluent-bit
namespace: monitoring
labels:
app: fluent-bit
spec:
selector:
matchLabels:
app: fluent-bit
template:
metadata:
labels:
app: fluent-bit
spec:
serviceAccountName: fluent-bit
tolerations:
- key: node-role.kubernetes.io/control-plane
effect: NoSchedule
- key: node-role.kubernetes.io/master
effect: NoSchedule
containers:
- name: fluent-bit
image: fluent/fluent-bit:3.0.0
volumeMounts:
- name: varlog
mountPath: /var/log
readOnly: true
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
- name: config
mountPath: /fluent-bit/etc/
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
volumes:
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
- name: config
configMap:
name: fluent-bit-config
---
apiVersion: v1
kind: ConfigMap
metadata:
name: fluent-bit-config
namespace: monitoring
data:
fluent-bit.conf: |
[SERVICE]
Flush 5
Daemon Off
Log_Level info
Parsers_File parsers.conf

[INPUT]
Name tail
Path /var/log/containers/*.log
Parser docker
Tag kube.*
Refresh_Interval 10
Mem_Buf_Limit 50MB
Skip_Long_Lines On

[FILTER]
Name kubernetes
Match kube.*
Kube_URL https://kubernetes.default.svc:443
Kube_CA_File /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
Kube_Token_File /var/run/secrets/kubernetes.io/serviceaccount/token
Merge_Log On
Merge_Log_Key log_processed
K8S-Parser.On On
K8S-Parser.Exclude On

[OUTPUT]
Name elasticsearch
Match kube.*
Host elasticsearch.monitoring.svc.cluster.local
Port 9200
Index ecommerce-logs
Type _doc
Logstash_Format On
Logstash_Prefix ecommerce
Retry_Limit False

parsers.conf: |
[PARSER]
Name docker
Format json
Time_Key time
Time_Format %Y-%m-%dT%H:%M:%S.%L

八、CI/CD 集成(ArgoCD)

8.1 GitOps 工作流

GitOps 的核心理念是:Git 仓库是唯一的事实来源(Single Source of Truth)。所有环境变更都通过提交代码来触发,ArgoCD 负责将 Git 仓库中的声明式配置同步到 Kubernetes 集群。

8.2 ArgoCD Application 配置

API 版本说明:ArgoCD 使用 argoproj.io/v1alpha1,这是 ArgoCD 的稳定 API 版本。AppProject、Application 和 ApplicationSet 均使用此版本。详见官方文档:ArgoCD CRD Reference

k8s/08-argocd/argocd-app.yaml
---
# ArgoCD 项目定义
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: ecommerce
namespace: argocd
spec:
description: "电商微服务项目"
sourceRepos:
- "https://github.com/your-org/ecommerce-k8s.git"
destinations:
- namespace: microservices
server: https://kubernetes.default.svc
- namespace: data
server: https://kubernetes.default.svc
- namespace: gateway
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ""
kind: Namespace
- group: "networking.k8s.io"
kind: IngressClass
orphanedResources:
warn: true
---
# ArgoCD 应用定义
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: ecommerce-infra
namespace: argocd
labels:
app.kubernetes.io/part-of: ecommerce
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: ecommerce
source:
repoURL: https://github.com/your-org/ecommerce-k8s.git
targetRevision: main
path: k8s
directory:
recurse: true
jsonnet: false
destination:
server: https://kubernetes.default.svc
namespace: microservices
syncPolicy:
automated:
prune: true # 自动删除 Git 中不存在的资源
selfHeal: true # 自动修复手动变更
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
- ServerSideApply=true
retry:
limit: 3
backoff:
duration: 5s
factor: 2
maxDuration: 3m
---
# ArgoCD 应用集(App of Apps 模式)
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: ecommerce-appset
namespace: argocd
spec:
generators:
- git:
repoURL: https://github.com/your-org/ecommerce-k8s.git
revision: main
directories:
- path: k8s/*
template:
metadata:
name: "{{ path.basename }}"
spec:
project: ecommerce
source:
repoURL: https://github.com/your-org/ecommerce-k8s.git
targetRevision: main
path: "{{ path }}"
destination:
server: https://kubernetes.default.svc
namespace: "{{ path.basename }}"
syncPolicy:
automated:
prune: true
selfHeal: true
生产环境注意事项
  1. selfHeal: true 要谨慎使用。开启后,任何手动通过 kubectl 修改的配置都会被 ArgoCD 自动覆盖。建议在开发/测试环境开启,生产环境使用手动同步。
  2. prune: true 会删除 Git 中不存在的资源。误删 Git 中的文件可能导致生产环境资源被意外删除。建议配合 syncOptions: PruneLast=true,让 ArgoCD 最后再执行删除操作。
  3. 使用 App of Apps 模式管理多应用。通过 ApplicationSet 可以自动发现 Git 仓库中的目录结构,为每个子目录创建一个 Application,避免手动维护大量 Application 资源。

九、请求完整链路

下面展示一个用户下单请求的完整链路,帮助你理解各组件之间的协作关系。


十、部署与验证

10.1 分步部署

按照依赖关系,从底层到上层依次部署:

# 1. 创建命名空间
kubectl apply -f k8s/00-namespace/

# 2. 部署配置与密钥
kubectl apply -f k8s/01-config/

# 3. 创建存储资源
kubectl apply -f k8s/02-storage/

# 4. 部署数据层(等待 PostgreSQL 和 Redis 就绪)
kubectl apply -f k8s/03-services/postgresql-statefulset.yaml
kubectl apply -f k8s/03-services/redis-statefulset.yaml

# 等待数据层就绪
kubectl wait --for=condition=ready pod \
-l app=postgresql -n data --timeout=120s
kubectl wait --for=condition=ready pod \
-l app=redis -n data --timeout=120s

# 5. 部署业务服务
kubectl apply -f k8s/03-services/user-service.yaml
kubectl apply -f k8s/03-services/product-service.yaml
kubectl apply -f k8s/03-services/order-service.yaml
kubectl apply -f k8s/03-services/payment-service.yaml

# 6. 部署网关
kubectl apply -f k8s/03-services/gateway-deployment.yaml

# 7. 部署 Ingress
kubectl apply -f k8s/05-ingress/

# 8. 部署弹性伸缩与高可用
kubectl apply -f k8s/06-scalability/

# 9. 部署监控
kubectl apply -f k8s/07-monitoring/

10.2 健康检查验证

# 检查所有命名空间下的 Pod 状态
kubectl get pods --all-namespaces -l app.kubernetes.io/part-of=ecommerce

# 检查各服务 Endpoints
kubectl get endpoints -n microservices
kubectl get endpoints -n data
kubectl get endpoints -n gateway

# 检查 Ingress 状态
kubectl get ingress -n gateway
kubectl describe ingress ecommerce-ingress -n gateway

# 检查 HPA 状态
kubectl get hpa -n microservices
kubectl describe hpa user-service-hpa -n microservices

# 检查 PDB 状态
kubectl get pdb -n microservices

# 检查 PVC 绑定状态
kubectl get pvc -n data

# 检查 ArgoCD 应用状态
kubectl get applications -n argocd

10.3 压力测试

使用 hey 工具对用户服务进行压力测试:

# 进入集群内部执行测试(或通过 port-forward)
kubectl run hey-test --image=williamyeh/hey:latest --rm -it --restart=Never -- \
-n 100 -c 20 -m POST \
-H "Content-Type: application/json" \
-d '{"username":"testuser","password":"testpass"}' \
http://user-service.microservices.svc.cluster.local:8080/api/v1/users/login

# 模拟并发下单请求
kubectl run hey-test --image=williamyeh/hey:latest --rm -it --restart=Never -- \
-n 500 -c 50 -m POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"product_id":1,"quantity":2}' \
http://order-service.microservices.svc.cluster.local:8082/api/v1/orders

# 观察 HPA 是否触发扩容
kubectl get hpa -n microservices -w

10.4 故障注入测试

# 测试 1:删除 Pod,验证自动恢复
kubectl delete pod -l app=user-service -n microservices --grace-period=0 --force
# 观察 Pod 是否自动重建
kubectl get pods -l app=user-service -n microservices -w

# 测试 2:验证优雅停机(不应出现 5xx 错误)
# 在另一个终端持续发送请求
kubectl run curl-test --image=curlimages/curl:latest --rm -it --restart=Never -- \
-s -o /dev/null -w "%{http_code}\n" \
http://user-service.microservices.svc.cluster.local:8080/healthz

# 测试 3:模拟节点故障(需要多节点集群)
kubectl cordon <node-name> # 标记节点不可调度
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# 观察 Pod 是否迁移到其他节点
kubectl get pods -l app=user-service -n microservices -o wide -w

# 测试 4:验证 PDB 保护
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# 如果违反 PDB,drain 会被阻止,并提示:
# "error: poddisruptionbudgets policy violation"

十一、生产环境最佳实践总结

经过上述完整的实战演练,以下是生产环境中的关键最佳实践:

架构设计

实践说明
命名空间隔离按团队/环境/业务域划分命名空间,配合 ResourceQuota 限制资源
配置外部化使用 ConfigMap/Secret 管理配置,禁止将配置硬编码在镜像中
密钥加密使用 Sealed Secrets 或 External Secrets Operator 管理敏感信息
服务网格可选对于服务间通信复杂度高的场景,考虑引入 Istio/Linkerd

部署策略

实践说明
滚动更新maxSurge: 1, maxUnavailable: 0 确保更新过程中不中断服务
健康检查三件套Startup + Liveness + Readiness Probe,使用不同端点
优雅停机preStop sleep + terminationGracePeriodSeconds 确保请求处理完成
Pod 反亲和性确保同一服务的 Pod 分布在不同节点上

弹性伸缩

实践说明
HPA 多指标同时关注 CPU、内存和自定义业务指标(如 QPS、延迟)
扩快缩慢扩容窗口短(30-60s),缩容窗口长(300-600s)
PDB 保护为关键服务配置 PDB,防止维护操作导致服务不可用
Cluster Autoscaler配合节点自动伸缩,当 Pod 因资源不足处于 Pending 状态时自动扩容节点

可观测性

实践说明
三大支柱Metrics(Prometheus)+ Logs(Fluent Bit)+ Traces(Jaeger/OpenTelemetry)
告警分级Critical(立即响应)+ Warning(工作时间内处理)+ Info(记录备案)
Dashboard 分层全局总览 -> 服务维度 -> Pod 维度,逐层下钻
SLO/SLI定义明确的服务质量目标(如 99.9% 可用性、P99 < 500ms)

持续交付

实践说明
GitOpsGit 仓库作为唯一事实来源,所有变更通过 PR 审批
环境隔离dev -> staging -> production 逐级发布,每级有独立的 ArgoCD Application
镜像标签策略使用 Git Commit SHA 作为镜像 Tag,确保可追溯性
回滚机制ArgoCD 支持一键回滚到 Git 历史中的任意版本
总结

生产级 Kubernetes 部署不仅仅是"把 YAML 写对",更是一套涵盖架构设计、部署策略、弹性伸缩、可观测性和持续交付的完整体系。本文通过电商微服务的实战案例,展示了如何将这些最佳实践落地到具体的 YAML 配置中。

希望这篇实战指南能够帮助你在实际项目中少走弯路,构建出真正可靠、可扩展的云原生微服务系统。

Kubernetes 全景解析 (2):网络模型与服务发现全链路解析

· 阅读需 29 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

前言

在 Kubernetes 的众多核心概念中,网络模型与服务发现机制是最为复杂也最为关键的部分之一。一个健康的 K8s 集群,其网络层必须满足 Pod 之间、Pod 与 Node 之间、以及 Pod 与外部世界的无缝通信。同时,随着微服务架构的普及,服务发现、负载均衡、流量路由等能力变得不可或缺。

本文将深入剖析 K8s 网络模型的三层架构、Service 的四种类型与流量路径、Ingress 控制器原理,以及 NetworkPolicy 安全策略,帮助你全面掌握 K8s 网络的核心机制。

一、K8s 网络模型基础

1.1 K8s 网络的三个基本要求

Kubernetes 对集群网络提出了三个基本要求,这些要求构成了 K8s 网络模型的核心:

  1. 所有 Pod 可以在没有 NAT 的情况下相互通信

    • 每个 Pod 都拥有独立的 IP 地址
    • Pod 之间可以直接通信,无需网络地址转换
    • 无论 Pod 位于哪个 Node 上
  2. 所有 Node 可以与所有 Pod 通信

    • Node 可以直接访问 Pod 的 IP
    • 无需额外的端口映射或代理
  3. Pod 看到的自己 IP 与其他 Pod 看到的 IP 相同

    • 不存在 IP 欺骗或地址转换
    • 网络行为可预测
设计理念

K8s 采用"扁平网络"模型,避免了传统容器网络中的复杂 NAT 配置,使得网络行为更加透明和可预测。这种设计大大简化了应用的开发和调试。

1.2 CNI(Container Network Interface)插件机制

Kubernetes 本身不实现具体的网络功能,而是通过 CNI(Container Network Interface)标准将网络实现委托给第三方插件。CNI 定义了一套接口规范,网络插件只需实现这些接口即可与 K8s 集成。

CNI 插件的主要职责包括:

  • IP 地址分配:为 Pod 分配 IP 地址
  • 网络配置:设置 Pod 的网络命名空间、路由表、防火墙规则
  • 网络连接:创建 veth pair、网桥、路由等网络设备

1.3 常见 CNI 插件对比

插件名称网络模式性能功能特性适用场景
FlannelVXLAN/Host-GW中等简单易用,支持多种后端中小规模集群,快速部署
CalicoBGP/纯路由强大的网络策略,支持 IPAM需要细粒度网络控制的企业级应用
CiliumeBPF极高基于 eBPF 的可观测性和安全高性能、高安全要求的场景
Weave NetVXLAN/加密中等内置加密,简单部署对安全性要求较高的场景
选择建议
  • Flannel:适合入门学习和中小规模集群
  • Calico:适合需要网络策略和企业级功能的场景
  • Cilium:适合追求极致性能和可观测性的场景
  • Weave Net:适合对数据加密有强需求的场景

1.4 K8s 网络三层模型

上图展示了 K8s 网络的三层架构:

  • 第 1 层:节点网络 - 物理机或虚拟机的网络,用于节点间通信
  • 第 2 层:Pod 网络 - 覆盖网络(Overlay)或纯路由网络,用于 Pod 间通信
  • 第 3 层:服务网络 - 虚拟 IP 网络,用于服务发现和负载均衡

二、Service:服务发现与负载均衡

2.1 Service 的四种类型

Service 是 K8s 中用于服务发现和负载均衡的核心抽象。它为一组功能相同的 Pod 提供稳定的访问入口,并实现流量分发。

2.1.1 ClusterIP(默认类型)

ClusterIP 是 Service 的默认类型,它为 Service 分配一个集群内部的虚拟 IP(VIP),仅在集群内部可访问。

apiVersion: v1
kind: Service
metadata:
name: nginx-clusterip
namespace: default
spec:
type: ClusterIP
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP

访问方式

  • 集群内部:http://nginx-clusterip.default.svc.cluster.local:80
  • 集群内部(短名):http://nginx-clusterip:80
最佳实践

对于不需要外部访问的内部服务(如数据库、缓存、内部 API),始终使用 ClusterIP 类型以确保安全性。

2.1.2 NodePort

NodePort 在每个 Node 上开放一个端口(默认范围 30000-32767),外部流量可以通过 NodeIP:NodePort 访问 Service。

apiVersion: v1
kind: Service
metadata:
name: nginx-nodeport
namespace: default
spec:
type: NodePort
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
nodePort: 30080
protocol: TCP

访问方式

  • 外部访问:http://<任意NodeIP>:30080
  • 集群内部:http://nginx-nodeport.default.svc.cluster.local:80
注意事项

NodePort 会占用 Node 的端口,且端口范围有限(默认 30000-32767)。生产环境不建议直接使用 NodePort 暴露服务,应结合 Ingress 或 LoadBalancer 使用。

2.1.3 LoadBalancer

LoadBalancer 类型会向云服务商申请一个负载均衡器,并将外部流量分发到后端的 Node。

apiVersion: v1
kind: Service
metadata:
name: nginx-loadbalancer
namespace: default
spec:
type: LoadBalancer
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
protocol: TCP
# 云服务商特定配置
loadBalancerIP: "203.0.113.10"
externalTrafficPolicy: Local

访问方式

  • 外部访问:http://<LoadBalancerIP>:80
  • 集群内部:http://nginx-loadbalancer.default.svc.cluster.local:80
云服务商依赖

LoadBalancer 类型需要云服务商的支持(如 AWS ELB、GCP Load Balancer、Azure Load Balancer)。在本地开发环境(如 Minikube、Kind)中,通常无法正常工作。

2.1.4 ExternalName

ExternalName 类型将 Service 映射到外部 DNS 名称,不创建 ClusterIP,主要用于访问外部服务。

apiVersion: v1
kind: Service
metadata:
name: external-database
namespace: default
spec:
type: ExternalName
externalName: database.example.com

访问方式

  • 集群内部:http://external-database.default.svc.cluster.local
  • 实际会被 DNS 解析为:database.example.com

2.6 Service 应用协议(appProtocol)

Service 的 appProtocol 字段(v1.20 Stable)允许为每个 Service 端口指定应用层协议,为实现提供更丰富的行为提示。

支持的协议类型

协议描述
IANA 标准服务名httphttpsftpsmtp 等(参考 IANA Service Names
kubernetes.io/h2cHTTP/2 over cleartext(明文 HTTP/2,参考 RFC 7540)
kubernetes.io/wsWebSocket over cleartext(明文 WebSocket,参考 RFC 6455)
kubernetes.io/wssWebSocket over TLS(加密 WebSocket,参考 RFC 6455)
自定义前缀协议mycompany.com/my-custom-protocol

使用示例

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 9376
protocol: TCP
appProtocol: http # 指定应用协议
- name: grpc
port: 9090
targetPort: 9090
protocol: TCP
appProtocol: kubernetes.io/h2c # HTTP/2 cleartext
appProtocol 的作用

appProtocol 字段的值会自动同步到对应的 Endpoints 和 EndpointSlice 对象中,供 Ingress Controller、Service Mesh 等组件使用,以提供协议感知的路由、健康检查等增强功能。

2.7 Service 名称验证规则(v1.34 Alpha)

FEATURE STATE: Kubernetes v1.34 [alpha](默认关闭)

Kubernetes v1.34 引入了 RelaxedServiceNameValidation 特性门控,允许 Service 对象名称以数字开头。

默认规则(特性门控关闭):

  • Service 名称必须是有效的 RFC 1035 标签名称
  • 必须以字母开头,只能包含字母、数字和连字符(-)

放宽规则(特性门控开启):

  • Service 名称可以是有效的 RFC 1123 标签名称
  • 允许以数字开头
启用 RelaxedServiceNameValidation

要启用此特性,需要在 API Server 和 Controller Manager 的启动参数中添加:

--feature-gates=RelaxedServiceNameValidation=true

2.2 kube-proxy 工作模式

kube-proxy 是 K8s 网络的核心组件,负责维护 Service 的网络规则。它支持三种工作模式:

2.2.1 iptables 模式

iptables 模式使用 iptables 规则实现负载均衡,每个 Service 会创建一系列 iptables 规则。

优点

  • 实现简单,兼容性好
  • 无需额外依赖

缺点

  • 规则数量随 Service 数量线性增长,性能下降明显
  • 不支持高级负载均衡算法(仅支持随机轮询)
  • 调试困难
  • iptables 正逐步被 Linux 内核的 nftables 框架取代

2.2.2 IPVS 模式

IPVS(IP Virtual Server)是基于 Linux 内核的负载均衡技术,性能远高于 iptables。

优点

  • 性能优异,支持大规模 Service
  • 支持多种负载均衡算法(rr、lc、dh、sh、sed、nq)
  • 连接跟踪更精确

缺点

  • 需要加载 IPVS 内核模块
  • 配置相对复杂

2.2.3 nftables 模式(v1.33 GA)

nftables 是 Linux 内核新一代的防火墙和数据包过滤框架,旨在取代传统的 iptables。kube-proxy 的 nftables 模式自 v1.29 引入(Alpha),v1.33 达到 GA(正式可用)。

优点

  • 替代逐步淘汰的 iptables 框架,面向未来
  • 规则处理性能优于 iptables
  • 统一了 IPv4/IPv6/ARP/桥接的过滤框架
  • 内核 5.13+ 及 nft 命令行工具 1.0.1+ 支持

缺点

  • 需要较新的内核版本(>= 5.13)
  • 需要 nft 命令行工具(>= 1.0.1)
  • 生态成熟度仍在提升中
性能优化建议

对于生产环境,建议根据内核版本选择 kube-proxy 模式:

  • 内核 >= 5.13:优先使用 nftables 模式(面向未来,性能更优)
  • 内核 < 5.13:使用 IPVS 模式以获得更好的性能

可以通过修改 kube-proxy 的配置来启用:

apiVersion: kubeproxy.config.k8s.io/v1alpha1
kind: KubeProxyConfiguration
mode: "nftables" # 可选值: "iptables" | "ipvs" | "nftables"

2.3 DNS 服务发现(CoreDNS)

CoreDNS 是 K8s 集群的默认 DNS 服务器,它为 Service 和 Pod 提供 DNS 解析服务。

DNS 记录格式

资源类型DNS 记录格式示例
Service(普通)<service-name>.<namespace>.svc.cluster.localnginx.default.svc.cluster.local
Service(Headless)<pod-ip>.<service-name>.<namespace>.svc.cluster.local10.244.1.2.nginx.default.svc.cluster.local
Pod<pod-ip>.<namespace>.pod.cluster.local10.244.1.2.default.pod.cluster.local

CoreDNS 配置示例

apiVersion: v1
kind: ConfigMap
metadata:
name: coredns
namespace: kube-system
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}

2.4 Headless Service 与 StatefulSet 的配合

Headless Service 是一种特殊的 Service,它不分配 ClusterIP,而是直接将 DNS 解析到后端的 Pod IP。这种设计常与 StatefulSet 配合使用,用于有状态应用。

apiVersion: v1
kind: Service
metadata:
name: nginx-headless
namespace: default
spec:
clusterIP: None # Headless Service 的关键配置
selector:
app: nginx
ports:
- name: http
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx-stateful
namespace: default
spec:
serviceName: nginx-headless # 关联 Headless Service
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.21
ports:
- containerPort: 80

DNS 解析结果

# 解析 Headless Service
$ dig nginx-headless.default.svc.cluster.local

;; ANSWER SECTION:
nginx-headless.default.svc.cluster.local. 5 IN A 10.244.1.2
nginx-headless.default.svc.cluster.local. 5 IN A 10.244.2.2
nginx-headless.default.svc.cluster.local. 5 IN A 10.244.3.2

# 解析单个 Pod
$ dig nginx-stateful-0.nginx-headless.default.svc.cluster.local

;; ANSWER SECTION:
nginx-stateful-0.nginx-headless.default.svc.cluster.local. 5 IN A 10.244.1.2
应用场景

Headless Service 适用于以下场景:

  • StatefulSet 应用(如数据库、消息队列)
  • 需要直接访问 Pod IP 的应用
  • 自定义负载均衡策略
  • 服务网格(Service Mesh)

2.8 Service 四种类型流量路径对比

上图展示了四种 Service 类型的流量路径:

  1. NodePort:外部流量通过 NodeIP:NodePort 进入,分发到各个 Node
  2. LoadBalancer:外部流量通过云负载均衡器进入,分发到各个 Node
  3. ClusterIP:集群内部流量通过虚拟 IP 分发到后端 Pod
  4. ExternalName:DNS 解析到外部服务名称

三、Ingress:七层路由入口

3.1 Ingress vs Service 的区别

特性ServiceIngress
网络层四层(L4 - TCP/UDP)七层(L7 - HTTP/HTTPS)
路由能力基于 IP 和端口基于 Host、Path、Header
协议支持TCP/UDPHTTP/HTTPS/gRPC
TLS 终止不支持支持
使用场景服务间通信、内部服务外部访问、多域名路由
核心区别

Service 是四层负载均衡,只能基于 IP 和端口进行路由;Ingress 是七层负载均衡,可以基于 HTTP Host、Path、Header 等进行智能路由。Ingress 通常作为多个 Service 的统一入口。

3.2 Ingress Controller

Ingress Controller 是 Ingress 资源的执行器,负责根据 Ingress 规则配置负载均衡器。常见的 Ingress Controller 包括:

Controller特点适用场景
Nginx Ingress成熟稳定,功能丰富通用场景,生产环境首选
Traefik自动配置,支持多种后端微服务架构,动态环境
APISIX高性能,支持云原生高流量、高并发场景
Istio Gateway服务网格集成已使用 Istio 的场景

Nginx Ingress Controller 部署示例

apiVersion: v1
kind: Namespace
metadata:
name: ingress-nginx
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ingress-nginx-controller
namespace: ingress-nginx
spec:
replicas: 2
selector:
matchLabels:
app: ingress-nginx
template:
metadata:
labels:
app: ingress-nginx
spec:
containers:
- name: controller
image: k8s.gcr.io/ingress-nginx/controller:v1.8.1
args:
- /nginx-ingress-controller
- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
- --publish-service=$(POD_NAMESPACE)/ingress-nginx
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
ports:
- name: http
containerPort: 80
- name: https
containerPort: 443
---
apiVersion: v1
kind: Service
metadata:
name: ingress-nginx
namespace: ingress-nginx
spec:
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
selector:
app: ingress-nginx

3.3 Ingress 资源配置

3.3.1 基于 Host 的路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-host-based
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-service
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-service
port:
number: 80

3.3.2 基于 Path 的路由

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-path-based
namespace: default
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$2
spec:
ingressClassName: nginx
rules:
- host: example.com
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 80
- path: /web(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: web-service
port:
number: 80
- path: /(.*)
pathType: ImplementationSpecific
backend:
service:
name: frontend-service
port:
number: 80

3.3.3 TLS 配置

apiVersion: v1
kind: Secret
metadata:
name: tls-secret
namespace: default
type: kubernetes.io/tls
data:
tls.crt: LS0tLS1CRUdJTi... # Base64 编码的证书
tls.key: LS0tLS1CRUdJTi... # Base64 编码的私钥
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-tls
namespace: default
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
ingressClassName: nginx
tls:
- hosts:
- secure.example.com
secretName: tls-secret
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-service
port:
number: 443
TLS 最佳实践
  1. 使用 Let's Encrypt 自动获取和更新证书(推荐使用 cert-manager)
  2. 强制 HTTPS 重定向(如 nginx.ingress.kubernetes.io/ssl-redirect: "true")
  3. 定期轮换证书
  4. 使用强加密套件

3.4 Ingress 路由分发流程

上图展示了 Ingress 的路由分发流程:

  1. 外部客户端发起 HTTP/HTTPS 请求
  2. Ingress Controller 接收请求,根据 Ingress 资源规则进行匹配
  3. 根据 HostPath 将请求路由到对应的 Service
  4. Service 将请求负载均衡到后端的 Pod

3.5 Gateway API:下一代服务路由标准

Gateway API 是 Kubernetes 官方项目,专注于 L4 和 L7 路由,代表了 Kubernetes Ingress、负载均衡和服务网格 API 的下一代演进方向。相比传统的 Ingress API,Gateway API 提供了更强大、更灵活的路由能力。

Gateway API 的核心优势

特性Ingress APIGateway API
角色导向单一角色支持基础设施提供者、集群操作者、应用开发者三种角色
表达能力依赖注解实现高级功能原生支持流量加权、Header 匹配等高级路由
可扩展性有限支持自定义资源扩展
跨命名空间不支持支持共享 Gateway 和跨命名空间路由
协议支持主要 HTTP/HTTPS支持 HTTP、gRPC、TCP、UDP 等多种协议
服务网格集成不支持通过 GAMMA 倡议支持服务网格

Gateway API 的核心资源

  • GatewayClass:定义 Gateway 的实现类型(如 Envoy、Nginx、Contour)
  • Gateway:定义网络入口点,监听端口、TLS 配置等
  • HTTPRoute/GRPCRoute:定义路由规则,支持流量权重、Header 匹配等
Gateway API vs Ingress

Gateway API 不是 Ingress 的简单替代,而是一个更强大、更灵活的 API 框架:

  • Gateway API for Ingress:管理南北向流量(从外部到集群内部)
  • Gateway API for Service Mesh (GAMMA):管理东西向流量(集群内部服务间通信)

Gateway API 已成为 SIG-Network 的官方项目,多个主流实现(如 Envoy Gateway、Kong Gateway、Contour)都已支持。

Gateway API 示例

# GatewayClass:定义 Gateway 的实现类型
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller

---
# Gateway:定义网络入口点
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: eg
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
hostname: "*.example.com"

---
# HTTPRoute:定义路由规则
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: backend
spec:
parentRefs:
- name: eg
hostnames:
- "api.example.com"
rules:
- backendRefs:
- name: api-service
port: 80
何时使用 Gateway API

Gateway API 适合以下场景:

  • 需要更强大的路由能力(流量加权、Header 匹配、灰度发布)
  • 需要跨命名空间共享 Gateway
  • 需要支持多种协议(HTTP、gRPC、TCP、UDP)
  • 计划使用服务网格(Service Mesh)

对于简单的 HTTP/HTTPS 路由需求,传统的 Ingress API 仍然足够。

四、NetworkPolicy:网络策略与安全隔离

4.1 默认网络策略

Kubernetes 集群默认情况下,所有 Pod 之间都是互通的(全通模式)。这种默认行为在多租户环境中可能带来安全风险。

默认全通模式

# 默认情况下,无需任何 NetworkPolicy,所有 Pod 都可以相互通信

默认全拒模式

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: default
spec:
podSelector: {} # 空选择器匹配所有 Pod
policyTypes:
- Ingress
- Egress
安全建议

在生产环境中,建议采用"默认拒绝,显式允许"的策略。首先创建默认拒绝所有流量的 NetworkPolicy,然后为需要通信的 Pod 创建允许规则。

4.2 ingress/egress 规则

NetworkPolicy 支持 ingress(入站)和 egress(出站)两种规则类型。

4.2.1 仅允许入站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-only
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080

4.2.2 仅允许出站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-only
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432

4.2.3 同时允许入站和出站流量

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-both
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
- Egress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
- to:
- namespaceSelector:
matchLabels:
name: kube-system
ports:
- protocol: UDP
port: 53

4.3 标签选择器与策略匹配

NetworkPolicy 使用标签选择器来匹配 Pod 和 Namespace。

选择器类型用途示例
podSelector匹配同一 Namespace 中的 Podapp: frontend
namespaceSelector匹配 Namespacename: production
podSelector + namespaceSelector匹配特定 Namespace 中的 PodNamespace: team: backend, Pod: app: api

复杂选择器示例

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: complex-selector
namespace: default
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
# 允许同一 Namespace 中标签为 app: frontend 的 Pod
- podSelector:
matchLabels:
app: frontend
# 允许 production Namespace 中所有 Pod
- namespaceSelector:
matchLabels:
env: production
# 允许 monitoring Namespace 中标签为 app: prometheus 的 Pod
- namespaceSelector:
matchLabels:
team: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 8080

4.4 NetworkPolicy 规则匹配示意图

上图展示了 NetworkPolicy 的规则匹配逻辑:

  1. 外部流量被拒绝(未在 ingress 规则中)
  2. frontend Namespace 中的 Pod 可以访问 backend(匹配 app: frontend
  3. production Namespace 中的 Pod 可以访问 backend(匹配 env: production
  4. backend Pods 可以访问 database(匹配 app: database
  5. backend Pods 可以访问 DNS(匹配 DNS 规则)
  6. backend Pods 无法访问互联网(未在 egress 规则中)
NetworkPolicy 依赖

NetworkPolicy 的实现依赖于 CNI 插件。并非所有 CNI 插件都支持 NetworkPolicy:

  • 支持:Calico、Cilium、Weave Net、Romana
  • 不支持:Flannel(需要配合 Canal 使用)

在选择 CNI 插件时,如果需要网络策略功能,请确保插件支持 NetworkPolicy。

五、EndpointSlice 与服务网格简介

5.1 EndpointSlice 机制

EndpointSlice 是 K8s 1.17+ 引入的新特性(v1.21 达到 Stable),用于替代传统的 Endpoints 资源。它将 Pod 端点信息分片存储,解决了大规模集群中 Endpoints 资源过大的问题。

Endpoints API 已废弃(v1.33)

自 Kubernetes v1.33 起,传统的 Endpoints API 已被正式标记为废弃(Deprecated)。相比 EndpointSlice,旧的 Endpoints API 存在以下问题:

  • 不支持双栈集群:无法同时表示 IPv4 和 IPv6 端点
  • 缺少新特性支持:不包含 trafficDistribution 等新功能所需的信息
  • 端点截断:当端点数量超过 1000 时,会截断端点列表,导致部分后端无法接收流量

官方推荐所有用户迁移到 EndpointSlice API。EndpointSlice 由 EndpointSlice 控制器自动管理,无需手动创建。

EndpointSlice 的优势

特性Endpoints(已废弃)EndpointSlice(推荐)
存储方式单个大对象分片存储(每个切片最多 100 个端点)
扩展性受限于单个对象大小(1000 端点截断)支持大规模集群
双栈支持不支持支持 IPv4/IPv6 双栈
拓扑感知不支持支持 Zone、Node 等拓扑信息
性能频繁更新导致性能下降增量更新,性能更好
新特性兼容不支持 trafficDistribution 等完整支持

EndpointSlice 示例

apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
name: nginx-service-abc123
namespace: default
labels:
kubernetes.io/service-name: nginx-service
addressType: IPv4
ports:
- name: http
protocol: TCP
port: 80
endpoints:
- addresses:
- "10.244.1.2"
nodeName: node-1
zone: zone-a
conditions:
ready: true
- addresses:
- "10.244.1.3"
nodeName: node-1
zone: zone-a
conditions:
ready: true
- addresses:
- "10.244.2.2"
nodeName: node-2
zone: zone-b
conditions:
ready: true
自动管理

EndpointSlice 由 EndpointSlice 控制器自动管理,无需手动创建。当创建 Service 时,控制器会自动创建对应的 EndpointSlice。

5.2 Service Mesh 与 K8s 网络的关系

Service Mesh(服务网格)是建立在 K8s 网络之上的应用层网络抽象,它通过在每个 Pod 中注入 Sidecar 代理来实现流量管理、安全、可观测性等功能。

主流 Service Mesh 对比

特性IstioLinkerdConsul Connect
架构控制平面 + 数据平面控制平面 + 数据平面控制平面 + 数据平面
SidecarEnvoyproxy-wasmEnvoy
性能中等中等
功能丰富中等中等
学习曲线陡峭平缓平缓

Service Mesh 与 K8s 网络的关系

上图展示了 Service Mesh 与 K8s 网络的层次关系:

  1. 应用层:应用容器通过 gRPC/HTTP 协议通信
  2. Sidecar 代理:拦截应用流量,实现流量管理、安全、可观测性
  3. K8s 网络:Service 和 NetworkPolicy 提供四层网络能力
  4. CNI 网络:提供 Pod 间通信的三层网络能力
  5. 节点网络:物理或虚拟机的二层网络
何时使用 Service Mesh

Service Mesh 并非必需,适合以下场景:

  • 微服务数量多(> 50 个服务)
  • 需要细粒度的流量控制(灰度发布、A/B 测试)
  • 需要统一的安全策略(mTLS、授权)
  • 需要深度可观测性(分布式追踪、指标)

对于小规模集群或简单应用,K8s 原生的 Service 和 Ingress 已足够。

六、网络故障排查常用命令

6.1 Pod 网络排查

# 1. 检查 Pod 的 IP 地址
kubectl get pod <pod-name> -o wide

# 2. 检查 Pod 的网络配置
kubectl exec -it <pod-name> -- ip addr
kubectl exec -it <pod-name> -- ip route

# 3. 检查 Pod 的 DNS 解析
kubectl exec -it <pod-name> -- nslookup kubernetes.default
kubectl exec -it <pod-name> -- cat /etc/resolv.conf

# 4. 测试 Pod 间连通性
kubectl exec -it <pod-name-1> -- ping <pod-ip-2>
kubectl exec -it <pod-name-1> -- curl http://<service-name>:<port>

# 5. 检查 Pod 的网络策略
kubectl get networkpolicy -n <namespace>
kubectl describe networkpolicy <policy-name> -n <namespace>

6.2 Service 网络排查

# 1. 检查 Service 的详细信息
kubectl get svc <service-name> -o yaml
kubectl describe svc <service-name>

# 2. 检查 Endpoints/EndpointSlice
kubectl get endpoints <service-name>
kubectl get endpointslice -l kubernetes.io/service-name=<service-name>

# 3. 检查 kube-proxy 日志
kubectl logs -n kube-system -l k8s-app=kube-proxy

# 4. 检查 iptables 规则(在 Node 上执行)
sudo iptables -t nat -L KUBE-SERVICES -n
sudo iptables -t nat -L KUBE-SVC-<hash> -n

# 5. 检查 IPVS 规则(如果使用 IPVS 模式)
sudo ipvsadm -Ln

6.3 Ingress 网络排查

# 1. 检查 Ingress 资源
kubectl get ingress
kubectl describe ingress <ingress-name>

# 2. 检查 Ingress Controller
kubectl get pods -n ingress-nginx
kubectl logs -n ingress-nginx <ingress-controller-pod>

# 3. 检查 Ingress Controller 配置
kubectl exec -n ingress-nginx <ingress-controller-pod> -- cat /etc/nginx/nginx.conf

# 4. 测试 Ingress 路由
curl -v http://<host>/path
curl -v -H "Host: <host>" http://<ingress-ip>/path

# 5. 检查 TLS 证书
kubectl get secret <tls-secret-name> -o yaml
openssl x509 -in <cert-file> -text -noout

6.4 CNI 网络排查

# 1. 检查 CNI 插件 Pod
kubectl get pods -n kube-system | grep -E "calico|cilium|flannel|weave"

# 2. 检查 CNI 插件日志
kubectl logs -n kube-system <cni-plugin-pod>

# 3. 检查节点网络配置(在 Node 上执行)
ip addr
ip route
brctl show # 如果使用网桥模式

# 4. 检查 veth pair(在 Node 上执行)
ip link show type veth

# 5. 测试节点间连通性
ping <other-node-ip>
traceroute <other-node-ip>
排查思路

网络故障排查的一般思路:

  1. 从内到外:先检查 Pod 内部网络,再检查 Service,最后检查 Ingress
  2. 从下到上:先检查 CNI 网络层,再检查 K8s 网络层,最后检查应用层
  3. 分层验证:每层网络都要验证连通性、DNS 解析、端口监听
  4. 日志优先:优先查看相关组件的日志,获取错误信息

七、本章小结

本文深入剖析了 Kubernetes 网络模型与服务发现的核心机制,涵盖了以下关键内容:

核心要点回顾

  1. K8s 网络模型

    • 三个基本要求:Pod 互通、Node 与 Pod 互通、Pod IP 一致性
    • CNI 插件机制:Flannel、Calico、Cilium、Weave Net 各有优劣
    • 三层网络架构:节点网络、Pod 网络、服务网络
  2. Service 服务发现

    • 四种类型:ClusterIP、NodePort、LoadBalancer、ExternalName
    • kube-proxy 工作模式:iptables、IPVS、nftables(v1.33 GA)
    • CoreDNS 服务发现机制
    • Headless Service 与 StatefulSet 的配合使用
    • appProtocol 字段(v1.20 Stable):支持 HTTP/2、WebSocket 等应用协议
    • RelaxedServiceNameValidation(v1.34 Alpha):允许 Service 名称以数字开头
  3. Ingress 七层路由

    • Ingress vs Service 的本质区别
    • 主流 Ingress Controller:Nginx、Traefik、APISIX
    • 基于 Host、Path 的路由配置
    • TLS 证书管理
    • Gateway API:下一代服务路由标准,支持角色导向、流量加权、跨命名空间等高级功能
  4. NetworkPolicy 网络安全

    • 默认全通 vs 默认全拒策略
    • ingress/egress 规则配置
    • 标签选择器的灵活使用
  5. 高级特性

    • EndpointSlice 机制解决大规模集群问题(Endpoints API v1.33 已废弃
    • Gateway API 与 Ingress 的演进关系
    • Service Mesh 与 K8s 网络的关系
  6. 故障排查

    • Pod、Service、Ingress、CNI 各层排查命令
    • 分层验证的排查思路

最佳实践建议

  1. CNI 插件选择

    • 中小集群:Flannel(简单易用)
    • 企业级应用:Calico(功能丰富)
    • 高性能场景:Cilium(eBPF)
  2. Service 类型选择

    • 内部服务:ClusterIP
    • 外部访问:Ingress + LoadBalancer
    • 特殊场景:NodePort(开发测试)、ExternalName(外部服务)
  3. 安全策略

    • 默认拒绝,显式允许
    • 最小权限原则
    • 定期审计 NetworkPolicy
  4. 性能优化

    • 优先使用 nftables 模式(v1.33 GA),其次 IPVS 模式
    • 使用 EndpointSlice(替代已废弃的 Endpoints API)
    • 合理规划 Service 数量

下一步学习

  • 深入学习特定 CNI 插件的配置和调优
  • 掌握 Service Mesh(Istio/Linkerd)的部署和使用
  • 学习网络可观测性工具(如 Cilium Hubble)
  • 实践网络故障排查和性能优化
  • 探索 Gateway API 在生产环境中的应用

Kubernetes 网络是一个庞大而复杂的主题,本文涵盖了其核心概念和最佳实践。在实际应用中,建议根据具体的业务需求和集群规模,选择合适的网络方案,并持续监控和优化网络性能。


相关阅读

作者简介:本文作者专注于云原生技术,拥有丰富的 Kubernetes 实践经验,致力于分享云原生技术最佳实践。