Commit cba14336 authored by xuchentao's avatar xuchentao

ci: configure nginx and add env template

parent b428809f
Pipeline #408 failed
# 本地开发配置:复制为 .env 后使用。
# 生产环境请生成独立密钥,并将数据目录改为仓库外的持久化绝对路径。
CMS_PORT=8789
CMS_PASSWORD=replace-with-a-strong-password
CMS_SECRET=replace-with-openssl-rand-hex-32
CMS_API_KEY=replace-with-openssl-rand-hex-32
CMS_DATA_DIR=.runtime/cms-data
CMS_BUILD_LOCK=.runtime/site-build.lock
......@@ -4,6 +4,7 @@ dist/
.codex-tmp/
.runtime/
.env
!.env.example
src/content/articles/
src/content/published/
src/content/categories.json
......
# 塑美俏官网 · GitLab CI/CD 安全部署
# 流程:准备 release → 安装依赖 → 安全部署 → 验证与清理
# 流程:准备 release → 安装依赖 → 安全部署 → 配置 Nginx → 验证与清理
# 触发:push 到 main 分支
#
# ⚠️ GitLab 版本:11.7(较旧)
......@@ -22,11 +22,13 @@ variables:
PM2_BIN: "/usr/bin/pm2"
PM2_HOME_DIR: "/root/.pm2"
PM2_USE_SUDO: "1"
PUBLIC_HOST: "101.126.10.129"
stages:
- prepare
- install
- deploy
- nginx
- verify
# 所有 job 使用同一目录。CI_PIPELINE_ID 保证不同 pipeline 不会写同一个 release。
......@@ -90,7 +92,64 @@ deploy:
- node --env-file="$SHARED_ENV" scripts/run-with-site-lock.mjs -- bash scripts/deploy-release.sh "$DEPLOY_ROOT" "$RELEASE_DIR" "$APP_NAME" "$PM2_BIN" "$PM2_HOME_DIR" "$PM2_USE_SUDO" "$SITE_URL" "$CI_PIPELINE_ID"
- echo "[deploy] 当前版本:$(readlink -f "$DEPLOY_ROOT/current")"
# ── 4. 验证并只保留当前 + 上一个成功版本 ─────────────
# ── 4. 安装并验证 Nginx 配置 ─────────────────────────
# 使用参数固定的 sudo 命令安装 current release 中的配置,并在失败时恢复上一份。
configure_nginx:
stage: nginx
tags:
- sumeiqiao-prod
only:
- main
script:
- echo "[nginx] 开始配置公网入口:$PUBLIC_HOST"
- |
set -Eeuo pipefail
source_file="$DEPLOY_ROOT/current/deploy/nginx/sumeiqiao.conf"
target_file="/etc/nginx/conf.d/sumeiqiao.conf"
backup_file="/etc/nginx/conf.d/sumeiqiao.conf.previous"
resolved_source=$(readlink -f "$source_file")
case "$resolved_source" in
"$RELEASES_DIR"/*/deploy/nginx/sumeiqiao.conf) ;;
*) echo "Nginx 配置不在当前项目 release 中:$resolved_source" >&2; exit 2 ;;
esac
test -f "$source_file" || (echo "缺少 Nginx 配置:$source_file" && exit 2)
test ! -L "$target_file" || (echo "拒绝覆盖软链接:$target_file" && exit 2)
exec 9>"$SHARED_DIR/nginx-config.lock"
flock -x 9
had_previous=0
if [ -f "$target_file" ]; then
sudo -n /usr/bin/install -o root -g root -m 0644 "$target_file" "$backup_file"
had_previous=1
fi
restore_previous() {
if [ "$had_previous" = "1" ]; then
sudo -n /usr/bin/install -o root -g root -m 0644 "$backup_file" "$target_file"
else
sudo -n /usr/bin/rm -f -- "$target_file"
fi
sudo -n /usr/sbin/nginx -t || true
sudo -n /usr/bin/systemctl reload nginx || true
}
sudo -n /usr/bin/install -o root -g root -m 0644 "$source_file" "$target_file"
if ! sudo -n /usr/sbin/nginx -t; then
echo "Nginx 配置检查失败,正在恢复上一份配置" >&2
restore_previous
exit 1
fi
if ! sudo -n /usr/bin/systemctl reload nginx; then
echo "Nginx 重新加载失败,正在恢复上一份配置" >&2
restore_previous
exit 1
fi
- curl --fail --silent --show-error --max-time 5 --header "Host: $PUBLIC_HOST" http://127.0.0.1/ >/dev/null
- echo "[nginx] Nginx 配置与公网入口验证完成"
# ── 5. 验证并只保留当前 + 上一个成功版本 ─────────────
# when: always 用于输出最终状态;生产健康检查和自动回退已经在 deploy 的共享锁内完成。
verify:
stage: verify
......
......@@ -4,6 +4,14 @@
## 本地启动
首次启动先创建本地配置:
```bash
cp .env.example .env
```
请至少替换其中的 `CMS_PASSWORD``CMS_SECRET``CMS_API_KEY`,不要在共享环境中使用示例占位值。
开发模式(Astro 默认端口,支持页面热更新与同端口后台):
```bash
......@@ -94,11 +102,11 @@ npm start
生产环境必须把用户数据放在仓库外,例如:
```env
CMS_DATA_DIR=/srv/sumeiqiao/shared/cms-data
CMS_BUILD_LOCK=/srv/sumeiqiao/shared/site-build.lock
CMS_DATA_DIR=/root/sumeiqiao/shared/cms-data
CMS_BUILD_LOCK=/root/sumeiqiao/shared/site-build.lock
```
CI/CD 应在新的 release 目录部署代码,不要在正在运行的目录执行 `git clean`。仓库中的 `.gitlab-ci.yml` 兼容 GitLab 11.7,分为准备、安装、部署、验证四个阶段,每个阶段都会实时输出日志。安装依赖只操作尚未启用的独立 release;最终生产构建、切换 current 软链接、重启 PM2 和健康检查在同一把共享锁内完成。健康检查失败时会自动切回上一版本。
CI/CD 应在新的 release 目录部署代码,不要在正在运行的目录执行 `git clean`。仓库中的 `.gitlab-ci.yml` 兼容 GitLab 11.7,分为准备、安装、部署、配置 Nginx、验证五个阶段,每个阶段都会实时输出日志。安装依赖只操作尚未启用的独立 release;最终生产构建、切换 current 软链接、重启 PM2 和健康检查在同一把共享锁内完成。应用健康检查通过后才安装 Nginx 配置,安装失败会恢复上一份配置。健康检查失败时会自动切回上一版本。
```bash
node --env-file=/root/sumeiqiao/shared/.env scripts/run-with-site-lock.mjs -- bash scripts/deploy-release.sh /root/sumeiqiao /root/sumeiqiao/releases/<release> sumeiqiao /usr/bin/pm2 /root/.pm2 1 http://127.0.0.1:8789/
......@@ -112,6 +120,8 @@ node --env-file=/root/sumeiqiao/shared/.env scripts/run-with-site-lock.mjs -- ba
- 持久化配置:`/root/sumeiqiao/shared/.env`
- PM2 应用名:`sumeiqiao`
- 服务端口:`8789`
- Nginx 配置:`/etc/nginx/conf.d/sumeiqiao.conf`
- 公网入口:`101.126.10.129:80`
如果服务器实际路径或 PM2 应用名不同,只修改 `.gitlab-ci.yml` 顶部变量即可。脚本只会对 `APP_NAME` 指定的一个 PM2 进程执行 `describe``delete``start`,不会停止、重启或保存其他 PM2 项目。
......@@ -123,6 +133,28 @@ CMS_BUILD_LOCK=/root/sumeiqiao/shared/site-build.lock
CMS_PORT=8789
```
首次运行 pipeline 前,需要允许 Runner 执行参数固定的 Nginx 安装、检查、恢复和 reload 命令:
```bash
sudo visudo -f /etc/sudoers.d/sumeiqiao-runner
```
`/etc/sudoers.d/sumeiqiao-runner` 内容:
```sudoers
Cmnd_Alias SUMEIQIAO_NGINX = \
/usr/bin/install -o root -g root -m 0644 /root/sumeiqiao/current/deploy/nginx/sumeiqiao.conf /etc/nginx/conf.d/sumeiqiao.conf, \
/usr/bin/install -o root -g root -m 0644 /etc/nginx/conf.d/sumeiqiao.conf /etc/nginx/conf.d/sumeiqiao.conf.previous, \
/usr/bin/install -o root -g root -m 0644 /etc/nginx/conf.d/sumeiqiao.conf.previous /etc/nginx/conf.d/sumeiqiao.conf, \
/usr/bin/rm -f -- /etc/nginx/conf.d/sumeiqiao.conf, \
/usr/sbin/nginx -t, \
/usr/bin/systemctl reload nginx
gitlab-runner ALL=(root) NOPASSWD: SUMEIQIAO_NGINX
```
保存后执行 `sudo visudo -cf /etc/sudoers.d/sumeiqiao-runner` 检查语法。CI 只会从 `/root/sumeiqiao/current` 读取本项目配置,并只会更新 `/etc/nginx/conf.d/sumeiqiao.conf`。不同 pipeline 的 Nginx 操作由共享目录中的文件锁串行执行。
多个 GitLab pipeline 即使同时进入部署阶段,也会由服务器共享锁依次执行;部署持锁时,文章后台不会排队受理发布操作,而会明确提示编辑人员稍后重试。
验证阶段只保留当前版本和上一个成功版本。清理脚本不会删除其他 pipeline 尚未完成的 release;本 pipeline 失败产生的目录会在自己的验证阶段清理。若 Runner 被强制终止导致验证阶段未执行,可能留下一个未完成目录,但不会影响线上版本,可在确认没有对应 pipeline 运行后手动删除。
......
# 塑美俏官网 Nginx 配置
# 通过服务器 IP 访问,反向代理到本机 8789 端口。
server {
listen 80;
server_name 101.126.10.129;
# 图片上限为 8MB,JSON 中的 Base64 数据会额外增大请求体。
client_max_body_size 12m;
location / {
proxy_pass http://127.0.0.1:8789;
proxy_http_version 1.1;
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;
proxy_set_header X-Forwarded-Host $host;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment