news 2026/7/7 10:24:51

基于 Distroless 镜像,移除所有 shell 和包管理器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于 Distroless 镜像,移除所有 shell 和包管理器

帮客户搞定 Spring Boot 生产部署:15 天踩坑与架构决策实录

前不久帮一家做供应链管理的客户重构了他们的后端系统,核心任务是把原本跑在虚拟机上的 Spring Boot 单体应用,迁移到 Kubernetes 集群里,还要保证高并发下的稳定性。客户那边用的是 Java 21 和最新的 Spring Boot 版本,毕竟到了 2026 年,这俩已经是标配了。项目规模不算大,但痛点很明确:老系统启动慢,每次扩容要等好几分钟,而且监控数据散落在各个日志文件里,出了问题根本定位不到根因。说实话,我当时觉得把 Dockerfile 写好、Jenkins 流水线配通就完事了,结果真正上手才发现,云原生部署里的坑比想象中多得多。

容器化与构建:从 JAR 到 Native Image 的挣扎

客户最开始想沿用传统的 JAR 包部署,但我坚持建议他们试试 GraalVM 原生镜像。你知道的,Spring Boot 3.5+ 对 AOT(提前编译)的支持已经非常成熟了,启动速度能压到毫秒级。对于他们那种需要快速弹性缩放的场景,原生镜像简直是救命稻草。

不过,试了一圈发现,迁移过程并不顺畅。我们在 CI/CD 流水线里用了 Kaniko 来构建镜像,因为它不需要 Docker Daemon,安全性高,适合在 K8s 内部署。但问题出在反射处理上。我们的业务代码里用了很多第三方库的动态代理,GraalVM 在编译时需要提前知道这些类的元数据。

当时我觉得只要加几个reflection-config.json文件就行,结果构建一直报错,说找不到某些类。排查了半天,发现是因为 Spring AI 的集成引入了一些新的依赖链,这些依赖在原生环境下行为变了。有意思的是,我们最终没有去手动维护那些配置文件,而是转向了更自动化的方案——引入了 Spring Boot 的原生代理生成插件。

```dockerfile

基于 Distroless 镜像,移除所有 shell 和包管理器

FROM gcr.io/distroless/java21-debian12:nonroot

将原生可执行文件复制进去

COPY target/my-app /home/app/my-app

设置运行用户

USER nonroot

暴露端口

EXPOSE 8080

启动命令

ENTRYPOINT ["/home/app/my-app"]
```

这里做了一个关键的架构决策:放弃 Alpine 镜像。虽然它体积小,但 musl libc 和 glibc 的差异在原生编译后容易引发隐蔽的系统调用错误。我们最终选择了 Distroless,虽然体积稍大一点,但胜在稳定,攻击面也最小。

可观测性与监控:OpenTelemetry 的统一之路

系统跑起来后,另一个头疼的问题是监控。以前我们用 Micrometer 对接 Prometheus,现在得全面转向 OpenTelemetry (OTel)。客户希望实现 Tracing、Metrics 和 Logs 的统一,这样在排查问题时,能从链路追踪直接关联到具体的日志行。

刚开始配置 OTel Agent 时,我发现 Trace 数据丢失严重。我以为是网络问题,调大了超时时间也没用。后来冷静下来想了想,可能是采样策略的问题。在生产环境全量采样肯定扛不住存储压力,但太稀疏又抓不到关键异常。

最终我们定了一个混合采样策略:对于错误请求和慢查询(P99 > 1s),强制采样;其他请求按 10% 随机采样。这样既保证了关键数据的完整性,又控制了成本。

```java
// 自定义 Sampler 配置示例
public class ProductionSampler implements Sampler {
@Override
public SamplingResult decide(SamplingContext context) {
// 如果是错误或者慢请求,永远采样
if (context.getAttributes().containsKey("error") ||
context.getDuration().toMillis() > 1000) {
return SamplingResult.recordAndSample();
}
// 否则 10% 概率采样
return Math.random() < 0.1 ? SamplingResult.recordAndSample() : SamplingResult.drop();
}
}
```

日志方面,我们也做了调整。不再输出纯文本,而是全部改为 JSON 格式。这样 Loki 解析起来非常快,而且可以直接通过 Trace ID 关联到 OTel 的数据。说实话,一开始团队挺抵触改日志格式的,因为本地调试看着费劲,但一旦上线,这种结构化日志的价值就体现出来了,查问题效率提升了不止一倍。

性能调优与动态配置

最后聊聊性能。Spring Boot 3.5+ 默认启用了 ZGC,这对低延迟应用非常友好。但在高并发场景下,我们发现 Full GC 还是偶尔会发生。排查发现,是因为一些旧的缓存实现没有及时释放引用。我们把这些缓存替换成了基于 Caffeine 的本地缓存,并设置了合理的过期策略。

配置管理上,客户之前用的是硬编码在 application.yml 里的方式,改个参数要重新打包镜像。这次我们接入了 Nacos 作为配置中心,实现了配置的热更新。不过,这里有个坑:有些核心配置(比如数据库连接池大小)修改后,Spring Boot 不会自动刷新底层连接池。我们不得不写了一个自定义的 RefreshScope 处理器,监听特定配置的变化,然后手动触发连接池的重置。

当时我觉得这样处理有点重,但为了确保生产环境的安全,没有连接池的中断比任何优雅的重启都重要。

这一套流程走下来,客户的系统启动时间从原来的 45 秒降到了 3 秒,内存占用减少了 40%,最重要的是,现在出了 bug,运维能在 5 分钟内定位到问题根源。虽然过程挺折腾,但看到效果,还是觉得值得。

本文基于实际项目经验整理,如有技术问题欢迎私信交流。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/7 10:24:41

终极指南:3分钟搞定Windows与Office智能激活

终极指南&#xff1a;3分钟搞定Windows与Office智能激活 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 还在为Windows系统激活和Office办公软件激活而烦恼吗&#xff1f;每次重装电脑后都要四处…

作者头像 李华
网站建设 2026/7/7 10:22:48

STM32与WSEN-ISDS实现6DoF运动追踪方案

1. 项目背景与核心需求在工业自动化、无人机控制和可穿戴设备领域&#xff0c;精确追踪物体在三维空间中的运动状态一直是个关键挑战。WSEN-ISDS&#xff08;2536030320001&#xff09;这款MEMS传感器与STM32F100ZE微控制器的组合&#xff0c;恰好为解决这个问题提供了高性价比…

作者头像 李华
网站建设 2026/7/7 10:17:28

KMP 入门:next 数组存的不是玄学,是最长相等前后缀

KMP 入门&#xff1a;next 数组存的不是玄学&#xff0c;是最长相等前后缀 一、KMP 难&#xff0c;不是因为代码长 字符串匹配里&#xff0c;KMP 经常被认为难背。真正难点不是代码&#xff0c;而是 next 或 lps 数组含义不清。很多人把它当成“失败后跳到哪里”的魔法表&#…

作者头像 李华
网站建设 2026/7/7 10:16:12

深入解析KMS_VL_ALL_AIO:智能激活脚本的技术原理与实战指南

深入解析KMS_VL_ALL_AIO&#xff1a;智能激活脚本的技术原理与实战指南 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 在Windows和Office的日常使用中&#xff0c;系统激活始终是困扰技术用户的…

作者头像 李华
网站建设 2026/7/7 10:13:27

Poly/ML:兼容 ML97 标准的 Standard ML 实现,具备多种独特特性!

导航菜单 切换导航&#xff0c;有登录选项&#xff0c;还有外观设置。 平台 AI 代码创作方面&#xff0c;有 GitHub Copilot 借助 AI 编写更优质代码、GitHub Copilot 应用从问题到合并直接代理、MCP 注册表新功能集成外部工具。开发者工作流包括 Actions 自动化任何工作流、Co…

作者头像 李华