从零搭建一个微服务:我是如何用Spring Boot + Docker + K8s踩遍坑又爬出来的 从零搭建一个微服务我是如何用Spring Boot Docker K8s踩遍坑又爬出来的去年接手公司用户中心重构项目时我面临着一个典型的技术升级场景老旧单体架构的用户系统已经无法支撑业务快速增长每次发布都需要停机维护扩展性几乎为零。作为团队里第一个吃螃蟹的人我决定用Spring Boot Docker Kubernetes这套云原生组合拳重构系统。没想到这个决定让我开启了为期三个月的花式踩坑之旅今天就把这段实战经历完整分享给大家。1. 微服务拆分与Spring Boot实战1.1 领域驱动设计实践用户管理系统看似简单但包含用户认证、权限管理、个人信息等复杂领域。我参考《领域驱动设计》中的限界上下文概念将系统拆分为四个微服务服务名称职责范围技术栈auth-service登录/注册/JWT签发Spring Security OAuth2user-service用户基础信息管理Spring Data JPApermission-service角色权限管理Spring Cachegateway统一入口/路由转发Spring Cloud Gateway踩坑记录最初按功能模块拆分导致服务间循环依赖。后来改用事件驱动架构通过Spring Cloud Stream实现服务间解耦// 用户注册事件发布示例 Autowired private StreamBridge streamBridge; public void register(User user) { userRepository.save(user); streamBridge.send(userRegistered-out-0, new UserEvent(user.getId(), REGISTER)); }1.2 Spring Boot的魔鬼细节在开发auth-service时我遇到了Spring Security的经典配置问题。以下是血泪总结的OAuth2最佳配置# application-security.yml security: oauth2: client: registration: github: clientId: ${GITHUB_CLIENT_ID} clientSecret: ${GITHUB_SECRET} scope: user:email provider: github: userInfoUri: https://api.github.com/user注意永远不要将密钥硬编码在配置文件中使用环境变量或Vault等密钥管理工具2. 容器化过程中的生死时速2.1 Dockerfile优化指南最初我的Dockerfile简单粗暴地直接打包整个项目导致镜像体积达到800MB。经过多次优化后最终采用多阶段构建将镜像压缩到95MB# 构建阶段 FROM maven:3.8.4-jdk-11 AS build COPY src /usr/src/app/src COPY pom.xml /usr/src/app RUN mvn -f /usr/src/app/pom.xml clean package # 运行阶段 FROM openjdk:11-jre-slim COPY --frombuild /usr/src/app/target/*.jar /app.jar EXPOSE 8080 ENTRYPOINT [java,-jar,/app.jar]关键优化点使用alpine或slim基础镜像清理Maven构建缓存合并RUN指令减少镜像层数使用.dockerignore排除无关文件2.2 容器网络迷局在本地测试时服务间调用一切正常。但上Docker后出现各种连接超时最终发现是容器DNS解析问题。解决方案是在docker-compose中显式配置网络别名services: user-service: networks: microservice-net: aliases: - user networks: microservice-net: driver: bridge3. Kubernetes部署的黑暗森林3.1 Deployment配置陷阱第一次部署到Minikube时Pod不断重启。查看日志才发现是内存不足导致OOMKilled。修正后的资源配置示例apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: template: spec: containers: - name: user resources: requests: memory: 512Mi cpu: 500m limits: memory: 1Gi cpu: 800m重要经验永远设置resources.requests和limits特别是生产环境3.2 服务发现惊魂记当auth-service尝试调用user-service时出现UnknownHostException。原来Kubernetes服务发现需要特别注意使用ClusterIP类型的Service调用时使用service-name.namespace.svc.cluster.local格式在Deployment中配置readinessProbe# user-service的Service定义 apiVersion: v1 kind: Service metadata: name: user-service spec: selector: app: user ports: - protocol: TCP port: 8080 targetPort: 80804. 可观测性体系建设4.1 监控三板斧系统上线后最难的是问题排查我逐步建立了完整的监控体系指标监控Prometheus Grafana# Prometheus抓取配置示例 - job_name: spring-actuator metrics_path: /actuator/prometheus static_configs: - targets: [user-service:8080]日志收集EFK栈ElasticsearchFluentdKibana分布式追踪Jaeger实现调用链追踪4.2 告警配置艺术好的告警规则应该具备明确的阈值如CPU80%持续5分钟分级告警Warning/Critical合理的静默策略# Prometheus告警规则示例 - alert: HighMemoryUsage expr: container_memory_usage_bytes{containeruser} 900 * 1024 * 1024 for: 5m labels: severity: critical annotations: summary: High memory usage on {{ $labels.pod }}5. 持续交付流水线5.1 GitOps实践采用ArgoCD实现声明式部署代码仓库结构如下infra/ kustomize/ base/ deployment.yaml service.yaml overlays/ production/ kustomization.yaml staging/ kustomization.yaml5.2 安全扫描集成在CI流水线中加入Trivy镜像漏洞扫描# GitHub Actions示例 - name: Scan image uses: aquasecurity/trivy-actionmaster with: image-ref: user-service:${{ github.sha }} format: table exit-code: 1 severity: CRITICAL这段云原生实践让我深刻体会到技术方案没有银弹每个架构决策都需要权衡。比如服务粒度过细会增加运维复杂度过度容器化可能得不偿失。现在回头看那些深夜调试的崩溃时刻反而成了最宝贵的技术财富。