背景
Yuni 的主体部分开发已近尾声,正好前段时间 DS 发布了 v4 ,在 maimai 上用的体验不错,遂在 DS API 文档里偷师了一手接入克劳德的小寄巧,在其大力帮助下搭建了一个简单的 CI/CD 工作流
整体总结
代码推送 --> gitea 代码托管 & 构建 action --> 构建产物推送到本地 docker 镜像服务 register --> 触发本地 webhook --> webhook 服务拉取镜像,部署
环境搭建
gitea 代码托管
先启动个 docker
docker run -d --name gitea --restart always \
-p 3000:3000 -p 2222:22 \
-v /srv/docker/gitea/data:/data \
gitea/gitea:latest上面把 22 端口映射为 2222 端口是为了避免与主机的 22 端口冲突。如果推拉代码都走 http 协议,那这个映射可以去掉
然后浏览器访问 3000 端口,进行一些初始化相关的工作。页面上会有引导,当时没截图,现在就不补了。
gitea action runner
到 gitea 的官方仓库下载一下 runner 二进制文件
https://gitea.com/gitea/runner/releases
然后放到环境目录
chmod 777 <package_name> gitea-runner
mv gitea-runner /usr/local/bin/gitea-runner进入 gitea 的 runner 管理页面 <location>/-/admin/actions/runners ,点击左上角 创建新运行器 复制一下 token

最后注册一个运行器
gitea-runner register \
--instance http://localhost:3000 \
--token a_token \
--name vps-runner \
--labels ubuntu-latest:host运行器貌似会默认启动一个容器用来跑工作流;但我为了省事,就选择 host 模式,直接在本地跑工作流。反正就一个构建-推送 docker 镜像的工作,也不可能把环境搞炸了。
done

本地 docker 镜像仓库
docker 启动脚本:
先在当前目录下创建 data/, config/ 两个目录;config 下创建一个 config.yml
# config.yml
version: 0.1
log:
fields:
service: registry
storage:
filesystem:
rootdirectory: /var/lib/registry
delete:
enabled: true
http:
addr: :5000
headers:
Access-Control-Allow-Origin: ["http://192.168.0.103:11461"] # 留给 webui 用
Access-Control-Allow-Methods: ["GET", "HEAD", "OPTIONS"]然后启动脚本
docker run -d --name registry --restart always \
-p 5000:5000 \
-v /srv/docker/registry/data:/var/lib/registry \
-v /srv/docker/registry/config/config.yml:/etc/docker/registry/config.yml:ro \
registry:2然后启动 webui 脚本
docker run -d --name registry-ui --restart always \
-p 11461:80 \
-e REGISTRY_URL="http://192.168.0.103:5000" \
joxit/docker-registry-uiwebhook
apt install webhook工作流设置
gitea action
在项目的 .gitea/workflows 目录下创建一个 deploy.yml
name: Build and Deploy
on:
push:
branches: [master]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest # 选择 runner
steps:
- name: Checkout # 拉取项目代码
run: |
git clone --depth 1 http://192.168.0.103:3000/yuier/Yuni3.git .
- name: Docker build & push # 构建并推送镜像
run: |
docker build -t localhost:5000/yuni3:latest .
docker push localhost:5000/yuni3:latest
- name: Notify deploy # 通知 webhook. 这里第二行其实也是要用一个 token 的,在 github 上是要用环境变量传入的。但这里反正是本地部署,我就随便明文了
run: |
curl -X POST http://localhost:9000/hooks/deploy-yuni3 \
-H "Authorization: Bearer yuni-deploy-token" \
-H "Content-Type: application/json" \
-d '{}'webhook 配置
找个地方放配置文件 hooks.json
[{
"id": "deploy-yuni3",
"execute-command": "/srv/docker/yuni/deploy.sh",
"command-working-directory": "/srv/docker/yuni",
"include-command-output-in-response": true,
"trigger-rule": {
"match": {
"type": "value",
"value": "Bearer yuni-deploy-token",
"parameter": {
"source": "header",
"name": "Authorization"
}
}
}
}]然后启动
# 配置文件 工作端口 日志输出文件
nohup webhook -hooks hooks.json -port 9000 -verbose > /path/to/logs/webhook.log 2>&1 &部署脚本
即 hooks.json 中的 deploy.sh
#!/bin/bash
# Yuni3 VPS 私有部署脚本
# 由 webhook 触发,或手动执行
set -euo pipefail
DEPLOY_DIR="/srv/docker/yuni"
IMAGE="localhost:5000/yuni3:latest"
#################
# 省略其他周边工作
#################
# ── 拉取镜像 ──
echo "[deploy] 拉取镜像 $IMAGE ..."
docker pull "$IMAGE"
# ── 重启容器 ──
if docker ps -q -f name=yuni3 | grep -q .; then
echo "停止旧容器"
docker stop yuni3 && docker rm yuni3
fi
docker run -d \
--name yuni3 --restart always \
-p 11451:11451 \
-p 11452:11452 \
-p 11533:3005 \
-v "$DEPLOY_DIR/config:/app/config:ro" \
-v "$DEPLOY_DIR/data:/app/data" \
-v "$DEPLOY_DIR/logs:/app/logs" \
-v "$DEPLOY_DIR/plugins:/app/plugins" \
-v "/etc/localtime:/etc/localtime:ro" \
-v "/etc/timezone:/etc/timezone:ro" \
"$IMAGE"
echo "[deploy] 完成。日志: docker logs -f yuni3"效果展示
修改项目后,推送代码至 gitea 即可触发流程
gitea action 👇

docker 镜像仓库 👇

webhook 日志 👇

docker 部署情况 👇
root@Randosoru:/srv/docker/registry# docker ps | grep yuni
8090a62df0e1 localhost:5000/yuni3:latest "/app/entrypoint.sh" 3 hours ago Up 3 hours 0.0.0.0:11451-11452->11451-11452/tcp, [::]:11451-11452->11451-11452/tcp, 0.0.0.0:11533->3005/tcp, [::]:11533->3005/tcp yuni3