当前位置: 代码网 > it编程>编程语言>Java > 使用Maven将Java项目打包为Docker镜像的实战方法

使用Maven将Java项目打包为Docker镜像的实战方法

2026年02月09日 Java 我要评论
引言在云原生时代,容器化(containerization) 已成为现代应用部署的标准范式。docker 凭借其轻量、可移植、一致性的优势,彻底改变了软件交付流程。而作为 java 生态中最主流的构建

引言

在云原生时代,容器化(containerization) 已成为现代应用部署的标准范式。docker 凭借其轻量、可移植、一致性的优势,彻底改变了软件交付流程。而作为 java 生态中最主流的构建工具,maven 如何与 docker 无缝集成,高效地将 spring boot 或普通 java 应用打包为 docker 镜像,是每一位 java 开发者必须掌握的核心技能。

本文将带你从零开始,深入实战 使用 maven 构建 java 项目并生成 docker 镜像的完整流程。我们将覆盖:

  • 传统手动构建镜像的痛点
  • 使用 jib 插件 实现“零 dockerfile”构建
  • 使用 dockerfile + maven resources 插件 精细控制镜像内容
  • 多阶段构建(multi-stage build)优化镜像大小
  • 镜像标签策略、推送私有仓库、ci/cd 集成
  • 安全最佳实践与性能调优

全文包含大量可运行代码示例、mermaid 架构图、真实外链资源(均经验证可访问),助你打造生产级 java 容器化流水线。

为什么需要将 java 项目容器化?

尽管 java 应用“一次编写,到处运行”,但在实际部署中仍面临诸多挑战:

  • 环境差异:开发机(macos)、测试服务器(ubuntu)、生产集群(centos)的 jdk 版本、系统库不一致。
  • 依赖冲突:应用依赖的本地库(如 native lib)在不同机器缺失。
  • 部署复杂:需手动配置 jvm 参数、日志路径、启动脚本。
  • 资源隔离差:多个应用共享同一台服务器,互相影响。

docker 的价值

  • 环境一致性:镜像包含 os + jdk + app,彻底消除“在我机器上能跑”问题。
  • 快速部署:docker run 一键启动,无需安装 jdk。
  • 资源隔离:cpu、内存、网络独立分配。
  • 弹性伸缩:配合 kubernetes 轻松实现水平扩展。

传统方式:手写 dockerfile + 手动构建

最直观的方式是先用 maven 打包 jar,再编写 dockerfile 构建镜像。

步骤 1:maven 打包

mvn clean package -dskiptests

生成 target/myapp-1.0.0.jar

步骤 2:编写 dockerfile

# 使用官方 openjdk 17 镜像
from openjdk:17-jdk-slim

# 设置工作目录
workdir /app

# 复制 jar 文件到容器
copy target/myapp-1.0.0.jar app.jar

# 暴露端口(假设应用监听 8080)
expose 8080

# 启动命令
entrypoint ["java", "-jar", "app.jar"]

步骤 3:构建并运行镜像

# 构建镜像
docker build -t myapp:1.0.0 .

# 运行容器
docker run -d -p 8080:8080 --name myapp-container myapp:1.0.0

优点:简单直观,适合学习
缺点

  • 需维护 dockerfile
  • 构建过程分两步(maven + docker),效率低
  • 镜像体积大(含完整 jdk)
  • 无法利用 maven 生命周期自动化

方案一:使用 google jib 插件 —— “零 dockerfile” 构建 

jib 是 google 开源的 maven/gradle 插件,专为 java 应用容器化设计。它无需 dockerfile、无需本地 docker daemon,直接从 maven 构建镜像并推送到仓库。

核心优势

  • 分层构建:将应用拆分为 dependenciessnapshot dependenciesresourcesclasses 多层,仅变更代码时只需上传少量层。
  • 安全:不依赖本地 docker,避免权限问题。
  • 快速:直接与 registry 通信,跳过本地镜像存储。
  • 小体积:默认使用 distroless 基础镜像(仅含必要运行时)。

官方 github(持续活跃):https://github.com/googlecontainertools/jib

实战:spring boot 项目集成 jib

1. 在pom.xml中添加插件

<build>
    <plugins>
        <plugin>
            <groupid>com.google.cloud.tools</groupid>
            <artifactid>jib-maven-plugin</artifactid>
            <version>3.4.0</version>
            <configuration>
                <!-- 目标镜像名 -->
                <to>
                    <image>myregistry.com/myapp:${project.version}</image>
                </to>
                <!-- 基础镜像(可选) -->
                <from>
                    <image>eclipse-temurin:17-jre-alpine</image>
                </from>
                <!-- 容器配置 -->
                <container>
                    <ports>
                        <port>8080</port>
                    </ports>
                    <environment>
                        <spring_profiles_active>prod</spring_profiles_active>
                    </environment>
                    <creationtime>use_current_timestamp</creationtime>
                </container>
            </configuration>
        </plugin>
    </plugins>
</build>

2. 构建并推送到本地 docker 守护进程

# 构建镜像并加载到本地 docker
mvn jib:dockerbuild

# 查看镜像
docker images | grep myapp

3. 推送到远程仓库(如 docker hub)

# 先登录
docker login

# 推送(需在 <to> 中配置镜像名,如 yourname/myapp)
mvn jib:build

提示:若使用私有仓库(如 harbor、nexus),需配置认证信息(见后文)。

jib 分层机制 mermaid 图示

优势体现:当仅修改业务代码时,只有 classes layer 变更,推送速度极快!

方案二:dockerfile + maven resources 插件 —— 精细控制

当需要完全掌控镜像内容(如添加 shell 脚本、配置文件、非 java 资源),可结合 maven resources 插件dockerfile

项目结构设计

myapp/
├── src/
├── pom.xml
├── docker/
│   ├── dockerfile
│   └── entrypoint.sh
└── target/
    └── myapp-1.0.0.jar

1. 配置 maven 将资源复制到 target/docker

pom.xml 中添加:

<build>
    <plugins>
        <!-- 复制 docker 相关文件到 target/docker -->
        <plugin>
            <artifactid>maven-resources-plugin</artifactid>
            <version>3.3.1</version>
            <executions>
                <execution>
                    <id>copy-docker-resources</id>
                    <phase>prepare-package</phase>
                    <goals>
                        <goal>copy-resources</goal>
                    </goals>
                    <configuration>
                        <outputdirectory>${project.build.directory}/docker</outputdirectory>
                        <resources>
                            <resource>
                                <directory>docker</directory>
                                <filtering>false</filtering>
                            </resource>
                        </resources>
                    </configuration>
                </execution>
            </executions>
        </plugin>

        <!-- 打包 jar -->
        <plugin>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-maven-plugin</artifactid>
        </plugin>
    </plugins>
</build>

2. 编写增强版 dockerfile

docker/dockerfile:

# 构建阶段:使用 jdk 编译(若需在容器内编译)
# from maven:3.8.6-openjdk-17 as builder
# copy . /app
# workdir /app
# run mvn clean package -dskiptests

# 运行阶段:仅使用 jre
from eclipse-temurin:17-jre-alpine

# 安装必要工具(如 curl、bash)
run apk add --no-cache bash curl

# 创建应用用户(安全最佳实践)
run addgroup -g 1001 -s appuser && \
    adduser -u 1001 -s appuser -g appuser

# 设置工作目录
workdir /app

# 复制 jar 和启动脚本
copy myapp-*.jar app.jar
copy entrypoint.sh /app/entrypoint.sh

# 赋予执行权限
run chmod +x /app/entrypoint.sh

# 切换到非 root 用户
user appuser

# 暴露端口
expose 8080

# 启动脚本(可处理信号、设置 jvm 参数等)
entrypoint ["/app/entrypoint.sh"]

docker/entrypoint.sh:

#!/bin/bash
set -e

# 设置默认 jvm 参数
jvm_opts="${jvm_opts:-"-xms256m -xmx512m"}"

# 处理优雅关闭(接收 sigterm)
function shutdown() {
    echo "shutting down gracefully..."
    kill -term "$pid"
    wait "$pid"
    exit 0
}

# 启动 java 应用
java $jvm_opts -jar app.jar &
pid=$!

# 注册信号处理器
trap shutdown sigterm sigint

# 等待应用结束
wait $pid

3. 构建镜像

# 先执行 maven 构建(复制资源 + 打包 jar)
mvn clean package -dskiptests

# 再构建 docker 镜像
docker build -t myapp:1.0.0 target/docker

适用场景

  • 需要自定义启动逻辑
  • 集成非 java 组件(如 python 脚本)
  • 严格的安全合规要求(非 root 用户、最小化 os)

多阶段构建(multi-stage build):极致优化镜像大小

java 应用镜像常因包含完整 jdk 而臃肿(>500mb)。多阶段构建 可将构建环境与运行环境分离,显著减小体积。

示例:spring boot + multi-stage

# 第一阶段:构建
from maven:3.8.6-eclipse-temurin-17 as builder

# 复制 pom.xml 和源码
copy pom.xml .
copy src ./src

# 执行 maven 构建(缓存依赖)
run mvn clean package -dskiptests

# 第二阶段:运行
from eclipse-temurin:17-jre-alpine

# 从 builder 阶段复制 jar
copy --from=builder /target/*.jar app.jar

expose 8080
entrypoint ["java", "-jar", "app.jar"]

构建效果对比

方式镜像大小是否含 maven/jdk
单阶段(openjdk:17)~450 mb
多阶段(jre-alpine)~180 mb
jib(distroless)~120 mb

建议:生产环境优先使用 jib + distroless多阶段 + alpine

镜像标签策略:语义化版本与 git commit id

合理的镜像标签(tag)便于追踪和回滚。

推荐策略

场景标签示例说明
发布版本v1.2.0与 git tag 对应
开发快照dev-abc1234dev- + git commit short sha
最新稳定版latest谨慎使用,避免意外升级

在 maven 中动态生成标签

利用 git-commit-id-plugin 获取 git 信息:

<plugin>
    <groupid>pl.project13.maven</groupid>
    <artifactid>git-commit-id-plugin</artifactid>
    <version>8.0.2</version>
    <executions>
        <execution>
            <goals>
                <goal>revision</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <verbose>false</verbose>
        <dateformat>yyyy.mm.dd hh:mm:ss</dateformat>
        <generategitpropertiesfile>true</generategitpropertiesfile>
        <includeonlyproperties>
            <includeonlyproperty>^git.commit.id.abbrev$</includeonlyproperty>
        </includeonlyproperties>
    </configuration>
</plugin>

然后在 jib 配置中使用:

<configuration>
    <to>
        <image>myapp:dev-${git.commit.id.abbrev}</image>
    </to>
</configuration>

插件官网:https://github.com/git-commit-id/git-commit-id-maven-plugin

推送镜像到私有仓库:harbor/nexus 示例

企业通常使用私有镜像仓库保障安全。

1. 配置 jib 推送至 harbor

<configuration>
    <to>
        <image>harbor.example.com/project/myapp:${project.version}</image>
        <auth>
            <username>admin</username>
            <password>harbor12345</password>
        </auth>
    </to>
</configuration>

安全提示:密码不应硬编码!应使用 maven settings 或 ci/cd secrets。

2. 使用 maven settings 管理凭证

~/.m2/settings.xml:

<settings>
    <servers>
        <server>
            <id>harbor-registry</id>
            <username>admin</username>
            <password>harbor12345</password>
        </server>
    </servers>
</settings>

pom.xml 中引用:

<configuration>
    <to>
        <image>harbor.example.com/project/myapp</image>
        <auth>
            <username>${settings.servers.harbor-registry.username}</username>
            <password>${settings.servers.harbor-registry.password}</password>
        </auth>
    </to>
</configuration>

3. ci/cd 中使用 secrets(github actions 示例)

- name: build and push with jib
  run: mvn jib:build
  env:
    jib_registry_username: ${{ secrets.harbor_user }}
    jib_registry_password: ${{ secrets.harbor_token }}

github secrets 文档:https://docs.github.com/en/actions/security-guides/encrypted-secrets

安全最佳实践:扫描漏洞与最小化攻击面

容器镜像可能包含已知漏洞(cve),需主动防御。

1. 使用 trivy 扫描镜像

trivy 是开源漏洞扫描器。

# 安装 trivy(macos)
brew install aquasecurity/trivy/trivy

# 扫描本地镜像
trivy image myapp:1.0.0

输出示例:

myapp:1.0.0 (alpine 3.17.2)
============================
total: 2 (unknown: 0, low: 1, medium: 1, high: 0, critical: 0)

2. 在 ci 中集成扫描

- name: scan docker image
  run: |
    docker build -t myapp:latest .
    trivy image --exit-code 1 --severity high,critical myapp:latest

trivy 官网(活跃维护):https://aquasecurity.github.io/trivy/

3. 其他安全建议

  • 使用非 root 用户运行(见前文 dockerfile)
  • 禁用 shell:distroless 镜像默认无 shell,防止入侵后执行命令
  • 定期更新基础镜像:订阅安全公告,及时 rebuild

性能调优:jvm 与容器资源匹配

容器环境中的 jvm 需正确识别 cpu/内存限制。

问题:jvm 忽略 docker 内存限制

旧版 jvm(<8u131, <9)无法感知 -m 512m 等 docker 内存限制,导致 oom。

解决方案

1. 使用新版 jdk(推荐)

jdk 10+ 默认启用 container awareness,jdk 8u191+ 需手动开启:

entrypoint ["java", "-xx:+usecontainersupport", "-jar", "app.jar"]

2. 显式设置堆内存

# 通过环境变量控制
env java_opts="-xmx300m -xms300m"
entrypoint ["sh", "-c", "java $java_opts -jar app.jar"]

3. 使用 jib 自动配置

jib 默认添加 -xx:+usecontainersupport,无需额外配置。

oracle 官方文档:jdk 8 updates for docker

ci/cd 集成:github actions 完整流水线

下面是一个完整的 github actions workflow,实现 代码提交 → 构建 → 扫描 → 推送

name: build and push docker image

on:
  push:
    branches: [ main ]
    tags: [ 'v*' ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: set up jdk 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: cache maven packages
        uses: actions/cache@v3
        with:
          path: ~/.m2/repository
          key: ${{ runner.os }}-maven-${{ hashfiles('**/pom.xml') }}

      - name: build with maven
        run: mvn -b clean verify

      - name: set up docker buildx
        uses: docker/setup-buildx-action@v3

      - name: login to docker hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.dockerhub_username }}
          password: ${{ secrets.dockerhub_token }}

      - name: extract metadata (tags, labels)
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: myname/myapp
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=sha

      - name: build and push with jib
        run: mvn jib:build -djib.to.image=${{ steps.meta.outputs.tags }}

      - name: scan image with trivy
        run: |
          docker pull ${{ steps.meta.outputs.tags }}
          trivy image --exit-code 1 --severity high,critical ${{ steps.meta.outputs.tags }}

流程说明

  1. 拉取代码
  2. 设置 jdk + 缓存 maven 依赖
  3. 执行单元测试
  4. 登录 docker hub
  5. 自动生成语义化标签
  6. 使用 jib 构建并推送
  7. 安全扫描,高危漏洞阻断发布

常见问题排查指南

问题 1:jib 构建失败,提示“cannot connect to the docker daemon”

原因:使用了 jib:build(需推送远程仓库),但未配置 registry 认证。

解决

  • 改用 jib:dockerbuild(仅构建到本地)
  • 或正确配置 <to><auth> 或 maven settings

问题 2:容器启动后立即退出

原因:entrypoint 命令执行完毕,容器生命周期结束。

解决

  • 确保 java 进程在前台运行(不要加 &
  • 检查应用是否抛出异常(docker logs container_name

问题 3:镜像太大,超过 500mb

解决

  • 改用 eclipse-temurin:17-jre-alpine 或 jib 的 distroless
  • 使用多阶段构建
  • 排查是否误将 src/target/ 全量复制

问题 4:时区不正确

解决:在 dockerfile 中设置时区

env tz=asia/shanghai
run ln -snf /usr/share/zoneinfo/$tz /etc/localtime && echo $tz > /etc/timezone

方案对比:jib vs dockerfile + maven 🆚

特性jibdockerfile + maven
是否需要 dockerfile❌ 否✅ 是
是否需要本地 docker❌ 否(jib:build
✅ 是(jib:dockerbuild
✅ 是
镜像分层优化✅ 自动分层⚠️ 需手动设计
构建速度⚡ 极快(仅传变更层)🐢 较慢(全量构建)
控制粒度中(插件配置)✅ 极高(任意命令)
学习成本
适用场景标准 java 应用复杂定制需求

结论

  • 90% 场景用 jib:快速、安全、高效
  • 10% 场景用 dockerfile:需深度定制(如集成 sidecar、特殊 init 流程)

结语:拥抱云原生,从容器化开始

将 java 应用容器化不再是“可选项”,而是现代软件工程的“必修课”。maven 与 docker 的结合,为我们提供了从构建到部署的端到端解决方案。无论是使用 jib 的便捷性,还是 dockerfile 的灵活性,核心目标都是:交付一致、安全、高效的容器镜像

记住:好的容器化不是终点,而是云原生旅程的起点。下一步,你可以探索:

  • 使用 helm 管理 kubernetes 部署
  • 集成 prometheus + grafana 监控
  • 实现蓝绿发布/金丝雀发布

以上就是使用maven将java项目打包为docker镜像的实战方法的详细内容,更多关于maven将java打包为docker镜像的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com