📖 前言
本文深入探讨 Spotify dockerfile-maven-plugin 和 Fabric8 docker-maven-plugin 的多种灵活配置方式,涵盖从基础到高级的各种应用场景。无论你是初学者还是资深开发者,都能找到适合你项目的配置方案。
🏗️ 第一部分:Spotify dockerfile-maven-plugin 深度配置
1.1 基础配置的多种变体
方式一:最小化配置(使用默认值)
java
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<repository>myapp</repository>
<tag>latest</tag>
</configuration>
</plugin>
特点 :使用项目根目录的 Dockerfile,构建上下文为项目目录
方式二:明确指定路径
java
<configuration>
<!-- 明确指定所有路径 -->
<dockerfile>${project.basedir}/src/main/docker/Dockerfile</dockerfile>
<contextDirectory>${project.basedir}/src/main/docker</contextDirectory>
<repository>registry.company.com/${project.groupId}/${project.artifactId}</repository>
<tag>${project.version}</tag>
</configuration>
方式三:多标签配置
java
<configuration>
<repository>mycompany/myapp</repository>
<!-- 多个标签 -->
<tags>
<tag>${project.version}</tag>
<tag>latest</tag>
<tag>${git.commit.id.abbrev}</tag>
<tag>${maven.build.timestamp}</tag>
</tags>
</configuration>
1.2 认证配置的5种方式
方式1:Maven settings.xml(最安全)
java
<!-- ~/.m2/settings.xml -->
<settings>
<servers>
<!-- 多个仓库配置 -->
<server>
<id>docker.io</id>
<username>${env.DOCKERHUB_USER}</username>
<password>${env.DOCKERHUB_TOKEN}</password>
</server>
<server>
<id>registry.cn-beijing.aliyuncs.com</id>
<username>${env.ALIYUN_USER}</username>
<password>${env.ALIYUN_PASSWORD}</password>
<configuration>
<email>user@company.com</email>
</configuration>
</server>
<server>
<id>harbor.company.com</id>
<username>robot$project+deploy</username>
<password>${env.HARBOR_ROBOT_TOKEN}</password>
</server>
</servers>
</settings>
插件配置:
java
<configuration>
<useMavenSettingsForAuth>true</useMavenSettingsForAuth>
<!-- 可选:指定serverId -->
<serverId>registry.cn-beijing.aliyuncs.com</serverId>
</configuration>
方式2:环境变量
bash
# Linux/Mac
export DOCKER_USERNAME="username"
export DOCKER_PASSWORD="password"
export DOCKER_EMAIL="email@company.com"
# Windows
set DOCKER_USERNAME=username
set DOCKER_PASSWORD=password
方式3:系统属性
java
# 命令行传递
mvn dockerfile:build \
-Ddockerfile.username=user \
-Ddockerfile.password=pass \
-Ddockerfile.email=email@company.com
方式4:POM 属性(不推荐用于生产)
java
<properties>
<!-- 注意:密码会暴露在POM中 -->
<docker.username>admin</docker.username>
<docker.password>password123</docker.password>
</properties>
<configuration>
<username>${docker.username}</username>
<password>${docker.password}</password>
</configuration>
方式5:加密配置
java
<!-- 使用Maven密码加密 -->
<server>
<id>docker-registry</id>
<username>{jasypt}ENC(密文)</username>
<password>{jasypt}ENC(密文)</password>
</server>
1.3 构建参数的高级配置
动态构建参数
javascript
<configuration>
<buildArgs>
<!-- Maven属性 -->
<APP_VERSION>${project.version}</APP_VERSION>
<BUILD_TIME>${maven.build.timestamp}</BUILD_TIME>
<GIT_COMMIT>${git.commit.id}</GIT_COMMIT>
<GIT_BRANCH>${git.branch}</GIT_BRANCH>
<!-- 环境特定参数 -->
<JAVA_OPTS>-Xmx512m</JAVA_OPTS>
<SPRING_PROFILES_ACTIVE>${profiles.active}</SPRING_PROFILES_ACTIVE>
<!-- 系统属性 -->
<DEBUG_ENABLED>${debug}</DEBUG_ENABLED>
</buildArgs>
</configuration>
条件化构建参数
java
<configuration>
<buildArgs>
<!-- 根据Profile设置不同值 -->
<ENVIRONMENT>${env.type}</ENVIRONMENT>
<LOG_LEVEL>${log.level}</LOG_LEVEL>
</buildArgs>
</configuration>
<profiles>
<profile>
<id>dev</id>
<properties>
<env.type>development</env.type>
<log.level>DEBUG</log.level>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<env.type>production</env.type>
<log.level>WARN</log.level>
</properties>
</profile>
</profiles>
1.4 Dockerfile 模板化
模板 Dockerfile
java
# Dockerfile.template
FROM ${BASE_IMAGE:-openjdk:17-jdk-slim}
ARG APP_VERSION
ARG BUILD_TIME
ARG GIT_COMMIT
ARG ENVIRONMENT
LABEL version="${APP_VERSION}"
LABEL build-time="${BUILD_TIME}"
LABEL git-commit="${GIT_COMMIT}"
LABEL environment="${ENVIRONMENT}"
COPY target/${JAR_FILE} app.jar
EXPOSE ${APP_PORT:-8080}
ENTRYPOINT ["java", "-jar", "/app.jar"]
Maven资源过滤
java
<build>
<resources>
<resource>
<directory>src/main/docker</directory>
<filtering>true</filtering>
<includes>
<include>Dockerfile.template</include>
</includes>
<targetPath>${project.build.directory}/docker</targetPath>
</resource>
</resources>
</build>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<configuration>
<dockerfile>${project.build.directory}/docker/Dockerfile.template</dockerfile>
<buildArgs>
<BASE_IMAGE>${docker.base.image}</BASE_IMAGE>
<APP_PORT>${app.port}</APP_PORT>
</buildArgs>
</configuration>
</plugin>
1.5 多环境配置策略
方案一:Profile 驱动
java
<profiles>
<!-- 开发环境 -->
<profile>
<id>dev</id>
<properties>
<docker.registry>docker.io</docker.registry>
<docker.namespace>dev-team</docker.namespace>
<docker.image.tag>${project.version}-SNAPSHOT</docker.image.tag>
<docker.build.skip>false</docker.build.skip>
</properties>
</profile>
<!-- 测试环境 -->
<profile>
<id>test</id>
<properties>
<docker.registry>harbor.test.com</docker.registry>
<docker.namespace>qa</docker.namespace>
<docker.image.tag>${project.version}-${build.number}</docker.image.tag>
</properties>
</profile>
<!-- 生产环境 -->
<profile>
<id>prod</id>
<properties>
<docker.registry>registry.prod.com</docker.registry>
<docker.namespace>production</docker.namespace>
<docker.image.tag>${project.version}</docker.image.tag>
<docker.base.image>prod-java:17</docker.base.image>
</properties>
</profile>
</profiles>
方案二:属性文件驱动
bash
# src/main/resources/docker/docker-dev.properties
docker.registry=harbor.dev.com
docker.namespace=dev
docker.base.image=openjdk:17-jdk-slim
docker.build.args=-DskipTests
java
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>properties-maven-plugin</artifactId>
<version>1.0.0</version>
<executions>
<execution>
<phase>initialize</phase>
<goals>
<goal>read-project-properties</goal>
</goals>
<configuration>
<files>
<file>src/main/resources/docker/docker-${profiles.active}.properties</file>
</files>
</configuration>
</execution>
</executions>
</plugin>
1.6 构建优化配置
缓存优化
java
<configuration>
<!-- 使用特定缓存目录 -->
<cacheDirectory>${project.build.directory}/docker-cache</cacheDirectory>
<!-- 清理策略 -->
<cleanup>true</cleanup>
<noCache>false</noCache>
<pullNewerImage>true</pullNewerImage>
</configuration>
构建性能优化
java
<configuration>
<!-- 并行构建 -->
<parallel>true</parallel>
<!-- 跳过测试 -->
<skipDockerBuild>${skip.docker}</skipDockerBuild>
<skipDockerTag>${skip.docker.tag}</skipDockerTag>
<skipDockerPush>${skip.docker.push}</skipDockerPush>
<!-- 详细日志 -->
<verbose>true</verbose>
</configuration>
资源限制
java
<configuration>
<!-- Docker构建资源限制 -->
<dockerBuildArgs>
<arg>--memory=2g</arg>
<arg>--memory-swap=2g</arg>
<arg>--cpus=2</arg>
<arg>--ulimit nofile=1024:1024</arg>
</dockerBuildArgs>
</configuration>
1.7 高级执行策略
条件化执行
java
<executions>
<execution>
<id>docker-build</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
<configuration>
<skip>${skip.docker.build}</skip>
</configuration>
</execution>
<execution>
<id>docker-push</id>
<phase>deploy</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<!-- 只在特定分支推送 -->
<skip>${skip.docker.push}</skip>
<tag>${docker.image.tag}</tag>
</configuration>
<!-- 执行条件 -->
<conditions>
<condition>
<and>
<not>
<equals>${env.CI_COMMIT_REF_NAME}</equals>
<value>develop</value>
</not>
<matches>
<string>${project.version}</string>
<pattern>\d+\.\d+\.\d+</pattern>
</matches>
</and>
</condition>
</conditions>
</execution>
</executions>
多阶段执行
java
<executions>
<!-- 阶段1:构建测试镜像 -->
<execution>
<id>build-test</id>
<phase>pre-integration-test</phase>
<goals>
<goal>build</goal>
</goals>
<configuration>
<tag>${project.version}-test</tag>
<buildArgs>
<SPRING_PROFILES_ACTIVE>test</SPRING_PROFILES_ACTIVE>
</buildArgs>
</configuration>
</execution>
<!-- 阶段2:推送生产镜像 -->
<execution>
<id>push-production</id>
<phase>deploy</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<tag>${project.version}</tag>
</configuration>
</execution>
</executions>
🔧 第二部分:Fabric8 docker-maven-plugin 深度配置
2.1 构建方式的4种模式
模式1:纯Dockerfile模式
java
<configuration>
<images>
<image>
<name>myapp:${project.version}</name>
<build>
<!-- 指定Dockerfile -->
<dockerFile>${project.basedir}/Dockerfile</dockerFile>
<!-- 可选:docker build参数 -->
<dockerBuildOptions>
<option>--no-cache</option>
<option>--pull</option>
<option>--build-arg</option>
<option>HTTP_PROXY=http://proxy:8080</option>
</dockerBuildOptions>
<!-- 构建参数 -->
<args>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
<BUILD_NUMBER>${build.number}</BUILD_NUMBER>
</args>
</build>
</image>
</images>
</configuration>
模式2:纯XML配置模式
java
<build>
<!-- 基础镜像 -->
<from>eclipse-temurin:17-jdk-jammy</from>
<!-- 维护者信息 -->
<maintainer>devops@company.com</maintainer>
<!-- 工作目录 -->
<workdir>/app</workdir>
<!-- 环境变量 -->
<env>
<JAVA_OPTS>-Xmx512m -Xms256m</JAVA_OPTS>
<TZ>Asia/Shanghai</TZ>
<LANG>en_US.UTF-8</LANG>
</env>
<!-- 复制文件 -->
<assembly>
<descriptorRef>artifact</descriptorRef>
<!-- 或自定义描述符 -->
<!-- <descriptor>assembly/docker.xml</descriptor> -->
</assembly>
<!-- 入口点 -->
<entryPoint>
<exec>
<arg>java</arg>
<arg>${JAVA_OPTS}</arg>
<arg>-jar</arg>
<arg>/maven/${project.build.finalName}.jar</arg>
</exec>
</entryPoint>
<!-- 执行命令 -->
<runCmds>
<run>
echo "Application starting..."
</run>
<run>
chmod +x /app/entrypoint.sh
</run>
</runCmds>
<!-- 用户 -->
<user>1001:1001</user>
<!-- 卷 -->
<volumes>
<volume>/app/logs</volume>
<volume>/app/data</volume>
</volumes>
</build>
模式3:混合模式
java
<build>
<!-- 基础部分用XML -->
<from>openjdk:17-jdk-slim</from>
<env>
<APP_HOME>/app</APP_HOME>
</env>
<!-- 复杂部分用Dockerfile -->
<dockerFile>Dockerfile.partial</dockerFile>
<dockerFileDir>${project.basedir}/docker</dockerFileDir>
</build>
模式4:继承模式
java
<images>
<!-- 基础镜像 -->
<image>
<name>base-image:${java.version}</name>
<build>
<from>openjdk:${java.version}-jdk-slim</from>
<runCmds>
<run>apt-get update && apt-get install -y curl vim</run>
</runCmds>
</build>
</image>
<!-- 应用镜像继承基础 -->
<image>
<name>app:${project.version}</name>
<build>
<from>base-image:${java.version}</from>
<assembly>
<descriptorRef>artifact</descriptorRef>
</assembly>
</build>
</image>
</images>
2.2 高级Assembly配置
自定义Assembly描述符
java
<!-- src/main/assembly/docker.xml -->
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0
http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>docker</id>
<formats>
<format>dir</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${project.basedir}/src/main/docker</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.sh</include>
<include>*.conf</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
<fileSet>
<directory>${project.basedir}/target</directory>
<outputDirectory>/app</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/config</directory>
<outputDirectory>/app/config</outputDirectory>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>/app/lib</outputDirectory>
<scope>runtime</scope>
<excludes>
<exclude>${project.groupId}:${project.artifactId}</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>
插件配置:
java
<build>
<assembly>
<descriptor>assembly/docker.xml</descriptor>
<!-- 或使用内联配置 -->
<inline>
<id>docker-inline</id>
<fileSets>
<fileSet>
<directory>target</directory>
<includes>
<include>*.jar</include>
</includes>
<outputDirectory>/app</outputDirectory>
</fileSet>
</fileSets>
</inline>
</assembly>
</build>
分层Assembly
java
<build>
<assembly>
<descriptorRef>artifact</descriptorRef>
<targetDir>/app</targetDir>
<!-- 分层复制 -->
<layerConfiguration>
<layers>
<layer>
<id>dependencies</id>
<files>
<file>
<source>${project.build.directory}/dependency</source>
<outputDirectory>/app/lib</outputDirectory>
</file>
</files>
</layer>
<layer>
<id>application</id>
<files>
<file>
<source>target/${project.build.finalName}.jar</source>
<outputDirectory>/app</outputDirectory>
</file>
</files>
</layer>
<layer>
<id>resources</id>
<files>
<file>
<source>src/main/resources</source>
<outputDirectory>/app/config</outputDirectory>
</file>
</files>
</layer>
</layers>
</layerConfiguration>
</assembly>
</build>
2.3 健康检查配置
多种健康检查策略
java
<build>
<healthCheck>
<!-- HTTP检查 -->
<http>
<url>http://localhost:${app.port}/actuator/health</url>
<method>GET</method>
<status>200</status>
<!-- 或者状态范围 -->
<statusRange>200..299</statusRange>
</http>
<!-- TCP检查 -->
<tcp>
<port>${app.port}</port>
<timeout>5s</timeout>
</tcp>
<!-- 命令检查 -->
<cmd>
<shell>curl -f http://localhost:${app.port}/health || exit 1</shell>
<!-- 或者 -->
<exec>
<arg>pgrep</arg>
<arg>java</arg>
</exec>
</cmd>
<!-- 复合检查 -->
<mode>any</mode> <!-- any或all -->
<!-- 参数 -->
<interval>30s</interval>
<timeout>10s</timeout>
<retries>3</retries>
<startPeriod>60s</startPeriod>
</healthCheck>
</build>
2.4 多镜像协同配置
微服务架构示例
java
<images>
<!-- API Gateway -->
<image>
<name>gateway:${project.version}</name>
<alias>gateway</alias>
<build>
<dockerFile>${project.basedir}/gateway/Dockerfile</dockerFile>
<ports>
<port>8080</port>
</ports>
</build>
<run>
<network>
<name>app-network</name>
<alias>gateway</alias>
</network>
<dependsOn>
<container>user-service</container>
<container>order-service</container>
</dependsOn>
</run>
</image>
<!-- User Service -->
<image>
<name>user-service:${project.version}</name>
<alias>user-service</alias>
<build>
<dockerFile>${project.basedir}/user-service/Dockerfile</dockerfile>
<ports>
<port>8081</port>
</ports>
</build>
<run>
<env>
<DB_HOST>mysql</DB_HOST>
<REDIS_HOST>redis</REDIS_HOST>
</env>
</run>
</image>
<!-- MySQL -->
<image>
<name>mysql:8.0</name>
<alias>mysql</alias>
<external>
<type>compose</type>
<alias>mysql</alias>
</external>
</image>
</images>
<!-- 网络配置 -->
<networks>
<network>
<name>app-network</name>
<driver>bridge</driver>
<labels>
<env>${profiles.active}</env>
</labels>
</network>
</networks>
2.5 运行时配置详解
完整运行配置
java
<run>
<!-- 容器名称 -->
<name>${project.artifactId}-${profiles.active}</name>
<!-- 主机名 -->
<hostname>app-${project.artifactId}</hostname>
<!-- 域名 -->
<domainname>internal.company.com</domainname>
<!-- 用户 -->
<user>appuser:appgroup</user>
<!-- 重启策略 -->
<restartPolicy>
<name>on-failure</name>
<retry>5</retry>
</restartPolicy>
<!-- 端口映射 -->
<ports>
<port>8080:8080</port>
<port>5005:5005</port> <!-- 调试端口 -->
</ports>
<!-- 环境变量 -->
<env>
<JAVA_OPTS>-Xmx512m -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005</JAVA_OPTS>
<SPRING_PROFILES_ACTIVE>${profiles.active}</SPRING_PROFILES_ACTIVE>
<TZ>Asia/Shanghai</TZ>
</env>
<!-- 卷挂载 -->
<volumes>
<bind>
<volume>/host/logs:/app/logs</volume>
<volume>/host/config:/app/config:ro</volume>
</bind>
<from>
<image>config-volume:latest</image>
</from>
</volumes>
<!-- 网络 -->
<network>
<name>app-network</name>
<aliases>
<alias>${project.artifactId}</alias>
</aliases>
</network>
<!-- 链接 -->
<links>
<link>mysql:db</link>
<link>redis:cache</link>
</links>
<!-- 依赖 -->
<dependsOn>
<container>mysql</container>
<container>redis</container>
</dependsOn>
<!-- 资源限制 -->
<resource>
<memory>1g</memory>
<memorySwap>2g</memorySwap>
<cpuShares>512</cpuShares>
<cpuQuota>50000</cpuQuota>
<cpuPeriod>100000</cpuPeriod>
</resource>
<!-- 健康检查 -->
<healthcheck>
<test>["CMD", "curl", "-f", "http://localhost:8080/health"]</test>
<interval>30s</interval>
<timeout>10s</timeout>
<retries>3</retries>
<startPeriod>40s</startPeriod>
</healthcheck>
<!-- 等待条件 -->
<wait>
<http>
<url>http://localhost:8080/actuator/health</url>
<status>200</status>
</http>
<time>120000</time>
<shutdown>5000</shutdown>
</wait>
<!-- 日志配置 -->
<log>
<prefix>${project.artifactId}</prefix>
<color>green</color>
<enabled>true</enabled>
<driver>json-file</driver>
<opts>
<max-size>10m</max-size>
<max-file>3</max-file>
</opts>
</log>
<!-- 标签 -->
<labels>
<env>${profiles.active}</env>
<version>${project.version}</version>
<maintainer>devops@company.com</maintainer>
</labels>
<!-- 特权模式 -->
<privileged>false</privileged>
<!-- 只读根文件系统 -->
<readOnly>false</readOnly>
<!-- 自动删除 -->
<autoRemove>true</autoRemove>
<!-- 入口点覆盖 -->
<entrypoint>
<arg>java</arg>
<arg>-jar</arg>
<arg>/app/app.jar</arg>
</entrypoint>
</run>
开发环境特殊配置
java
<profile>
<id>dev</id>
<properties>
<docker.run.debug>true</docker.run.debug>
</properties>
</profile>
<run>
<!-- 开发模式特殊配置 -->
<env>
<JAVA_OPTS>${docker.run.debug ? '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -Dspring.devtools.restart.enabled=true' : ''}</JAVA_OPTS>
</env>
<ports>
<port>8080:8080</port>
<port>${docker.run.debug ? '5005:5005' : ''}</port>
</ports>
<volumes>
<bind>
<volume>${project.basedir}/src:/app/src:ro</volume>
<volume>${project.basedir}/target/classes:/app/classes</volume>
</bind>
</volumes>
</run>
2.6 Docker Compose集成
完整的Compose配置
java
<configuration>
<!-- Docker Compose配置 -->
<compose>
<!-- Compose文件 -->
<composeFile>${project.basedir}/docker-compose.yml</composeFile>
<composeFile>${project.basedir}/docker-compose.override.yml</composeFile>
<!-- 启动配置 -->
<up>
<detached>true</detached>
<build>true</build>
<forceBuild>${docker.compose.force.build}</forceBuild>
<recreate>true</recreate>
<removeOrphans>true</removeOrphans>
<wait>true</wait>
<waitTimeout>300</waitTimeout>
<waitLog>Service started</waitLog>
<scale>
<service>app=2</service>
<service>nginx=1</service>
</scale>
</up>
<!-- 停止配置 -->
<down>
<removeVolumes>true</removeVolumes>
<removeImages>all</removeImages>
<removeOrphans>true</removeOrphans>
<timeout>10</timeout>
</down>
<!-- 日志配置 -->
<logs>
<follow>false</follow>
<timestamps>true</timestamps>
<tail>100</tail>
</logs>
<!-- 环境变量 -->
<envProperties>
<ENV>${profiles.active}</ENV>
<APP_VERSION>${project.version}</APP_VERSION>
</envProperties>
<!-- 项目名称 -->
<projectName>${project.artifactId}-${profiles.active}</projectName>
</compose>
</configuration>
动态Compose生成
java
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<executions>
<execution>
<id>generate-compose</id>
<phase>process-resources</phase>
<goals>
<goal>resource</goal>
</goals>
<configuration>
<images>
<!-- 镜像定义 -->
</images>
<generator>
<config>
<compose>
<enabled>true</enabled>
<name>${project.artifactId}</name>
<version>3.8</version>
<outputFile>${project.build.directory}/docker-compose.yml</outputFile>
</compose>
</config>
</generator>
</configuration>
</execution>
</executions>
</plugin>
2.7 监控与日志集成
Prometheus监控配置
java
<build>
<!-- 添加监控agent -->
<runCmds>
<run>
wget -O /app/jmx_prometheus_javaagent.jar \
https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.19.0/jmx_prometheus_javaagent-0.19.0.jar
</run>
</runCmds>
<!-- 暴露监控端口 -->
<ports>
<port>9090</port>
</ports>
<!-- 监控配置 -->
<env>
<JAVA_OPTS>-javaagent:/app/jmx_prometheus_javaagent.jar=9090:/app/config/prometheus.yml ${JAVA_OPTS}</JAVA_OPTS>
</env>
</build>
<assembly>
<fileSets>
<fileSet>
<directory>${project.basedir}/monitoring</directory>
<outputDirectory>/app/config</outputDirectory>
<includes>
<include>prometheus.yml</include>
<include>alert-rules.yml</include>
</includes>
</fileSet>
</fileSets>
</assembly>
集中式日志配置
java
<run>
<log>
<driver>fluentd</driver>
<opts>
<fluentd-address>fluentd:24224</fluentd-address>
<tag>docker.{{.Name}}</tag>
<labels>env,version</labels>
<env>OS,SPRING_PROFILES_ACTIVE</env>
</opts>
</log>
<env>
<LOG_LEVEL>${log.level}</LOG_LEVEL>
<LOG_FORMAT>json</LOG_FORMAT>
<LOG_APPENDER>CONSOLE,FILE</LOG_APPENDER>
</env>
<labels>
<log.rotate>daily</log.rotate>
<log.retention>30d</log.retention>
</labels>
</run>
2.8 安全增强配置
安全上下文配置
java
<run>
<!-- 安全选项 -->
<securityOpts>
<securityOpt>no-new-privileges</securityOpt>
<securityOpt>label=type:svirt_apache_t</securityOpt>
</securityOpts>
<!-- Capabilities -->
<capAdd>
<cap>CHOWN</cap>
</capAdd>
<capDrop>
<cap>ALL</cap>
</capDrop>
<!-- 只读根文件系统 -->
<readOnly>true</readOnly>
<!-- 临时文件系统 -->
<tmpfs>
<mount>/tmp</mount>
<mount>/run</mount>
</tmpfs>
</run>
镜像签名验证
java
<configuration>
<!-- 镜像验证 -->
<imageVerification>
<enabled>true</enabled>
<verify>always</verify>
<failOnMissing>true</failOnMissing>
<registry>docker.io</registry>
<publicKey>${docker.signing.key}</publicKey>
</imageVerification>
<!-- 内容信任 -->
<contentTrust>
<enabled>true</enabled>
<server>https://notary.docker.io</server>
</contentTrust>
</configuration>
🎯 第三部分:高级集成方案
3.1 与CI/CD工具集成
Jenkins Pipeline集成
java
// Jenkinsfile
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.company.com'
DOCKER_NAMESPACE = 'team'
}
stages {
stage('Build') {
steps {
script {
// 使用Spotify插件
sh 'mvn clean package -DskipTests'
}
}
}
stage('Build Docker Image') {
steps {
script {
// 选择插件
if (params.DOCKER_PLUGIN == 'spotify') {
sh '''
mvn dockerfile:build \
-Ddockerfile.repository=${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${JOB_NAME} \
-Ddockerfile.tag=${BUILD_NUMBER}
'''
} else if (params.DOCKER_PLUGIN == 'fabric8') {
sh '''
mvn docker:build \
-Ddocker.image.name=${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${JOB_NAME} \
-Ddocker.image.tag=${BUILD_NUMBER}
'''
}
}
}
}
stage('Push Image') {
steps {
withCredentials([
usernamePassword(
credentialsId: 'docker-registry',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)
]) {
script {
sh '''
echo "${DOCKER_PASS}" | docker login ${DOCKER_REGISTRY} \
-u "${DOCKER_USER}" --password-stdin
if [ "$DOCKER_PLUGIN" = "spotify" ]; then
mvn dockerfile:push \
-Ddockerfile.username=${DOCKER_USER} \
-Ddockerfile.password=${DOCKER_PASS}
else
mvn docker:push \
-Ddocker.username=${DOCKER_USER} \
-Ddocker.password=${DOCKER_PASS}
fi
'''
}
}
}
}
stage('Scan Image') {
steps {
script {
// 镜像安全扫描
sh 'trivy image ${DOCKER_REGISTRY}/${DOCKER_NAMESPACE}/${JOB_NAME}:${BUILD_NUMBER}'
}
}
}
}
}
GitLab CI集成
java
# .gitlab-ci.yml
variables:
DOCKER_REGISTRY: "registry.gitlab.com"
DOCKER_NAMESPACE: "${CI_PROJECT_PATH}"
stages:
- build
- test
- package
- deploy
build-maven:
stage: build
image: maven:3.9.6-eclipse-temurin-17
script:
- mvn clean compile
artifacts:
paths:
- target/
build-docker-spotify:
stage: package
image: docker:20.10
services:
- docker:20.10-dind
variables:
DOCKER_HOST: "tcp://docker:2375"
DOCKER_TLS_CERTDIR: ""
script:
- |
mvn dockerfile:build \
-Ddockerfile.repository=${DOCKER_REGISTRY}/${DOCKER_NAMESPACE} \
-Ddockerfile.tag=${CI_COMMIT_SHORT_SHA} \
-Ddockerfile.username=${CI_REGISTRY_USER} \
-Ddockerfile.password=${CI_REGISTRY_PASSWORD}
only:
- master
- tags
build-docker-fabric8:
stage: package
image: docker:20.10
services:
- docker:20.10-dind
variables:
DOCKER_HOST: "tcp://docker:2375"
script:
- |
mvn docker:build \
-Ddocker.registry=${DOCKER_REGISTRY} \
-Ddocker.image.name=${DOCKER_NAMESPACE} \
-Ddocker.image.tag=${CI_COMMIT_SHORT_SHA}
only:
- develop
push-image:
stage: deploy
image: docker:20.10
services:
- docker:20.10-dind
script:
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${DOCKER_REGISTRY}
- |
if [ "$DOCKER_PLUGIN" = "spotify" ]; then
mvn dockerfile:push
else
mvn docker:push
fi
only:
- master
- tags
3.2 多模块项目配置
父POM配置
java
<!-- parent/pom.xml -->
<properties>
<docker.plugin>fabric8</docker.plugin>
<docker.registry>registry.company.com</docker.registry>
<docker.namespace>${project.groupId}</docker.namespace>
<docker.base.image>openjdk:17-jdk-slim</docker.base.image>
</properties>
<build>
<pluginManagement>
<plugins>
<!-- Spotify插件管理 -->
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.13</version>
<configuration>
<skip>${skip.docker}</skip>
<useMavenSettingsForAuth>true</useMavenSettingsForAuth>
</configuration>
</plugin>
<!-- Fabric8插件管理 -->
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.41.0</version>
<configuration>
<skip>${skip.docker}</skip>
<images>
<image>
<name>${docker.registry}/${docker.namespace}/${project.artifactId}:${project.version}</name>
</image>
</images>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
<profiles>
<profile>
<id>docker-all</id>
<modules>
<module>service-a</module>
<module>service-b</module>
<module>gateway</module>
</modules>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<executions>
<execution>
<id>build-all-docker</id>
<phase>package</phase>
<goals>
<goal>exec</goal>
</goals>
<configuration>
<executable>mvn</executable>
<arguments>
<argument>-pl</argument>
<argument>service-a,service-b,gateway</argument>
<argument>-am</argument>
<argument>clean</argument>
<argument>package</argument>
<argument>${docker.plugin}:build</argument>
<argument>-DskipTests</argument>
</arguments>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
子模块配置
java
<!-- service-a/pom.xml -->
<build>
<plugins>
<plugin>
<groupId>${docker.plugin.groupId}</groupId>
<artifactId>${docker.plugin.artifactId}</artifactId>
<configuration>
<!-- 通用配置继承父POM -->
<!-- 模块特定覆盖 -->
<dockerfile>${project.basedir}/Dockerfile.service</dockerfile>
<buildArgs>
<SERVICE_NAME>${project.artifactId}</SERVICE_NAME>
<SERVICE_PORT>8081</SERVICE_PORT>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
3.3 条件化构建策略
基于分支的构建
java
<configuration>
<skip>${skip.docker.build}</skip>
<tags>
<tag>${project.version}</tag>
<!-- 分支特定标签 -->
<tag>${env.GIT_BRANCH}</tag>
<!-- 条件化标签 -->
<tag>${env.GIT_BRANCH == 'master' ? 'latest' : 'snapshot'}</tag>
</tags>
</configuration>
构建触发器
java
<executions>
<execution>
<id>docker-build-on-release</id>
<phase>package</phase>
<goals>
<goal>build</goal>
</goals>
<configuration>
<skip>${skip.docker.build}</skip>
<!-- 只在发布版本构建 -->
<filter>${project.version}</filter>
<includes>
<include>**/*-RELEASE.jar</include>
<include>**/*-FINAL.jar</include>
</includes>
</configuration>
</execution>
<execution>
<id>docker-push-on-tag</id>
<phase>deploy</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<!-- 只在Git标签时推送 -->
<skip>${env.GIT_TAG == ''}</skip>
<tag>${env.GIT_TAG}</tag>
</configuration>
</execution>
</executions>
3.4 性能优化配置
并行构建优化
java
<configuration>
<!-- Spotify并行构建 -->
<parallel>true</parallel>
<threads>4</threads>
<!-- Fabric8构建优化 -->
<buildOptions>
<dockerBuildOptions>
<option>--parallel</option>
<option>--memory=2g</option>
<option>--cpus=2</option>
</dockerBuildOptions>
</buildOptions>
<!-- 缓存优化 -->
<cacheFrom>
<cache>${docker.registry}/${docker.namespace}/${project.artifactId}:latest</cache>
</cacheFrom>
<cacheTo>
<cache>type=registry,ref=${docker.registry}/${docker.namespace}/${project.artifactId}:buildcache</cache>
</cacheTo>
</configuration>
分层构建优化
java
<!-- 对于Spring Boot应用 -->
<build>
<env>
<JAR_LAYERS_ENABLED>true</JAR_LAYERS_ENABLED>
<SPRING_BOOT_LAYERED_ENABLED>true</SPRING_BOOT_LAYERED_ENABLED>
</env>
<runCmds>
<!-- 使用Spring Boot分层JAR -->
<run>
java -Djarmode=layertools -jar application.jar extract
</run>
</runCmds>
<assembly>
<layers>
<layer>
<id>dependencies</id>
<includes>
<include>**/*.jar</include>
</includes>
<layerConfiguration>dependencies</layerConfiguration>
</layer>
<layer>
<id>spring-boot-loader</id>
<layerConfiguration>spring-boot-loader</layerConfiguration>
</layer>
<layer>
<id>snapshot-dependencies</id>
<layerConfiguration>snapshot-dependencies</layerConfiguration>
</layer>
<layer>
<id>application</id>
<layerConfiguration>application</layerConfiguration>
</layer>
</layers>
</assembly>
</build>
📊 第四部分:对比总结与选择建议
4.1 详细功能对比矩阵
| 功能维度 | Spotify | Fabric8 | 优胜方 |
|---|---|---|---|
| 基础构建 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | 平手 |
| 认证管理 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 多镜像支持 | ⭐⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 运行时控制 | ⭐⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 网络管理 | ⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 存储卷 | ⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 健康检查 | ⭐⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 资源限制 | ⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| Compose集成 | ❌ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 监控集成 | ⭐ | ⭐⭐⭐⭐ | Fabric8 |
| 安全特性 | ⭐⭐ | ⭐⭐⭐⭐ | Fabric8 |
| 构建性能 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 平手 |
| 配置灵活性 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
| 文档完整性 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐ | 平手 |
| 社区活跃度 | ⭐⭐ | ⭐⭐⭐⭐⭐ | Fabric8 |
4.2 具体场景选择指南
场景1:简单单服务应用
**# 选择: Spotify
理由:
- 配置简单,学习成本低
- 已有Dockerfile可复用
- 构建速度快
配置要点:
- 使用默认Dockerfile位置
- Maven settings管理认证
- 基础构建参数即可**
场景2:微服务架构
**# 选择: Fabric8
理由:
- 多镜像统一管理
- 服务依赖和网络配置
- 完整的运行时控制
配置要点:
- 使用XML定义所有服务
- 配置服务间网络
- 健康检查和依赖管理**
场景3:CI/CD流水线
**# 选择: 根据团队熟悉度
Spotify优势:
- 命令简单明了
- 环境变量支持好
- 与大多数CI工具兼容**
**Fabric8优势:
- 丰富的构建后操作
- 自动生成Compose文件
- 集成测试支持**
**推荐配置:
- 统一认证管理
- 标签策略标准化
- 构建缓存优化**
场景4:本地开发环境
**# 选择: Fabric8
理由:
- Docker Compose集成
- 热重载支持
- 开发环境特殊配置
配置要点:
- 开发模式Volume映射
- 调试端口暴露
- 快速重启配置**
4.3最佳实践总结
通用最佳实践
-
版本固定:始终固定插件版本
-
认证安全:使用Maven settings或环境变量
-
构建可重现:固定基础镜像版本
-
镜像标签:使用语义化版本控制
-
构建缓存:合理利用Docker层缓存
Spotify特有实践
-
Dockerfile规范:遵循最佳实践编写
-
.dockerignore:减少构建上下文大小
-
多阶段构建:减少最终镜像大小
-
构建参数:使用ARG传递动态值
Fabric8特有实践
-
配置模块化:拆分不同环境的配置
-
健康检查:配置完善的健康检查
-
资源限制:设置合理的资源限制
-
监控集成:集成应用监控
-
安全加固:应用容器安全最佳实践
4.4未来趋势
Spotify插件状态
-
当前状态:已归档,不再维护
-
建议:现有项目可继续使用,新项目考虑迁移
-
替代方案:Google Jib、Buildpacks
Fabric8发展方向
-
活跃维护,持续更新
-
云原生特性增强
-
Kubernetes集成深化
-
安全特性加强
新兴技术
-
Google Jib:无需Docker守护进程
-
Buildpacks:标准化构建流程
-
ko(Go应用):极简容器构建
-
nixpacks:多语言支持
🎁 第五部分:实用工具和脚本
5.1 辅助脚本
构建检查脚本
bash
#!/bin/bash
# check-docker-build.sh
set -e
echo "🔍 Docker构建环境检查"
# 检查Docker
if ! command -v docker &> /dev/null; then
echo "❌ Docker未安装"
exit 1
fi
echo "✅ Docker已安装: $(docker --version)"
# 检查Docker运行状态
if ! docker info &> /dev/null; then
echo "❌ Docker守护进程未运行"
exit 1
fi
echo "✅ Docker守护进程运行正常"
# 检查Maven
if ! command -v mvn &> /dev/null; then
echo "❌ Maven未安装"
exit 1
fi
echo "✅ Maven已安装: $(mvn --version | head -1)"
# 检查插件
echo "📦 检查Maven插件..."
if mvn help:describe -Dplugin=dockerfile-maven-plugin -Ddetail &> /dev/null; then
echo "✅ Spotify插件已安装"
else
echo "⚠️ Spotify插件未安装"
fi
if mvn help:describe -Dplugin=docker-maven-plugin -Ddetail &> /dev/null; then
echo "✅ Fabric8插件已安装"
else
echo "⚠️ Fabric8插件未安装"
fi
# 检查Dockerfile
if [ -f "Dockerfile" ]; then
echo "✅ Dockerfile存在"
echo " 大小: $(wc -l < Dockerfile) 行"
else
echo "⚠️ Dockerfile不存在"
fi
# 检查settings.xml认证
if [ -f "$HOME/.m2/settings.xml" ]; then
if grep -q "docker.io\|registry" "$HOME/.m2/settings.xml"; then
echo "✅ Maven settings包含Docker认证"
else
echo "⚠️ Maven settings中未找到Docker认证"
fi
fi
echo ""
echo "🎉 环境检查完成!"
批量构建脚本
bash
#!/bin/bash
# multi-build.sh
# 配置
REGISTRY="registry.company.com"
NAMESPACE="team"
PROJECTS=("service-a" "service-b" "gateway")
TAG="${1:-latest}"
PLUGIN="${2:-fabric8}" # spotify 或 fabric8
# 颜色输出
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color
echo -e "${GREEN}🚀 开始批量构建${NC}"
echo "仓库: ${REGISTRY}/${NAMESPACE}"
echo "标签: ${TAG}"
echo "插件: ${PLUGIN}"
echo "项目: ${PROJECTS[*]}"
echo ""
success_count=0
fail_count=0
for project in "${PROJECTS[@]}"; do
echo -e "\n📦 构建项目: ${project}"
if [ ! -d "$project" ]; then
echo -e "${RED}❌ 项目目录不存在: ${project}${NC}"
fail_count=$((fail_count+1))
continue
fi
cd "$project" || continue
# 选择插件构建
if [ "$PLUGIN" = "spotify" ]; then
mvn_cmd="mvn dockerfile:build \
-Ddockerfile.repository=${REGISTRY}/${NAMESPACE}/${project} \
-Ddockerfile.tag=${TAG} \
-DskipTests"
else
mvn_cmd="mvn docker:build \
-Ddocker.registry=${REGISTRY} \
-Ddocker.namespace=${NAMESPACE} \
-Ddocker.image.name=${project} \
-Ddocker.image.tag=${TAG} \
-DskipTests"
fi
if eval "$mvn_cmd"; then
echo -e "${GREEN}✅ ${project} 构建成功${NC}"
success_count=$((success_count+1))
else
echo -e "${RED}❌ ${project} 构建失败${NC}"
fail_count=$((fail_count+1))
fi
cd ..
done
echo -e "\n📊 构建结果:"
echo -e "${GREEN}成功: ${success_count}${NC}"
echo -e "${RED}失败: ${fail_count}${NC}"
if [ $fail_count -eq 0 ]; then
echo -e "\n🎉 所有项目构建成功!"
exit 0
else
echo -e "\n😞 有项目构建失败"
exit 1
fi
5.2 配置生成器
Dockerfile模板生成
java
// DockerfileGenerator.java
import java.io.FileWriter;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
public class DockerfileGenerator {
public static void generate(String templateFile, Map<String, String> variables,
String outputFile) throws IOException {
String template = readTemplate(templateFile);
for (Map.Entry<String, String> entry : variables.entrySet()) {
template = template.replace("${" + entry.getKey() + "}", entry.getValue());
}
try (FileWriter writer = new FileWriter(outputFile)) {
writer.write(template);
}
System.out.println("✅ Dockerfile已生成: " + outputFile);
}
private static String readTemplate(String templateFile) throws IOException {
// 读取模板文件
return Files.readString(Paths.get(templateFile));
}
public static void main(String[] args) {
Map<String, String> vars = new HashMap<>();
vars.put("BASE_IMAGE", "openjdk:17-jdk-slim");
vars.put("MAINTAINER", "devops@company.com");
vars.put("APP_PORT", "8080");
vars.put("BUILD_TIME", LocalDateTime.now().format(
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
try {
generate("Dockerfile.template", vars, "Dockerfile");
} catch (IOException e) {
e.printStackTrace();
}
}
}
5.3 监控和告警
构建监控脚本
bash
#!/bin/bash
# monitor-build.sh
# 监控Docker构建,发送告警
LOG_FILE="docker-build.log"
ALERT_EMAIL="devops@company.com"
SLACK_WEBHOOK="https://hooks.slack.com/services/xxx"
# 监控函数
monitor_build() {
local project=$1
local start_time=$(date +%s)
echo "$(date): 开始构建 $project" >> "$LOG_FILE"
# 执行构建
if mvn clean package docker:build -DskipTests; then
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo "$(date): $project 构建成功,耗时 ${duration}秒" >> "$LOG_FILE"
# 发送成功通知
send_notification "success" "$project" "$duration"
else
echo "$(date): $project 构建失败" >> "$LOG_FILE"
# 发送失败告警
send_notification "failure" "$project"
# 收集错误信息
collect_errors "$project"
fi
}
# 发送通知
send_notification() {
local status=$1
local project=$2
local duration=$3
if [ "$status" = "success" ]; then
# Slack通知
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"✅ $project 构建成功,耗时 ${duration}秒\"}" \
"$SLACK_WEBHOOK"
else
# 邮件告警
echo "项目 $project 构建失败,请检查!" | \
mail -s "Docker构建告警: $project" "$ALERT_EMAIL"
# Slack告警
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"🚨 $project 构建失败,需要立即处理!\"}" \
"$SLACK_WEBHOOK"
fi
}
# 收集错误信息
collect_errors() {
local project=$1
local error_file="errors/$project-$(date +%Y%m%d-%H%M%S).log"
# 收集最后100行日志
tail -100 build.log > "$error_file"
# 收集Docker错误
docker ps -a --filter "name=$project" --format "table {{.Names}}\t{{.Status}}" >> "$error_file"
# 收集系统资源
echo "=== 系统资源 ===" >> "$error_file"
free -h >> "$error_file"
echo "" >> "$error_file"
df -h >> "$error_file"
}
# 主监控循环
main() {
while true; do
for project in $(find . -name "pom.xml" -type f | xargs dirname); do
if [ -f "$project/pom.xml" ]; then
cd "$project" || continue
monitor_build "$(basename "$project")"
cd - > /dev/null
fi
done
# 每小时检查一次
sleep 3600
done
}
# 启动监控
main
📚 总结
本文详细介绍了 Spotify dockerfile-maven-plugin 和 Fabric8 docker-maven-plugin 的各种灵活配置方式,涵盖:
-
基础到高级的配置方案
-
多种认证管理策略
-
多环境、多模块支持
-
性能优化和安全加固
-
CI/CD集成方案
-
实用工具和脚本
核心建议:
-
新项目首选 Fabric8:功能丰富,活跃维护
-
简单项目可用 Spotify:配置简单,学习成本低
-
重视安全性:妥善管理认证信息
-
自动化一切:集成到CI/CD流水线
-
监控和告警:及时发现和解决问题