背景
最近做大疆项目时,后台更新部署时,机场和无人机就会掉线。设备自动重连注册时间比较长,应用长时间不可用。所以需要灰色发布服务。docker-compose的swarm模式可解决此问题。
服务构建脚本Dockerfile
bash
# 使用官方Java基础镜像(推荐选择Alpine轻量版)
FROM openjdk:17-jdk-alpine
# 更新时区
RUN apk add --no-cache tzdata
ENV TZ=Asia/Shanghai
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& echo "Asia/Shanghai" > /etc/timezone \
# 设置工作目录
WORKDIR /app
# 将JAR文件复制到容器中(注意替换为你的JAR文件名)
COPY ./*.jar /app/
COPY ./lib/*.jar /app/lib/
ARG JAVA_OPTS="-server -Xmx512m -Xms512m -Xmn256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256M -Xss256k -XX:+DisableExplicitGC -XX:+UseG1GC -XX:LargePageSizeInBytes=128m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dumps/oom_dump.hprof -Djava.security.egd=file:/dev/./urandom"
RUN mkdir -p /app/dumps
RUN mkdir -p /app/logs
# 启动命令
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar /app/${project.build.finalName}.jar > /app/logs/${project.build.finalName}.log 2>&1"]
容器编排配置docker-compose.yml
下面的内容没有一行是多余的,修改时要慎重考虑
bash
version: '3.8'
networks:
${project.artifactId}-stack-proxy-tier: # 自定义的网络名称(可任意定义,但是需要提前创建)
external: true
services:
${project.artifactId}: #docker应用服务名称,不能包含非法字符比如点
image: ${project.build.finalName}:${project.version}
deploy:
replicas: 2 # 至少保持2个副本
update_config:
parallelism: 1 # 每次更新1个实例
delay: 10s # 新实例启动间隔
order: start-first # 先启动新实例,再停止旧实例
restart_policy:
condition: any
healthcheck:
test: ["CMD-SHELL", "wget -q --spider http://localhost:23111/actuator/health || exit 1"]
interval: 10s
timeout: 5s
retries: 3
start_period: 20s
volumes:
- ./${project.build.finalName}.jar:/app/${project.build.finalName}.jar
- ./lib:/app/lib
environment:
- VIRTUAL_HOST=192.168.1.4 # ip或者域名
- VIRTUAL_PORT=23111 # 应用启动的端口
networks:
- ${project.artifactId}-stack-proxy-tier
# 配置Nginx反向代理
nginx:
image: jwilder/nginx-proxy #专应用docker负载均衡、服务发现额镜像,功能非常强大
environment:
- TZ=Asia/Shanghai
- HTTP_CHECK=/"actuator/health"
ports:
- "22011:80"
volumes:
- /var/run/docker.sock:/tmp/docker.sock:ro
networks:
- ${project.artifactId}-stack-proxy-tier
depends_on:
- ${project.artifactId}
启动脚本restart
linux
bash
#!/bin/bash
# 设置脚本在遇到错误时立即退出
set -e
init(){
docker swarm init
# 检查Overlay网络是否存在:ml-citation{ref="3,4" data="citationList"}
if [[ -z $(docker network ls --filter name=^${project.artifactId}-stack-proxy-tier$ --format '{{.Name}}') ]]; then
echo "Creating overlay network..."
docker network create --driver overlay --attachable ${project.artifactId}-stack-proxy-tier
else
echo "Network ${project.artifactId}-stack-proxy-tier already exists, skipping creation."
fi
deploy
}
restart(){
deploy
}
deploy() {
# 构建Docker镜像
docker build -t ${project.build.finalName}:${project.version} .
# 部署Docker堆栈
docker stack deploy --detach=false -c docker-compose.yml ${project.artifactId}-stack
}
remove() {
#删除堆栈
docker stack rm ${project.artifactId}-stack
#删除网络
docker network rm ${project.artifactId}-stack-proxy-tier
}
case $1 in
init) init;;
restart) restart;;
remove) status;;
*) echo "require init|restart|remove";;
esac
windows
bash
@echo off
setlocal enabledelayedexpansion
if "%1"=="init" goto init
if "%1"=="restart" goto restart
if "%1"=="remove" goto remove
echo require init^|restart^|remove
exit /b 1
:init
docker swarm init
for /f "tokens=*" %%i in ('docker network ls --filter name^^=^${project.artifactId}-stack-proxy-tier$ --format "{{.Name}}"') do set network=%%i
if "!network!"=="" (
echo Creating overlay network...
docker network create --driver overlay --attachable ${project.artifactId}-stack-proxy-tier
) else (
echo Network ${project.artifactId}-stack-proxy-tier already exists, skipping creation.
)
goto deploy
:restart
goto deploy
:deploy
docker build -t ${project.build.finalName}:${project.version} .
docker stack deploy --detach=false -c docker-compose.yml ${project.artifactId}-stack
exit /b 0
:remove
docker stack rm ${project.artifactId}-stack
docker network rm ${project.artifactId}-stack-proxy-tier
exit /b 0
整合maven
上面脚本配置中的类似${project.build.finalName}变量是需要maven在构建时替换的。
resources进行替换,打包工具我用的maven-assembly-plugin,根据项目自行替换
bash
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/assembly/docker</directory>
<includes>
<include>docker-compose.yml</include>
<include>Dockerfile</include>
<include>restart.sh</include>
<include>restart.bat</include>
<include>nginx.conf</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugin>
<!-- 配置打包文件 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<descriptors>
<descriptor>src/assembly/distribution.xml</descriptor>
</descriptors>
<finalName>${project.artifactId}-${project.version}</finalName>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<!-- 配置启动入口 -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.2</version>
<configuration>
<archive>
<manifest>
<mainClass>com.dji.sample.CloudApiSampleApplication</mainClass>
<addClasspath>true</addClasspath>
<classpathPrefix>lib</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
我所有的脚本以及配置文件是放在src/assembly/docker下的
maven构建配置distribution.xml
bash
<?xml version="1.0" encoding="UTF-8"?>
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.2 http://maven.apache.org/xsd/assembly-1.1.2.xsd">
<id>package</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>target</directory>
<outputDirectory>./</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>target/classes</directory>
<outputDirectory>./</outputDirectory>
<includes>
<include>docker-compose.yml</include>
<include>Dockerfile</include>
<include>restart.sh</include>
<include>restart.bat</include>
<include>nginx.conf</include>
</includes>
</fileSet>
</fileSets>
<dependencySets>
<dependencySet>
<outputDirectory>lib</outputDirectory>
</dependencySet>
</dependencySets>
</assembly>
docker容器启动
首次部署命令
bash
restart.bat init
重启命令
bash
restart.bat restart
删除命令(仅限调试)
bash
restart.bat remove
修改镜像版本
每次更新时需要修改项目的版本号${revision}