交付一个 Feat Cloud 应用
This content is not available in your language yet.
到了这一页,默认你已经能把应用跑起来了。
现在要解决的问题不再是“怎么写 Controller”,而是“怎么把它交给别的环境去跑”。
本文不追求覆盖所有部署形态,而是基于仓库里的 demo/helloworld_docker 给出一条最短交付路径。
flowchart LR
A["代码可运行"] --> B["mvn clean package"]
B --> C["得到可执行 Jar"]
C --> D{"交付目标是什么?"}
D --> E["先走 JRE 容器"]
D --> F["评估 Native"]
E --> G["调试简单、链路稳定"]
F --> H["启动更快、构建更复杂"]
直接对应这几个文件:
demo/helloworld_docker/pom.xmldemo/helloworld_docker/Dockerfile_jredemo/helloworld_docker/Dockerfile_nativedemo/helloworld_docker/Makefile
如果你在看文档时想对照真实工程,这一组文件就是最好的入口。
先回答一个问题:你要交付什么
Section titled “先回答一个问题:你要交付什么”对于 Feat Cloud 应用,最常见的两种交付物是:
- 一个可执行 Fat Jar
- 一个容器镜像
而容器镜像又常见地分成两种:
- 基于 JRE 运行 Jar
- 基于 GraalVM Native Image 运行本地可执行文件
第一步:先把 Jar 打出来
Section titled “第一步:先把 Jar 打出来”demo/helloworld_docker/pom.xml 里已经给出了典型的 maven-shade-plugin 配置:
<plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.5.0</version> <executions> <execution> <phase>package</phase> <goals> <goal>shade</goal> </goals> <configuration> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> <resource>META-INF/services/tech.smartboot.feat.cloud.CloudService</resource> </transformer> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>tech.smartboot.feat.demo.Bootstrap</mainClass> </transformer> </transformers> </configuration> </execution> </executions></plugin>这里最关键的不是 Shade 本身,而是两件事:
- 合并
CloudService的服务发现文件 - 把启动主类写进 manifest
打包命令:
mvn clean package完成后,target/ 目录下就会出现可执行 Jar。
第二步:决定你是走 JRE 还是 Native
Section titled “第二步:决定你是走 JRE 还是 Native”方案一:JRE 容器
Section titled “方案一:JRE 容器”如果你想要更简单、调试更直接的交付方式,先用 JRE 镜像。
demo/helloworld_docker/Dockerfile_jre:
FROM eclipse-temurin:21.0.7_6-jre-alpineWORKDIR /featCOPY target/helloworld_docker*.jar helloworld.jar
EXPOSE 8080
CMD ["java", "-jar", "helloworld.jar"]这是最容易理解的一种交付方式:
- 你交付的是 Jar
- 容器里只负责提供 JRE
- 启动命令仍然是
java -jar
方案二:Native 容器
Section titled “方案二:Native 容器”如果你的目标是更快启动、更小运行时依赖,可以考虑 Native Image。
demo/helloworld_docker/Dockerfile_native:
FROM container-registry.oracle.com/graalvm/native-image:21-ol8 AS builder
COPY target/helloworld_docker*.jar helloworld.jar
RUN native-image --no-fallback -jar helloworld.jar
FROM ubuntu:18.04EXPOSE 8080
COPY --from=builder /app/helloworld helloworldENTRYPOINT ["/helloworld"]这条路径的核心区别是:
- 前半段用 GraalVM 生成本地可执行文件
- 后半段运行时不再需要完整 JRE
第三步:构建并运行
Section titled “第三步:构建并运行”示例工程里给了一个简单的 Makefile:
native: mvn clean package podman build -t feat-docker:native -f Dockerfile_native podman build -t feat-docker:jre -f Dockerfile_jre如果你使用 docker,把 podman 替换掉即可。
手动执行也很直接:
mvn clean packagedocker build -t feat-docker:jre -f Dockerfile_jre demo/helloworld_dockerdocker run -p 8080:8080 feat-docker:jre然后访问:
http://localhost:8080/hello先选 JRE 容器,如果:
Section titled “先选 JRE 容器,如果:”- 你更在乎交付简单
- 你还在频繁调试
- 你希望运行环境和本地 Java 行为更接近
再考虑 Native,如果:
Section titled “再考虑 Native,如果:”- 你很在意启动时间
- 你部署在资源敏感环境
- 你已经能稳定构建并运行普通 Jar 版本
不要一开始就把 Native 当默认路径。
先把 JRE 交付链路跑通,再上 Native,通常更稳。
实际交付时最容易漏掉什么
Section titled “实际交付时最容易漏掉什么”1. 忘了配置 Shade 插件
Section titled “1. 忘了配置 Shade 插件”结果通常是:
- Jar 打出来了
- 但运行时找不到 Feat Cloud 生成的服务信息
2. mainClass 写错
Section titled “2. mainClass 写错”结果是 Jar 可以打包,但不能直接启动。
3. Dockerfile 的 COPY 路径对不上
Section titled “3. Dockerfile 的 COPY 路径对不上”尤其是模块项目里,经常会因为构建目录和 Docker build 上下文不一致导致文件根本没复制进去。
Jar 能跑,本地正常,但容器里启动失败
Section titled “Jar 能跑,本地正常,但容器里启动失败”优先检查:
- Jar 是否真的被复制进容器
mainClass是否正确- 容器日志里有没有类路径或资源加载失败
Native 构建失败
Section titled “Native 构建失败”先不要马上怀疑业务代码。
优先检查:
- 当前环境是否具备 GraalVM Native Image 构建能力
- 你是不是已经先验证过普通 JRE 容器版本
- 依赖和构建镜像是否匹配
为什么这页不详细展开 Kubernetes、systemd、Helm
Section titled “为什么这页不详细展开 Kubernetes、systemd、Helm”因为这页的目标是先交付出“第一个可部署产物”。
更复杂的运维编排属于后续工程体系,而不是 Feat 文档主线的第一步。
- 为 Feat 服务启用 HTTPS:如果你准备把服务接到真实网络环境
- Native Image 支持:如果你要继续深入原生镜像这条路径
- 常见问题解答:部署阶段遇到现象时先从这里排查