docker流水线
- 1、安装docker
- 2、安装docker-compose
- 3、编写docker-compose.yml
-
- [3.1 基础服务、中间件docker-compose.yml](#3.1 基础服务、中间件docker-compose.yml)
- [3.2 应用服务docker-compose-app.yml](#3.2 应用服务docker-compose-app.yml)
- [4、准备代码(以 RuoYi-Vue-Plus 为例)](#4、准备代码(以 RuoYi-Vue-Plus 为例))
-
-
- [方法一:Git 命令行标准流程(推荐)](#方法一:Git 命令行标准流程(推荐))
-
- [步骤 1:查看并添加原作者的仓库为 `upstream`](#步骤 1:查看并添加原作者的仓库为
upstream) - [步骤 2:确保你本地的修改都已经提交](#步骤 2:确保你本地的修改都已经提交)
- [步骤 3:拉取原作者的最新代码](#步骤 3:拉取原作者的最新代码)
- [步骤 4:合并原作者的代码到你的本地分支](#步骤 4:合并原作者的代码到你的本地分支)
- [步骤 5:将合并后的完整结果推送到你自己的 Gitee 仓库](#步骤 5:将合并后的完整结果推送到你自己的 Gitee 仓库)
- [步骤 1:查看并添加原作者的仓库为 `upstream`](#步骤 1:查看并添加原作者的仓库为
- [方法二:在 IntelliJ IDEA 中可视化操作(最方便解冲突)](#方法二:在 IntelliJ IDEA 中可视化操作(最方便解冲突))
- [💡 后续开发建议(最佳实践)](#💡 后续开发建议(最佳实践))
-
- 5、编辑阿里云效流水线
-
-
- 服务启动成功,但是容器里面没有日志文件?
- 第一步:排查日志实际写到了哪里
- [第二步:如何修复?(2 种方案选其一)](#第二步:如何修复?(2 种方案选其一))
-
- [方案 A:直接修改 `docker-compose-app.yml` 的挂载路径(最简单)](#方案 A:直接修改
docker-compose-app.yml的挂载路径(最简单)) - [方案 B:通过参数指定日志写入到 `/ruoyi/server/logs`](#方案 B:通过参数指定日志写入到
/ruoyi/server/logs)
- [方案 A:直接修改 `docker-compose-app.yml` 的挂载路径(最简单)](#方案 A:直接修改
- 补充检查:确认服务是否真的输出了文件日志
-
- 6、部署前端
- 7、nginx配置

1、安装docker
bash
# 移除旧版本docker
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
# 配置docker yum源。
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 安装 最新 docker
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 启动& 开机启动docker; enable + start 二合一
systemctl enable docker --now
# 配置加速
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.cn",
"https://docker.1panel.live",
"https://hub.rat.dev"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
2、安装docker-compose
bash
sudo curl -L https://github.com/docker/compose/releases/download/1.24.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
由于网络问题,常常安装不上,换成国内的
bash
sudo curl -L "https://get.daocloud.io/docker/compose/releases/download/1.24.1/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
添加可执行权限
bash
sudo chmod +x /usr/local/bin/docker-compose
测试安装结果
bash
docker-compose -v
3、编写docker-compose.yml
3.1 基础服务、中间件docker-compose.yml
bash
services:
# ==========================================
# 1. 关系型数据库 MySQL 8.0
# ==========================================
mysql:
image: mysql:8.0.42
container_name: mysql
restart: unless-stopped
environment:
TZ: Asia/Shanghai
MYSQL_ROOT_PASSWORD: "123456"
ports:
- "3306:3306"
volumes:
- /docker/mysql/data:/var/lib/mysql
- /docker/mysql/conf:/etc/mysql/conf.d
- /etc/localtime:/etc/localtime:ro
command:
--default-authentication-plugin=mysql_native_password
--character-set-server=utf8mb4
--collation-server=utf8mb4_general_ci
--explicit_defaults_for_timestamp=true
--lower_case_table_names=1
networks:
- jl-net
# ==========================================
# 2. 高性能缓存 Redis
# ==========================================
redis:
image: redis:7.2.8
container_name: redis
restart: unless-stopped
environment:
TZ: Asia/Shanghai
ports:
- "6379:6379"
volumes:
- /docker/redis/data:/data
- /etc/localtime:/etc/localtime:ro
command: redis-server --requirepass "123456" --appendonly yes
networks:
- jl-net
# ==========================================
# 3. 反向代理与 Web 服务 Nginx
# ==========================================
nginx:
image: nginx:1.23.4
container_name: nginx
restart: unless-stopped
environment:
TZ: Asia/Shanghai
ports:
- "80:80"
- "443:443"
volumes:
- /docker/nginx/conf.d:/etc/nginx/conf.d
- /docker/nginx/html:/usr/share/nginx/html
- /docker/nginx/log:/var/log/nginx
- /docker/nginx/webroot:/var/www/certbot:ro
- /docker/certbot/conf:/etc/letsencrypt:ro
- /etc/localtime:/etc/localtime:ro
networks:
- jl-net
# ==========================================
# 4. 免费 SSL 证书管理工具 Certbot
# ==========================================
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- /docker/certbot/conf:/etc/letsencrypt
- /docker/nginx/webroot:/var/www/certbot
profiles:
- tools
# ==========================================
# 5. 对象存储服务 MinIO
# ==========================================
minio:
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
container_name: minio
restart: unless-stopped
environment:
TZ: Asia/Shanghai
MINIO_ROOT_USER: admin
MINIO_ROOT_PASSWORD: "admin123" # 必须 >= 8 位
MINIO_COMPRESS: "off"
MINIO_COMPRESS_EXTENSIONS: ""
MINIO_COMPRESS_MIME_TYPES: ""
ports:
- "9000:9000" # API 端口
- "9001:9001" # 控制台后台端口
volumes:
- /docker/minio/data:/data
- /docker/minio/config:/root/.minio
- /etc/localtime:/etc/localtime:ro
command: server --address ':9000' --console-address ':9001' /data
networks:
- jl-net
# ==========================================
# 6. 分布式键值存储 etcd (v3.5)
# ==========================================
etcd:
image: quay.io/coreos/etcd:v3.5.15
container_name: etcd
restart: unless-stopped
environment:
TZ: Asia/Shanghai
ETCD_NAME: etcd01
ETCD_DATA_DIR: /etcd-data
# 允许无鉴权访问(如需密码后续可通过 etcdctl 配置)
ALLOW_NONE_AUTHENTICATION: "yes"
ETCD_LISTEN_CLIENT_URLS: http://0.0.0.0:2379
ETCD_ADVERTISE_CLIENT_URLS: http://etcd:2379
ETCD_LISTEN_PEER_URLS: http://0.0.0.0:2380
ETCD_INITIAL_ADVERTISE_PEER_URLS: http://etcd:2380
ETCD_INITIAL_CLUSTER: etcd01=http://etcd:2380
ETCD_INITIAL_CLUSTER_TOKEN: etcd-cluster-token
ETCD_INITIAL_CLUSTER_STATE: new
ports:
# 客户端通信端口
- "2379:2379"
# 集群节点间通信端口(单节点可不映射到宿主机,仅容器内可见)
- "2380:2380"
volumes:
- /docker/etcd/data:/etcd-data
- /etc/localtime:/etc/localtime:ro
networks:
- jl-net
# ==========================================
# 7. 消息队列 Kafka (KRaft模式,无需Zookeeper)
# ==========================================
kafka:
image: apache/kafka:3.8.0
container_name: kafka
restart: unless-stopped
environment:
TZ: Asia/Shanghai
KAFKA_NODE_ID: 1
KAFKA_PROCESS_ROLES: broker,controller
# 控制节点仲裁者
KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:9093
# 监听器定义:内部容器用 9092,控制器用 9093,宿主机外部用 9094
KAFKA_LISTENERS: PLAINTEXT://0.0.0.0:9092,CONTROLLER://0.0.0.0:9093,EXTERNAL://0.0.0.0:9094
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,EXTERNAL://120.26.247.53:9094
KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,EXTERNAL:PLAINTEXT
KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
# 单节点副本因子必须设置为 1
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: 1
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: 1
KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 0
KAFKA_NUM_PARTITIONS: 3
ports:
# 容器间走 9092,宿主机/开发工具走 9094
- "9092:9092"
- "9094:9094"
volumes:
- /docker/kafka/data:/var/lib/kafka/data
- /etc/localtime:/etc/localtime:ro
networks:
- jl-net
# ==========================================
# 共享桥接网络定义
# ==========================================
networks:
jl-net:
# 强制将 Docker 内部网络名命名为 jl-net(防止被加上目录前缀)
name: jl-net
driver: bridge
Nginx 官方镜像的机制是:主配置文件
nginx.conf内部会自动引入/etc/nginx/conf.d/*.conf下的所有子配置。后续想配置反向代理(比如代理若依后台),随时在宿主机的/docker/nginx/conf.d/目录下丢一个ruoyi.conf进去,执行docker exec nginx-web nginx -s reload即可生效。
3.2 应用服务docker-compose-app.yml
bash
services:
ruoyi-admin:
image: ruoyi-admin:latest
container_name: ruoyi-admin
restart: unless-stopped
environment:
TZ: Asia/Shanghai
SERVER_PORT: 8080
SNAIL_JOB_PORT: 28080
SNAIL_AI_PORT: 38080
volumes:
- /docker/ruoyi/server/logs/:/apps/logs/
- /etc/localtime:/etc/localtime:ro
networks:
- jl-net
networks:
jl-net:
external: true
4、准备代码(以 RuoYi-Vue-Plus 为例)
从https://gitee.com/dromara/RuoYi-Vue-Plus fork到自己的仓库,然后clone到本地。
在 Git 中,这种场景的标准做法是配置 upstream(上游仓库) 并进行 合并(Merge)。
Git 的合并算法会把原作者的提交和你自己的修改智能融合在一起,绝不会无故覆盖你的代码。
切记 :千万不要 在 Gitee 网页端直接点击仓库名旁边的 "强制同步" 按钮!
Gitee 的网页强制同步相当于
git reset --hard,会直接抹掉你自己修改的代码!必须在本地通过 Git 命令或 IDEA 进行合并。
方法一:Git 命令行标准流程(推荐)
在你的本地项目根目录下打开终端(Git Bash 或 PowerShell),按以下 5 步操作:
步骤 1:查看并添加原作者的仓库为 upstream
先查看你现有的远程仓库源:
bash
git remote -v
此时通常只有 origin(指向你自己的 Gitee 仓库)。
将原作者的项目地址 添加为上游源(命名为 upstream):
bash
git remote add upstream https://gitee.com/原作者用户名/项目名.git
再次执行 git remote -v,确认能看到 origin(你的)和 upstream(原作者的)。
步骤 2:确保你本地的修改都已经提交
在拉取前,必须保证你本地改动的代码已经 commit(不能处于未提交的脏工作区):
bash
git status
# 如果有未提交的文件,先暂存并提交:
git add .
git commit -m "保存我本地的修改"
步骤 3:拉取原作者的最新代码
切到你的工作分支(比如 master 或 main):
bash
git checkout master
拉取原作者仓库的更新(仅下载更新,不会影响你当前的文件):
bash
git fetch upstream
步骤 4:合并原作者的代码到你的本地分支
把原作者最新的代码合并到你当前的分支:
bash
git merge upstream/master
# 如果原作者的主分支叫 main,则执行:git merge upstream/main
此时会出现以下两种情况之一:
- 情况 A(无冲突,最常见) :
如果你们修改的不是同一行代码,Git 会自动完成合并,并提示Merge made by the 'ort' strategy,你的改动与作者的更新已经完美共存。 - 情况 B(有冲突) :
如果你们修改了同一个文件的同一行代码 ,Git 会暂停并标记冲突文件(CONFLICT (content): Merge conflict in ...)。-
打开冲突文件,会看到
<<<<<<< HEAD(你的代码)和>>>>>>> upstream/master(原作者的代码)。 -
手工选择保留哪部分,删掉标记符号。
-
解决后执行:
bashgit add . git commit -m "合并上游代码并解决冲突"
-
步骤 5:将合并后的完整结果推送到你自己的 Gitee 仓库
合并完成后,把最新的代码推送到你自己 Gitee 的远程分支:
bash
git push origin master
此时,你的本地仓库和你的 Gitee 远程仓库都同步到了原作者的最新版本,同时完整保留了你之前的所有代码。
方法二:在 IntelliJ IDEA 中可视化操作(最方便解冲突)
如果你更喜欢在 IDEA 中操作,IDEA 提供了非常直观的三栏(3-way)合并界面:
- 添加 Upstream 源 :
- 顶部菜单:
Git->Manage Remotes... - 点击
+号:- Name :填
upstream - URL :填原作者的 Gitee 仓库地址 -> 点击 OK。
- Name :填
- 顶部菜单:
- 抓取更新 :
- 顶部菜单:
Git->Fetch。
- 顶部菜单:
- 合并代码 :
- 打开右下角的分支管理器(Git Branches)。
- 在 Remote 下找到
upstream/master(或upstream/main)。 - 点击它,选择
Merge 'upstream/master' into 'master'。
- 处理冲突(如果有) :
- 如果遇到冲突,IDEA 会弹出一个 Conflicts 对话框,点击
Merge...。 - 左边是你的代码,右边是原作者的代码,中间是合并结果。你可以点击箭头决定保留哪边,或者两边都保留。
- 如果遇到冲突,IDEA 会弹出一个 Conflicts 对话框,点击
- 推送到 Gitee :
- 合并完成后,按快捷键
Ctrl + Shift + K(Mac 为Cmd + Shift + K)直接Push到你自己的origin。
- 合并完成后,按快捷键
💡 后续开发建议(最佳实践)
为了避免后续频繁遇到冲突:
-
建议将原作者的
master分支保持纯净,仅用于同步。 -
自己的业务修改可以拉一个新分支开发:
bashgit checkout -b my-dev每次原作者更新后:
- 切到
master:git merge upstream/master(直接快进同步,永无冲突)。 - 切回
my-dev:git merge master把新特性合进自己的开发分支。
- 切到
修改 application-prod.yml 然后推送到远程仓库
5、编辑阿里云效流水线
Java 构建上传 -> 执行命令
mvn clean install -Dmaven.test.skip=true
构建物上传 -> 打包路径
ruoyi-admin/target/*.jar
主机部署 -> 下载路径
/home/admin/app/ruoyi-admin/package.tgz
部署脚本
bash
set -e
DEPLOY_DIR="/home/admin/app/ruoyi-admin"
cd ${DEPLOY_DIR} && rm -rf classes generated-sources maven-* *.original *.jar && tar zxvf ./package.tgz && rm -rf classes generated-sources maven-* *.original
cat > Dockerfile << 'EOF'
FROM bellsoft/liberica-openjdk-rocky:21.0.12-cds
ENV SERVER_PORT=8080 SNAIL_JOB_PORT=28080 SNAIL_AI_PORT=38080 LANG=C.UTF-8 LC_ALL=C.UTF-8 JAVA_OPTS="" APP_PATH=/apps PARAMS="--spring.profiles.active=prod"
WORKDIR $APP_PATH
ADD *.jar $APP_PATH/app.jar
EXPOSE ${SERVER_PORT} ${SNAIL_JOB_PORT} ${SNAIL_AI_PORT}
SHELL ["/bin/bash", "-c"]
ENTRYPOINT exec java -Djava.security.egd=file:/dev/./urandom -Dserver.port=${SERVER_PORT} -Dsnail-job.port=${SNAIL_JOB_PORT} -Dsnail-ai.port=${SNAIL_AI_PORT} -XX:+HeapDumpOnOutOfMemoryError -XX:+UseZGC ${JAVA_OPTS} -jar app.jar ${PARAMS}
EOF
docker build -t ruoyi-admin .
docker compose -f /docker/docker-compose-app.yml up -d ruoyi-admin
docker image prune -f && rm -rf ${DEPLOY_DIR}/*
服务启动成功,但是容器里面没有日志文件?
出现这种情况,通常是因为日志其实已经生成了,但没有写到 /ruoyi/server/logs 这个路径下 ,最常见的原因是相对路径问题。
你可以通过以下 2 步快速排查并解决:
第一步:排查日志实际写到了哪里
请直接在当前容器内的终端(或者重新 docker exec -it ruoyi-admin bash)执行以下命令查找 .log 文件:
bash
# 全盘搜索 .log 结尾的文件
find / -name "*.log" 2>/dev/null
或者直接看 /apps 下面:
bash
ls -la /apps/logs
为什么会这样?
你在 Dockerfile 中设置了:
dockerfile
WORKDIR /apps
而若依(RuoYi)或 Spring Boot 默认的 logback.xml 中通常配置的是相对路径:
xml
<property name="log.path" value="./logs"/> <!-- 或者 value="logs" -->
在 Linux/Java 中,相对路径是以**当前工作目录(WORKDIR)**为基准的。
因此日志实际上被写到了 /apps/logs 目录下,而不是根目录的 /ruoyi/server/logs!
第二步:如何修复?(2 种方案选其一)
方案 A:直接修改 docker-compose-app.yml 的挂载路径(最简单)
把容器内的挂载路径直接改成实际生成的 /apps/logs/:
yaml
volumes:
- /docker/ruoyi/server/logs/:/apps/logs/
- /etc/localtime:/etc/localtime:ro
修改后执行 docker compose -f /docker/docker-compose-app.yml up -d ruoyi-admin 重启即可。宿主机就能立即看到日志文件了。
方案 B:通过参数指定日志写入到 /ruoyi/server/logs
如果你希望保持 /ruoyi/server/logs 这个规范路径,只需在 docker-compose-app.yml 的 environment 里告诉 Spring Boot 日志输出目录:
yaml
environment:
TZ: Asia/Shanghai
SERVER_PORT: 8080
LOG_PATH: /ruoyi/server/logs
LOGGING_FILE_PATH: /ruoyi/server/logs
(若依会优先读取系统环境变量 LOG_PATH 作为日志输出目录)
补充检查:确认服务是否真的输出了文件日志
在宿主机执行一下:
bash
docker logs --tail 50 ruoyi-admin
如果终端打印了 Spring Boot 启动 Banner 和文字日志,说明服务正常跑起来了。只要按照上述方案改一下挂载路径,宿主机就能同步看到所有的 .log 文件了。
6、部署前端
Node.js 构建 -> 执行脚本
npm install --registry=https://registry.npmmirror.com
npm run build:prod
主机部署 -> 下载路径
/home/admin/app/ruoyi-ui/package.tgz
部署脚本
bash
set -e
DEPLOY_DIR="/home/admin/app/ruoyi-ui"
cd ${DEPLOY_DIR}
rm -rf html && mkdir -p html && tar zxvf ./package.tgz -C html/
[ -d "html/dist" ] && mv html/dist/* html/ && rm -rf html/dist
cat > Dockerfile << 'EOF'
FROM nginx:1.23.4-alpine
COPY html/ /usr/share/nginx/html/
RUN echo 'server { listen 80; location / { root /usr/share/nginx/html; try_files $uri $uri/ /index.html; index index.html; } }' > /etc/nginx/conf.d/default.conf
EXPOSE 80
EOF
docker build -t ruoyi-ui .
docker compose -f /docker/docker-compose-app.yml up -d ruoyi-ui
docker image prune -f && rm -rf ${DEPLOY_DIR}/*
7、nginx配置
/docker/nginx/conf.d新建default.conf
bash
server {
listen 80;
server_name jlstu.vip www.jlstu.vip;
location / {
proxy_pass http://ruoyi-ui:80;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /prod-api/ {
proxy_pass http://ruoyi-admin:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
热重载外层 Nginx
bash
docker exec -it nginx-web nginx -t && docker exec -it nginx-web nginx -s reload
如果不配置ssl证书,到此就完成了