项目背景与技术栈演进

在之前的项目架构中,我们采用了Spring Cloud微服务框架,主要技术组件包括:

  • Spring Gateway - API网关和路由管理

  • Nacos - 服务注册发现与配置中心

  • MinIO - 分布式对象存储服务

  • Load Balancer - 客户端负载均衡

  • OpenFeign - 声明式REST服务调用

所有微服务均通过Docker容器化部署在虚拟机环境中。随着业务发展和安全要求的提升,我们决定将整个架构迁移至Azure Kubernetes Service (AKS)。这一迁移决策主要基于以下考虑:

安全与运维优势

  • AKS提供托管的Kubernetes服务,无需手动维护节点安全策略

  • 自动化的安全补丁和版本更新

  • 集成的Azure安全中心和监控能力

  • 减少基础设施维护负担,专注于业务开发

架构迁移总体规划

项目结构分析

现有微服务架构包含:

  • 5个核心业务服务

  • 4个公共服务支撑模块

主要改造范围

  • 网关层:Spring Gateway功能替换

  • 认证授权:适配新的流量入口模式

  • 服务通信:OpenFeign调用方式调整

  • 配置管理:从Nacos迁移至Kubernetes原生方案

技术选型决策

  • 使用Traefik 2作为Ingress Controller替代Spring Gateway

  • 授权验证通过自定义中间件实现

  • 服务间通信从服务名发现改为直接URL调用

具体实施步骤

1. 代码分支管理

创建专用功能分支进行迁移改造:

bash

git checkout -b feature/AKS-migrate

采用独立分支确保迁移过程不影响主分支稳定性,便于渐进式改造和回滚。

2. Gateway层重构

依赖清理
首先移除Spring Cloud相关依赖:

xml

<!-- 注释掉的依赖 -->
<!--
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
-->

技术路线探索与决策
初始尝试保留WebFlux以最小化代码改动,但发现与现有@RestController架构存在兼容性问题。经过评估,决定采用更彻底的改造方案。

Filter迁移策略
将原有的Gateway Filter转换为Spring标准Filter:

java

@Component
@Order(1)
public class AuthenticationFilter extends OncePerRequestFilter {
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, 
                                  HttpServletResponse response, 
                                  FilterChain filterChain) throws ServletException, IOException {
        // 移植原有的认证逻辑
        String token = request.getHeader("Authorization");
        if (!validateToken(token)) {
            response.setStatus(HttpStatus.UNAUTHORIZED.value());
            return;
        }
        
        filterChain.doFilter(request, response);
    }
    
    private boolean validateToken(String token) {
        // Token验证逻辑
        return true;
    }
}

关键改造要点

  • 全局Filter迁移至公共模块

  • 确保Filter执行顺序与原有逻辑一致

  • 特别注意请求体重复读取等边界情况处理

3. Nacos功能替代方案

服务发现改造
将基于服务名的Feign调用改为直接URL调用:

java

// 改造前
@FeignClient(name = "user-service")
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

// 改造后
@FeignClient(name = "user-service", url = "${services.user-service.url}")
public interface UserServiceClient {
    @GetMapping("/users/{id}")
    User getUserById(@PathVariable("id") Long id);
}

配置管理迁移
从Nacos配置中心迁移至Kubernetes ConfigMap和Secret:

原始配置格式

yaml

spring:
  redis:
    host: 127.0.0.1
    port: 6379
    database: 0
    password: 123456

迁移后配置格式

yaml

spring:
  redis:
    host: ${env_redis_host}
    port: ${env_redis_port}
    database: ${env_redis_database}
    password: ${env_redis_password}
4. 基于Helm的部署架构

项目结构设计

text

.
├── .helmignore
├── Chart.yaml
├── values-prod.yaml
├── values-qa.yaml
├── values-test.yaml
├── values.yaml
├── charts/
├── config/
│   ├── dev/
│   │   ├── config.yaml
│   │   └── secret.yaml
│   ├── prod/
│   │   ├── config.yaml
│   │   └── secret.yaml
│   └── test/
│       ├── config.yaml
│       └── secret.yaml
└── templates/
    ├── configmap.yaml
    ├── deployment.yaml
    ├── hpa.yaml
    ├── secret.yaml
    ├── service.yaml
    └── _helpers.tpl

环境配置管理
开发环境配置文件示例:

config/dev/config.yaml

yaml

env_redis_host: localhost
env_redis_port: 6379
env_redis_database: 1

config/dev/secret.yaml

yaml

env_redis_password: 123456

Helm模板配置

yaml

# values.yaml - 配置文件映射定义
configOverrides:
  - config/dev/config.yaml
secretOverrides:
  - config/dev/secret.yaml

# templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ include "think-manifesto.fullname" . }}-configmap
  namespace: {{ .Values.nameSpace }}
data:
{{- $files := .Files }}
{{- range .Values.configOverrides }}
{{- range $key, $value := ($files.Get (printf "%s" .) | fromYaml) }}
  {{ $key }}: {{ $value | quote }}
{{- end }}
{{- end }}

# templates/secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: {{ include "think-manifesto.fullname" . }}-secret
  namespace: {{ .Values.nameSpace }}
type: Opaque
data:
{{- $files := .Files }}
{{- range .Values.secretOverrides }}
{{- range $key, $value := ($files.Get (printf "%s" .) | fromYaml) }}
  {{ $key }}: {{ $value | b64enc }}
{{- end }}
{{- end }}

完整的Deployment配置

yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "think-manifesto.fullname" . }}
  namespace: {{ .Values.nameSpace }}
  labels:
    {{- include "think-manifesto.labels" . | nindent 4 }}
spec:
  replicas: {{ .Values.replicaCount }}
  selector:
    matchLabels:
      {{- include "think-manifesto.selectorLabels" . | nindent 6 }}
  template:
    metadata:
      labels:
        {{- include "think-manifesto.selectorLabels" . | nindent 8 }}
      annotations:
        {{- if .Values.configOverrides }}
        checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
        {{- end }}
        {{- if .Values.secretOverrides }}
        checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }}
        {{- end }}
    spec:
      {{- with .Values.imagePullSecrets }}
      imagePullSecrets:
        {{- toYaml . | nindent 8 }}
      {{- end }}
      containers:
        - name: {{ .Chart.Name }}
          image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
          imagePullPolicy: {{ .Values.image.pullPolicy }}
          ports:
            - name: {{ .Values.service.portName }}
              containerPort: {{ .Values.service.port }}
          envFrom:
            {{- if .Values.configOverrides }}
            - configMapRef:
                name: {{ include "think-manifesto.fullname" . }}-configmap
            {{- end }}
            {{- if .Values.secretOverrides }}
            - secretRef:
                name: {{ include "think-manifesto.fullname" . }}-secret
            {{- end }}
          resources:
            {{- toYaml .Values.resources | nindent 12 }}
5. Traefik 2部署配置

bash

# 添加Traefik官方仓库
helm repo add traefik https://traefik.github.io/charts

# 添加国内镜像仓库加速
helm repo add aliyun https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
helm repo update

# 安装Traefik 2
helm install traefik traefik/traefik \
  --set deployment.kind=DaemonSet \
  --set namespaceOverride=traefik \
  --set service.enabled=false

质量保障与验证流程

本地验证方案

bash

# Helm模板渲染验证
helm template --dry-run --debug \
  --disable-openapi-validation \
  ${chart-name} ./${chart-dir}/
关键验证点
  • 配置模板正确渲染

  • 环境变量注入准确

  • 资源限制配置合理

  • 健康检查端点可达

迁移经验总结

技术收获
  1. Helm vs Kustomize:Helm在第三方Chart管理和模板化方面更具优势

  2. 配置管理:Kubernetes原生配置方案比Nacos更符合云原生标准

  3. 流量管理:Traefik 2提供了更灵活的Ingress路由能力

挑战与解决方案
  1. WebFlux兼容性:放弃兼容路线,采用标准Servlet Filter重构

  2. 配置文件规范:统一使用application.yml格式,避免bootstrap.yml混淆

  3. 请求体处理:确保Filter链中请求流的正确处理

协作价值
  • Azure技术支持团队在Pipeline配置方面提供了专业指导

  • Helm完善的文档体系降低了学习成本

  • 渐进式迁移策略确保了业务连续性

迁移成果

通过本次迁移,我们实现了:

  • 从自维护虚拟机到全托管Kubernetes服务的平滑过渡

  • 安全责任的转移,由Azure负责基础设施安全

  • 部署流程的标准化和自动化

  • 多环境配置的一致性管理

  • 技术栈的现代化升级

这次迁移不仅提升了系统的可靠性和安全性,也为后续的云原生转型奠定了坚实基础。

更多推荐