跳到正文
把 Spring Boot 容器化部署:串成一条链,和那次「能跑不懂」的债
把 Spring Boot 容器化部署:串成一条链,和那次「能跑不懂」的债

把 Spring Boot 容器化部署:串成一条链,和那次「能跑不懂」的债

学了 Java、Docker、Nginx,这篇把它们串成一条部署链。不讲部署清单,讲多阶段构建那次我借的、后来还的债,以及端口只绑回环这个防护判断。

速读#

这篇把 Java、Docker、Nginx 串成一条上线链路,重点不是部署清单,而是补上那次“能跑不懂”的理解债。多阶段构建、Docker 层缓存、端口只绑回环,是这篇最该留下的三个动作。

多阶段构建:我借过、后来还过的债#

直接把 JDK 和源码塞进镜像会很臃肿。多阶段:第一阶段编译,第二阶段只拷贝产物。

FROM maven:3.9-eclipse-temurin-21 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn dependency:go-offline
COPY src ./src
RUN mvn clean package -DskipTests
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
ENTRYPOINT ["java", "-jar", "app.jar"]

拆开看收益很实在:第一阶段带着 Maven 和 JDK,第二阶段只剩 JRE + 一个 jar。构建工具链不进最终镜像,体积从 GB 级掉到几百 MB,攻击面也跟着小——镜像里没有的东西,不用担心被利用。

顺带交代 -DskipTests:它跳过的只是「构建镜像」这一步里的测试,前提是测试在别处跑过——CI 里有独立的测试环节,这行才写得心安;否则等于把「能构建出镜像」当成了「能上线」。

这段 Dockerfile 当初是 AI 生成的,能跑,我直接用了。两天后想加缓存优化,发现自己完全不理解 COPY --from=build,也不知道为什么 pom.xml 单独先拷能加速依赖缓存。

回头从 Docker 层缓存重学,花的时间比一开始自己写还多。这笔「能跑不懂」的债,后来成了我用 AI 生成代码时反复对照的原发场景。

省下的不是时间,是把时间从「现在写」挪到「以后重学」,还附赠「以为会其实不会」的错觉。之后对 AI 生成的构建 / 配置文件,一律「逐行能解释」才算完成。

pom.xml 先拷#

Docker 每条指令一层,层有缓存。

顺序指令为何这样排
COPY pom.xml + 下依赖pom.xml 少变,缓存易命中
COPY src + 打包源码天天变,只让源码层失效

变化频率低的放前面,高的放后面,缓存命中率才高。 这是层缓存的取舍,不是背下来的咒语。缓存也不是永不失效——真改了 pom.xml,依赖层照样重新下载,这是应该的;要防的只是天天变的源码,连坐不常变的依赖层。

端口只绑回环#

services:
api:
ports:
- "127.0.0.1:8080:8080" # 只绑本地,由 Nginx 对外
写法效果
8080:8080默认 0.0.0.0,可能直接公网暴露
127.0.0.1:8080:8080仅本机,外网必须走反代

这和 Cloudflare Tunnel「零入站」是同一防护思路的不同粒度:Tunnel 是连缝都不开;绑回环是开了缝但只开给本机。共同点是默认不对公网暴露,暴露必须是显式决定

多说一句为什么不靠防火墙兜底:Docker 发布端口时直接写 iptables 规则,ufw 的拒绝策略未必拦得住 0.0.0.0 的发布。与其赌防火墙和 Docker 谁的规则先命中,不如让端口从一开始就只存在于回环上。

Terminal window
docker compose up -d --build
docker compose logs -f api

带走两个判断:

  1. 多阶段构建的层缓存搞懂了,才不欠债
  2. 端口绑回环,是防护面的最小动作
版权许可

CC BY-NC-SA 4.0 本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

相关文章

s1oopX

登录 s1oopX