- infra/cicd:docker-compose(gitea/jenkins)、JCasC(凭据统一 ${ENV} 注入,无硬编码)、
备份/恢复、健康检查、凭据轮换与 git 历史清除脚本
- docs/deployment/cicd:安装、高可用备份监控、凭据事故复盘
- .env.example 仅为占位模板;真实 .env 由 .gitignore 排除
7.1 KiB
7.1 KiB
01 · 安装部署 / 安全配置 / Gitea↔Jenkins 集成
配置源码:infra/cicd/。本文描述目标态与从现状迁移的步骤。
1. 安装部署
1.1 前置条件
# Docker 20.10+,docker compose v2
docker --version && docker compose version
# 外部网络(nginx 反代依赖同一网络做服务名解析)
docker network create novalon-network 2>/dev/null || true
1.2 部署 Gitea
cd /home/novalon/docker-app/infra/cicd
cp .env.example .env && vim .env # 填 GITEA_DB_PASSWORD 等,.env 绝不入库
docker compose -f docker-compose.gitea.yml up -d
docker compose -f docker-compose.gitea.yml ps # 等 health=healthy
要点:
- 资源限制用
cpus/mem_limit顶层键,不要用deploy.resources。 现状 compose 写的是deploy:,非 swarm 下被docker compose静默忽略, 等于"看起来限制了实际没有"。这是本次审计确认的实锤。 - SSH 映射
2222:22,HTTP 仅127.0.0.1:3001,由 nginx 终止 TLS。
1.3 部署 Jenkins
# ⚠️ 现状卷属主是 root(因历史 user: root),切非 root 前必须先改属主
docker stop jenkins
docker run --rm -v jenkins_jenkins_home:/v alpine chown -R 1000:1000 /v
cd /home/novalon/docker-app/infra/cicd
docker compose -f docker-compose.jenkins.yml up -d
1.4 迁移时的破坏性变更(需维护窗口)
| 变更 | 为什么 | 影响 |
|---|---|---|
Jenkins user: root → 1000:1000 |
root 容器 = 宿主 root | 需先 chown 卷,服务中断 ~2 分钟 |
移除 /var/run/docker.sock |
作业 config.xml 实测 0 处 docker 调用 | 若未来作业要用 docker,需改用 dind/socket-proxy |
移除 /home/novalon/docker-app 挂载 |
deploy.sh 是 rsync 到远端,不读本地该目录 |
无 |
/root/.ssh → ./jenkins-ssh |
不再暴露宿主 root 私钥 | 需生成专用部署密钥并放服务器 authorized_keys |
2. 基础安全配置
2.1 HTTPS
由 nginx-static-production.conf + conf.d/*.conf 终止,无需应用自配。
# 已有配置(conf.d/git.f.novalon.cn.conf)
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
location /.well-known/acme-challenge/ { root /var/www/certbot; }
location / { return 301 https://$host$request_uri; }
已知小瑕疵(P3):nginx add_header 与 Gitea 自身都注入 x-frame-options/x-content-type-options,
响应里出现两次。不影响安全(重复值同语义),如要清理:nginx 侧改用 proxy_hide_header 后再 add_header。
2.2 访问权限
| 层 | 配置 | 实测状态 |
|---|---|---|
| Gitea 注册 | DISABLE_REGISTRATION=true + REQUIRE_SIGNIN_VIEW=true |
环境变量已设;注意:env 只在首次安装写库,需核对 app.ini [service] 是否同步 |
| Gitea 匿名读 | REQUIRE_SIGNIN_VIEW |
未登录访问 / 返回 303 → /explore |
| Jenkins 匿名读 | allowAnonymousRead: false(casc.yaml) |
待 JCasC 生效后验证 |
| Jenkins 自助注册 | allowsSignup: false |
同上 |
| agent 协议 | 仅 Ping,关 JNLP 入站 |
无 agent,可整段删 50000 映射 |
2.3 凭据管理(重点)
原则:任何密码/令牌只允许出现在两个地方 —— 服务器 .env(600 权限)或 Jenkins Credentials。
infra/cicd/.env ← 服务器本地,600,不入库
infra/cicd/.env.example ← 模板,只有占位符,可入库
jenkins/casc.yaml ← 凭据用 ${VAR} 引用环境变量,不写明文
禁止再出现 cicd.config 那种明文密码入库。相关 P0 事故见 03。
Gitea 令牌最小权限:Jenkins 拉代码只需 read:repository,不要给 write:administration。
3. Gitea ↔ Jenkins 集成
3.1 数据流
git push (main/develop)
└─► Gitea webhook → https://ci.f.novalon.cn/generic-webhook-trigger/invoke?token=***
└─► GenericTrigger 解析 $.ref / $.repository.full_name / $.after
└─► regexpFilterExpression ^refs/heads/(main|develop)$ 通过 → 触发构建
└─► Jenkinsfile 各 stage
3.2 作业定义(现状)
<!-- /var/jenkins_home/jobs/novalon-website-ci-cd/config.xml -->
<definition class="...CpsScmFlowDefinition">
<scm class="hudson.plugins.git.GitSCM">
<url>https://git.f.novalon.cn/novalon/novalon-website.git</url>
<credentialsId>gitea-credentials</credentialsId>
<branches><name>*/main</name></branches>
</scm>
<scriptPath>Jenkinsfile</scriptPath>
<lightweight>true</lightweight>
</definition>
<triggers/>
3.3 ⚠️ 排障:推送不触发构建
这是本次排查中最容易踩的坑,按顺序核对:
<triggers/>为空是正常的吗? 是 ——GenericTrigger声明在 Jenkinsfile 的triggers {}块里,第一次成功构建之后才会在作业上注册 trigger。 所以:全新作业 push 后不触发,先手动 Build Once 一次。- Token 是否一致:Jenkinsfile 里
token: '${PROJECT_NAME}-ci-token'是 Groovy 字符串插值,在双引号字符串里才会展开。 写在token: '...'(单引号)里就是字面量${PROJECT_NAME}-ci-token。 Gitea webhook 里填的 token 必须与 Jenkins 实际生效值一致(去作业配置页看真实 token)。 - Webhook 状态:Gitea → 仓库 → 设置 → Web 钩子 → 最近推送记录,
看 HTTP 状态码。
403= token 错;404= 插件端点错;200/204= 已接受。 - 过滤表达式:
regexpFilterExpression: '^(refs/heads/main|refs/heads/develop)$'—— 只在这两个分支触发,其它分支 push 静默忽略属预期。
3.4 流水线阶段(Jenkinsfile 现状)
| Stage | 内容 | 分支条件 |
|---|---|---|
| 🔧 环境检测 | 工具版本 + SSH 连通性 + registry | 全部 |
| 📥 安装依赖 | npm ci(失败回退 npm install --legacy-peer-deps) |
全部 |
| 🔍 质量检查 | ESLint ∥ TypeScript | 全部 |
| 🧪 单元测试 | test:coverage:check + Coverage Report |
全部 |
| 🌐 E2E | build + @smoke|@critical + @journey |
main |
| 👁️ 视觉回归 | visual-regression.spec.ts |
main |
| 🔒 安全扫描 | npm audit + 安全响应头 |
main |
| 🏗️ 构建 | build:clean → dist/,archiveArtifacts |
全部 |
| 🚀 部署 | deploy.sh deploy --skip-build,失败自动 rollback |
main + 参数开关 |
注意:E2E/视觉回归 stage 里
|| echo "⚠️ ..."会吞掉失败退出码, 测试挂了流水线仍然绿。若要严格门禁需改为exit 1。
3.5 集成自检命令
# 1. Gitea 可达
curl -s -o /dev/null -w '%{http_code}\n' https://git.f.novalon.cn/api/v1/version
# 2. Jenkins 通用 webhook 端点存在(错误 token 应 403 而非 404)
curl -s -o /dev/null -w '%{http_code}\n' \
'https://ci.f.novalon.cn/generic-webhook-trigger/invoke?token=wrong'
# 3. Gitea webhook 配置(需管理员或有仓库权限的令牌)
# Gitea UI → 仓库 → 设置 → Web 钩子