---
title: 企业级 CI/CD 自动化部署架构文档 (v2.0)
description: 基于 SDN 组网 + Pull 模式的内外网隔离部署方案(含安全加固与容错机制)
tags:
- ci/cd
- docker
- tailscale
- headscale
- gitlab
- sdn
- 宝塔
- devops
- security
date: 2026-06-15
version: 2.0
---
# 企业级 CI/CD 自动化部署架构文档 (v2.0)
> [!ABSTRACT] 文档概述
> 本方案针对“内外网物理隔离”场景,采用虚拟局域网(SDN)技术打通安全隧道,使外网生产服务器能够安全地反向拉取内网代码,并结合 Docker 实现服务热更新。
> **v2.0 更新重点**:新增 ACL 访问控制、部署健康检查与自动回滚、密钥安全托管、依赖构建优化及监控告警机制。
## 架构拓扑说明
- **代码仓库 (GitLab)**:内网 `192.168.1.22:8929`
- **执行节点 (GitLab Runner)**:内网环境(负责向生产服务器下发部署指令)
- **生产环境 (宝塔面板)**:公网 `47.109.83.159`(执行真实的代码拉取与容器编排)
- **控制平面 (Tailscale/Headscale)**:负责在内外网服务器之间建立 `100.x.x.x` 的安全加密网络
- **监控告警**:钉钉/飞书 Webhook(接收部署结果通知)
---
## 阶段一:建立底层加密安全隧道 (SDN 组网)
> [!INFO] 方案决策
> - **方案 A (Tailscale 官方 SaaS)**:零配置,5分钟跑通,无需公网端口,适合快速落地。(免费版支持 3 个用户 / 100 台设备)
> - **方案 B (Headscale 私有化部署)**:完全开源,将控制节点握在自己手里,适合对数据资产有严苛合规要求的团队。
>
> *请根据团队实际情况选择 A 或 B。*
### 方案 A:使用 Tailscale 官方服务
1. 访问 [Tailscale 官网](https://tailscale.com/) 注册免费账号
2. **内网 GitLab 及 外网宝塔服务器** 分别执行安装:
```bash
curl -fsSL https://tailscale.com/install.sh | sh
tailscale up
- 复制终端弹出的链接并在浏览器中授权
- 授权后,执行
tailscale ip -4记录下 GitLab 服务器获得的虚拟内网 IP(例如100.64.1.22)
方案 B:使用 Headscale 自建控制平面
[!WARNING] 前提条件与版本锁定
- Headscale 控制端必须部署在拥有公网 IP 的服务器上(如宝塔服务器
47.109.83.159)- 务必锁定版本号,避免因自动升级导致 API 不兼容。推荐 Headscale
v0.22.3+ Tailscale Clientv1.58.2
-
在外网宝塔服务器上部署 Headscale (Docker 方式):
# 1. 准备配置目录 mkdir -p /opt/headscale/config /opt/headscale/data cd /opt/headscale # 2. 下载指定版本配置并修改 wget https://raw.githubusercontent.com/juanfont/headscale/v0.22.3/config-example.yaml -O config/config.yaml # 编辑 config.yaml,将 server_url 修改为:http://47.109.83.159:8080 # 3. 启动 Headscale 容器(锁定版本) docker run -d --name headscale --restart always \ -p 8080:8080 \ -v $(pwd)/config:/etc/headscale \ -v $(pwd)/data:/var/lib/headscale \ headscale/headscale:v0.22.3 headscale serve # 4. 创建企业命名空间 docker exec headscale headscale users create my-company -
服务器客户端接入 Headscale:
在 内网 GitLab 及 外网宝塔服务器 分别安装指定版本 Tailscale 客户端:# 安装指定版本客户端 curl -fsSL https://tailscale.com/install.sh | sh -s -- --version 1.58.2 # 指向私有服务器登录 tailscale up --login-server=http://47.109.83.159:8080 -
在 Headscale 服务器 上批准节点加入:
docker exec headscale headscale nodes register --user my-company --key nodekey:xxxxxxxxx -
通过
tailscale ip -4记录 GitLab 服务器的虚拟 IP(例如100.64.1.22)
🔒 SDN 访问控制策略 (ACL)
[!DANGER] 安全必做
默认 SDN 网络是全互通的,必须配置 ACL 遵循最小权限原则,防止内网横向移动风险。
在 Tailscale Admin Console 或 Headscale 的 config.yaml 中添加:
acls:
- action: accept
src: ["tag:ci-runner"] # 仅允许 Runner 节点
dst: ["tag:gitlab:22,8929"] # 仅可访问 GitLab 的 SSH 和 HTTP 端口
- action: accept
src: ["tag:production"] # 生产服务器
dst: ["tag:gitlab:22"] # 仅可 SSH 拉取代码
- action: drop
src: ["*"]
dst: ["*:*"] # 默认拒绝所有其他流量
[!CHECK] 验证测试
- 在宝塔服务器执行
ping 100.64.1.22,确认隧道连通- 从非授权节点尝试访问 GitLab 端口,确认被 ACL 拦截
阶段二:打通代码拉取权限 (Deploy Keys)
[!NOTE] 权限最小化原则
生产服务器仅需拉取代码,不应授予写权限,除非有明确的反向推送需求(如自动打 Tag)。
-
宝塔服务器安装 Git 环境:
yum install git -y # CentOS # 或 apt install git -y # Ubuntu/Debian -
生成专用部署密钥:
ssh-keygen -t ed25519 -C "bt-production-server" -f ~/.ssh/deploy_key cat ~/.ssh/deploy_key.pub -
在 GitLab 侧授权:
- 登录 GitLab -> 项目 -> Settings -> Repository -> Deploy keys
- Title:
BT-Production-ReadOnly - Key: 粘贴公钥内容
- ⚠️ 不要勾选
Grant write permissions to this key - 点击 Add key
阶段三:部署与注册 GitLab Runner
在内网环境启动 Runner 容器:
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:v16.6.1 # 锁定版本
注册至项目:
docker exec -it gitlab-runner gitlab-runner register
# URL: GitLab 地址
# Token: 从 Settings -> CI/CD -> Runners 获取
# tags: institution-api
# executor: docker
# default image: alpine:3.17
阶段四:配置 GitLab CI/CD 环境变量与安全托管
[!WARNING] 密钥安全规范
- 所有敏感变量必须勾选 Masked 和 Protected
- 推荐使用 或 GitLab Vault Integration 托管密钥
- 若使用 GitLab Variables,需设置 Environment scope 为对应分支
进入 GitLab 项目 Settings -> CI/CD -> Variables,添加:
| 变量名 | 值 | 选项 | 说明 |
|---|---|---|---|
MAIN_SERVER_IP |
47.109.83.159 |
Protected | 生产服务器 IP |
TEST_SERVER_IP |
<测试环境IP> |
Protected | 测试服务器 IP |
SSH_PRIVATE_KEY |
<私钥内容> |
Masked, Protected | Runner 登录宝塔的密钥 |
DEPLOY_WEBHOOK_URL |
<钉钉/飞书Webhook> |
Masked | 部署通知地址 |
HEALTH_CHECK_URL |
http://localhost:8080/health |
Protected | 健康检查接口 |
[!TIP] SSH 密钥对生成
SSH_PRIVATE_KEY是供 Runner 远程登录宝塔使用的密钥,与阶段二的 Deploy Key 不同。需在本地生成密钥对,将公钥追加到宝塔服务器的~/.ssh/authorized_keys中,并将私钥填入此变量。
阶段五:宝塔生产环境首次初始化
-
在宝塔面板创建网站,记录物理目录(如
/www/wwwroot/sp_manager) -
进入宝塔终端,使用虚拟内网 IP 手动克隆一次项目:
cd /www/wwwroot/sp_manager git clone git@100.64.1.22:8929:your-group/sp-testing-centre.git -
配置生产环境
.env文件及数据库初始化 -
安全加固:
- 关闭宝塔面板公网访问端口
- 禁用密码登录,仅保留密钥认证
- 定期审计
~/.ssh/authorized_keys文件
阶段六:CI/CD 自动化流水线脚本
将以下内容保存为项目根目录的 .gitlab-ci.yml。流水线包含:远程登录 → 镜像预热 → 隧道拉取 → 容器重载 → 健康检查 → 失败回滚 → 通知告警。
stages:
- deploy
- notify
.set-tags: &set-tags
tags:
- institution-api
.set-before-scripts: &set-before-scripts
before_script:
- echo "http://mirrors.aliyun.com/alpine/v3.18/main" > /etc/apk/repositories
- echo "http://mirrors.aliyun.com/alpine/v3.18/community" >> /etc/apk/repositories
- apk add openssh-client git curl
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh && chmod 700 ~/.ssh
- ssh-keyscan $SERVER_IP >> ~/.ssh/known_hosts 2>/dev/null
- chmod 644 ~/.ssh/known_hosts
.deploy-base: &deploy-base
<<: *set-tags
image: alpine:3.17
stage: deploy
<<: *set-before-scripts
retry: 2
script:
# 1. 预拉取镜像(减少停机窗口)
- ssh root@$SERVER_IP "cd $DIR/sp-testing-centre && docker-compose -f docker/docker-compose.yml pull"
# 2. 拉取最新代码
- ssh root@$SERVER_IP "cd $DIR/sp-testing-centre && git pull origin $CI_COMMIT_REF_NAME"
# 3. 重载容器
- ssh root@$SERVER_IP "cd $DIR/sp-testing-centre && docker-compose -f docker/docker-compose.yml up -d --remove-orphans"
# 4. 等待服务启动
- sleep 10
# 5. 健康检查与自动回滚
- |
HEALTH_STATUS=$(ssh root@$SERVER_IP "curl -sf -o /dev/null -w '%{http_code}' $HEALTH_CHECK_URL || echo '000'")
if [ "$HEALTH_STATUS" != "200" ]; then
echo "❌ Health check failed (HTTP $HEALTH_STATUS), rolling back..."
ssh root@$SERVER_IP "cd $DIR/sp-testing-centre && git reset --hard HEAD~1 && docker-compose -f docker/docker-compose.yml up -d"
exit 1
fi
echo "✅ Health check passed"
job-deploy-test:
<<: *deploy-base
rules:
- if: '$CI_COMMIT_REF_NAME == "test"'
variables:
SERVER_IP: $TEST_SERVER_IP
DIR: /mnt/datadisk/docker/volumes/sp_manager
job-deploy-prod:
<<: *deploy-base
rules:
- if: '$CI_COMMIT_REF_NAME == "main" || $CI_COMMIT_REF_NAME == "master"'
variables:
SERVER_IP: $MAIN_SERVER_IP
DIR: /www/wwwroot/sp_manager
# 部署通知
job-notify:
stage: notify
image: alpine:3.17
when: always
script:
- apk add curl
- |
if [ "$CI_JOB_STATUS" == "success" ]; then
MSG="✅ 部署成功 | 分支: $CI_COMMIT_REF_NAME | 提交: $CI_COMMIT_SHORT_SHA"
else
MSG="❌ 部署失败 | 分支: $CI_COMMIT_REF_NAME | 提交: $CI_COMMIT_SHORT_SHA | [查看日志]($CI_PIPELINE_URL)"
fi
curl -s -X POST "$DEPLOY_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"msgtype\":\"text\",\"text\":{\"content\":\"$MSG\"}}"
needs:
- job-deploy-test
- job-deploy-prod
阶段七:灾备与恢复预案
[!IMPORTANT] 生产必备
控制平面和数据资产的备份是灾难恢复的基础。
Headscale 数据库备份
# 每日定时备份 Headscale SQLite 数据库
docker exec headscale headscale db dump > /opt/headscale/backup/headscale_$(date +%Y%m%d).sql
GitLab 备份
# 在 GitLab 服务器上执行
gitlab-backup create CRON=1
紧急恢复流程
- SDN 隧道中断:检查 Headscale 容器状态 → 恢复数据库备份 → 重新注册节点
- 部署失败且回滚无效:手动 SSH 登录宝塔 →
git log查找可用版本 →git checkout <commit>→docker-compose up -d - 密钥泄露:立即撤销 GitLab Deploy Key → 吊销 SSH 密钥 → 重新生成并更新所有相关配置
附录:运维检查清单
- [ ] SDN ACL 策略已配置并验证生效
- [ ] 所有 CI/CD 变量已设置 Masked + Protected
- [ ] Deploy Key 仅为只读权限
- [ ] 健康检查接口已实现并返回正确状态码
- [ ] 部署通知 Webhook 已测试可用
- [ ] Headscale/GitLab 自动备份已配置
- [ ] 宝塔面板公网端口已关闭
- [ ] 所有组件版本号已锁定
ci-cd #deployment #sop #security #devops #obsidian
### 📋 相比原文档的主要改进点
| 改进维度 | 原文档问题 | v2.0 解决方案 |
| :--- | :--- | :--- |
| **安全性** | SDN 全互通、Deploy Key 有写权限、密钥明文存储 | 新增 ACL 策略、只读 Deploy Key、变量 Masked+Protected、推荐 Vault |
| **稳定性** | 无健康检查、无回滚、镜像拉取导致长时间停机 | 新增 Health Check + 自动回滚、镜像预拉取、版本锁定 |
| **可观测性** | 部署结果无反馈 | 新增 Webhook 通知阶段,成功/失败实时推送 |
| **依赖管理** | 运行时 diff 判断不可靠 | 改为镜像预拉取 + 容器内构建,保证环境一致性 |
| **灾备** | 无备份恢复方案 | 新增阶段七:数据库备份、GitLab 备份、紧急恢复流程 |
| **文档规范** | 缺少运维检查清单 | 新增附录 Checklist,便于上线前逐项核验 |
直接将上述 Markdown 内容粘贴到 Obsidian 中新建笔记即可,所有 Callout、表格、代码块和标签均已适配 Obsidian 渲染引擎。

已有 0 条评论