企业级SpringCloud服务部署到腾讯云K8S完整实战
从零到一,系统化构建SpringCloud微服务的容器化、K8S编排与腾讯云TKE生产级部署全流程
📖 目录
一、架构全景:我们要搭建什么
1.1 完整架构视图
本文将以一个典型的企业级电商微服务架构为例,展示从本地开发到腾讯云K8S生产的完整路径。系统包含Gateway网关、认证服务、用户服务、订单服务等核心模块,通过K8S进行编排管理。
┌─────────────────────────────────────────────────────────────────┐
│ 腾讯云 VPC │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ TKE K8S Cluster │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Gateway │ │ Auth │ │ User │ │ │
│ │ │Service │ │Service │ │Service │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ │ │ │ │ │ │
│ │ ┌────▼──────────────▼──────────────▼────┐ │ │
│ │ │ Service Discovery │ │ │
│ │ │ (K8S Service DNS) │ │ │
│ │ └─────────────────────────────────────┘ │ │
│ │ ┌────────────┐ ┌────────────┐ │ │
│ │ │ MySQL │ │ Redis │ │ │
│ │ │ (Stateful) │ │ (Deploy) │ │ │
│ │ └────────────┘ └────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ CLB │ │ TCR │ │ CLS/监控 │ │
│ │ (负载均衡) │ │ (镜像仓库) │ │ (日志/监控) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
1.2 服务清单与技术栈
本次部署涉及以下微服务模块,每个服务都是独立的Spring Boot应用,通过Spring Cloud Gateway统一路由:
| 服务名称 | 端口 | 功能 | 依赖 |
|---|---|---|---|
| gateway-service | 8080 | API网关、路由、鉴权 | 无 |
| auth-service | 8081 | 认证授权、JWT签发 | MySQL、Redis |
| user-service | 8082 | 用户管理、个人信息 | MySQL、Redis |
| order-service | 8083 | 订单处理、支付对接 | MySQL、RabbitMQ |
| product-service | 8084 | 商品管理、库存 | MySQL、Redis |
1.3 技术栈版本选择
版本兼容性是企业级部署的第一道门槛,以下是经过生产验证的版本组合:
# 核心技术版本(经过生产验证)
Spring Boot: 3.2.5
Spring Cloud: 2023.0.1
Spring Cloud Alibaba: 2023.0.1.0
Kubernetes: 1.28+ (TKE托管版)
Docker: 24.0+
Helm: 3.14+
Tencent TCR: 企业版(支持镜像安全扫描)
Tencent TKE: Serverless版或托管版(推荐)
# 中间件版本
MySQL: 8.0.36 (容器化部署)
Redis: 7.2.4 (容器化部署)
RabbitMQ: 3.12.12 (可选,或腾讯云CMQ)
架构师提示:Spring Boot 3.x要求Java 17+,在Docker镜像中务必使用eclipse-temurin:17-jre作为基础镜像,不要用Java 8/11镜像,否则运行时直接报错。
二、腾讯云环境准备
2.1 CVM基础环境配置
首先购买并配置跳板机(操作机),所有kubectl、helm等客户端工具都安装在这台机器上。建议规格:2核4G,系统盘50GB,Ubuntu 22.04 LTS。
# 1. SSH登录腾讯云CVM
ssh ubuntu@your-cvm-ip
# 2. 安装Docker(所有节点都需要)
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
sudo usermod -aG docker $USER
newgrp docker # 刷新组权限
# 3. 验证Docker安装
docker --version # Docker version 24.0.x
# 4. 安装kubectl(仅操作机需要)
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
chmod +x kubectl
sudo mv kubectl /usr/local/bin/
# 5. 安装Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# 6. 配置腾讯云CLI(用于TCR镜像推送)
wget https://download.finance.qq.com/cli/latest/linux/tccli-x86_64.tar.gz
tar -xzf tccli-x86_64.tar.gz
sudo mv tccli /usr/local/bin/
tccli configure # 按提示输入SecretId和SecretKey
2.2 创建TKE Kubernetes集群
腾讯云提供了两种K8S集群模式:TKE托管版(推荐)和TKE独立集群。对于绝大多数企业场景,TKE托管版是最佳选择——Master节点由腾讯云维护,你只需管理Worker节点。
# 方式一:通过腾讯云控制台创建(推荐新手)
# 1. 登录 https://console.cloud.tencent.com/tke2
# 2. 集群 → 新建 → 托管集群
# 3. 配置建议:
# - Kubernetes版本: 1.28(当前稳定版)
# - 节点规格: 至少2台 4核8G(生产建议8核16G起)
# - 网络: VPC默认,容器网络CIDR建议 10.244.0.0/16
# - 运行时: containerd(比docker性能更好)
# 方式二:通过CLI创建(适合自动化)
tccli tke CreateCluster \
--ClusterType MANAGED_CLUSTER \
--ClusterVersion 1.28.2 \
--ClusterName springcloud-prod \
--VpcId vpc-xxxxxxxx \
--ClusterCIDR 10.244.0.0/16 \
--ClusterOs ubuntu20.04.1x86_64
# 获取kubeconfig(控制台 → 集群 → 基本信息 → kubeconfig)
# 将内容保存到 ~/.kube/config
mkdir -p ~/.kube
vim ~/.kube/config # 粘贴kubeconfig内容
# 验证集群连接
kubectl cluster-info
kubectl get nodes # 应该看到Worker节点列表
2.3 配置腾讯云容器镜像仓库(TCR)
TCR(Tencent Container Registry)是腾讯云的企业级镜像仓库,支持镜像安全扫描、跨区域同步。我们将用它存储所有SpringCloud服务的Docker镜像。
# 1. 控制台创建TCR实例
# 容器镜像服务 → 实例列表 → 新建实例
# 选择:企业版(支持漏洞扫描)或 个人版(免费)
# 2. 创建命名空间(对应项目)
# 实例 → 命名空间 → 新建
# 命名空间: springcloud-demo
# 3. 配置访问凭证(kubectl从TCR拉取镜像需要)
# 控制台 → 实例 → 访问凭证 → 新建
# 生成用户名和密码,保存到K8S Secret:
kubectl create namespace springcloud
kubectl create secret docker-registry tencent-tcr-secret \
--docker-server=ccr.ccs.tencentyun.com \
--docker-username=your-tcr-username \
--docker-password=your-tcr-password \
--namespace=springcloud
# 验证Secret创建成功
kubectl get secrets -n springcloud | grep tencent-tcr-secret
坑点提醒:如果后续Pod启动报 ErrImagePull 或 ImagePullBackOff,90%是Secret配置问题。检查三个方面:(1) Secret是否在正确的namespace (2) docker-server地址是否包含 https:// (3) 密码是否包含特殊字符需要转义。
三、SpringCloud服务容器化
3.1 编写生产级Dockerfile
每个SpringCloud服务都需要一个Dockerfile。生产环境强烈推荐使用多阶段构建(Multi-stage Build),既能减小镜像体积,又能避免源码泄露。
# 以gateway-service为例,其他服务Dockerfile结构相同
# 文件路径:gateway-service/Dockerfile
# 第一阶段:构建(使用Maven镜像)
FROM maven:3.9-eclipse-temurin-17 AS builder
WORKDIR /app
COPY pom.xml .
COPY src ./src
# 跳过测试,只打包
RUN mvn clean package -DskipTests
# 第二阶段:运行(使用精简JRE镜像)
FROM eclipse-temurin:17-jre-jammy
WORKDIR /app
# 从构建阶段复制jar包
COPY --from=builder /app/target/gateway-service-0.0.1-SNAPSHOT.jar app.jar
# 创建非root用户(安全最佳实践)
RUN addgroup --system spring && adduser --system spring --ingroup spring
RUN chown -R spring:spring /app
USER spring:spring
# JVM参数优化(容器环境必须设置)
ENV JAVA_OPTS="-Xms256m -Xmx512m -XX:+UseContainerSupport -XX:+MaxRAMPercentage=75.0"
# 健康检查(K8S会用到)
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
3.2 本地docker-compose联调验证
在推送镜像到TCR之前,先在本地用docker-compose验证服务间调用是否正常。这能提前发现配置错误,避免部署到K8S后再排查。
# 文件:docker-compose.yml(项目根目录)
version: '3.8'
services:
mysql:
image: mysql:8.0.36
container_name: local-mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: springcloud
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7.2.4
container_name: local-redis
ports:
- "6379:6379"
gateway-service:
build: ./gateway-service
ports:
- "8080:8080"
environment:
- SPRING_PROFILES_ACTIVE=local
depends_on:
- auth-service
networks:
- springcloud-net
auth-service:
build: ./auth-service
ports:
- "8081:8081"
environment:
- SPRING_PROFILES_ACTIVE=local
- SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/springcloud
- SPRING_REDIS_HOST=redis
depends_on:
- mysql
- redis
networks:
- springcloud-net
volumes:
mysql_data:
networks:
springcloud-net:
driver: bridge
3.3 构建并推送镜像到TCR
验证通过后,为每个服务构建Docker镜像并推送到腾讯云TCR。推荐编写统一的构建脚本,避免人工操作出错。
# 构建脚本:build-and-push.sh
#!/bin/bash
set -e
TCR_REGISTRY="ccr.ccs.tencentyun.com/springcloud-demo"
SERVICES=("gateway-service" "auth-service" "user-service" "order-service" "product-service")
# 登录TCR(需要提前配置访问凭证)
echo "====== 登录TCR ======"
tccli cr CreateUserToken --RegistryId your-registry-id # 获取临时密码
docker login ccr.ccs.tencentyun.com -u your-username -p your-password
for SERVICE in "${SERVICES[@]}"; do
echo "====== 构建 $SERVICE ======"
docker build -t ${TCR_REGISTRY}/${SERVICE}:latest ./${SERVICE}
docker tag ${TCR_REGISTRY}/${SERVICE}:latest ${TCR_REGISTRY}/${SERVICE}:$(date +%Y%m%d-%H%M%S)
echo "====== 推送 $SERVICE ======"
docker push ${TCR_REGISTRY}/${SERVICE}:latest
docker push ${TCR_REGISTRY}/${SERVICE}:$(date +%Y%m%d-%H%M%S)
done
echo "====== 所有镜像推送完成 ======"
工程经验:不要只打 latest 标签!生产环境必须使用带版本号的标签(如 20260526-143000)。K8S的镜像更新策略 imagePullPolicy: Always 配合具体版本标签,才能确保回滚时能精确到某次构建。
四、K8S基础资源定义
4.1 Namespace与资源配置
K8S的Namespace是逻辑隔离的基本单位。我们为SpringCloud服务创建独立的Namespace,并在其中配置ResourceQuota(资源配额)和LimitRange(默认资源限制)。
# 文件:namespace.yaml
apiVersion: v1
kind: Namespace
metadata:
name: springcloud
labels:
name: springcloud
env: prod
---
# 资源配额:限制整个namespace的资源使用
apiVersion: v1
kind: ResourceQuota
metadata:
name: springcloud-quota
namespace: springcloud
spec:
hard:
requests.cpu: "4"
requests.memory: 8Gi
limits.cpu: "8"
limits.memory: 16Gi
persistentvolumeclaims: "10"
---
# 默认资源限制:每个容器如果没有显式声明,使用这个默认值
apiVersion: v1
kind: LimitRange
metadata:
name: springcloud-limitrange
namespace: springcloud
spec:
limits:
- default:
cpu: "500m"
memory: "512Mi"
defaultRequest:
cpu: "200m"
memory: "256Mi"
type: Container
# 应用配置
kubectl apply -f namespace.yaml
kubectl get ns springcloud # 验证创建成功
4.2 ConfigMap:外部化配置
SpringCloud服务的配置(如数据库连接、Redis地址)不应该硬编码在代码或镜像中。使用K8S的ConfigMap可以集中管理配置,并且支持热更新(需配合Spring Cloud Kubernetes)。
# 文件:configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: springcloud-config
namespace: springcloud
data:
# MySQL配置
mysql.host: "mysql.springcloud.svc.cluster.local"
mysql.port: "3306"
mysql.database: "springcloud"
mysql.username: "root"
# Redis配置
redis.host: "redis.springcloud.svc.cluster.local"
redis.port: "6379"
# 应用通用配置
log.level: "INFO"
spring.profiles.active: "k8s"
# 应用ConfigMap
kubectl apply -f configmap.yaml
# 验证
kubectl get configmap -n springcloud
kubectl describe configmap springcloud-config -n springcloud
4.3 Secret:敏感信息处理
数据库密码、JWT密钥等敏感信息必须存放在Secret中,而不是ConfigMap。K8S的Secret是base64编码(不是加密),生产环境建议启用KMS(密钥管理服务)进行加密存储。
# 方式一:通过kubectl直接创建(适合简单场景)
kubectl create secret generic springcloud-secret \
--from-literal=mysql-password='MySql@Pass123' \
--from-literal=jwt-secret='YourJWTSecretKeyxxxxxxxx' \
--from-literal=redis-password='' \
--namespace=springcloud
# 方式二:YAML文件定义(推荐,可以版本控制)
# 文件:secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: springcloud-secret
namespace: springcloud
type: Opaque
data:
# 注意:value必须是base64编码
# echo -n 'MySql@Pass123' | base64
mysql-password: TXlTcWxAUGFzczEyMw==
jwt-secret: WW91ckpXVFNlY3JldEtleXh4eHh4eHg=
redis-password: ""
# 应用Secret
kubectl apply -f secret.yaml
# 验证(注意:不会显示明文)
kubectl get secret springcloud-secret -n springcloud
五、中间件部署:MySQL、Redis、RabbitMQ
5.1 MySQL部署(StatefulSet + PVC)
数据库是有状态服务,必须使用StatefulSet部署(而不是Deployment),以保证Pod重启后网络标识和存储不变。同时需要配置PVC(PersistentVolumeClaim)持久化数据。
# 文件:mysql.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: springcloud
spec:
ports:
- port: 3306
targetPort: 3306
selector:
app: mysql
clusterIP: None # Headless Service,用于StatefulSet
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
namespace: springcloud
spec:
serviceName: "mysql"
replicas: 1 # 生产环境建议至少3副本(主从架构)
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0.36
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: springcloud-secret
key: mysql-password
- name: MYSQL_DATABASE
value: "springcloud"
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
livenessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 3306
initialDelaySeconds: 5
periodSeconds: 5
volumeClaimTemplates:
- metadata:
name: mysql-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "cbs-standard" # 腾讯云CBS存储类
resources:
requests:
storage: 20Gi
# 应用MySQL
kubectl apply -f mysql.yaml
# 验证StatefulSet和PVC
kubectl get statefulset -n springcloud
kubectl get pvc -n springcloud
kubectl get pods -n springcloud -l app=mysql -w # 等待Running状态
5.2 Redis部署(Deployment + Service)
Redis作为缓存服务,使用Deployment部署即可(不需要StatefulSet)。如果生产环境需要高可用,可以部署Redis Sentinel或Redis Cluster。
# 文件:redis.yaml
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: springcloud
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: springcloud
spec:
replicas: 1 # 生产建议哨兵模式或Cluster
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.2.4
ports:
- containerPort: 6379
resources:
requests:
cpu: "200m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 5
periodSeconds: 5
kubectl apply -f redis.yaml
kubectl get pods -n springcloud -l app=redis
5.3 初始化数据库表结构
MySQL启动后,需要初始化表结构。生产环境建议使用Flyway或Liquibase进行数据库版本管理,这里先用简单的Init Container演示。
# 使用init container初始化数据库
# 修改mysql.yaml,在StatefulSet的spec.template.spec中添加:
initContainers:
- name: init-mysql
image: mysql:8.0.36
command:
- sh
- -c
- |
mysql -h mysql -u root -p${MYSQL_ROOT_PASSWORD} ${MYSQL_DATABASE} < /sql/init.sql
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: springcloud-secret
key: mysql-password
volumeMounts:
- name: mysql-data
mountPath: /var/lib/mysql
- name: init-sql
mountPath: /sql
volumes:
- name: init-sql
configMap:
name: mysql-init-sql
# 创建初始化SQL的ConfigMap
# 文件:mysql-init-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-init-sql
namespace: springcloud
data:
init.sql: |
CREATE TABLE IF NOT EXISTS users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(100) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 其他表结构...
kubectl apply -f mysql-init-configmap.yaml
坑点提醒:在K8S中,Pod的DNS格式是 service-name.namespace.svc.cluster.local。比如MySQL的访问地址应该是 mysql.springcloud.svc.cluster.local:3306,而不是简单的 localhost 或 mysql。在Spring配置中务必使用完整域名。
六、SpringCloud服务部署
6.1 编写Deployment与Service YAML
每个SpringCloud服务都需要Deployment(定义Pod模板)和Service(服务发现)。以下是auth-service的完整部署配置,其他服务参照修改即可。
# 文件:auth-service.yaml
apiVersion: v1
kind: Service
metadata:
name: auth-service
namespace: springcloud
labels:
app: auth-service
spec:
selector:
app: auth-service
ports:
- port: 8081
targetPort: 8081
name: http
type: ClusterIP # 内部服务,不暴露公网
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service
namespace: springcloud
labels:
app: auth-service
spec:
replicas: 2 # 至少2副本保证高可用
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多启动1个Pod
maxUnavailable: 0 # 更新期间必须保持全部可用
selector:
matchLabels:
app: auth-service
template:
metadata:
labels:
app: auth-service
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8081"
spec:
containers:
- name: auth-service
image: ccr.ccs.tencentyun.com/springcloud-demo/auth-service:latest
imagePullPolicy: Always # 生产建议IfNotPresent
ports:
- containerPort: 8081
name: http
env:
- name: SPRING_PROFILES_ACTIVE
value: "k8s"
- name: SPRING_DATASOURCE_URL
value: "jdbc:mysql://mysql.springcloud.svc.cluster.local:3306/springcloud"
- name: SPRING_DATASOURCE_USERNAME
valueFrom:
configMapKeyRef:
name: springcloud-config
key: mysql.username
- name: SPRING_DATASOURCE_PASSWORD
valueFrom:
secretKeyRef:
name: springcloud-secret
key: mysql-password
- name: SPRING_REDIS_HOST
valueFrom:
configMapKeyRef:
name: springcloud-config
key: redis.host
resources:
requests:
cpu: "200m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "1Gi"
livenessProbe: # 存活探针:失败会重启Pod
httpGet:
path: /actuator/health/liveness
port: 8081
initialDelaySeconds: 60
periodSeconds: 10
timeoutSeconds: 5
readinessProbe: # 就绪探针:失败会从Service摘除
httpGet:
path: /actuator/health/readiness
port: 8081
initialDelaySeconds: 30
periodSeconds: 5
timeoutSeconds: 3
imagePullSecrets:
- name: tencent-tcr-secret # 引用TCR访问凭证
# 应用部署
kubectl apply -f auth-service.yaml
# 查看部署状态
kubectl get pods -n springcloud -l app=auth-service
kubectl describe pod -n springcloud -l app=auth-service
6.2 Gateway网关配置与Ingress暴露
Spring Cloud Gateway是整个系统的入口,需要通过Ingress(或腾讯云CLB)暴露到公网。以下是Gateway的配置和Ingress规则。
# 文件:gateway-service.yaml(部分)
apiVersion: v1
kind: Service
metadata:
name: gateway-service
namespace: springcloud
spec:
selector:
app: gateway-service
ports:
- port: 8080
targetPort: 8080
type: ClusterIP
---
# Ingress配置:暴露Gateway到公网
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: springcloud-ingress
namespace: springcloud
annotations:
kubernetes.io/ingress.class: "nginx"
# 腾讯云CLB配置(如果使用腾讯云Ingress控制器)
# kubernetes.io/ingress.class: "qcloud"
# tencent.com/ingress-clb-id: "lb-xxxxxxxx"
spec:
rules:
- host: api.yourdomain.com # 绑定域名
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gateway-service
port:
number: 8080
# 应用Gateway和Ingress
kubectl apply -f gateway-service.yaml
# 查看Ingress状态(需要等待分配外部IP)
kubectl get ingress -n springcloud
6.3 验证服务间调用
所有服务部署完成后,需要验证服务间调用是否正常。可以通过在集群内启动一个临时Pod,用curl测试服务连通性。
# 启动一个临时测试Pod
kubectl run test-curl --image=curlimages/curl -it --rm --restart=Never --namespace=springcloud -- sh
# 在测试Pod中执行:
# 1. 测试Service DNS解析
nslookup auth-service.springcloud.svc.cluster.local
# 2. 测试服务间调用(从gateway调用auth)
curl http://auth-service.springcloud.svc.cluster.local:8081/actuator/health
# 3. 测试通过Gateway访问
curl http://gateway-service.springcloud.svc.cluster.local:8080/api/auth/login \
-X POST \
-H "Content-Type: application/json" \
-d '{"username":"test","password":"123456"}'
# 如果配置了In# 如果配置了Ingress域名
curl http://api.yourdomain.com/api/auth/login -X POST -H "Content-Type: application/json" -d '{"username":"test","password":"123456"}'
# 4. 查看Pod日志排查问题
kubectl logs -f deployment/gateway-service -n springcloud
kubectl logs -f deployment/auth-service -n springcloud
七、服务注册与发现的K8S方案
7.1 为什么不用Eureka/Nacos?
传统SpringCloud使用Eureka或Nacos做服务发现,但在K8S环境中,这是多余的。K8S本身就提供了强大的Service DNS机制,可以完全替代第三方注册中心。
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Eureka/Nacos | 功能丰富(配置中心、健康检查) | 额外维护成本、资源消耗 | 跨K8S集群、混合云 |
| K8S Service DNS | 零维护、原生集成、自动健康检查 | 仅限于K8S集群内 | 纯K8S环境(推荐) |
7.2 Spring Cloud Kubernetes服务发现
使用K8S原生服务发现,需要在Spring Boot应用中集成 spring-cloud-starter-kubernetes-client 依赖,并配置Service映射。
# pom.xml 添加依赖
org.springframework.cloud
spring-cloud-starter-kubernetes-client
org.springframework.cloud
spring-cloud-starter-openfeign
# application-k8s.yml 配置
spring:
cloud:
kubernetes:
discovery:
enabled: true # 启用K8S服务发现
config:
enabled: true # 启用ConfigMap配置加载
secrets:
enabled: true # 启用Secret加载
# Gateway路由配置(lb://service-name格式)
spring:
cloud:
gateway:
routes:
- id: auth-service
uri: lb://auth-service # K8S Service名称
predicates:
- Path=/api/auth/**
filters:
- StripPrefix=1
- id: user-service
uri: lb://user-service
predicates:
- Path=/api/user/**
filters:
- StripPrefix=1
# 验证服务发现
# 在Gateway Pod中查看可用的服务地址
kubectl exec -it deployment/gateway-service -n springcloud -- sh
curl http://auth-service:8081/actuator/health # 应该能访问到
架构师提示:如果你们公司有多套环境(dev/test/prod)且不在同一个K8S集群,或者需要跨云部署,那还是需要用Nacos做统一服务发现。但对于单集群部署,K8S Service就是最佳实践。
八、配置中心与动态配置
8.1 使用ConfigMap替代Spring Cloud Config
Spring Cloud Config Server需要单独部署和维护,在K8S环境下,可以直接用ConfigMap + Spring Cloud Kubernetes Config实现配置外部化,更简单高效。
# 创建多环境ConfigMap
# 文件:application-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: auth-service-config
namespace: springcloud
data:
application.yml: |
server:
port: 8081
spring:
application:
name: auth-service
datasource:
url: jdbc:mysql://mysql.springcloud.svc.cluster.local:3306/springcloud
username: ${MYSQL_USERNAME}
password: ${MYSQL_PASSWORD}
jwt:
secret: ${JWT_SECRET}
expiration: 86400
# 挂载ConfigMap到Pod
# 修改auth-service.yaml的容器定义:
spec:
containers:
- name: auth-service
image: ccr.ccs.tencentyun.com/springcloud-demo/auth-service:latest
volumeMounts:
- name: config-volume
mountPath: /app/config
readOnly: true
volumes:
- name: config-volume
configMap:
name: auth-service-config
items:
- key: application.yml
path: application.yml
# Spring Boot启动时指定外部配置
# 在deployment的env中添加:
env:
- name: SPRING_CONFIG_ADDITIONAL_LOCATION
value: "file:/app/config/application.yml"
kubectl apply -f application-config.yaml
8.2 配置热更新机制
K8S的ConfigMap更新后,需要让Spring Boot应用感知到变化并重新加载配置。可以通过Actuator + /refresh端点实现。
# 1. 添加依赖(pom.xml)
org.springframework.boot
spring-boot-starter-actuator
# 2. 配置Actuator暴露refresh端点
management:
endpoints:
web:
exposure:
include: health,info,refresh,configprops
# 3. 在需要热更新的配置类上添加注解
@RefreshScope
@RestController
public class ConfigController {
@Value("${jwt.secret}")
private String jwtSecret;
@GetMapping("/config/jwt-secret")
public String getJwtSecret() {
return jwtSecret;
}
}
# 4. 更新ConfigMap后手动触发刷新
kubectl exec -it deployment/auth-service -n springcloud -- \
curl -X POST http://localhost:8081/actuator/refresh
# 或者配置K8S的watch机制自动刷新(需要自定义Controller)
8.3 使用腾讯云COS存储大配置文件
对于大的配置文件(如证书、JSON数据),ConfigMap有1MB大小限制。此时可以存储到腾讯云COS,应用启动时下载。
# 使用Init Container下载COS配置文件
initContainers:
- name: init-config
image: ccr.ccs.tencentyun.com/springcloud-demo/cos-downloader:latest
env:
- name: COS_BUCKET
value: "springcloud-config-1234567890"
- name: COS_REGION
value: "ap-guangzhou"
- name: COS_SECRET_ID
valueFrom:
secretKeyRef:
name: cos-secret
key: secret-id
- name: COS_SECRET_KEY
valueFrom:
secretKeyRef:
name: cos-secret
key: secret-key
command:
- sh
- -c
- |
coscli cp cos://${COS_BUCKET}/config/application-prod.yml /tmp/config/
volumeMounts:
- name: config-volume
mountPath: /tmp/config
# 需要先创建COS访问凭证Secret
kubectl create secret generic cos-secret \
--from-literal=secret-id='your-secret-id' \
--from-literal=secret-key='your-secret-key' \
-n springcloud
九、监控与日志体系
9.1 Spring Boot Actuator + Micrometer
监控的第一步是在应用中暴露指标。Spring Boot Actuator配合Micrometer可以输出Prometheus格式的指标,供监控系统采集。
# pom.xml 添加依赖
org.springframework.boot
spring-boot-starter-actuator
io.micrometer
micrometer-registry-prometheus
# application.yml 配置
management:
server:
port: 8081 # 管理端口(可独立)
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
metrics:
export:
prometheus:
enabled: true
distribution:
percentiles-histogram:
http.server.requests: true
# 验证指标输出
kubectl port-forward deployment/auth-service 8081:8081 -n springcloud
curl http://localhost:8081/actuator/prometheus
9.2 部署Prometheus + Grafana
推荐使用Prometheus Operator或Helm Chart快速部署监控系统。腾讯云也提供了托管版Prometheus,可以免去自建运维成本。
# 方式一:Helm部署(自建)
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update
helm install prometheus prometheus-community/kube-prometheus-stack \
--namespace monitoring \
--create-namespace \
--set prometheus.service.type=ClusterIP
# 方式二:使用腾讯云托管Prometheus(推荐生产)
# 控制台 → 监控 → Prometheus监控服务 → 创建实例
# 关联TKE集群,自动采集Pod指标
# Grafana导入Spring Boot Dashboard
# Dashboard ID: 14368 (Spring Boot Statistics)
# 或 4701 (JVM Micrometer)
# 验证ServiceMonitor(Prometheus自动发现)
kubectl get servicemonitor -n springcloud
9.3 日志收集:Filebeat + 腾讯云CLS
K8S中Pod日志是临时的,必须集中收集。推荐使用腾讯云日志服务(CLS),通过LogListener或Filebeat自动采集容器日志。
# 方案:Sidecar容器运行Filebeat
# 修改Deployment,添加Filebeat sidecar
containers:
- name: auth-service
image: ccr.ccs.tencentyun.com/springcloud-demo/auth-service:latest
# ... 其他配置
- name: filebeat
image: docker.elastic.co/beats/filebeat:8.11.0
volumeMounts:
- name: filebeat-config
mountPath: /etc/filebeat/filebeat.yml
subPath: filebeat.yml
- name: varlog
mountPath: /var/log
- name: varlibdockercontainers
mountPath: /var/lib/docker/containers
readOnly: true
volumes:
- name: filebeat-config
configMap:
name: filebeat-config
- name: varlog
hostPath:
path: /var/log
- name: varlibdockercontainers
hostPath:
path: /var/lib/docker/containers
# 或者使用腾讯云CLS的LogListener(更简单)
# 控制台 → 日志服务 → 日志主题 → 采集配置
# 选择容器日志,自动关联TKE集群
# 无需在Pod中注入Sidecar,由节点级Agent统一采集
工程经验:日志格式一定要用JSON!在 application.yml 中配置 logging.pattern.console: '{"timestamp":"%d","level":"%p","service":"${spring.application.name}","message":"%m","traceId":"%X{traceId}"}%n'。结构化日志在CLS中可以直接解析和检索。
十、CI/CD流水线:从代码到线上
10.1 使用腾讯云CODING DevOps
腾讯云CODING提供了完整的CI/CD能力,可以直接从Git仓库触发,自动构建、推送镜像、部署到TKE。以下是核心的构建计划配置。
# CODING Jenkinsfile(图形化编排也可)
pipeline {
agent any
environment {
TCR_REGISTRY = 'ccr.ccs.tencentyun.com/springcloud-demo'
TKE_CLUSTER = 'cls-xxxxxxxx'
KUBECONFIG_CREDENTIAL_ID = 'tke-kubeconfig'
}
stages {
stage('检出代码') {
steps {
checkout([
$class: 'GitSCM',
branches: [[name: '*/master']],
extensions: [],
userRemoteConfigs: [[
url: 'https://e.coding.net/your-project/springcloud-demo.git',
credentialsId: 'coding-git-credential'
]]
])
}
}
stage('Maven构建') {
steps {
sh 'mvn clean package -DskipTests'
}
}
stage('构建Docker镜像') {
steps {
script {
def services = ['gateway-service', 'auth-service', 'user-service']
for (service in services) {
sh """
docker build -t ${TCR_REGISTRY}/${service}:${BUILD_NUMBER} ./${service}
docker push ${TCR_REGISTRY}/${service}:${BUILD_NUMBER}
"""
}
}
}
}
stage('部署到TKE') {
steps {
withKubeConfig([credentialsId: KUBECONFIG_CREDENTIAL_ID]) {
sh """
# 更新镜像版本
kubectl set image deployment/gateway-service \
gateway-service=${TCR_REGISTRY}/gateway-service:${BUILD_NUMBER} \
-n springcloud
kubectl set image deployment/auth-service \
auth-service=${TCR_REGISTRY}/auth-service:${BUILD_NUMBER} \
-n springcloud
# 等待滚动更新完成
kubectl rollout status deployment/gateway-service -n springcloud
kubectl rollout status deployment/auth-service -n springcloud
"""
}
}
}
}
post {
success {
echo '部署成功!'
}
failure {
echo '部署失败,开始回滚...'
sh """
kubectl rollout undo deployment/gateway-service -n springcloud
kubectl rollout undo deployment/auth-service -n springcloud
"""
}
}
}
10.2 金丝雀发布与蓝绿部署
生产环境发布新版本时,不能一次性全量更新。使用Ingress注解可以实现金丝雀发布(Canary Deployment),先让少量流量打到新版本,验证无误后再全量切换。
# 金丝雀发布配置:10%流量打到新版本
# 现有稳定版本Service
apiVersion: v1
kind: Service
metadata:
name: auth-service
namespace: springcloud
spec:
selector:
app: auth-service
version: stable # 稳定版本
ports:
- port: 8081
---
# 新版本Deployment(canary)
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth-service-canary
spec:
replicas: 1
selector:
matchLabels:
app: auth-service
version: canary # 金丝雀版本
template:
metadata:
labels:
app: auth-service
version: canary
spec:
containers:
- name: auth-service
image: ccr.ccs.tencentyun.com/springcloud-demo/auth-service:v2.0
---
# Ingress金丝雀配置
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: auth-service-canary
namespace: springcloud
annotations:
kubernetes.io/ingress.class: "nginx"
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10" # 10%流量
spec:
rules:
- host: api.yourdomain.com
http:
paths:
- path: /api/auth
pathType: Prefix
backend:
service:
name: auth-service # 指向canary service
port:
number: 8081
# 验证金丝雀生效
for i in {1..100}; do curl -s http://api.yourdomain.com/api/auth/version; done | grep -c "v2.0"
# 应该约10次返回v2.0
10.3 深挖点:TKE vs 自建K8S成本对比
企业决策时经常需要权衡:是使用腾讯云TKE托管版,还是自建Kubernetes集群?以下是基于100个Pod规模的成本和运维复杂度对比。
| 维度 | TKE托管版 | 自建K8S(CVM部署) |
|---|---|---|
| Master节点成本 | 免费(腾讯云托管) | 3台 4核8G = 约1500元/月 |
| Worker节点成本 | 与普通CVM相同 | 与普通CVM相同 |
| 运维人力 | 低(只需管理Worker) | 高(需维护ETCD、API Server等) |
| 升级灵活性 | 控制台一键升级 | 需手动逐个节点升级 |
| 高可用保障 | SLA 99.95% | 取决于自建运维能力 |
| 适用场景 | 绝大多数企业(推荐) | 对K8S有完全控制权需求 |
架构师建议:除非你有专门的运维团队且需要深度定制K8S,否则强烈推荐TKE托管版。把精力放在业务开发上,而不是"维护基础设施"上。省下的运维成本足够支付TKE的服务费。
总结
本文从腾讯云环境准备、SpringCloud服务容器化、K8S资源定义、中间件部署、服务注册发现、配置中心、监控日志到CI/CD流水线,完整覆盖了企业级SpringCloud服务部署到K8S的全流程。
关键要点回顾:
- 多阶段Dockerfile: 减小镜像体积,提升安全性
- StatefulSet部署MySQL: 保证有状态服务的稳定性
- K8S Service替代Eureka: 简化架构,减少维护成本
- ConfigMap + Secret: 配置外部化,敏感信息不进代码仓库
- 金丝雀发布: 渐进式发布,降低线上风险
- CODING CI/CD: 自动化从代码到部署的全流程
遵循这套流程,你可以系统性地将任何SpringCloud项目部署到腾讯云K8S生产环境,并且具备监控、日志、自动发布等企业级能力。