Jenkins+Docker(docker-compose、Dockerfile)+Gitee实现自动化部署

发布于:2025-07-17 ⋅ 阅读:(20) ⋅ 点赞:(0)

 项目目录结构

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: dev2
  application:
    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();
    @PostConstruct
    public 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}$"; then
    echo "停止并删除容器: $CONTAINER_NAME"
    docker stop $CONTAINER_NAME >/dev/null 2>&1
    docker 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=dev

sleep 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();
    @PostConstruct
    public 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}$"; then
    echo "停止并删除容器: $CONTAINER_NAME"
    docker stop $CONTAINER_NAME >/dev/null 2>&1
    docker 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_NAME

sleep 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/Dockerfile
    image: docker-compose-test:1.0-SNAPSHOT # 镜像名称 --- 不知道怎么从pom.xml中取出来,所以写死。
    container_name: compose-web-prod
    environment:
      - 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/lanting
    ports:
      - "9091:8090"
    logging:
      driver: json-file
      options:
        max-size: "10m"

Dockerfile

# 使用官方 OpenJDK 镜像
FROM my-jdk17:17

MAINTAINER 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();
    @PostConstruct
    public 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; then
  echo "❌ 错误:未找到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、立刻编译&查看日志


网站公告

今日签到

点亮在社区的每一天
去签到