Kubernetes 部署完全指南
一、什么是 Kubernetes?
Kubernetes(简称 K8s)是一个容器编排平台,解决的核心问题是:如何在大规模集群中自动化部署、扩缩容、管理容器化应用。
没有 K8s 时:
手动 SSH 到服务器 → docker run → 服务挂了手动重启 → 扩容手动加机器
有 K8s 后:
提交一份 YAML 声明 → K8s 自动部署 → 自动重启 → 自动扩缩容 → 自动负载均衡
一句话:你告诉 K8s "我要什么",它负责"怎么做到"。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
二、核心概念
2.1 架构总览
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─── Master Node(控制平面)───────────────────────────────┐ │
│ │ API Server │ Scheduler │ Controller Manager │ etcd │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌───────────────┼───────────────┐ │
│ ↓ ↓ ↓ │
│ ┌─── Worker Node 1 ──┐ ┌─── Worker Node 2 ──┐ ┌─── Node 3 ─┐ │
│ │ kubelet │ kube-proxy│ │ kubelet │ kube-proxy│ │ │ │
│ │ ┌─────┐ ┌─────┐ │ │ ┌─────┐ ┌─────┐ │ │ │ │
│ │ │ Pod │ │ Pod │ │ │ │ Pod │ │ Pod │ │ │ │ │
│ │ └─────┘ └─────┘ │ │ └─────┘ └─────┘ │ │ │ │
│ └─────────────────────┘ └────────────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2.2 核心组件
| 组件 | 职责 | 类比 |
|---|---|---|
| API Server | 集群的入口,所有操作都通过它 | 公司前台 |
| Scheduler | 决定 Pod 运行在哪个 Node 上 | HR 分配工位 |
| Controller Manager | 确保集群状态符合预期 | 主管巡检 |
| etcd | 存储集群所有状态数据 | 公司数据库 |
| kubelet | 每个 Node 上的代理,管理 Pod | 每层楼的物业 |
| kube-proxy | 网络代理,实现 Service 负载均衡 | 网络路由器 |
2.3 核心资源对象
| 资源 | 含义 | 类比 |
|---|---|---|
| Pod | 最小部署单元,包含一个或多个容器 | 一个房间(可住1-N个人) |
| Deployment | 管理 Pod 的副本数、滚动更新 | 部署计划书 |
| Service | 为一组 Pod 提供稳定的访问入口 | 公司总机号码 |
| ConfigMap | 非敏感配置(键值对) | 配置文件 |
| Secret | 敏感配置(密码、证书) | 保险箱 |
| Namespace | 逻辑隔离(如 dev/staging/prod) | 不同楼层 |
| Ingress | HTTP 路由入口(域名 → Service) | 大楼门牌 |
| HPA | 水平自动扩缩容 | 根据客流加减收银台 |
| PV/PVC | 持久化存储 | 文件柜 |
2.4 资源层级关系
Deployment(声明式管理)
└── ReplicaSet(保证副本数)
└── Pod(运行容器)
└── Container(实际进程)
└── Docker Image(代码 + 环境)
三、从 Docker 到 K8s
3.1 Dockerfile
dockerfile
# 基础镜像
FROM eclipse-temurin:17-jre-alpine
# 工作目录
WORKDIR /app
# 复制 JAR(从 Maven 构建产出)
COPY target/my-order-service-1.0.0.jar app.jar
# 暴露端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/actuator/health || exit 1
# 启动命令
ENTRYPOINT ["java", \
"-XX:+UseG1GC", \
"-XX:MaxGCPauseMillis=200", \
"-Xms512m", \
"-Xmx512m", \
"-jar", "app.jar"]
3.2 构建并推送镜像
bash
# 构建镜像
docker build -t registry.example.com/team/order-service:1.0.0 .
# 推送到镜像仓库
docker push registry.example.com/team/order-service:1.0.0
四、K8s 核心资源 YAML 详解
4.1 Namespace
yaml
# namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: order-system
labels:
env: production
kubectl apply -f namespace.yaml
kubectl get namespaces
4.2 ConfigMap(非敏感配置)
yaml
# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: order-service-config
namespace: order-system
data:
# 单个值
SERVER_PORT: "8080"
SPRING_PROFILES_ACTIVE: "prod"
LOG_LEVEL: "info"
# 整个配置文件
application-prod.yml: |
spring:
datasource:
url: jdbc:mysql://mysql-service:3306/order_db
hikari:
maximum-pool-size: 50
data:
redis:
host: redis-service
port: 6379
server:
port: 8080
tomcat:
threads:
max: 400
4.3 Secret(敏感配置)
yaml
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
namespace: order-system
type: Opaque
data:
# 值必须 base64 编码
DB_USERNAME: b3JkZXJfdXNlcg==
DB_PASSWORD: UEBzc3cwcmQxMjM=
REDIS_PASSWORD: cmVkaXNQQHNz
# 命令行创建 Secret(避免手动 base64)
kubectl create secret generic order-service-secret \
--namespace=order-system \
--from-literal=DB_USERNAME=order_user \
--from-literal=DB_PASSWORD='P@xxx' \
--from-literal=REDIS_PASSWORD='xxxx@ss'
4.4 Deployment(核心)
yaml
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: order-system
labels:
app: order-service
version: "1.0.0"
spec:
replicas: 3 # 运行 3 个 Pod 副本
# 选择器:管理哪些 Pod
selector:
matchLabels:
app: order-service
# 滚动更新策略
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 更新时最多多创建 1 个 Pod
maxUnavailable: 0 # 更新时不允许有 Pod 不可用
# Pod 模板
template:
metadata:
labels:
app: order-service
version: "1.0.0"
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/actuator/prometheus"
spec:
# 优雅停机
terminationGracePeriodSeconds: 60
# 容器定义
containers:
- name: order-service
image: registry.example.com/team/order-service:1.0.0
imagePullPolicy: Always
# 端口
ports:
- name: http
containerPort: 8080
protocol: TCP
# 环境变量(从 ConfigMap 和 Secret 引用)
env:
- name: SPRING_PROFILES_ACTIVE
valueFrom:
configMapKeyRef:
name: order-service-config
key: SPRING_PROFILES_ACTIVE
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: order-service-secret
key: DB_USERNAME
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: order-service-secret
key: DB_PASSWORD
- name: JAVA_OPTS
value: "-Xms512m -Xmx512m -XX:+UseG1GC"
# 挂载配置文件
volumeMounts:
- name: config-volume
mountPath: /app/config
readOnly: true
# 资源限制
resources:
requests: # 最低保证
cpu: "500m" # 0.5 核
memory: "512Mi" # 512MB
limits: # 最大上限
cpu: "2000m" # 2 核
memory: "1024Mi" # 1GB
# 存活探针:判断容器是否需要重启
livenessProbe:
httpGet:
path: /actuator/health/liveness
port: 8080
initialDelaySeconds: 60 # 启动后等 60s 再检查
periodSeconds: 10 # 每 10s 检查一次
timeoutSeconds: 3 # 超时 3s
failureThreshold: 3 # 连续失败 3 次则重启
# 就绪探针:判断 Pod 是否可以接收流量
readinessProbe:
httpGet:
path: /actuator/health/readiness
port: 8080
initialDelaySeconds: 30 # 启动后等 30s
periodSeconds: 5 # 每 5s 检查
timeoutSeconds: 3
failureThreshold: 3
# 启动探针:慢启动应用专用
startupProbe:
httpGet:
path: /actuator/health
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 30 # 最多等 10 + 5*30 = 160s
# 生命周期钩子
lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"] # 优雅停机等待
# 卷定义
volumes:
- name: config-volume
configMap:
name: order-service-config
# 镜像拉取凭证
imagePullSecrets:
- name: registry-credentials
4.5 Service(网络访问)
yaml
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: order-service
namespace: order-system
labels:
app: order-service
spec:
type: ClusterIP # 集群内部访问(默认)
selector:
app: order-service # 匹配 Pod 的 label
ports:
- name: http
port: 80 # Service 端口
targetPort: 8080 # Pod 端口
protocol: TCP
Service 类型:
| 类型 | 用途 | 访问方式 |
|---|---|---|
ClusterIP |
集群内部通信(默认) | order-service.order-system.svc.cluster.local |
NodePort |
通过节点 IP + 端口暴露 | <NodeIP>:30080 |
LoadBalancer |
云厂商负载均衡器 | 外部 IP |
4.6 Ingress(外部路由)
yaml
# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: order-service-ingress
namespace: order-system
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
spec:
ingressClassName: nginx
tls:
- hosts:
- api.example.com
secretName: tls-secret
rules:
- host: api.example.com
http:
paths:
- path: /api/order
pathType: Prefix
backend:
service:
name: order-service
port:
number: 80
- path: /api/product
pathType: Prefix
backend:
service:
name: product-service
port:
number: 80
4.7 HPA(水平自动扩缩容)
yaml
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: order-service-hpa
namespace: order-system
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: order-service
minReplicas: 3 # 最少 3 个 Pod
maxReplicas: 20 # 最多 20 个 Pod
metrics:
# CPU 使用率超过 70% 则扩容
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# 内存使用率超过 80% 则扩容
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
behavior:
scaleUp:
stabilizationWindowSeconds: 60 # 扩容冷却 60s
policies:
- type: Pods
value: 2 # 每次最多扩 2 个
periodSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300 # 缩容冷却 5 分钟
policies:
- type: Pods
value: 1 # 每次最多缩 1 个
periodSeconds: 120
五、健康检查(Probes)详解
5.1 三种探针
| 探针 | 触发时机 | 失败后果 | 用途 |
|---|---|---|---|
| startupProbe | 容器启动阶段 | 继续等待(不杀容器) | 慢启动应用(Java 启动需 30-60s) |
| livenessProbe | 容器运行期间 | 杀掉容器重启 | 检测死锁、僵死 |
| readinessProbe | 容器运行期间 | 从 Service 摘除(不接流量) | 检测是否准备好服务 |
5.2 执行顺序
Pod 启动
↓
startupProbe 检查中...(其他探针暂停)
↓ 成功
livenessProbe + readinessProbe 同时开始周期检查
↓
readinessProbe 成功 → Pod 加入 Service 端点(开始接流量)
livenessProbe 成功 → Pod 正常运行
↓
readinessProbe 失败 → Pod 从端点摘除(暂停接流量)
livenessProbe 失败 → 容器被杀掉重启
5.3 Spring Boot Actuator 配置
yaml
# application.yml
management:
endpoint:
health:
probes:
enabled: true # 开启 K8s 探针端点
show-details: always
health:
livenessState:
enabled: true
readinessState:
enabled: true
endpoints:
web:
exposure:
include: health,info,prometheus
探针端点:
/actuator/health/liveness--- 存活检查(只要 JVM 没挂就返回 200)/actuator/health/readiness--- 就绪检查(数据库、Redis 连接正常才返回 200)
六、滚动更新与回滚
6.1 滚动更新过程
初始状态(3 个 Pod v1):
[v1] [v1] [v1]
发布 v2(maxSurge=1, maxUnavailable=0):
Step 1: 创建 1 个新 Pod
[v1] [v1] [v1] [v2-starting]
Step 2: v2 就绪后,终止 1 个 v1
[v1] [v1] [v2] [v2-starting]
Step 3: 继续...
[v1] [v2] [v2] [v2-starting]
Step 4: 完成
[v2] [v2] [v2]
6.2 触发更新
bash
# 方式1:更新镜像版本
kubectl set image deployment/order-service \
order-service=registry.example.com/team/order-service:1.0.1 \
-n order-system
# 方式2:修改 YAML 后 apply
kubectl apply -f deployment.yaml
# 方式3:修改环境变量也会触发更新
kubectl set env deployment/order-service LOG_LEVEL=debug -n order-system
6.3 查看更新状态
bash
# 查看滚动更新进度
kubectl rollout status deployment/order-service -n order-system
# 查看历史版本
kubectl rollout history deployment/order-service -n order-system
# 查看某个版本详情
kubectl rollout history deployment/order-service --revision=3 -n order-system
6.4 回滚
bash
# 回滚到上一版本
kubectl rollout undo deployment/order-service -n order-system
# 回滚到指定版本
kubectl rollout undo deployment/order-service --to-revision=2 -n order-system
# 暂停更新(排查问题)
kubectl rollout pause deployment/order-service -n order-system
# 恢复更新
kubectl rollout resume deployment/order-service -n order-system
七、完整部署示例
7.1 项目文件结构
my-order-service/
├── src/
├── pom.xml
├── Dockerfile
└── k8s/
├── base/ # 基础配置
│ ├── namespace.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ └── hpa.yaml
├── overlays/ # 环境差异配置
│ ├── dev/
│ │ └── kustomization.yaml
│ ├── staging/
│ │ └── kustomization.yaml
│ └── prod/
│ └── kustomization.yaml
└── deploy.sh # 部署脚本
7.2 一键部署脚本
bash
#!/bin/bash
# deploy.sh - 构建并部署到 K8s
set -e
# 参数
APP_NAME="order-service"
VERSION=${1:-"latest"}
NAMESPACE=${2:-"order-system"}
REGISTRY="registry.example.com/team"
echo "=========================================="
echo "部署 $APP_NAME:$VERSION 到 $NAMESPACE"
echo "=========================================="
# 1. 构建 JAR
echo "[1/5] 构建 JAR..."
mvn clean package -DskipTests -q
# 2. 构建 Docker 镜像
echo "[2/5] 构建 Docker 镜像..."
docker build -t $REGISTRY/$APP_NAME:$VERSION .
docker tag $REGISTRY/$APP_NAME:$VERSION $REGISTRY/$APP_NAME:latest
# 3. 推送镜像
echo "[3/5] 推送镜像..."
docker push $REGISTRY/$APP_NAME:$VERSION
docker push $REGISTRY/$APP_NAME:latest
# 4. 部署到 K8s
echo "[4/5] 部署到 Kubernetes..."
kubectl apply -f k8s/base/namespace.yaml
kubectl apply -f k8s/base/configmap.yaml
kubectl apply -f k8s/base/secret.yaml
kubectl apply -f k8s/base/deployment.yaml
kubectl apply -f k8s/base/service.yaml
kubectl apply -f k8s/base/ingress.yaml
kubectl apply -f k8s/base/hpa.yaml
# 更新镜像版本
kubectl set image deployment/$APP_NAME \
$APP_NAME=$REGISTRY/$APP_NAME:$VERSION \
-n $NAMESPACE
# 5. 等待部署完成
echo "[5/5] 等待部署就绪..."
kubectl rollout status deployment/$APP_NAME -n $NAMESPACE --timeout=300s
echo "=========================================="
echo "部署成功!"
echo "版本: $VERSION"
echo "Pod 状态:"
kubectl get pods -n $NAMESPACE -l app=$APP_NAME
echo "=========================================="
7.3 GitLab CI 自动部署
yaml
# .gitlab-ci.yml
stages:
- build
- test
- package
- deploy
variables:
REGISTRY: registry.example.com/team
APP_NAME: order-service
# 构建 JAR
build:
stage: build
image: maven:3.9-eclipse-temurin-17
script:
- mvn clean package -DskipTests
artifacts:
paths:
- target/*.jar
# 构建并推送 Docker 镜像
docker-build:
stage: package
image: docker:24.0
services:
- docker:24.0-dind
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA .
- docker push $REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA
rules:
- if: '$CI_COMMIT_BRANCH == "main" || $CI_COMMIT_BRANCH == "develop"'
# 部署到开发环境
deploy-dev:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context dev-cluster
- kubectl set image deployment/$APP_NAME
$APP_NAME=$REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA
-n order-system-dev
- kubectl rollout status deployment/$APP_NAME -n order-system-dev --timeout=300s
environment:
name: development
url: https://dev-api.example.com
rules:
- if: '$CI_COMMIT_BRANCH == "develop"'
# 部署到生产环境(手动触发)
deploy-prod:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context prod-cluster
- kubectl set image deployment/$APP_NAME
$APP_NAME=$REGISTRY/$APP_NAME:$CI_COMMIT_SHORT_SHA
-n order-system
- kubectl rollout status deployment/$APP_NAME -n order-system --timeout=300s
environment:
name: production
url: https://api.example.com
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
八、常用 kubectl 命令
8.1 基本操作
bash
# 查看资源
kubectl get pods -n order-system # 查看 Pod
kubectl get pods -o wide # 显示更多信息(IP、Node)
kubectl get deployment -n order-system # 查看 Deployment
kubectl get svc -n order-system # 查看 Service
kubectl get ingress -n order-system # 查看 Ingress
kubectl get all -n order-system # 查看所有资源
# 查看详细信息
kubectl describe pod <pod-name> -n order-system # Pod 详情(事件、状态)
kubectl describe deployment order-service -n order-system
8.2 日志和调试
bash
# 查看日志
kubectl logs <pod-name> -n order-system # 当前日志
kubectl logs <pod-name> -n order-system --tail=100 # 最后 100 行
kubectl logs <pod-name> -n order-system -f # 实时追踪
kubectl logs <pod-name> -n order-system --previous # 上一次(重启前)的日志
# 进入容器
kubectl exec -it <pod-name> -n order-system -- /bin/sh
# 端口转发(本地调试)
kubectl port-forward svc/order-service 8080:80 -n order-system
# 现在可以访问 http://localhost:8080
# 查看资源使用
kubectl top pods -n order-system # Pod 的 CPU/内存
kubectl top nodes # Node 的资源使用
8.3 扩缩容
bash
# 手动扩容
kubectl scale deployment order-service --replicas=5 -n order-system
# 查看 HPA 状态
kubectl get hpa -n order-system
# 查看 HPA 详情(当前指标、目标指标)
kubectl describe hpa order-service-hpa -n order-system
8.4 配置管理
bash
# 修改 ConfigMap
kubectl edit configmap order-service-config -n order-system
# 修改后需要重启 Pod 使配置生效(ConfigMap 挂载为文件时会自动更新,但环境变量需要重启)
kubectl rollout restart deployment/order-service -n order-system
九、优雅停机
9.1 为什么需要优雅停机?
更新或缩容时,K8s 会终止旧 Pod。如果直接杀进程,正在处理的请求会中断。
9.2 停机流程
K8s 发出终止信号
↓
1. Pod 从 Service 端点摘除(不再接收新请求)
↓
2. 执行 preStop 钩子(等待 10s,让已转发的请求完成)
↓
3. 发送 SIGTERM 给容器进程
↓
4. Spring Boot 开始优雅停机(完成正在处理的请求)
↓
5. 等待 terminationGracePeriodSeconds(默认 30s)
↓
6. 如果仍未退出,发送 SIGKILL 强制杀掉
9.3 Spring Boot 配置
yaml
# application.yml
server:
shutdown: graceful # 开启优雅停机
spring:
lifecycle:
timeout-per-shutdown-phase: 30s # 等待正在处理的请求最多 30s
9.4 K8s 配置
yaml
# deployment.yaml 中
spec:
template:
spec:
terminationGracePeriodSeconds: 60 # 给足时间(>= preStop + Spring shutdown)
containers:
- lifecycle:
preStop:
exec:
command: ["sh", "-c", "sleep 10"] # 等 10s 让流量切走
完整时序:
t=0s K8s 下发终止指令,从 Service 端点摘除
t=0s 执行 preStop: sleep 10(等待 kube-proxy 更新路由规则)
t=10s preStop 完成,发送 SIGTERM
t=10s Spring Boot 收到 SIGTERM,停止接受新请求
t=10-40s 等待已有请求处理完毕(timeout-per-shutdown-phase: 30s)
t=40s Spring Boot 关闭完成,进程退出
t=60s 如果进程还没退出,SIGKILL 强制终止(terminationGracePeriodSeconds)
十、资源管理
10.1 requests 和 limits
yaml
resources:
requests: # 调度保证:K8s 确保节点上至少有这么多资源可用
cpu: "500m" # 0.5 核
memory: "512Mi"
limits: # 使用上限:超过则被限流(CPU)或杀掉(内存)
cpu: "2000m" # 2 核
memory: "1024Mi"
| 参数 | 含义 | 超出后果 |
|---|---|---|
requests.cpu |
CPU 最低保证 | 用于调度决策 |
limits.cpu |
CPU 最大使用 | 被 throttle(限流变慢,不杀) |
requests.memory |
内存最低保证 | 用于调度决策 |
limits.memory |
内存最大使用 | OOM Kill(容器被杀重启) |
10.2 CPU 单位说明
| 表达式 | 含义 |
|---|---|
1 |
1 个完整 CPU 核 |
500m |
0.5 核(m = milli,千分之一) |
250m |
0.25 核 |
2000m 或 2 |
2 核 |
10.3 内存单位说明
| 表达式 | 含义 |
|---|---|
128Mi |
128 MiB(1 MiB = 1024 KiB) |
1Gi |
1 GiB |
512M |
512 MB(1 MB = 1000 KB,注意不同于 Mi) |
10.4 Java 应用资源配置建议
yaml
# Java 应用推荐配置
resources:
requests:
cpu: "500m"
memory: "768Mi" # JVM 堆 + 非堆 + 额外开销
limits:
cpu: "2000m"
memory: "1024Mi"
对应的 JVM 参数:
-Xms512m -Xmx512m # 堆内存 512MB
# 非堆(元空间、线程栈、NIO Buffer 等)约 200-300MB
# 总内存需求 ≈ 512 + 300 = 812MB → limits 设 1024Mi 留余量
关键原则 :limits.memory > JVM 堆 + 非堆 + 200MB 余量,否则会被 OOM Kill。
十一、持久化存储
11.1 PV 和 PVC
yaml
# persistent-volume-claim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: order-service-logs
namespace: order-system
spec:
accessModes:
- ReadWriteOnce # 单 Pod 读写
storageClassName: standard # 存储类型(云厂商提供)
resources:
requests:
storage: 10Gi # 申请 10GB
在 Deployment 中使用:
yaml
spec:
template:
spec:
containers:
- name: order-service
volumeMounts:
- name: log-storage
mountPath: /app/logs
volumes:
- name: log-storage
persistentVolumeClaim:
claimName: order-service-logs
11.2 存储类型
| accessMode | 含义 | 适用 |
|---|---|---|
ReadWriteOnce (RWO) |
单节点读写 | 数据库、单实例日志 |
ReadOnlyMany (ROX) |
多节点只读 | 静态配置文件 |
ReadWriteMany (RWX) |
多节点读写 | 共享文件存储 |
十二、多环境管理(Kustomize)
12.1 目录结构
k8s/
├── base/ # 基础配置(所有环境共享)
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── configmap.yaml
│ └── kustomization.yaml
├── overlays/
│ ├── dev/ # 开发环境差异
│ │ ├── kustomization.yaml
│ │ └── patch-replicas.yaml
│ ├── staging/ # 预发布环境差异
│ │ ├── kustomization.yaml
│ │ └── patch-replicas.yaml
│ └── prod/ # 生产环境差异
│ ├── kustomization.yaml
│ ├── patch-replicas.yaml
│ └── patch-resources.yaml
12.2 base/kustomization.yaml
yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml
- configmap.yaml
12.3 overlays/prod/kustomization.yaml
yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# 引用 base
resources:
- ../../base
# 命名空间
namespace: order-system-prod
# 通用标签
commonLabels:
env: production
# 镜像覆盖
images:
- name: registry.example.com/team/order-service
newTag: "1.0.0"
# 补丁
patches:
- path: patch-replicas.yaml
- path: patch-resources.yaml
12.4 overlays/prod/patch-replicas.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
replicas: 5 # 生产环境 5 个副本
12.5 overlays/prod/patch-resources.yaml
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
spec:
template:
spec:
containers:
- name: order-service
resources:
requests:
cpu: "1000m" # 生产环境给更多资源
memory: "1024Mi"
limits:
cpu: "4000m"
memory: "2048Mi"
12.6 部署命令
bash
# 预览生成的完整 YAML(不实际部署)
kubectl kustomize k8s/overlays/prod
# 部署到生产环境
kubectl apply -k k8s/overlays/prod
# 部署到开发环境
kubectl apply -k k8s/overlays/dev
十三、监控与告警
13.1 Prometheus + Grafana
yaml
# ServiceMonitor(让 Prometheus 自动发现并抓取指标)
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: order-service-monitor
namespace: order-system
spec:
selector:
matchLabels:
app: order-service
endpoints:
- port: http
path: /actuator/prometheus
interval: 15s
13.2 关键监控指标
| 指标 | 来源 | 告警阈值 |
|---|---|---|
| Pod 重启次数 | kube_pod_container_status_restarts_total |
> 3 次/小时 |
| CPU 使用率 | container_cpu_usage_seconds_total |
> 80% |
| 内存使用率 | container_memory_usage_bytes |
> 85% |
| Pod 就绪状态 | kube_pod_status_ready |
不等于 1 |
| 请求错误率 | http_server_requests_seconds_count{status=~"5.."} |
> 1% |
| 请求延迟 P99 | http_server_requests_seconds |
> 2s |
十四、安全最佳实践
14.1 Pod 安全上下文
yaml
spec:
template:
spec:
securityContext:
runAsNonRoot: true # 禁止以 root 运行
runAsUser: 1000 # 指定用户 ID
fsGroup: 1000
containers:
- name: order-service
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true # 根文件系统只读
capabilities:
drop:
- ALL # 移除所有 Linux capabilities
14.2 网络策略
yaml
# 只允许 Ingress 访问 order-service,其他 Pod 不允许
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: order-service-network-policy
namespace: order-system
spec:
podSelector:
matchLabels:
app: order-service
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx # 只允许 Ingress 命名空间
ports:
- protocol: TCP
port: 8080
十五、常见问题 FAQ
Q1: Pod 一直 CrashLoopBackOff 怎么排查?
bash
# 1. 查看 Pod 状态和事件
kubectl describe pod <pod-name> -n order-system
# 2. 查看日志(上一次崩溃的日志)
kubectl logs <pod-name> -n order-system --previous
# 3. 常见原因:
# - OOM Kill(内存不够)→ 增加 limits.memory
# - 启动失败(端口冲突、配置错误)→ 看日志
# - 健康检查失败(initialDelaySeconds 太短)→ 增加等待时间
Q2: Pod 一直 Pending 怎么办?
bash
kubectl describe pod <pod-name> -n order-system
# 看 Events 部分,通常是:
# - 资源不足(Insufficient cpu/memory)→ 减少 requests 或加节点
# - PVC 无法绑定 → 检查 StorageClass
# - 镜像拉取失败 → 检查 imagePullSecrets
Q3: 如何实现零停机部署?
确保以下配置:
readinessProbe正确配置 → 新 Pod 就绪后才接流量strategy.rollingUpdate.maxUnavailable: 0→ 不允许可用 Pod 减少preStopsleep 10s → 等待路由规则更新- Spring Boot
server.shutdown: graceful→ 完成已有请求
Q4: ConfigMap 更新后如何让 Pod 生效?
- 环境变量方式引用 :需要重启 Pod(
kubectl rollout restart) - 文件挂载方式:约 1-2 分钟后自动更新(但应用需要监听文件变化)
Q5: 如何限制单个 Namespace 的总资源?
yaml
# ResourceQuota
apiVersion: v1
kind: ResourceQuota
metadata:
name: order-system-quota
namespace: order-system
spec:
hard:
requests.cpu: "20" # 总 CPU 请求上限
requests.memory: "40Gi" # 总内存请求上限
limits.cpu: "40"
limits.memory: "80Gi"
pods: "100" # 最多 100 个 Pod
Q6: 如何给不同节点分配不同服务?
使用 nodeSelector 或 nodeAffinity:
yaml
spec:
template:
spec:
nodeSelector:
node-type: compute # 只调度到带此标签的节点
# 或更灵活的亲和性
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-type
operator: In
values:
- compute
- general
十六、总结
| 资源 | 作用 | 一句话 |
|---|---|---|
| Deployment | 声明式管理 Pod 副本和更新 | "我要 3 个 v1.0 的实例" |
| Service | 提供稳定的内部访问地址 | "统一入口,自动负载均衡" |
| Ingress | 外部 HTTP 路由 | "域名/路径 → Service" |
| ConfigMap | 非敏感配置管理 | "配置和代码分离" |
| Secret | 敏感信息管理 | "密码、证书安全存储" |
| HPA | 自动扩缩容 | "流量大自动加 Pod" |
| Probe | 健康检查 | "挂了自动重启,没就绪不接流量" |
| Kustomize | 多环境配置管理 | "base + overlay 管理差异" |
部署一个 Java 微服务的最小清单:
必须:Deployment + Service + ConfigMap + Secret
推荐:+ Ingress + HPA + Probe 配置
生产:+ ResourceQuota + NetworkPolicy + PodDisruptionBudget
核心原则:声明式管理(用 YAML 描述期望状态)+ 不可变基础设施(不改运行中的容器,只发新版本)。