SpringCloud Kubernetes 腾讯云 TKE DevOps

企业级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-service8080API网关、路由、鉴权
auth-service8081认证授权、JWT签发MySQL、Redis
user-service8082用户管理、个人信息MySQL、Redis
order-service8083订单处理、支付对接MySQL、RabbitMQ
product-service8084商品管理、库存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启动报 ErrImagePullImagePullBackOff,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,而不是简单的 localhostmysql。在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生产环境,并且具备监控、日志、自动发布等企业级能力。