项目目录结构

project-root/
├── pom.xml
├── docker
│	 ├── copy.sh
│	 ├── file
│	 │	  ├── jar
│	 │	  │    └──  存放执行copy.sh以后jar包的位置
│	 │	  └── Dockerfile
│    └── docker-compose.yml
├── docker-only-test
│	 ├── src
│	 ├── target
│    └── pom.xml
├── docker-maven-test
│	 ├── src
│	 ├── target
│    └── pom.xml
└── docker-compose-test├── src├── target└── pom.xml

写在前文

本文主要是做一个总结,包含三个内容:

一:单独的使用docker+maven插件实现自动化编译;

二:使用docker+maven插件结合jenkins实现自动化编译部署;

三:基于docker-compose+Dockerfile结合jenkins实现自动化编译部署;

本文默认已经安装好了gitee、jenkins、Docker、并且已经将项目上传到Gitee上(如果没有安装好的,可以查看我上一篇...)

案例一和案例二,在上一篇已经做了详细说明,安装Jenkins并配置等操作,不做详解,本文主要做一个总结,有需要直接看上一篇。

关于本文的案例三,jenkins项目采用maven版本,通过Jenkins执行shell脚本指令去操作docker-compose读取docker-compose.yml文件,在文件中读取Dockerfile内容,然后通过docker-compose来build;

上一篇位置:Jenkins+Gitee+Docker容器化部署-CSDN博客

案例一:仅使用Docker+Maven插件实现自动化编译

创建项目-模块docker-only-test

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.lanting</groupId><artifactId>docker-test</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>docker-only-test</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><!-- 添加maven命令:clean package -Pprod,test docker:removeImage docker:build --><!-- 添加maven命令:clean install -Dmaven.test.skip=true docker:removeImage docker:build --><!-- 添加maven命令:clean package -Pprod,test,dev -Dmaven.test.skip=true docker:removeImage docker:build --><groupId>com.spotify</groupId><artifactId>docker-maven-plugin</artifactId><version>1.2.2</version><configuration><!-- <imageName>${docker.image.prefix}/app</imageName>--><!--  镜像名称:[DockerTest:1.0-SNAPSHOT]。镜像名称必须小写...否在要报错。会报错:on project DockerTest: Exception caught: Request error: DELETE ... 400, body: {"message":"invalid reference format: repository name (library/DockerTest) must be lowercase"}: HTTP 400 Bad Request -> [Help 1] --><imageName>${project.artifactId}:${project.version}</imageName><!-- 基于这个镜像构建镜像 --><baseImage>my-jdk17:17</baseImage><!-- JAR 文件路径:docker-test-1.0-SNAPSHOT.jar 必须位于容器的根目录(/)--><!-- <entryPoint>["java","-jar","/${project.build.finalName}.jar"]</entryPoint>--><!-- 下面sh -c 这个命令可以解析JVM(-Xmx、-Xms等)参数,不然我们只能使用默认JVM参数,即在docker启动中可以使用“-e JAVA_OPTS="-Xms=512m -Dlanting.name=张三"”来设置启动参数,不然系统无法通过“@Value(${lanting.name}})”解析参数, --><entryPoint>["sh", "-c","java $JAVA_OPTS -jar /${project.build.finalName}.jar \"$@\""]</entryPoint><!-- <dockerDirectory>src/main/docker</dockerDirectory>--><!--<dockerDirectory>${project.basedir}/src/main/resources/docker</dockerDirectory>--><!-- 制作的镜像放在哪里 --><dockerHost>http://ip:2375</dockerHost><resources><resource><targetPath>/</targetPath><directory>${project.build.directory}</directory><include>${project.build.finalName}.jar</include></resource></resources></configuration></plugin></plugins></build>
</project>

项目内容

可以不使用一致的。

application.yml
server:port: ${SERVER_PORT:8090}
#Spring Boot 的 Profile 激活优先级(从高到低):
### 命令行参数 (--spring.profiles.active=prod)
### 环境变量 (SPRING_PROFILES_ACTIVE=prod)
### JVM 参数 (-Dspring.profiles.active=prod)
### application.yml 中的静态配置
### 默认 Profile(如果没有设置)
spring:profiles:# 注意大小写...。配置了这个就可以在docekr run中传入
#    active: ${SPRING_PROFILES_ACTIVE:dev}active: dev2application:name: @project.artifactId@
application-dev.yml
lanting:application:key: only-dev版本
 DockerOnlyController.java
package com.lanting.only.controller;import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.RuntimeMXBean;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@RestController
@RequestMapping("/only")
public class DockerOnlyController {@Value("${lanting.application.key}")private String key;@Value("${lanting.name.args}")private String args;@Value("${lanting.name.env}")private String env;@Value("${lanting.name.jvm}")private String jvm;@Value("${spring.application.name}")private String appName;@Value("${spring.profiles.active:Unknown}")private String activeProfile;private StringBuilder builder = new StringBuilder();@PostConstructpublic void init() {builder.append("args:").append(args).append("\n").append("key:").append(key).append("\n").append("env:").append(env).append("\n").append("jvm:").append(jvm).append("\n").append("appName:").append(appName).append("\n").append("ARGS_1:").append(System.getenv("ARGS_1")).append("\n").append("activeProfile:").append(activeProfile).append("\n").append("System.getenv(\"SPRING_PROFILES_ACTIVE\"):").append(System.getenv("SPRING_PROFILES_ACTIVE")).append("\n");System.out.println(builder);}@GetMapping("/get/{name}")public String getKey(@PathVariable("name") String name) {return builder.append(name).toString();}@GetMapping("/jvm")public Map<String, Object> getJvmDetails() {Map<String, Object> details = new LinkedHashMap<>();// 1. 获取内存信息MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();details.put("HeapMemory", memoryMxBean.getHeapMemoryUsage());details.put("NonHeapMemory", memoryMxBean.getNonHeapMemoryUsage());// 2. 获取GC信息List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();List<String> gcInfo = gcMxBeans.stream().map(bean -> bean.getName() + " (count=" + bean.getCollectionCount() + ", time=" + bean.getCollectionTime() + "ms)").collect(Collectors.toList());details.put("GarbageCollectors", gcInfo);// 3. 获取运行时参数(包含-X -XX参数)RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();details.put("JVMArguments", runtimeMxBean.getInputArguments());// 4. 获取系统属性(-D参数)details.put("SystemProperties", runtimeMxBean.getSystemProperties());return details;}
}

配置Jenkins

 注意:本模块仅使用项目直接添加mvn命令编译即可将编译好的docker镜像jar上传到远程docker,无需使用Jenkins和Gitee;

但是此种方法只能实现自动编译,无法实现自动部署,要实现自动部署,需要如下操作:

注意本文此案例只做记录,具体操作参考上一篇:

Step1、git配置、触发时机Triggers等不做介绍;
Step2、配置Maven的Build

Root POM:docker-only-test/pom.xml(找到子模块下面的具体某一个pom.xml)

Goals and options:clean package -Dmaven.test.skip=true docker:removeImage docker:build

Step3、编写Post Steps>>>Execute shell
#!/bin/bash
cd $WORKSPACE
echo "当前工作目录WORKSPACE:$WORKSPACE"
# 固定写死 - 激活环境/对外暴露的端口
# 与上面的mvn命令保持激活的一致即可。不要使用-P激活多个环境
# clean package -Pprod -Dmaven.test.skip=true  docker:removeImage docker:build
ACTIVE_PROFILE=prod
CONTAINER_NAME=only-web-$ACTIVE_PROFILE # 容器名称
# 子模块名称
MODEL_NAME=docker-only-test
# 对外暴露的端口,默认和容器启动端口一样---需要和server.port保持一致。
HOST_IMAGE_PORT=8090# 切换目录
cd $MODEL_NAME
echo "切换后的目录:$(pwd)"
IMAGE_NAME=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout):$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)# 停止并删除旧容器
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; thenecho "停止并删除容器: $CONTAINER_NAME"docker stop $CONTAINER_NAME >/dev/null 2>&1docker rm $CONTAINER_NAME >/dev/null 2>&1
fi# 清理悬空镜像(可选)
docker image prune -f >/dev/null 2>&1# 启动新容器
echo "启动容器: $CONTAINER_NAME (镜像: $IMAGE_NAME) (对外端口:$HOST_IMAGE_PORT,容器内端口:$HOST_IMAGE_PORT)(激活环境:$ACTIVE_PROFILE)"
# 设置环境优先级:(前面的会覆盖后面的,也就是最后生效的是prod)
### -Dspring.profiles.active=prod
### >>> SPRING_PROFILES_ACTIVE=dev
### >>> --spring.profiles.active=test
### >>> mvn命令通过-Ptest,dev,prod指定的,默认为第一个test
### >>> pom.xml通过activeByDefault指定的dev# 以下会ACTIVE_PROFILE对应的prod生效:
docker run -d \
--name $CONTAINER_NAME \
--log-driver json-file \
--log-opt max-size=10m \
-p $HOST_IMAGE_PORT:$HOST_IMAGE_PORT \
-e ARGS_1="DockerOnly" \
-e LANTING_NAME_ARGS="Args参数DockerOnly" \
-e LANTING_NAME_ENV="Env参数DockerOnly" \
-e SPRING_PROFILES_ACTIVE="test" \
-e SERVER_PORT=$HOST_IMAGE_PORT \
-e JAVA_OPTS="-Xms128m -Xmx128m -Dlanting.name.jvm=JVM参数DockerOnly -Dspring.profiles.active=$ACTIVE_PROFILE -XX:+UseZGC -XX:MaxMetaspaceSize=128m -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=6 -XX:MaxTenuringThreshold=15 -XX:+AlwaysPreTouch" \
$IMAGE_NAME \
--spring.profiles.active=devsleep 5
echo "🔍 查看容器日志(尾部):"
docker logs $CONTAINER_NAME --tail=50

案例二:使用docker+maven插件结合jenkins实现自动化编译部署

本案例与案例一的区别主要是在于“本案例的多环境配置是在pom.xml环境中的profile中进行配置,而案例一是在application.yml中”

创建项目-模块docker-maven-test

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.lanting</groupId><artifactId>docker-test</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>docker-maven-test</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin><plugin><!-- 添加maven命令:clean package -Pprod,test docker:removeImage docker:build --><!-- 添加maven命令:clean install -Dmaven.test.skip=true docker:removeImage docker:build --><!-- 添加maven命令:clean package -Pprod,test,dev -Dmaven.test.skip=true docker:removeImage docker:build --><groupId>com.spotify</groupId><artifactId>docker-maven-plugin</artifactId><version>1.2.2</version><configuration><!-- <imageName>${docker.image.prefix}/app</imageName>--><!--  镜像名称:[DockerTest:1.0-SNAPSHOT]。镜像名称必须小写...否在要报错。会报错:on project DockerTest: Exception caught: Request error: DELETE ... 400, body: {"message":"invalid reference format: repository name (library/DockerTest) must be lowercase"}: HTTP 400 Bad Request -> [Help 1] --><imageName>${project.artifactId}-${my.profiles.active}:${project.version}</imageName><!-- 基于这个镜像构建镜像 --><baseImage>my-jdk17:17</baseImage><!-- JAR 文件路径:docker-test-1.0-SNAPSHOT.jar 必须位于容器的根目录(/)--><!-- <entryPoint>["java","-jar","/${project.build.finalName}.jar"]</entryPoint>--><!-- 下面sh -c 这个命令可以解析JVM(-Xmx、-Xms等)参数,不然我们只能使用默认JVM参数,即在docker启动中可以使用“-e JAVA_OPTS="-Xms=512m -Dlanting.name=张三"”来设置启动参数,不然系统无法通过“@Value(${lanting.name}})”解析参数, --><entryPoint>["sh", "-c","java $JAVA_OPTS -jar /${project.build.finalName}.jar \"$@\""]</entryPoint><!-- <dockerDirectory>src/main/docker</dockerDirectory>--><!--<dockerDirectory>${project.basedir}/src/main/resources/docker</dockerDirectory>--><!-- 制作的镜像放在哪里 --><dockerHost>http://ip:2375</dockerHost><resources><resource><targetPath>/</targetPath><directory>${project.build.directory}</directory><include>${project.build.finalName}.jar</include></resource></resources></configuration></plugin><!--            <plugin><groupId>com.spotify</groupId><artifactId>dockerfile-maven-plugin</artifactId><version>1.4.2</version><configuration><repository>docker_storage/${project.artifactId}</repository><buildArgs><JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE></buildArgs></configuration></plugin>--><!-- 这个如果不配置,只能在application.yml中只能通过@...@带入,docker又无法通过@...@带入 --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-resources-plugin</artifactId><configuration><delimiters><!-- 使用${..}作为占位符 --><delimiter>${*}</delimiter></delimiters><!-- 同时还支持使用默认的占位符(@..@) --><useDefaultDelimiters>true</useDefaultDelimiters></configuration></plugin></plugins><!-- 要使用下面的多环境,就需要配置 --><resources><resource><directory>src/main/resources</directory><filtering>true</filtering> <!-- 启用资源过滤 --><includes><include>**/application*.yml</include> <!-- 过滤所有环境配置文件 --></includes></resource></resources></build><!-- 多环境配置:可以在application.yml中使用@profiles.active@获取,在jenkins脚本中也可以使用@server.port@获取端口 --><profiles><!-- 开发环境 --><profile><id>dev</id><properties>
<!--                <profiles.active>dev</profiles.active>--><my.profiles.active>dev</my.profiles.active><server.port>10084</server.port> <!-- 开发环境端口 --></properties><activation><activeByDefault>true</activeByDefault> <!-- 默认激活 --></activation></profile><profile><id>dev2</id><properties>
<!--                <profiles.active>dev</profiles.active>--><my.profiles.active>dev2</my.profiles.active><server.port>10085</server.port> <!-- 开发环境端口 --></properties></profile><!-- 生产环境 --><profile><id>prod</id><properties>
<!--                <profiles.active>prod</profiles.active>--><my.profiles.active>prod</my.profiles.active><server.port>10086</server.port> <!-- 生产环境端口 --></properties></profile><!-- 测试环境 --><profile><id>test</id><properties>
<!--                <profiles.active>test</profiles.active>--><my.profiles.active>test</my.profiles.active><server.port>10087</server.port> <!-- 生产环境端口 --></properties></profile></profiles>
</project>

项目内容

项目内容,可以不一样。

application.yml

通过maven的pom.xml配置的多环境选择,在application.yml中通过配置“MY_PROFILES_ACTIVE”来读取pom.xml中的profile对象中的<my.profiles.active>XX</my.profiles.active>属性的值

server: # 可以不指定,直接读取yml对应的端口即可
#  port: @server.port@ # 读取的是固定的值,在maven编译时,我们通过-Pxxx指定编译环境,默认读取的是第一个的值,无法根据后续“active”选择的环境进行更改。port: ${server.port} # 读取的是固定的值,在maven编译时,我们通过-Pxxx指定编译环境,默认读取的是第一个的值,无法根据后续“active”选择的环境进行更改。
#  port: ${SERVER_PORT} # 使用环境变量来覆盖 -e SERVER_PORT=xxx
#Spring Boot 的 Profile 激活优先级(从高到低):
### 命令行参数 (--spring.profiles.active=prod)
### 环境变量 (SPRING_PROFILES_ACTIVE=prod)
### JVM 参数 (-Dspring.profiles.active=prod)
### application.yml 中的静态配置
### 默认 Profile(如果没有设置)
spring:profiles:# 注意大小写...。配置了这个就可以在docekr run中传入active: ${my.profiles.active}
#    active: ${MY_PROFILES_ACTIVE} # 可以通过启动时设置环境变量覆盖
#    active: @profiles.active@
#    active: ${profiles.active:dev}
#    active: ${SPRING_PROFILES_ACTIVE:dev}application:name: @project.artifactId@
application-dev.yml版本
# 如果使用这个就无法在maven命令中通过:“IMAGE_PORT=$(mvn help:evaluate -P$ACTIVE_PROFILE -Dexpression=server.port -q -DforceStdout)”获取maven激活的端口
#server:
#  port: 10084
lanting:application:key: maven-dev版本
注意事项

如何选择pom.xml的profiles?

可以在编译时,通过传递“-P”参数来指定,如果只传递一个参数“-Ptest”,那么maven在编译的时候会将“MY_PROFILES_ACTIVE”的值设置为固定的test;
如果传递N个参数“-Ptest,prod,...”,那么maven在编译的时候会将“test,prod,...”打包进jar包,后续需要通过指定“--spring.profiles.active”或者JVM参数或者启动参数或者环境变量来选择具体的环境。

 多环境编译时的注意事项

如果我们在Jenkins中配置了编译命令为:clean package -Ptest,dev2,dev,prod -Dmaven.test.skip=true docker:removeImage docker:build 我们在打包编译的时候,设置了多个“-Ptest,prod”,那么我们在执行maven命令时 “docker:removeImage”时,系统会默认获取第一个“test”(如果我们在编译时test,prod,dev等环境的镜像image名称根据环境的不同而不同时,会删除-P的第一个环境的镜像) 。所以,要解决这个问题,我们针对于每一个环境都单独设置-P,或者将镜像名称设置为一样的也可以。 本项目的镜像images名称会根据选择的环境不同后缀不同,所以会出现这个问题。

启动端口和运行时端口不一致

此时注意一个问题,如果我们在编译时使用了“mvn clean package -Ptest....”,然后我们在运行时通过“--spring.profiles.active=prod”设置了运行时环境为prod时,此时可能会出现运行时的目标端口不一致但是里面的内容却是prod的内容这个问题。(也就是如果test指定端口为9999,lanting.name=zhangsan;而prod指定的端口为9998,lanting.name=lisi时,我们通过mvn clean package -Ptest...编译后的包在运行时的端口应该是9999而里面的内容lanting=lisi的这种情况。)

 编译环境和运行时环境

之所以会有这个问题,是因为环境指定不一致导致(也就是编译环境和运行时环境不一致导致

编译环境和运行时环境:Maven 的资源过滤(Resource Filtering)是在编译阶段进行的,而 spring.profiles.active 是运行时参数。
@server.port@是Maven的资源过滤功能实现的,也就是我们在执行mvn命令时,Maven 会将 @server.port@ 替换为当前激活的 Maven profile 中定义的值。
而spring.profiles.active 是运行时参数变量,对 Maven 编译时的行为没有任何影响。
如果期望在运行时通过 spring.profiles.active 动态控制 server.port 的值,但 @server.port@ 已经在编译阶段被固定成了某个值(比如 dev 对应的 8090),无法再变了。

实现端口隔离

1、可以使用“${server.port:8080}”搭配在application-XXX.yml中定义不同的“server.port: xxx”来控制即可。但是这种方法在后续操作中无法通过“IMAGE_PORT=$(mvn help:evaluate -P$ACTIVE_PROFILE -Dexpression=server.port -q -DforceStdout)动态获取maven激活的端口”
2、依然在maven中通过<server.port>来设置对应的端口,但是我们可以在后续的启动命令中添加-e SERVER_PORT来覆盖端口,或者在执行“mvn package -P命令时只激活一个环境,后续就不单独设置环境”

DockerMavenController.java 
package com.lanting.maven.controller;import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.RuntimeMXBean;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@RestController
@RequestMapping("/maven")
public class DockerMavenController {@Value("${lanting.application.key}")private String key;@Value("${lanting.name.args}")private String args;@Value("${lanting.name.env}")private String env;@Value("${lanting.name.jvm}")private String jvm;@Value("${spring.application.name}")private String appName;@Value("${spring.profiles.active:Unknown}")private String activeProfile;private StringBuilder builder = new StringBuilder();@PostConstructpublic void init() {builder.append("args:").append(args).append("\n").append("key:").append(key).append("\n").append("env:").append(env).append("\n").append("jvm:").append(jvm).append("\n").append("appName:").append(appName).append("\n").append("ARGS_1:").append(System.getenv("ARGS_1")).append("\n").append("activeProfile:").append(activeProfile).append("\n").append("System.getenv(\"SPRING_PROFILES_ACTIVE\"):").append(System.getenv("SPRING_PROFILES_ACTIVE")).append("\n");System.out.println(builder);}@GetMapping("/get/{name}")public String getKey(@PathVariable("name") String name) {return builder.append(name).toString();}@GetMapping("/jvm")public Map<String, Object> getJvmDetails() {Map<String, Object> details = new LinkedHashMap<>();// 1. 获取内存信息MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();details.put("HeapMemory", memoryMxBean.getHeapMemoryUsage());details.put("NonHeapMemory", memoryMxBean.getNonHeapMemoryUsage());// 2. 获取GC信息List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();List<String> gcInfo = gcMxBeans.stream().map(bean -> bean.getName() + " (count=" + bean.getCollectionCount() + ", time=" + bean.getCollectionTime() + "ms)").collect(Collectors.toList());details.put("GarbageCollectors", gcInfo);// 3. 获取运行时参数(包含-X -XX参数)RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();details.put("JVMArguments", runtimeMxBean.getInputArguments());// 4. 获取系统属性(-D参数)details.put("SystemProperties", runtimeMxBean.getSystemProperties());return details;}
}

配置Jenkins

本例和案例一一样,Jenkins的各类配置,安装等不介绍,可以参考上一篇文章;

Step1、创建项目

Step2、配置git

Step3、配置拉取方式

 

Step4、编译命令Build

Root POM:docker-maven-test/pom.xml(子模块的具体的pom.xml)

Goals and options:clean package -Pprod -Dmaven.test.skip=true docker:removeImage docker:build

 
Step5、执行脚本Post Steps
#!/bin/bash
cd $WORKSPACE
echo "当前工作目录WORKSPACE:$WORKSPACE"
# 固定写死 - 激活环境/对外暴露的端口
# 与上面的mvn命令保持激活的一致即可。不要使用-P激活多个环境
# clean package -Pprod -Dmaven.test.skip=true  docker:removeImage docker:build
ACTIVE_PROFILE=prod
CONTAINER_NAME=maven-web-$ACTIVE_PROFILE # 容器名称
# 子模块名称
MODEL_NAME=docker-maven-test# 动态获取当前激活的主profile(从构建参数中提取)
# --只能获取“clean package -Pprod,test -Dmaven.test.skip=true docker:removeImage docker:build”中的 test环境
# 如果要启用这个,那么我们可以只启动一个-Pprod即可。
# ACTIVE_PROFILE=$(echo "$MAVEN_GOALS" | grep -oP '(?<=-P)[^ ]+' | cut -d',' -f1)# 获取镜像名称和端口(关联激活的profile)
# 项目结构是,获取到的是“project.artifactId为project-root”
# project-root/
#   └── pom.xml
#IMAGE_NAME=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$ACTIVE_PROFILE:$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
#IMAGE_PORT=$(mvn help:evaluate -P$ACTIVE_PROFILE -Dexpression=server.port -q -DforceStdout)
#echo "激活端口:$IMAGE_PORT;激活镜像:$IMAGE_NAME"# 项目结构是:
# project-root/
# ├── pom.xml
# ├── project-child1
# │    └── pom.xml
# └── docker-maven-test
#      └── pom.xml
# 切换目录
cd $MODEL_NAME
echo "切换后的目录:$(pwd)"
IMAGE_NAME=$(mvn help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$ACTIVE_PROFILE:$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout)
IMAGE_PORT=$(mvn help:evaluate -P$ACTIVE_PROFILE -Dexpression=server.port -q -DforceStdout)
echo "激活端口:$IMAGE_PORT;激活镜像:$IMAGE_NAME"# 不切换目录,直接指定子模块 POM 文件
#IMAGE_NAME=$(mvn -f docker-maven-test/pom.xml help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$ACTIVE_PROFILE:$(mvn -f docker-maven-test/pom.xml help:evaluate -Dexpression=project.version -q -DforceStdout)
#IMAGE_PORT=$(mvn -f docker-maven-test/pom.xml help:evaluate -P$ACTIVE_PROFILE -Dexpression=server.port -q -DforceStdout)
#echo "激活端口:$IMAGE_PORT;激活镜像:$IMAGE_NAME"# 对外暴露的端口,默认和容器启动端口一样
HOST_IMAGE_PORT=$IMAGE_PORT# 停止并删除旧容器
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; thenecho "停止并删除容器: $CONTAINER_NAME"docker stop $CONTAINER_NAME >/dev/null 2>&1docker rm $CONTAINER_NAME >/dev/null 2>&1
fi# 清理悬空镜像(可选)
docker image prune -f >/dev/null 2>&1# 启动新容器
echo "启动容器: $CONTAINER_NAME (镜像: $IMAGE_NAME) (对外端口:$HOST_IMAGE_PORT,容器内端口:$IMAGE_PORT)(激活环境:$ACTIVE_PROFILE)"
# 设置环境优先级:(前面的会覆盖后面的,也就是最后生效的是prod)
### -Dspring.profiles.active=prod
### >>> SPRING_PROFILES_ACTIVE=dev
### >>> --spring.profiles.active=test
### >>> mvn命令通过-Ptest,dev,prod指定的,默认为第一个test
### >>> pom.xml通过activeByDefault指定的dev# 同时激活多个环境时----不推荐:
#docker run -d \
#--name $CONTAINER_NAME \
#--log-driver json-file \
#--log-opt max-size=10m \
#-p $HOST_IMAGE_PORT:$IMAGE_PORT \
#-e ARGS_1=DockerMaven \
#-e LANTING_NAME_ARGS="Args参数DockerMaven" \
#-e LANTING_NAME_ENV="Env参数DockerMaven" \
#-e SPRING_PROFILES_ACTIVE="test" \
#-e JAVA_OPTS="-Xms128m -Xmx128m -Dlanting.name.jvm=JVM参数DockerMaven -Dspring.profiles.active=$ACTIVE_PROFILE -XX:+UseZGC -XX:MaxMetaspaceSize=128m -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=6 -XX:MaxTenuringThreshold=15 -XX:+AlwaysPreTouch" \
#$IMAGE_NAME \
#--spring.profiles.active=dev# 只激活一个环境时:
docker run -d \
--name $CONTAINER_NAME \
--log-driver json-file \
--log-opt max-size=10m \
-p $HOST_IMAGE_PORT:$IMAGE_PORT \
-e JAVA_OPTS="-Xms128m -Xmx128m -Dlanting.name.jvm=JVM参数DockerMaven -XX:+UseZGC -XX:MaxMetaspaceSize=128m -XX:NewSize=128m -XX:MaxNewSize=128m -XX:SurvivorRatio=6 -XX:MaxTenuringThreshold=15 -XX:+AlwaysPreTouch" \
-e ARGS_1=DockerMaven \
-e LANTING_NAME_ARGS="Args参数DockerMaven" \
-e LANTING_NAME_ENV="Env参数DockerMaven" \
$IMAGE_NAMEsleep 5
echo "🔍 查看容器日志(尾部):"
docker logs $CONTAINER_NAME --tail=50
 

案例三:docker-compose+Dockerfile实现自动化编译并部署

安装Jenkins

docker run --name jenkins \-p 8099:8080 -p 50000:50000 \-v /var/run/docker.sock:/var/run/docker.sock \-v $(which docker):/bin/docker \-v $(which docker-compose):/bin/docker-compose \-v /app/jenkins:/var/jenkins_home \--group-add=$(getent group docker | cut -d: -f3) \-d jenkins/jenkins:lts参数解释:
$(which docker-compose):/bin/docker-compose:将宿主机的docker-compose安装目录映射到Jenkins容器内部,这样在Jenkins容器内部就可以使用宿主机的docker-compose;其余参数解释见上一篇

注意:本文只展示和上一篇不同的地方,可以参考上一篇对Docker的镜像制作以及Jenkins的安装和配置环境...

创建项目-模块docker-compose-test

 docker-compose.yml 

version: "3.8"
services:compose-web:build:context: ..  # 将上下文设为项目根目录(我的yml文件在docker目录下,所以“..”是为了回到根目录)dockerfile: ./docker/file/Dockerfileimage: docker-compose-test:1.0-SNAPSHOT # 镜像名称 --- 不知道怎么从pom.xml中取出来,所以写死。container_name: compose-web-prodenvironment:- ARGS_1=DockerCompose- LANTING_NAME_ENV=Env参数DockerCompose # 系统自动解析成“@Value("${lanting.name.env}")”- JAVA_OPTS=-Xms64m -Xmx64m -Dlanting.name.jvm=JVM参数DockerCompose
#      - JAVA_OPTS=-Xms64m -Xmx64m -Dlanting.name=张三- ACTIVE_PROFILE=prod # 传递到了application.yml中,通过${ACTIVE_PROFILE}户区- PARAMS=--lanting.name.args=Args参数DockerCompose
#    volumes: #挂载目录
#      - /app/lanting:/home/lantingports:- "9091:8090"logging:driver: json-fileoptions:max-size: "10m"

Dockerfile

# 使用官方 OpenJDK 镜像
FROM my-jdk17:17MAINTAINER lanting# 挂载目录--- 宿主机的目录
VOLUME /app/lanting# 在这个容器中创建目录并设置权限 --- 容器内目录
RUN mkdir -p /home/lanting && \chmod 777 /home/lanting# 镜像中创建一个工作目录 --- 容器内目录
WORKDIR /home/lanting# 环境变量(可选)
#ENV JAVA_OPTS="-Xms256m -Xmx256m -XX:+UseZGC -XX:MaxMetaspaceSize=256m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:SurvivorRatio=6 -XX:MaxTenuringThreshold=15 -XX:+AlwaysPreTouch -Dlanting.name=\"张三\""
#ENV TZ=Asia/Shanghai
#ENV PARAMS=""# 我们将jar移动到了当前目录下的jar包后 --- 容器内目录
COPY ./docker/file/jar/*.jar /home/lanting/app.jar# 使用 JAVA_OPTS 与 ACTIVE_PROFILE 都可在 docker run 通过 -e 注入。
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /home/lanting/app.jar $PARAMS --spring.profiles.active=${ACTIVE_PROFILE}"]

项目内容

--- 注意:只需要保持项目结构一致就行,不需要项目内容一致。

application.yml
server:port: 8090
spring:profiles:active: ${ACTIVE_PROFILE}application:name: docker-compose-test
application-prod.yml
lanting:application:key: compose-prod版本
 DockerComposeController.java
package com.lanting.compose.controller;import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.RuntimeMXBean;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;@RestController
@RequestMapping("/maven")
public class DockerComposeController {@Value("${lanting.application.key}")private String key;@Value("${lanting.name.args}")private String args;@Value("${lanting.name.env}")private String env;@Value("${lanting.name.jvm}")private String jvm;@Value("${spring.application.name}")private String appName;@Value("${spring.profiles.active:Unknown}")private String activeProfile;private StringBuilder builder = new StringBuilder();@PostConstructpublic void init() {builder.append("args:").append(args).append("\n").append("key:").append(key).append("\n").append("env:").append(env).append("\n").append("jvm:").append(jvm).append("\n").append("appName:").append(appName).append("\n").append("ARGS_1:").append(System.getenv("ARGS_1")).append("\n").append("activeProfile:").append(activeProfile).append("\n").append("System.getenv(\"SPRING_PROFILES_ACTIVE\"):").append(System.getenv("SPRING_PROFILES_ACTIVE")).append("\n");System.out.println(builder);}@GetMapping("/get/{name}")public String getKey(@PathVariable("name") String name) {return builder.append(name).toString();}@GetMapping("/jvm")public Map<String, Object> getJvmDetails() {Map<String, Object> details = new LinkedHashMap<>();// 1. 获取内存信息MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();details.put("HeapMemory", memoryMxBean.getHeapMemoryUsage());details.put("NonHeapMemory", memoryMxBean.getNonHeapMemoryUsage());// 2. 获取GC信息List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();List<String> gcInfo = gcMxBeans.stream().map(bean -> bean.getName() + " (count=" + bean.getCollectionCount() + ", time=" + bean.getCollectionTime() + "ms)").collect(Collectors.toList());details.put("GarbageCollectors", gcInfo);// 3. 获取运行时参数(包含-X -XX参数)RuntimeMXBean runtimeMxBean = ManagementFactory.getRuntimeMXBean();details.put("JVMArguments", runtimeMxBean.getInputArguments());// 4. 获取系统属性(-D参数)details.put("SystemProperties", runtimeMxBean.getSystemProperties());return details;}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.lanting</groupId><artifactId>docker-test</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>docker-compose-test</artifactId><properties><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

配置Jenkins

Step1、创建Item

Step2、备注

Step3、配置git(git配置方法查看上一篇)

在这之前,我们应该是在Gitee上上传了项目仓库,具体方法见上一篇

Step4、配置编译时间

其余的webhook、remote、SCM等配置方法自行查看上一篇

Step5、配置Maven-build


    Root POM:docker-compose-test/pom.xml
    Goals and options:clean package -Pprod -DskipTests

Step6、配置执行脚本Post Steps 

Execute shell内容如下:

#!/bin/bash -x
cd $WORKSPACE
echo "当前工作目录:$(pwd)"
# 子模块名称
MODEL_NAME=docker-compose-test
# Docker-compose.yml文件位置
DOCKER_COMPOSE_DIR='docker'# 检查生成的JAR文件
if ! ls $MODEL_NAME/target/*.jar >/dev/null 2>&1; thenecho "❌ 错误:未找到JAR文件,请检查Maven构建结果"exit 1
fi
echo "✅ JAR包位置:$(ls -l $MODEL_NAME/target/*.jar)"# 进入docker目录执行操作
cd $DOCKER_COMPOSE_DIR
echo "当前工作目录:$(pwd)"
sh copy.sh
echo "✅ 移动后的JAR包位置:$(ls -l ./file/jar/*.jar)"# 清除所有构建缓存
docker builder prune -af# 清理旧容器和镜像(忽略可能的错误)
docker-compose down --rmi local || true# 强制重新构建镜像(不使用缓存)
echo "🔨 开始构建镜像(不使用缓存)..."
docker-compose build --no-cache# 启动容器并强制重建
echo "🚀 启动容器..."
docker-compose up -d --force-recreate# 检查容器状态
sleep 5
echo "📊 容器状态检查:"
docker-compose ps# 输出日志检查(可选)
echo "🔍 查看容器日志(尾部):"
docker-compose logs --tail=50

Step7、立刻编译&查看日志

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/89178.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/89178.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/89178.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TASK01【datawhale组队学习】地瓜机器人具身智能概述

https://github.com/datawhalechina/ai-hardware-robotics 参考资料地址 具身智能&#xff08;Embodied AI&#xff09; 具身智能 智能的大脑 行动的身体。 比例&#xff08;Proportional&#xff09;、积分&#xff08;Integral&#xff09;、微分&#xff08;Derivative&a…

uni-app 配置华为离线推送流程

1、首先需要创建一个华为开发者账号&#xff0c;我这个是个人开发账号 申请开发者账号 2、去AppGallery Connect登陆我们刚刚创建好的账号&#xff0c;点击页面的APP进入到如下3 AppGallery Connect ‎‎‎‎‎ ‎3、在AppGallery Connect 网站中创建一个 Android应用、点击…

当下主流摄像头及其核心参数详解

&#x1f4d6; 推荐阅读&#xff1a;《Yocto项目实战教程:高效定制嵌入式Linux系统》 &#x1f3a5; 更多学习视频请关注 B 站&#xff1a;嵌入式Jerry 当下主流摄像头及其核心参数详解 一、摄像头发展概述 摄像头作为现代智能设备&#xff08;如手机、安防、车载、工业等&am…

下载了docker但是VirtualBox突然启动不了了

今天下docker后发现 eNSP 路由器&#xff0c;防火墙启动不了了去virtualbox检查的时候发现无法启动&#xff1a;报错&#xff1a;不能为虚拟电脑 AR_Base 打开一个新任务.Raw-mode is unavailable courtesy of Hyper-V. (VERR_SUPDRV_NO_RAW_MODE_HYPER_V_ROOT).返回代码: E_F…

C++11之lambda表达式与包装器

lambda与包装器lambda语法捕捉列表lambda的应用lambda的原理包装器functionbindlambda语法 lambda 表达式本质是⼀个匿名函数对象&#xff0c;跟普通函数不同的是他可以定义在函数内部。 lambda 表达式语法使⽤层⽽⾔没有类型&#xff0c;所以我们⼀般是⽤auto或者模板参数定义…

有痛呻吟!!!

XiTuJueJin:YYDS 分盘 有些平台吃相太难看&#xff0c;同样的文章&#xff0c;我还先选择现在这里发布&#xff0c;TMD. 莫名其妙将我的文章设置为仅VIP可见&#xff0c;还是今天才发现&#xff0c;之前只是将一两篇设置为仅VIP可见&#xff0c;今天突然发现这种标识的都自动…

2025年7-9月高含金量数学建模竞赛清单

2025年7-9月高含金量数学建模竞赛 ——“高教社杯”国赛 & “华为杯”研赛作为过来人&#xff0c;真心觉得参加数学建模比赛是我本科阶段做的最值的事之一。 它锻炼的那种把实际问题转化成模型求解的思维&#xff0c;对做研究、写论文甚至以后工作都帮助很大。我当时就是靠…

SpringBoot为什么使用new RuntimeException() 来获取调用栈?

为什么不直接使用 Thread.currentThread().getStackTrace()&#xff1f;这确实看起来有点“奇怪”或者“绕”&#xff0c;但其实这是 Java 中一种非常常见、巧妙且合法的技巧&#xff0c;用于在运行时动态获取当前代码的调用栈信息。Spring 选择用 new RuntimeException().getS…

小白成长之路-haproxy负载均衡

文章目录一、概述1、HAProxy简介2、HAProxy特点和优点&#xff1a;3、HAProxy保持会话的三种解决方法4、HAProxy的balance 8种负载均衡算法1&#xff09;RR&#xff08;Round Robin&#xff09;2&#xff09;LC&#xff08;Least Connections&#xff09;3&#xff09;SH&#…

Kafka 与 RocketMQ 消息确认机制对比分析

目录 生产者消息确认机制 Kafka 生产者 ACK 机制 RocketMQ 生产者确认机制 消费者消息确认机制 Kafka 消费者确认机制 RocketMQ 消费者确认机制 核心差异对比 选型建议 消息确认机制是分布式消息中间件的核心功能之一&#xff0c;它直接关系到消息传递的可靠性和系统性能…

C/C++---rdbuf()函数

在C中&#xff0c;rdbuf() 是I/O流库中的一个核心成员函数&#xff0c;主要用于访问和操作流对象的缓冲区。这个函数在底层数据处理、流重定向以及自定义流操作等场景中应用广泛。下面将从多个方面详细解析 rdbuf() 函数。 基本概念与函数原型 rdbuf() 是 std::basic_ios 类的成…

【LLM】从零到一构建一个小型LLM--MiniGPT

从零到一构建一个小型LLM (Small Language Model)暂时起名为MiniGPT。这个模型将专注于因果语言建模 (Causal Language Modeling)&#xff0c;这是许多现代LLM&#xff08;如GPT系列&#xff09;的核心预训练任务。模型设计&#xff1a; 我们设计的模型是一个仅包含解码器 (Dec…

网络安全威胁下的企业困境与破局技术实践

前言&#xff1a;网络安全威胁下的企业困境 在数字化转型的浪潮中&#xff0c;企业对信息技术的依赖程度日益加深&#xff0c;但随之而来的网络安全威胁也愈发严峻。据统计&#xff0c;全球每年因网络安全事件造成的经济损失高达数万亿美元&#xff0c;其中中小企业更是成为了网…

[RAG system] 信息检索器 | BM25 Vector | Pickle格式 | HybridRetriever重排序

第六章&#xff1a;信息检索器 在上一章中&#xff0c;我们成功完成了知识库摄入流程。这是巨大的进步~ 我们精心准备了文档"块"&#xff08;类似独立的索引卡&#xff09;&#xff0c;并将其存储在两套智能归档系统中&#xff1a;向量数据库&#xff08;用于基于含…

Android 高通平台修改音频参数效果文件-优化音频效果

Android 高通平台如何音频效果 修改音频参数效果文件-优化音频效果 按如下方式修改。 开发云 - 一站式云服务平台 diff --git a/vendor/qcom/proprietary/mm-audio/audcal/family-b/acdbdata//MTP/workspaceFile.qwsp b/vendor/qcom/proprietary/mm-audio/audcal/family-b/acdb…

Install Docker Engine on UbuntuMySQL

Install Docker Engine on Ubuntu&&MySQL安装docker安装mysql客户端连接数据库我真气鼠了&#xff0c;今天得到一个血泪的教训&#xff0c;以后一定看官方文档&#xff01;&#xff01;&#xff01;学的课用的centos&#xff0c;指令全是yum&#xff0c;我这边不通用&a…

智能人体感应模块HC-SR501应用指南---使用esp32

人体热释电探头红外感应模块 人体感应开关HC-SR501蓝板新款 绿板-淘宝网 HC-SR501 人体红外感应电子模块传感器热释电探头感应开关RD-624-tmall.com天猫 模块信息 HC-SR501人体感应开关是一种基于红外线技术的自动控制模块&#xff0c;广泛应用于安防、智能家居和自动控制等领…

加速度传感器方向校准方法

保持平板平放在桌面上&#xff0c;将后置摄像头保持在平板的左上后方&#xff0c;或者右上后方&#xff0c;此为机器的正方向 1、以一台重力方向正常的机器做测试&#xff0c;通过DeviceInfoHw这个软件的加速度测试功能【Accelerometer Test】我们可以知道 X方向数据测试&#…

【OpenHarmonyOS应用开发】

OpenHarmonyOS应用开发1.OpenHarmonyOS应用开发环境安装2.初始化项目3.连接润和软件的开发板套件1.OpenHarmonyOS应用开发环境安装 进入HarmonyOS下载鸿蒙应用开发工具DevEco Studio 5.0.7.200版本。 双击打开下载好的可执行文件&#xff0c;点击下一步。 如果已经安装过&am…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | AutoTextEffect(自动打字机)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— AutoTextEffect组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/。 利用 Vue 3 的 Composition API 和一些简单的 CSS 动画来构…