技术探索与实践踩坑记录:一个试用期萌新的 Spring Boot + K8s 云原生历险记

曹勇~
2025-12-13 10:18
阅读 243

大家好,我是小张,刚入职上海这家做 SaaS 平台的创业公司不到两个月,目前还在试用期,每天战战兢兢如履薄冰。坐标浦东张江,租了个老小区的一居室,走路十分钟到公司——主要是为了省打车钱,毕竟试用期工资还没转正呢 😅。

我之前在上一家公司主要搞后端开发,技术栈偏传统,Spring Boot + MySQL + Redis 那一套玩得挺熟。但新公司一上来就给我整了个“惊喜”:微服务架构、Kubernetes 编排、CI/CD 全自动化、服务网格……好家伙,直接给我干懵了。虽然简历里写了“熟悉云原生”,但实际上也就是本地 minikube 跑过几个 demo,真正上生产?不存在的。

上周五晚上,团队临时接到需求:要在双11前上线一个实时库存预警模块,要求高可用、低延迟、能快速扩缩容。Leader 直接点名:“小张,你不是说对 K8s 熟吗?这模块你来搭,Spring Boot 写服务,部署上 K8s,下周一演示。”
我当时内心 OS:我熟的是 Hello World on K8s 啊!

但没办法,试用期嘛,领导让你上,你就得冲。于是,一场充满 Bug、报错、重启和深夜泡面的技术探索之旅,正式开启。


从“Hello World”到“Why World”

项目需求其实不复杂:用户下单时,调用库存服务检查商品余量,如果低于阈值(比如 10 件),就发一条消息到 Kafka,触发预警通知。典型的 CRUD + 异步解耦场景。

我第一反应:这还不简单?Spring Boot + JPA + Kafka Producer,本地跑通,打包成 JAR,扔进 Dockerfile,推到镜像仓库,写个 Deployment 和 Service,apply 到 K8s,搞定!

结果第一天就翻车了。

坑一:本地能跑,K8s 上连数据库都连不上?

我把服务打包成镜像,部署到测试集群,Pod 一启动就 CrashLoopBackOff。查日志:

Caused by: com.mysql.cj.jdbc.exceptions.CommunicationsException: 
Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. 
The driver has not received any packets from the server.

??? 我本地连得好好的啊!MySQL 在另一个 Pod,Service 名叫 mysql-service,我在 application.yml 里写的也是这个地址。

后来问了运维大哥(他看我的眼神充满了怜悯),才知道:K8s 里 Pod 之间通信默认走 ClusterIP,但如果你没配 NetworkPolicy,或者 DNS 解析有问题,就会连不上

更关键的是,我本地测试用的是 localhost:3306,而 K8s 里要用 mysql-service.default.svc.cluster.local:3306。虽然理论上 mysql-service 就够了,但有些网络插件(比如我们用的 Calico)对短域名解析有点脾气。

解决方案

  1. 确保 MySQL Pod 正常运行,Service 类型为 ClusterIP
  2. 在 Spring Boot 的 application-prod.yml 中明确写全限定域名:
    spring:
      datasource:
        url: jdbc:mysql://mysql-service.default.svc.cluster.local:3306/inventory_db?useSSL=false&serverTimezone=UTC
    
  3. 加个 initContainer 测试连通性(后面会讲)

💡 实战经验:别信“本地能跑线上就能跑”这种鬼话。K8s 是另一个世界,网络、存储、权限全是新规则。


工具链拉满:从手搓 YAML 到 Helm + ArgoCD

一开始我傻乎乎地手写 Deployment、Service、ConfigMap、Secret,每次改个配置就要 kubectl apply -f 一堆文件。有次改错了一个缩进,Pod 起不来,排查半小时。

直到隔壁工位的老王(入职三年的老油条)看不下去了:“兄弟,现在谁还手写 YAML 啊?用 Helm 啊!再配个 ArgoCD,GitOps 搞起来,改代码自动部署,多香。”

我:GitOps?那是什么?能吃吗?

被科普后才知道,工具链现代化是云原生的基本修养。于是我花了一天时间重构部署流程:

  • 用 Helm Chart 管理应用模板
  • ConfigMap 存放非敏感配置(比如 Kafka 地址)
  • Secret 存放数据库密码(Base6 wrap 过)
  • ArgoCD 监听 Git 仓库的 manifests/ 目录,自动同步到集群

Helm values.yaml 示例:

replicaCount: 2

image:
  repository: registry.company.com/inventory-service
  tag: "v1.2.0"
  pullPolicy: IfNotPresent

env:
  SPRING_PROFILES_ACTIVE: prod
  KAFKA_BOOTSTRAP_SERVERS: kafka-service:9092

resources:
  limits:
    cpu: "500m"
    memory: "512Mi"
  requests:
    cpu: "200m"
    memory: "256Mi"

Deployment 模板里引用:

env:
  - name: SPRING_PROFILES_ACTIVE
    value: {{ .Values.env.SPRING_PROFILES_ACTIVE }}
  - name: KAFKA_BOOTSTRAP_SERVERS
    value: {{ .Values.env.KAFKA_BOOTSTRAP_SERVERS }}

效果立竿见影:改完代码 → 提交 → CI 自动 build 镜像 → 推送 → ArgoCD 检测到 manifest 更新 → 自动 rollout 新版本。再也不用手动 kubectl 了!

🤯 感悟:工具不是炫技,而是解放生产力。试用期程序员的时间很宝贵,别把青春浪费在重复劳动上。


健康检查:Liveness vs Readiness,别再搞混了!

服务终于能起来了,但 QA 同学反馈:“你们服务有时候请求超时,但 Pod 明明是 Running 状态!”

我一脸懵,查 K8s 事件,发现 Pod 被频繁重启。再看日志,发现应用启动要 30 秒(因为要初始化缓存、连接池等),但我的 Liveness Probe 设置得太激进:

livenessProbe:
  httpGet:
    path: /actuator/health
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 5

问题来了:应用还没完全启动,健康检查就失败,K8s 认为 Pod 不健康,直接 kill 重建。恶性循环!

后来查文档才明白:

  • Readiness Probe:决定 Pod 是否准备好接收流量。没 ready 就不会加入 Service 的 endpoints。
  • Liveness Probe:决定 Pod 是否还活着。失败就重启。

正确姿势应该是:

readinessProbe:
  httpGet:
    path: /actuator/health/readiness
    port: 8080
  initialDelaySeconds: 20  # 给足启动时间
  periodSeconds: 10

livenessProbe:
  httpGet:
    path: /actuator/health/liveness
    port: 8080
  initialDelaySeconds: 60  # 等应用稳定后再检查
  periodSeconds: 30

同时,在 Spring Boot 2.3+ 中,需要开启 Probes 支持:

management:
  endpoint:
    health:
      probes:
        enabled: true
  endpoints:
    web:
      exposure:
        include: health,info

这样,/actuator/health/readiness 会检查外部依赖(DB、Kafka),而 /actuator/health/liveness 只检查 JVM 是否存活。

最佳实践永远不要用同一个 endpoint 做 liveness 和 readiness。否则,外部依赖抖动(比如 DB 瞬间不可用)会导致整个 Pod 被误杀。


日志、监控、告警:别等线上炸了才想起它们

上线前一天,Leader 问我:“日志怎么查?CPU 飙高怎么定位?有没有告警?”

我:……(冷汗)

赶紧补课。我们公司用的是 EFK(Elasticsearch + Fluentd + Kibana)栈,但我的服务日志格式还是默认的 console 格式,没法结构化查询。

改造日志输出

  1. 引入 logstash-logback-encoder
  2. 输出 JSON 格式日志
<!-- pom.xml -->
<dependency>
    <groupId>net.logstash.logback</groupId>
    <artifactId>logstash-logback-encoder</artifactId>
    <version>7.4</version>
</dependency>
<!-- logback-spring.xml -->
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
    <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
        <providers>
            <timestamp/>
            <logLevel/>
            <loggerName/>
            <message/>
            <mdc/> <!-- 支持 traceId 注入 -->
            <stackTrace/>
        </providers>
    </encoder>
</appender>

这样,每条日志都会带 traceIdlevellogger 等字段,Kibana 里可以直接过滤、聚合。

监控方面,Spring Boot Actuator + Micrometer + Prometheus 几乎是标配:

management:
  metrics:
    tags:
      application: inventory-service
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus

然后在 K8s Service 上加 annotation,让 Prometheus 自动发现:

metadata:
  annotations:
    prometheus.io/scrape: "true"
    prometheus.io/port: "8080"
    prometheus.io/path: "/actuator/prometheus"

最后配个 Grafana 面板,CPU、内存、HTTP 请求数、JVM GC 时间……一目了然。

🔔 血泪教训可观测性不是可选项,是保命符。试用期程序员最怕线上事故,有了日志+监控,至少能快速定位,而不是背锅。


性能调优:从 OOM 到丝滑运行

服务上线后,压力测试一来,Pod 内存飙升,直接 OOMKilled。

查 heap dump 发现:Kafka Producer 没关闭,连接池泄漏;另外,Spring Boot 默认的 Tomcat 线程池太大(200),并发一高就吃光内存。

优化措施

  1. 合理设置 JVM 参数(通过环境变量注入):

    env:
      - name: JAVA_OPTS
        value: "-Xmx384m -Xms256m -XX:+UseG1GC"
    
  2. 限制 Tomcat 线程数

    server:
      tomcat:
        max-threads: 50
        min-spare-threads: 10
    
  3. Kafka Producer 复用(别每次 new):

    @Bean
    public KafkaTemplate<String, String> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
    
  4. 加资源 limit/request(前面 Helm 已配置)

优化前后对比:

指标 优化前 优化后
平均内存 600Mi 320Mi
P99 延迟 1200ms 180ms
Pod 稳定性 频繁 OOM 7 天无重启

总结:试用期程序员的云原生生存指南

折腾了两周,这个库存预警服务终于稳稳跑在 K8s 上,双11 期间扛住了流量高峰,Leader 还夸我“上手快”。虽然我知道他只是客套,但心里还是美滋滋的。

回顾这段经历,踩过的坑、熬过的夜、查过的文档,都变成了实战经验。分享几点心得:

  1. 别怕问:运维、测试、老员工都是宝藏。一句“这个我不太熟,能请教下吗?”比自己死磕三小时强。
  2. 工具提效:Helm、ArgoCD、Prometheus 这些不是摆设,用好了能让你从“救火队员”变成“架构师”。
  3. 健康检查要分清:Liveness 和 Readiness 的区别,值得每个 K8s 开发者刻在 DNA 里。
  4. 可观测性先行:日志、监控、告警,上线前必须配齐。别等事故发生了才后悔。
  5. 资源要克制:Java 应用在容器里别乱设 Xmx,留点余量给 OS 和其他进程。

最后,作为试用期员工,我深刻体会到:技术深度重要,但工程化思维更重要。能写出优雅代码是本事,能让服务稳定跑在生产环境,才是真本事。

这篇文章写完,我也该去改下一个需求了——听说要上 Service Mesh?Istio?救命……

(完)

P.S. 如果你也正在试用期,别慌。每个大佬都曾是菜鸟,只要肯学、肯问、肯踩坑,终会发光。共勉!

评论 0

最热最新
暂无评论
匿名用户Lv.1
0
影响力
0
文章
0
粉丝