443 lines
15 KiB
HTML
443 lines
15 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>研发型企业 CRMEB 自动发布方案 — Gitea CI/CD 流水线</title>
|
||
<style>
|
||
:root {
|
||
--color-primary: #1677FF; --color-primary-hover: #4096FF; --color-primary-active: #0958D9; --color-primary-bg: #E6F4FF;
|
||
--color-success: #52C41A; --color-success-bg: #F6FFED; --color-success-border: #B7EB8F;
|
||
--color-warning: #FAAD14; --color-warning-bg: #FFFBE6; --color-warning-border: #FFE58F;
|
||
--color-error: #FF4D4F; --color-error-bg: #FFF2F0; --color-error-border: #FFCCC7;
|
||
--bg: #F5F5F5; --bg-container: #FFFFFF; --bg-elevated: #FAFAFA;
|
||
--border: #D9D9D9; --border-light: #F0F0F0;
|
||
--text-primary: #141414; --text-secondary: #595959; --text-tertiary: #767676;
|
||
--radius-sm: 6px; --radius-md: 8px; --radius-lg: 12px;
|
||
--shadow-sm: 0 1px 2px rgba(0,0,0,0.03), 0 1px 6px -1px rgba(0,0,0,0.02);
|
||
--shadow-md: 0 2px 4px rgba(0,0,0,0.04), 0 4px 12px -2px rgba(0,0,0,0.04);
|
||
}
|
||
*{margin:0;padding:0;box-sizing:border-box;}
|
||
body{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'PingFang SC','Hiragino Sans GB','Microsoft YaHei',sans-serif;background:var(--bg);color:var(--text-primary);line-height:1.57;font-size:14px;}
|
||
.header{position:sticky;top:0;z-index:100;height:64px;background:linear-gradient(135deg,#1677FF,#722ED1);display:flex;align-items:center;padding:0 2rem;box-shadow:0 2px 8px rgba(22,119,255,0.15);}
|
||
.header h1{font-size:18px;font-weight:700;background:linear-gradient(135deg,var(--color-primary),#722ED1);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent;
|
||
.header .back-link{margin-right:16px;font-size:13px;color:rgba(255,255,255,0.85);text-decoration:none;flex-shrink:0;}
|
||
.header .back-link:hover{color:#fff;text-decoration:underline;}
|
||
.container{max-width:1200px;margin:0 auto;padding:32px 40px 64px;}
|
||
section{margin:48px 0;}
|
||
section h2{font-size:24px;line-height:32px;padding-bottom:8px;border-bottom:1px solid var(--border);margin-bottom:20px;color:var(--color-primary);}
|
||
section h3{font-size:20px;line-height:28px;margin:24px 0 12px;color:#722ED1;}
|
||
section h4{font-size:15px;line-height:22px;margin:16px 0 8px;color:#444;font-weight:600;}
|
||
p{color:var(--text-secondary);font-size:14px;margin:10px 0;}
|
||
table{width:100%;border-collapse:collapse;font-size:13px;margin:14px 0;}
|
||
th,td{padding:10px 14px;border:1px solid var(--border);text-align:left;vertical-align:top;}
|
||
th{background:var(--bg-elevated);font-weight:700;color:var(--text-primary);white-space:nowrap;}
|
||
td{background:var(--bg-container);color:var(--text-secondary);}
|
||
tbody tr:hover td{background:var(--color-primary-bg);}
|
||
.code-block{background:#1E1E1E;color:#D4D4D4;border-radius:var(--radius-md);padding:16px 20px;margin:14px 0;font-family:'SF Mono',Monaco,monospace;font-size:13px;line-height:1.7;overflow-x:auto;white-space:pre;}
|
||
.code-block .comment{color:#6A9955;}
|
||
pre{margin:0;}
|
||
ul,ol{margin:8px 0 8px 20px;}
|
||
li{margin:4px 0;color:var(--text-secondary);font-size:14px;}
|
||
.tree{font-family:'SF Mono',Monaco,monospace;font-size:13px;color:var(--text-secondary);line-height:1.6;margin:14px 0;padding:12px 16px;background:var(--bg-elevated);border-radius:var(--radius-sm);border:1px solid var(--border-light);}
|
||
.footer{text-align:center;padding:28px 0;color:var(--text-tertiary);font-size:13px;border-top:1px solid var(--border-light);margin-top:40px;}
|
||
@media(max-width:768px){.container{padding:20px 16px 40px;}.header{padding:0 1rem;}}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<header class="header">
|
||
<a class="back-link" href="../../index.html">← 知识库</a>
|
||
<h1>CRMEB 自动发布方案</h1>
|
||
</header>
|
||
|
||
<div class="container">
|
||
|
||
<section>
|
||
<h2>一、整体架构</h2>
|
||
|
||
<div class="tree">
|
||
┌──────────────────────────────────────────────────────────┐
|
||
│ Gitea (47.115.231.200) │
|
||
│ 代码仓库: admin-api / front-api / admin-web │
|
||
│ Gitea Actions CI/CD │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ Runner (47.122.125.216) │
|
||
│ Maven 构建 → npm 构建 → 停服 → 替换 JAR → 启服 │
|
||
├──────────────────────────────────────────────────────────┤
|
||
│ 部署目标 (47.122.125.216) │
|
||
│ /opt/crmeb/admin/ ← admin-api JAR │
|
||
│ /opt/crmeb/front/ ← front-api JAR │
|
||
│ /opt/crmeb/web/dist/ ← admin-web 静态文件 │
|
||
└──────────────────────────────────────────────────────────┘
|
||
</div>
|
||
|
||
<p><strong>三台服务器分工</strong>:</p>
|
||
|
||
<table>
|
||
<tr><th>服务器</th><th>角色</th></tr>
|
||
<tr><td>47.115.231.200</td><td>Gitea 代码托管 + CI 调度</td></tr>
|
||
<tr><td>47.122.125.216</td><td>Runner(构建+部署)+ 生产运行</td></tr>
|
||
<tr><td>39.100.114.100</td><td>Nginx 入口(已有,不变)</td></tr>
|
||
</table>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>二、Gitea 安装(47.115.231.200)</h2>
|
||
|
||
<div class="code-block">ssh root@47.115.231.200
|
||
|
||
# Docker 已装,直接部署
|
||
docker run -d \
|
||
--name gitea \
|
||
--restart always \
|
||
-p 3000:3000 \
|
||
-p 2222:22 \
|
||
-v /data/gitea:/data \
|
||
-e GITEA__server__DOMAIN=gitea.ycbat.com \
|
||
-e GITEA__server__ROOT_URL=https://gitea.ycbat.com \
|
||
-e GITEA__actions__ENABLED=true \
|
||
gitea/gitea:latest
|
||
|
||
# DNS: gitea.ycbat.com → 47.115.231.200
|
||
# Nginx 反代在 39.100.114.100 上加一条</div>
|
||
|
||
<p><strong>Nginx 反代</strong>(39.100.114.100 <code>/etc/nginx/conf.d/gitea.ycbat.com.conf</code>):</p>
|
||
|
||
<div class="code-block">server {
|
||
listen 80;
|
||
server_name gitea.ycbat.com;
|
||
client_max_body_size 100m;
|
||
location / {
|
||
proxy_pass http://47.115.231.200:3000;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>三、NPM 私服(Verdaccio)</h2>
|
||
|
||
<p>前端依赖统一走私有 NPM 仓库,加速安装并缓存上游包。</p>
|
||
|
||
<h3>3.1 架构</h3>
|
||
|
||
<table>
|
||
<tr><th>层</th><th>IP</th><th>角色</th><th>配置</th></tr>
|
||
<tr><td>Nginx 入口</td><td>39.100.114.100</td><td>反向代理</td><td><code>/etc/nginx/conf.d/npm.ycbat.com.conf</code></td></tr>
|
||
<tr><td>Verdaccio</td><td>47.115.231.200</td><td>私服</td><td>端口 4873,<code>/data/verdaccio/config.yaml</code></td></tr>
|
||
</table>
|
||
|
||
<h3>3.2 Nginx 反代</h3>
|
||
|
||
<div class="code-block"># 39.100.114.100 — /etc/nginx/conf.d/npm.ycbat.com.conf
|
||
server {
|
||
listen 80;
|
||
server_name npm.ycbat.com;
|
||
client_max_body_size 100m;
|
||
location / {
|
||
proxy_pass http://47.115.231.200:4873;
|
||
proxy_set_header Host $host;
|
||
proxy_set_header X-Real-IP $remote_addr;
|
||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||
proxy_set_header X-Forwarded-Proto $scheme;
|
||
}
|
||
}</div>
|
||
|
||
<h3>3.3 Verdaccio 配置</h3>
|
||
|
||
<div class="code-block"># 47.115.231.200 — /data/verdaccio/config.yaml
|
||
storage: /data/verdaccio/storage
|
||
uplinks:
|
||
npmjs:
|
||
url: https://registry.npmmirror.com/ # 阿里云镜像
|
||
packages:
|
||
'@mycompany/*':
|
||
access: $all
|
||
publish: $authenticated
|
||
'**':
|
||
access: $all
|
||
proxy: npmjs
|
||
logs: { type: stdout, format: pretty, level: info }</div>
|
||
|
||
<h3>3.4 客户端配置</h3>
|
||
|
||
<div class="code-block"># 项目根目录 .npmrc
|
||
registry=http://npm.ycbat.com/</div>
|
||
|
||
<h3>3.5 发布私有包</h3>
|
||
|
||
<div class="code-block"># 首次登录(只需一次)
|
||
npm login --registry=http://npm.ycbat.com/
|
||
|
||
# 发布 @mycompany 作用域包
|
||
npm publish --registry=http://npm.ycbat.com/</div>
|
||
|
||
<p>Verdaccio 进程通过 <code>pm2</code> 守护:<code>pm2 start verdaccio</code>。</p>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>四、Runner 安装(47.122.125.216)</h2>
|
||
|
||
<p>Runner 负责执行构建任务,装在<strong>生产服务器上</strong>,省去跨服务器传 JAR 的步骤。</p>
|
||
|
||
<div class="code-block">ssh root@47.122.125.216
|
||
|
||
# 1. 安装 Maven
|
||
apt install -y maven
|
||
|
||
# 2. 下载 Gitea Actions Runner
|
||
curl -L -o /usr/local/bin/act_runner \
|
||
https://gitea.com/gitea/act_runner/releases/download/v0.2.11/act_runner-0.2.11-linux-amd64
|
||
chmod +x /usr/local/bin/act_runner
|
||
|
||
# 3. 注册 Runner(先去 Gitea → Site Admin → Runners → 获取 token)
|
||
act_runner register \
|
||
--instance https://gitea.ycbat.com \
|
||
--token <RUNNER_TOKEN> \
|
||
--name "prod-runner" \
|
||
--labels "prod,ubuntu,maven"
|
||
|
||
# 4. systemd 守护
|
||
cat > /etc/systemd/system/gitea-runner.service << 'EOF'
|
||
[Unit]
|
||
Description=Gitea Actions Runner
|
||
After=network.target
|
||
[Service]
|
||
Type=simple
|
||
User=root
|
||
ExecStart=/usr/local/bin/act_runner daemon
|
||
Restart=on-failure
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
EOF
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable gitea-runner
|
||
systemctl start gitea-runner</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>五、代码仓库结构</h2>
|
||
|
||
<p>在 Gitea 上创建 3 个仓库,推送源码:</p>
|
||
|
||
<div class="tree">
|
||
gitea.ycbat.com/crmeb/
|
||
├── admin-api/ # 管理后台 API(Java)
|
||
│ ├── src/
|
||
│ ├── pom.xml
|
||
│ ├── .gitea/workflows/deploy.yml
|
||
│ └── .gitea/settings.xml # Maven 私服配置
|
||
│
|
||
├── front-api/ # 小程序 API(Java)
|
||
│ ├── src/
|
||
│ ├── pom.xml
|
||
│ └── .gitea/workflows/deploy.yml
|
||
│
|
||
└── admin-web/ # 管理后台前端(Vue.js)
|
||
├── src/
|
||
├── package.json
|
||
├── .npmrc # registry=http://npm.ycbat.com/
|
||
└── .gitea/workflows/deploy.yml
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>六、CI/CD 流水线配置</h2>
|
||
|
||
<h3>5.1 admin-api(Java 后端)</h3>
|
||
|
||
<div class="code-block"># .gitea/workflows/deploy.yml
|
||
name: Build & Deploy Admin API
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: prod
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Build JAR
|
||
run: mvn clean package -DskipTests -s .gitea/settings.xml
|
||
|
||
- name: Stop Service
|
||
run: /opt/crmeb/admin/ctl.sh stop || true
|
||
|
||
- name: Deploy JAR
|
||
run: |
|
||
cp target/Crmeb-admin.jar /opt/crmeb/admin/
|
||
cp target/classes/application-prod.yml /opt/crmeb/admin/ 2>/dev/null || true
|
||
|
||
- name: Start Service
|
||
run: /opt/crmeb/admin/ctl.sh start
|
||
|
||
- name: Health Check
|
||
run: |
|
||
sleep 10
|
||
curl -sf -o /dev/null https://adminapi.hblj.asia/ && echo "OK" || echo "FAIL"</div>
|
||
|
||
<h3>5.2 front-api(Java 后端)</h3>
|
||
|
||
<div class="code-block"># .gitea/workflows/deploy.yml
|
||
name: Build & Deploy Front API
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: prod
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Build JAR
|
||
run: mvn clean package -DskipTests -s .gitea/settings.xml
|
||
|
||
- name: Stop Service
|
||
run: /opt/crmeb/front/ctl.sh stop || true
|
||
|
||
- name: Deploy JAR
|
||
run: |
|
||
cp target/Crmeb-front.jar /opt/crmeb/front/
|
||
|
||
- name: Start Service
|
||
run: /opt/crmeb/front/ctl.sh start
|
||
|
||
- name: Health Check
|
||
run: |
|
||
sleep 10
|
||
curl -sf -o /dev/null https://miniappapi.hblj.asia/ && echo "OK" || echo "FAIL"</div>
|
||
|
||
<h3>5.3 admin-web(Vue.js 前端)</h3>
|
||
|
||
<div class="code-block"># .gitea/workflows/deploy.yml
|
||
name: Build & Deploy Admin Web
|
||
on:
|
||
push:
|
||
branches: [main]
|
||
|
||
jobs:
|
||
deploy:
|
||
runs-on: prod
|
||
steps:
|
||
- uses: actions/checkout@v4
|
||
|
||
- name: Install Dependencies
|
||
run: npm ci --registry=http://npm.ycbat.com/
|
||
|
||
- name: Build
|
||
run: npm run build:prod
|
||
|
||
- name: Deploy
|
||
run: |
|
||
rm -rf /opt/crmeb/web/dist/*
|
||
cp -r dist/* /opt/crmeb/web/dist/
|
||
|
||
- name: Health Check
|
||
run: curl -sf -o /dev/null https://admin.hblj.asia/ && echo "OK"</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>七、Gitea Secrets 配置</h2>
|
||
|
||
<p>仓库 → Settings → Secrets,添加:</p>
|
||
|
||
<table>
|
||
<tr><th>Secret 名</th><th>值</th><th>用途</th></tr>
|
||
<tr><td>(不需 secrets)</td><td>—</td><td>Runner 在本机执行,直接读写本地文件</td></tr>
|
||
</table>
|
||
|
||
<p>因为 Runner 和部署目标在同一台机器,<strong>不需要 SCP/SSH 密码</strong>。构建产物直接 <code>cp</code> 到目标目录。</p>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>八、Maven settings.xml(私服配置)</h2>
|
||
|
||
<div class="code-block"><!-- .gitea/settings.xml — 仓库根目录 -->
|
||
<settings>
|
||
<mirrors>
|
||
<mirror>
|
||
<id>aliyun</id>
|
||
<mirrorOf>central</mirrorOf>
|
||
<name>Aliyun Maven</name>
|
||
<url>https://maven.aliyun.com/repository/public</url>
|
||
</mirror>
|
||
</mirrors>
|
||
</settings></div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>九、实施步骤</h2>
|
||
|
||
<div class="tree">
|
||
Phase 1: 基础设施(1 小时)
|
||
├── 47.115.231.200 安装 Gitea
|
||
├── 39.100.114.100 配置 gitea.ycbat.com 反代
|
||
├── 47.122.125.216 安装 Maven + Runner
|
||
└── Gitea 创建组织 crmeb + 3 个仓库
|
||
|
||
Phase 2: 代码入库(2 小时)
|
||
├── 从生产服务器提取源码(反编译或从原始开发环境)
|
||
├── admin-api 源码 → git push
|
||
├── front-api 源码 → git push
|
||
├── admin-web 源码 → git push
|
||
└── 每个仓库配置 .gitea/workflows/deploy.yml
|
||
|
||
Phase 3: 验证(30 分钟)
|
||
├── 修改一个无害的改动(如版本号)
|
||
├── git push → 观察 Gitea Actions 自动构建
|
||
├── 确认服务自动重启
|
||
└── 确认 health check 通过
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>十、Gitea 分支保护</h2>
|
||
|
||
<p>每个仓库 Settings → Branches → 添加规则:</p>
|
||
|
||
<table>
|
||
<tr><th>规则</th><th>值</th></tr>
|
||
<tr><td>分支</td><td>main</td></tr>
|
||
<tr><td>禁止直接 push</td><td>✅</td></tr>
|
||
<tr><td>PR 必须 Approve</td><td>1 人</td></tr>
|
||
<tr><td>PR 必须 CI 通过</td><td>✅</td></tr>
|
||
</table>
|
||
|
||
<p>日常开发流程:</p>
|
||
|
||
<div class="tree">
|
||
feature 分支 → 开发 → git push → 创建 PR
|
||
→ Gitea Actions 自动测试(不部署)
|
||
→ Code Review
|
||
→ Merge to main
|
||
→ Gitea Actions 自动部署到生产
|
||
</div>
|
||
</section>
|
||
|
||
<section>
|
||
<h2>十一、回滚</h2>
|
||
|
||
<div class="code-block"># 在 47.122.125.216 上
|
||
cd /opt/crmeb/admin
|
||
cp Crmeb-admin.jar Crmeb-admin.jar.bak # 部署前 Runner 自动备份
|
||
# 回滚
|
||
cp Crmeb-admin.jar.bak Crmeb-admin.jar && ./ctl.sh restart</div>
|
||
|
||
<p>在 deploy.yml 中加入自动备份:</p>
|
||
|
||
<div class="code-block">- name: Backup
|
||
run: cp /opt/crmeb/admin/Crmeb-admin.jar /opt/crmeb/admin/Crmeb-admin.jar.bak</div>
|
||
</section>
|
||
|
||
<div class="footer">CRMEB 自动发布方案 · <a href="../../index.html" style="color:var(--color-primary);">返回知识库</a></div>
|
||
|
||
</div>
|
||
</body>
|
||
</html> |