需求
之前项目的部署方式,是将每个子模块编译后产生的 jar 包拷贝到指定目录,然后执行脚本,该脚本的大致内容就是根据每个模块的 Dockerfile 文件构建镜像,然后将编译产生的 jar 包拷贝进镜像里面,依次执行 docker run 命令来启动程序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #! /bin/bash
docker pull mongo:4.4.1
docker pull ....
docker build -t boat-common:v1.0.0 ./boat-common docker build -t boat-server:v1.0.0 ./boat-server
docker network create boat-network
docker run -d \ --net boat-network \ --name proxy-node-1 \ --restart=always \ -p 127.0.0.1:7050:80 \ -p 127.0.0.1:7051:10415 \ -p 127.0.0.1:7052:10416 \ -p 127.0.0.1:7053:10417 \ boat-proxy:v1.0.0
|
这么多容器要运行,并且容器之间还有编排的关系,这里可以使用 docker-compose 去管理容器,将这么多容器划分为一个服务,以服务的级别去管理这些容器。
问题
如果在现在这个项目里面使用 docker-compose 的话有几个问题:
- 除了基础服务(如 mongodb、redis、mqtt)外,其他容器镜像都需要依赖一个定义的镜像(这里是 boat-common)
- 脚本文件里,需要将每个模块的 jar 包拷贝到模块目录下面,与 Dockerfile 同级。
实现
现在这个项目是一个主模块和多个子模块的目录结构,在项目根目录有一个 deploy 目录专门存放模块部署文件的文件夹,每个文件夹存放 Dockerfile 和服务运行所依赖的文件,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| . ├── boat-media ├── daily-check.sh ├── deploy │ ├── common │ ├── docker-compose.yml │ ├── logger │ ├── media │ ├── proxy │ ├── server │ ├── static │ └── web ├── docs ├── pom.xml ├── usv-cloud-balance ├── usv-cloud-base ├── usv-cloud-logger ├── usv-cloud-media ├── usv-cloud-proxy ├── usv-cloud-server └── usv-cloud-web
|
第一个问题可以借助 docker-compose.yaml 文件中的 build 选项来实现。docker-compose文件的编写中,除了可以指定本地镜像和 DockerHub 仓库中的镜像之外,也可以在运行时根据 Dockerfile 去构建镜像,比如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| version: '3' services: porxy: image: boat-proxy:v1.0.0 build: context: ./proxy dockerfile: Dockerfile container_name: proxy-node-1 restart: always privileged: true environment: - TZ=Asia/Shanghai ports: - 7050:80 - 7051:10415 - 7052:10416 - 7053:10417 networks: - boat-network depends_on: - mqtt
|
在上面的 yaml 文件中可以看到使用了 build 选项,这个选项指定为构建镜像上下文路径
- context:上下文路径,也就是存放 Dockerfile 文件的文件夹目录地址
- dockerfile:指定构建镜像的 Dockerfile 文件名
- args:添加构建参数,这是只能在构建过程中访问的环境变量。
- labels:设置构建镜像的标签。
- target:多层构建,可以指定构建哪一层。
后面三个参数我没有使用,在当前场景下没有必要使用。
使用 build 选项就可以解决容器使用自定义镜像的问题,但是 Dockerfile 文件是需要把 jar 包拷贝进容器的,在之前的部署中都是需要手动编译并且拷贝 jar 包到指定目录下才能部署。那现在也是遇到了这个问题,要如何将每个模块编译后产生的 jar 包移动到 deploy 目录下呢?
先来看看现在的目录结构:
1 2 3 4 5 6 7 8 9 10 11 12 13
| ├── deploy ## 存放部署文件 │ ├── common ## 每个模块都有一个文件夹,存放Dockerfile和服务运行依赖文件 │ ├── docker-compose.yml │ ├── logger │ ├── media │ ├── proxy │ ├── server │ ├── static │ └── web ├── pom.xml ## 项目根目录 ├── usv-cloud-media │ ├── pom.xml ## 子模块的pom文件 ...忽略其他子模块,目录结构都与usv-cloud-media保持一致
|
在根目录中执行 mvn install -DskipTests=true 编译命令的话,最终生成的 jar 包都会是在每个子模块下的 target 目录下,比如:
1 2 3 4 5 6 7 8 9 10 11
| ├── usv-cloud-media │ ├── pom.xml │ ├── target │ │ ├── antrun │ │ ├── classes │ │ ├── generated-sources │ │ ├── generated-test-sources │ │ ├── maven-archiver │ │ ├── maven-status │ │ ├── test-classes │ │ ├── usv-cloud-media-V1.0.0.jar
|
那有什么办法可以把每个模块所编译出来的 jar 包都放在子模块的上一级的 deploy 目录中呢?
上网冲浪了一下,发现在 pom 文件中添加 maven-antrun-plugin 插件可以解决这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> <version>1.8</version> <executions> <execution> <id>install</id> <phase>install</phase> <configuration> <target> <copy todir="../deploy/proxy" overwrite="true"> <fileset dir="${project.build.directory}" erroronmissingdir="false"> <include name="*.jar" /> </fileset> </copy> </target> </configuration> <goals> <goal>run</goal> </goals> </execution> </executions> </plugin>
|
execution 标签代表着可执行命令,可以修改 maven 的命令执行过程,这里是修改了 install;
copy 是复制命令,其中 todir 是目标文件夹,overwrite 属性代表着如果存在旧文件是否覆盖。而代码中我是将文件拷贝到当前目录的上一级的 deploy 目录中。
fileset 标签中的 dir 属性代表着源文件夹,include 标签是源文件,可以使用符号匹配,这里使用了 *
在上面的代码中还是用到了一些环境变量:
project.build.directory 代表着 target 目录
还有其他环境变量,比如:
basedir 表示该 pom 文件所代表模块的根目录
project.build.finalName 代表 jar 包的前缀名,不包括后缀名。
而这样,就可以解决模块编译后的 jar 文件拷贝到指定目录,然后使用 docker-compose 去统一管理容器。