sync: d8d-release 架构分层更新

- phases: 6 阶段文档(init/infra/config/ci-setup/deploy/verify)
- modes: 3 种运行模式(data-sync/deployment/registry-only)
- rules: 3 大规则体系(iron-laws/optimizations/security)
- reference: 参考文档(asset-manifest/common-commands/eci-container-spec)
- SKILL.md: 极简版入口
- workflow.md: 主流程说明
This commit is contained in:
yourname
2026-04-24 15:43:48 +00:00
parent 864e5bfb78
commit f3d5d7c537
17 changed files with 2896 additions and 306 deletions

View File

@@ -1,310 +1,6 @@
---
name: d8d-release
description: "阿里云 ECI 一键部署工具。提供 RAM AK/SK 后自动完成基础设施创建VPC/VSwitch/SG/NAS/EIP→ 私有 RegistrySpot 实例)→ Docker 构建 → ECI 容器组部署(内网拉取镜像)→ 输出固定 EIP 访问地址。支持 PostgreSQL 数据同步工作流模式AI 动态执行,非固定脚本)。所有配置从 eci.conf 读取,无需 Gitea Secrets。"
description: '阿里云 ECI 一键部署工具。提供 RAM AK/SK 后自动完成基础设施创建VPC/VSwitch/SG/NAS/EIP→ 私有 RegistrySpot 实例)→ Docker 构建 → ECI 容器组部署(内网拉取镜像)→ 输出固定 EIP 访问地址。支持 PostgreSQL 数据同步工作流模式AI 动态执行)。'
---
# d8d-release
阿里云 ECI 容器化部署:基础设施自动创建 + 私有 RegistrySpot 实例)+ CI/CD 构建 + 内网镜像拉取 + 一键部署。
**完整流程**: 提供 RAM AK/SK → AI 创建云资源 → 部署私有 RegistrySpot 实例,自动保存内网 IP → 填充 eci.conf → 部署 CI 工作流 → 推送 tag → CI 构建 Docker + 预热 postgres:17 镜像 → 推送到私有 Registry → ECI 从内网拉取双镜像部署 → 输出固定 EIP 访问地址。
**核心优势**:
- ✅ 完全自托管,不依赖 ACR/Docker Hub
- ✅ Registry 使用 Spot 实例,成本降低 90%
- ✅ 镜像持久化存储在 NAS
- ✅ 两个独立 EIP应用 (3001) 和 Registry (5000)
-**内网拉取镜像**: 应用 + postgres 从 VPC 内网拉取,速度提升 10-20 倍
-**postgres 镜像预热**: CI 构建时同步推送 postgres:17-alpine 到私有 Registry
-**内网 IP 自动发现**: Registry 创建后自动保存内网 IP 到 eci.conf
-**AI 数据同步工作流**: 动态执行备份/恢复/迁移,不依赖固定脚本,自适应各种场景
---
## 前置条件
1. 阿里云 RAM 用户 AccessKey ID + AccessKey Secret
2. RAM 用户已授权ECI、NAS、EIP、VPC、CR容器镜像服务
3. 无需配置 Gitea Secrets所有配置从 `eci.conf` 读取
---
## 执行步骤
### 步骤 1: 环境检查与配置
由 AI 自动执行:
- 检测并安装 `aliyun` CLI如未安装
- 配置阿里云凭证(用户提供 AK/SK
- 检测 `pnpm``docker` 等构建工具
### 步骤 2: 创建/验证云基础设施
按名称标记 `d8d-eci-*`,不存在则创建,存在则复用:
| 资源 | 名称标记 | 说明 |
|------|----------|------|
| VPC | `d8d-eci-vpc` | CIDR 192.168.0.0/16 |
| VSwitch | `d8d-eci-vswitch-{zone}` | CIDR 192.168.1.0/24 |
| Security Group | `d8d-eci-sg` | 放行 TCP 3001, 5000 |
| NAS | `d8d-eci-nas` | PostgreSQL 数据 + 镜像持久化 |
| EIP 应用 | `d8d-eci-eip-app` | 5Mbps 固定公网 IP应用访问 |
| EIP Registry | `d8d-eci-eip-registry` | 5Mbps 固定公网 IPRegistry 访问) |
| ECI Registry | `d8d-eci-registry` | Spot 实例registry:2 镜像 |
### 步骤 3: 填充配置文件
将创建的资源 ID 写入 `eci.conf`
```bash
ECI_REGION=cn-beijing
ECI_VPC_ID=vpc-xxx
ECI_VSWITCH_ID=vsw-xxx
ECI_SECURITY_GROUP_ID=sg-xxx
ECI_NAS_SERVER=xxx.cn-beijing.nas.aliyuncs.com
# 应用 EIP固定不变
ECI_EIP_ID=eip-xxx
ECI_EIP_ADDRESS=x.x.x.x
# 私有 Registry EIP
ECI_REGISTRY_EIP_ID=eip-xxx
ECI_REGISTRY_EIP_ADDRESS=x.x.x.x
ECI_REGISTRY_PORT=5000
ECI_REGISTRY_URL=x.x.x.x:5000
# 私有 Registry 内网 IP自动发现用于应用 ECI 内网拉取镜像)
ECI_REGISTRY_INTRANET_IP=172.29.xxx.xxx
ECI_REGISTRY_INTRANET_URL=${ECI_REGISTRY_INTRANET_IP}:5000
# 阿里云凭证
ALI_ACCESS_KEY_ID=RAM AK
ALI_ACCESS_KEY_SECRET=RAM SK
# 应用配置
ECI_CONTAINER_GROUP_NAME=map-collector
ECI_CPU=2.0
ECI_MEMORY=4.0
```
### 步骤 4: 部署 CI 工作流和脚本
复制模板文件到项目:
- `assets/release.yaml``.gitea/workflows/release.yaml`
- `assets/release_tag.sh``scripts/release_tag.sh`(替换 `__REMOTE__`
- `assets/eci-deploy.sh``scripts/eci-deploy.sh`
- `assets/eci.conf.template``eci.conf`(填充资源 ID提交到仓库
- `assets/gitea-log.py``scripts/gitea-log.py`
- `assets/gitea.conf.template``gitea.conf`(本地开发用,不提交)
提交并推送这些文件到仓库。
### 步骤 5: 触发发布
```bash
./scripts/release_tag.sh 0.1.0
```
自动完成:
1. 验证版本号格式
2. 检查工作目录
3. 拉取最新代码
4. 创建 `v{版本号}` git tag
5. 推送 tag 触发 Gitea Actions CI
### 步骤 6: CI 自动执行
CI 工作流自动完成:
1. Docker 多阶段构建standalone Next.js
2. 推送应用镜像到私有 Registry内网地址从 eci.conf 读取)
3. **预热 postgres:17-alpine**: 从 Docker Hub 拉取并推送到私有 Registry
4. 调用 `scripts/eci-deploy.sh` 部署 ECI 容器组AK/SK 从 eci.conf 读取)
5. ECI 从 **内网 IP** 拉取应用 + postgres 双镜像(速度 < 1
6. 输出固定 EIP 访问地址
### 步骤 7: 验证部署
```bash
curl http://{EIP}:3001/api/health
```
期望返回 `{ status: "ok", timestamp: ... }`
---
## ECI 容器组规格
| 容器 | 镜像 | CPU | 内存 | 说明 |
|------|------|-----|------|------|
| map-collector | 内网私有 Registry: `172.29.xxx.xxx:5000/d8d-user-release:tag` | 2 vCPU | 2 GiB | Next.js 应用内网拉取 |
| postgres | 内网私有 Registry: `172.29.xxx.xxx:5000/postgres:17-alpine` | 0.5 vCPU | 1.5 GiB | PostgreSQL 数据库CI 预热镜像 |
| **总计** | | **2.5 vCPU** | **3.5 GiB** | |
---
## 常用命令
### 查看已有标签
```bash
./scripts/release_tag.sh -l # 所有标签
./scripts/release_tag.sh # 最近10个标签
```
### 手动部署新版本
```bash
./scripts/release_tag.sh 1.0.0
```
### 手动触发 ECI 部署
```bash
./scripts/eci-deploy.sh <image-tag>
```
### 部署/重启私有 Registry
```bash
./scripts/eci-registry.sh
```
### 查询 Registry 镜像列表
```bash
curl http://<registry-ip>:5000/v2/_catalog
curl http://<registry-ip>:5000/v2/d8d-user-release/tags/list
```
### 查询 Gitea Actions 构建日志
```bash
python scripts/gitea-log.py <run_id> [job_id]
```
查看 CI 构建的详细执行日志用于调试和排错
---
## PostgreSQL 数据同步工作流AI 动态执行)
> **设计理念**: 不使用固定脚本,由 AI 根据实际项目情况动态生成并执行命令。灵活适配不同数据库类型、表结构、数据量。
### 触发方式
调用 `/d8d-release` 选择数据同步模式」:
- **备份**: ECI 本地
- **恢复**: 本地 ECI
- **迁移**: 两个 ECI 实例间数据迁移
### AI 执行步骤(备份模式)
**步骤 1: 环境检查**
- 读取 `eci.conf` 获取 ECI EIP安全组 ID区域
- 验证 aliyun CLI 已配置
- 验证本地 pg_dump 可用
- 确认 ECI 容器组运行状态
**步骤 2: 临时开放端口**
- 使用 `aliyun ecs AuthorizeSecurityGroup` 开放 5432 端口
- 记录规则 ID操作完成后自动关闭
- 验证端口可达性
**步骤 3: 数据库分析**
- 查询 ECI PostgreSQL 所有表名和记录数
- 估算数据大小
- 确认备份范围全量 / 指定表
**步骤 4: 执行备份**
- 动态生成 `pg_dump` 命令根据数据库类型编码表结构
- 保存到 `backups/{项目名}_{时间戳}.dump`
- 实时显示备份进度
**步骤 5: 验证与清理**
- 验证备份文件完整性
- 关闭安全组 5432 端口
- 创建 `latest.dump` 软链
- 输出备份报告文件大小记录数耗时
### AI 执行步骤(恢复模式)
**步骤 1: 确认风险**
- 显示 ECI 数据库当前记录数
- 警告恢复将覆盖现有数据
- 要求用户二次确认
**步骤 2-3: 同上(端口开放、环境检查)**
**步骤 4: 执行恢复**
- 动态选择 `pg_restore` / `psql` 命令根据备份文件格式
- 处理特殊情况编码转换表空间映射权限保留
- 实时显示恢复进度
**步骤 5: 数据验证**
- 恢复后查询各表记录数
- 与备份前记录数对比
- 输出差异报告
- 关闭安全组端口
### AI 自适应能力
| 场景 | AI 处理方式 |
|------|------------|
| 大表>100万行 | 自动使用并行导出 + 分批次验证 |
| 特殊编码数据库 | 自动检测编码,添加转码参数 |
| 无 pg_restore 环境 | 自动降级为 psql 纯 SQL 恢复 |
| 网络不稳定 | 自动重试机制 + 断点续传(管道压缩) |
| 外键依赖复杂 | 临时禁用约束 → 恢复 → 重新启用 |
### 安全设计原则
1. **端口自动关闭**: 无论成功/失败/中断AI 都会执行清理步骤关闭端口
2. **确认机制**: 恢复前必须显示数据对比并要求用户确认
3. **备份优先**: 恢复前自动先备份 ECI 现有数据作为回滚点
4. **幂等操作**: 所有命令设计为可重试,不会产生副作用
5. **审计日志**: AI 执行的所有命令都记录到 `logs/db-sync-{时间戳}.log`
### 使用场景示例
- **首次部署**: "把我本地开发库的测试数据同步到刚部署的 ECI"
- **定期备份**: "每天凌晨 2 点备份生产库数据到本地 NAS"
- **灾难恢复**: "昨天的备份文件,恢复到新的 ECI 实例"
- **数据迁移**: "把测试环境 ECI 的数据同步到生产环境 ECI"
---
## 资产文件清单
| 模板文件 | 部署目标 | 说明 |
|----------|----------|------|
| `assets/release.yaml` | `.gitea/workflows/release.yaml` | CI 工作流(构建→私有 Registry→ECI 部署) |
| `assets/release_tag.sh` | `scripts/release_tag.sh` | 发布标签脚本 |
| `assets/eci-deploy.sh` | `scripts/eci-deploy.sh` | ECI 应用部署脚本 |
| `assets/eci-registry.sh` | `scripts/eci-registry.sh` | ECI 私有 Registry 部署脚本Spot 实例) |
| `assets/eci.conf.template` | `eci.conf` | ECI 配置文件(含所有凭证,提交到仓库) |
| `assets/gitea-log.py` | `scripts/gitea-log.py` | Gitea 构建日志查询工具 |
| `assets/gitea.conf.template` | `gitea.conf` | Gitea 本地配置(不提交) |
---
## 重要说明
1. **固定 EIP**: 首次部署创建的两个 EIP应用 + Registry会被后续部署复用IP 地址永久不变
2. **数据持久化**: PostgreSQL 数据和 Docker 镜像都存储在 NAS容器组重建不会丢失数据
3. **资源复用**: 所有云资源带 `d8d-eci-*` 标记,同一账号下的项目可共用基础设施
4. **增量部署**: 每次发布只会重建 ECI 应用容器组Registry 和基础设施不会重复创建
5. **配置管理**: 所有凭证RAM AK/SK、Gitea 账号)都存在 `eci.conf`,无需 Gitea Secrets
6. **私有仓库安全**: `eci.conf` 可提交到私有仓库,无需额外加密
7. **Spot 实例成本**: Registry 使用 Spot 实例,相比按量付费节省约 90% 成本
8. **Registry 无需认证**: 当前 HTTP 模式无需认证,生产环境可考虑添加 HTTPS + Basic Auth
### 数据同步工作流模式
9. **AI 动态执行**: 不依赖固定脚本AI 根据实际项目情况(数据库类型、表结构、数据量)动态生成并执行命令
10. **安全端口管理**: 临时开放 5432 端口,无论成功/失败/中断AI 都会执行清理步骤关闭端口
11. **恢复前自动备份**: 数据恢复前 AI 自动先备份 ECI 现有数据作为回滚点
12. **二次确认机制**: 恢复前显示源和目标数据库记录数对比,必须用户确认后才执行
13. **自适应处理**: 大表自动并行导出、特殊编码自动转码、网络不稳定自动重试
14. **审计日志**: AI 执行的所有命令都记录到日志文件,便于问题排查
15. **灵活场景**: 首次部署同步、定期备份、灾难恢复、环境间迁移AI 按需调整策略
### 内网镜像拉取优化(新增)
9. **内网 IP 自动发现**: Registry 容器组创建后,自动调用 `DescribeContainerGroups` 获取 `IntranetIp` 并写入 `eci.conf`
10. **内网拉取速度**: 应用和 postgres 镜像从 VPC 内网拉取,速度 < 1 vs 外网拉取 30-60
11. **postgres 镜像预热**: CI 构建时自动从 Docker Hub 拉取 `postgres:17-alpine` 并推送到私有 Registry避免 ECI 部署时超时
12. **PlainHttpRegistry 支持**: ECI 部署时使用 `--PlainHttpRegistry` 参数支持 HTTP 内网 Registry无需 HTTPS 证书
13. **节省外网流量**: 内网拉取免费相比外网拉取节省流量费用
14. **部署成功率**: 内网拉取避免 Docker Hub 限流和网络波动部署成功率从 ~70% 提升到 > 99%
Follow the instructions in ./workflow.md.

View File

@@ -0,0 +1,357 @@
# 数据同步模式
**适用场景**
- 首次部署后同步本地开发数据到 ECI
- 定期备份生产数据到本地
- 灾难恢复:从备份恢复到新 ECI 实例
- 环境间数据迁移
---
## 核心设计原则
### AI 动态执行(非固定脚本)
> **重要**:本模式不提供预写死的 shell 脚本。AI 在执行时:
> 1. **分析当前环境**PostgreSQL 版本、可用工具、网络状况
> 2. **动态生成命令**:根据实际情况选择 `pg_dump` / `pg_restore` 策略
> 3. **自适应处理**:大表、编码、外键、网络波动等自动适配
> 4. **全程安全保障**5 项安全机制贯穿执行
---
## 三种子模式
| 子模式 | 触发词 | 数据流向 | 典型场景 |
|--------|--------|----------|----------|
| **备份** | `backup` | ECI PostgreSQL → 本地文件 | 定期备份、更新前快照 |
| **恢复** | `restore` | 本地文件 → ECI PostgreSQL | 灾难恢复、数据回滚 |
| **迁移** | `migrate` | 本地 PostgreSQL → ECI PostgreSQL | 首次部署同步历史数据 |
---
## AI 执行流程(通用)
调用 `/d8d-release` 选择「数据同步模式」后,按以下步骤执行:
### 步骤 1: 环境探测
```bash
# 探测本地环境
psql --version
pg_dump --version
pg_restore --version
which psql pg_dump pg_restore
# 探测 ECI 数据库连接
source eci.conf
psql -h $ECI_EIP_ADDRESS -p 5432 -U postgres -d map_stores -c "SELECT version();"
```
**AI 决策点**
- 版本不匹配 → 警告并选择兼容的 dump 格式
- 工具缺失 → 引导安装或降级策略
- 网络不通 → 检查安全组、尝试内网连接
### 步骤 2: 数据量评估
```sql
-- 查询各表记录数
SELECT schemaname, relname, n_live_tup
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
-- 查询数据库总大小
SELECT pg_size_pretty(pg_database_size('map_stores'));
```
**AI 决策点**
- 单表 > 10 万行 → 启用并行导出 (`--jobs=N`)
- 总大小 > 1GB → 启用压缩、分块、进度显示
- 有关联外键 → 调整导出/导入顺序
### 步骤 3: 生成并确认执行计划
向用户展示同步计划,包含:
- 源/目标数据库信息
- 预计数据量和耗时
- 安全组端口操作计划
- 备份文件存储位置
- 回滚策略
**必须获得用户确认后才继续执行**
---
## 子模式详细流程
### 子模式 A: 备份ECI → 本地)
#### 执行步骤
```
1. 临时开放 ECI 安全组 5432 端口(仅限当前公网 IP
2. 创建本地 backups/ 目录
3. 执行 pg_dump 导出:
- 格式custom (-Fc) 支持并行恢复
- 压缩zstd 或 gzip
- 大表:--jobs=4 并行导出
4. 自动关闭安全组 5432 端口trap 确保异常也关闭)
5. 创建 latest.dump 软链到最新备份
6. 验证备份文件完整性
7. 输出备份报告:文件大小、记录数、耗时
```
#### 备份目录结构
```
backups/
├── map_stores_20260424_143022.dump # 时间戳命名
└── latest.dump -> map_stores_20260424_143022.dump
```
#### AI 生成的典型命令
```bash
# 开放端口(仅限当前 IP
MY_IP=$(curl -s ifconfig.me)
aliyun ecs AuthorizeSecurityGroup \
--SecurityGroupId $ECI_SECURITY_GROUP_ID \
--IpProtocol tcp \
--PortRange 5432/5432 \
--SourceCidrIp "$MY_IP/32" \
--Description "Temporary DB sync $(date +%Y%m%d)"
# 导出数据
pg_dump -h $ECI_EIP_ADDRESS -p 5432 -U postgres \
-d map_stores -Fc -Z 6 --jobs=4 \
-f backups/map_stores_$(date +%Y%m%d_%H%M%S).dump
# 自动关闭端口trap 确保执行)
trap 'aliyun ecs RevokeSecurityGroup ...' EXIT INT TERM
```
---
### 子模式 B: 恢复(本地 → ECI
#### 执行步骤
```
1. 确认恢复操作(二次确认)
2. 自动创建恢复前备份(回滚点)
3. 临时开放安全组 5432 端口
4. 终止所有活跃数据库连接
5. 执行 pg_restore
- 先恢复 schema再恢复数据
- 禁用触发器加速导入
- 大表并行恢复
6. 重建索引、分析表
7. 自动关闭安全组端口
8. 验证恢复结果:对比各表记录数
9. 输出恢复报告
```
#### 关键安全机制
**恢复前自动备份**:执行恢复前,先对目标 ECI 数据库做一次即时备份作为回滚点。
**二次确认**AI 必须明确询问用户确认,格式:
```
⚠️ 即将恢复数据,这将覆盖目标数据库!
备份文件: backups/map_stores_xxx.dump
目标数据库: $ECI_EIP_ADDRESS:5432/map_stores
回滚点已创建: backups/before_restore_xxx.dump
请输入 "YES, I UNDERSTAND" 确认继续:
```
#### AI 生成的典型命令
```bash
# 终止所有连接
psql -h $ECI_EIP_ADDRESS -p 5432 -U postgres -d postgres \
-c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'map_stores';"
# 先恢复 schema
pg_restore -h $ECI_EIP_ADDRESS -p 5432 -U postgres \
-d map_stores --schema-only --clean --if-exists \
backups/map_stores_xxx.dump
# 再恢复数据(并行)
pg_restore -h $ECI_EIP_ADDRESS -p 5432 -U postgres \
-d map_stores --data-only --disable-triggers --jobs=4 \
backups/map_stores_xxx.dump
# 重建索引并分析
psql -h $ECI_EIP_ADDRESS -p 5432 -U postgres -d map_stores \
-c "REINDEX DATABASE map_stores; ANALYZE;"
```
---
### 子模式 C: 迁移(本地 PostgreSQL → ECI PostgreSQL
双向直接同步,不经过中间文件。适用于首次部署时同步本地开发环境的全部历史数据。
#### 执行步骤
```
1. 探测源数据库本地和目标数据库ECI版本
2. 评估数据量和复杂度
3. 临时开放 ECI 安全组 5432 端口
4. 创建目标数据库回滚点
5. 选择迁移策略:
- 小数据量 (<100MB): pg_dump | psql 管道
- 大数据量: 先 dump 到临时文件,再 restore
6. 执行迁移
7. 自动关闭安全组端口
8. 双向验证:对比源和目标各表记录数
9. 输出迁移报告
```
#### 管道迁移(小数据量)
```bash
pg_dump -h 127.0.0.1 -p 5432 -U postgres -d map_stores -Fc \
| pg_restore -h $ECI_EIP_ADDRESS -p 5432 -U postgres -d map_stores
```
#### AI 自适应能力
| 场景 | 自适应策略 |
|------|------------|
| **大表并行** | 单表 > 10 万行 → `--jobs=N` 并行导入 |
| **编码转码** | 检测到编码不匹配 → 自动转码或提示转换 |
| **工具降级** | pg_dump 版本不兼容 → 降级为 plain 格式 |
| **网络重试** | 连接中断 → 断点续传 + 指数退避重试 |
| **外键处理** | 先禁用外键 → 导入数据 → 重建外键检查 |
---
## 5 项安全保障
### 1. 端口自动关闭
```bash
# 使用 trap 确保脚本无论如何退出都关闭端口
trap '
echo "正在关闭安全组 5432 端口..."
aliyun ecs RevokeSecurityGroup ...
echo "端口已关闭"
' EXIT INT TERM HUP
```
### 2. 二次确认机制
所有写入操作(恢复、迁移)必须获得用户显式确认。
### 3. 回滚点备份
执行任何覆盖操作前,先对目标数据库创建即时备份。
### 4. 幂等操作设计
- 备份操作可重复执行(生成新时间戳文件)
- 恢复失败可重试schema-only 带 --clean
- 端口操作可重复执行(存在则跳过)
### 5. 审计日志
所有操作记录到 `backups/sync_audit.log`
- 操作类型、时间、操作用户
- 源/目标数据库信息
- 备份文件名、大小、记录数
- 操作耗时、成功/失败状态
---
## 自适应能力详解
### 大表并行处理
**AI 检测逻辑**
```sql
SELECT relname, n_live_tup
FROM pg_stat_user_tables
WHERE n_live_tup > 100000
ORDER BY n_live_tup DESC;
```
**自适应策略**
- > 10 万行: `--jobs=2`
- > 50 万行: `--jobs=4`
- > 100 万行: `--jobs=8` + 禁用索引重建
### 编码自动转码
**检测**`SHOW server_encoding;` 对比源和目标
**策略**
- UTF8 → UTF8: 直接导入
- GBK/GB18030 → UTF8: 管道转码 `| iconv -f GBK -t UTF-8`
- 不兼容: 提示用户手动处理
### 网络重试机制
```bash
# 指数退避重试
retry_count=0
max_retries=5
while [ $retry_count -lt $max_retries ]; do
if pg_restore ...; then
break
fi
retry_count=$((retry_count + 1))
sleep $((2 ** retry_count)) # 2,4,8,16,32s
done
```
### 外键智能处理
```bash
# 导入前禁用所有外键
psql -c "SET session_replication_role = replica;"
# 导入数据...
# 导入后启用并验证
psql -c "SET session_replication_role = DEFAULT;"
psql -c "
DO \$\$
DECLARE r RECORD;
BEGIN
FOR r IN SELECT conname FROM pg_constraint WHERE contype = 'f' LOOP
EXECUTE 'ALTER TABLE ' || r.conrelid::regclass || ' VALIDATE CONSTRAINT ' || r.conname;
END LOOP;
END \$\$;
"
```
---
## 输出报告格式
同步完成后输出格式化报告:
```
✅ 数据同步完成!
📊 同步统计:
模式: 备份 / 恢复 / 迁移
总记录数: 125,432 行
文件大小: 234 MB
耗时: 2 分 34 秒
🔒 安全状态:
安全组 5432 端口: 已关闭
回滚备份: backups/before_restore_20260424_143022.dump
📝 审计日志: backups/sync_audit.log
💡 提示: 如需验证数据,可连接数据库查询各表记录数
```
---
→ 返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,107 @@
# 标准部署模式
**适用场景**
- 首次完整部署
- 增量更新部署
- 完整端到端流程
---
## 执行流程
调用 `/d8d-release` 选择「标准部署模式」后,按以下阶段执行:
| 阶段 | 文件 | 说明 |
|------|------|------|
| Phase 00 | [phases/phase-00-init.md](../phases/phase-00-init.md) | 环境检查、CLI 配置、依赖检测 |
| Phase 01 | [phases/phase-01-infra.md](../phases/phase-01-infra.md) | 基础设施创建7 项资源) |
| Phase 02 | [phases/phase-02-config.md](../phases/phase-02-config.md) | 配置生成、内网 IP 发现 |
| Phase 03 | [phases/phase-03-ci-setup.md](../phases/phase-03-ci-setup.md) | CI 工作流部署 |
| Phase 04 | [phases/phase-04-deploy.md](../phases/phase-04-deploy.md) | 触发发布、ECI 容器组部署 |
| Phase 05 | [phases/phase-05-verify.md](../phases/phase-05-verify.md) | 部署验证、健康检查 |
---
## 首次部署 vs 增量部署
| 项 | 首次部署 | 增量部署 |
|----|----------|----------|
| 基础设施创建 | ✅ 完整执行7 项) | ⏭️ 跳过(资源已存在,复用) |
| Registry 内网 IP | ✅ 自动发现并记录 | ⏭️ 复用已有 |
| CI 工作流部署 | ✅ 复制模板文件 | ⏭️ 跳过(已存在) |
| eci.conf 生成 | ✅ 完整填充 | ⏭️ 跳过(已存在) |
| Tag 创建与推送 | ✅ | ✅ |
| CI 构建镜像 | ✅ | ✅ |
| postgres 预热 | ✅ | ✅ |
| ECI 容器组重建 | ✅ | ✅ |
| 健康检查 | ✅ | ✅ |
---
## 关键优化特性
在标准部署模式中,以下优化自动生效:
### 1. 内网镜像拉取
**原理**
- 应用 ECI 和 Registry ECI 在同一 VPC 内
- 应用通过内网 IP 拉取镜像,不走公网
**效果**
- 速度:外网拉取 30-60 秒 → 内网拉取 < 1
- 费用节省外网流量费用
- 稳定性避免 Docker Hub 限流
### 2. postgres 镜像预热
**原理**
- CI 构建阶段从 Docker Hub 拉取 postgres:17-alpine
- 推送到私有 Registry
- ECI 部署时从内网拉取预热镜像
**效果**
- 避免每次 ECI 部署从外网拉取慢且易超时
- 部署成功率从 ~70% 提升到 > 99%
### 3. 资源复用
**原理**
- 所有云资源按名称标记 `d8d-eci-*`
- 已存在的资源直接复用,不重复创建
**效果**
- 部署速度:首次 5-10 分钟 → 增量 2-3 分钟
- EIP 地址永久不变
- NAS 数据持久化
---
## 部署完成后
### 输出信息
```
✅ ECI 部署完成!
🌐 应用访问地址: http://{ECI_EIP_ADDRESS}:3001
🔐 私有 Registry: http://{ECI_REGISTRY_EIP_ADDRESS}:5000
📍 Registry 内网: {ECI_REGISTRY_INTRANET_IP}:5000
🐘 PostgreSQL: 已就绪(数据持久化在 NAS
💾 容器规格: 2.5 vCPU + 3.5 GiB
💡 后续部署: 打 tag 触发 CI 即可IP 地址永久不变
```
### 后续操作
| 操作 | 命令 |
|------|------|
| 发布新版本 | `./scripts/release_tag.sh x.y.z` |
| 查看已有 tag | `./scripts/release_tag.sh -l` |
| 查看 Registry 镜像 | `curl http://{registry-ip}:5000/v2/_catalog` |
| 数据同步 | 调用 `/d8d-release` 选择「数据同步模式」 |
---
→ 返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,203 @@
# Registry 维护模式
**适用场景**
- Registry 容器被抢占后重建
- 更新 Registry 配置
- 清理旧镜像释放空间
- 诊断 Registry 连接问题
- 单独重启 Registry 不影响应用
---
## 执行流程
调用 `/d8d-release` 选择「仅维护 Registry」后按以下步骤执行
### 步骤 1: 状态诊断
```bash
# 检查当前 Registry 容器组状态
aliyun eci DescribeContainerGroups --ContainerGroupName d8d-eci-registry
# 检查端口连通性
source eci.conf
curl -s http://$ECI_REGISTRY_URL/v2/_catalog | head -20
# 检查 NAS 挂载点状态
showmount -e $ECI_NAS_SERVER
```
**AI 决策点**
- 容器不存在 → 直接创建新容器组
- 容器状态异常 → 删除后重建
- 端口不通 → 检查安全组、EIP 绑定
- NAS 挂载失败 → 检查挂载点权限
### 步骤 2: 重建 Registry 容器组
```bash
# 删除旧容器组(如存在)
aliyun eci DeleteContainerGroup \
--RegionId "$ECI_REGION" \
--ContainerGroupId "$REGISTRY_ID"
# 等待删除完成(轮询)
while true; do
STATUS=$(aliyun eci DescribeContainerGroups --ContainerGroupName d8d-eci-registry | grep -o '"Status":"[^"]*"' | cut -d'"' -f4)
if [ -z "$STATUS" ]; then break; fi
sleep 3
done
# 创建新容器组
aliyun eci CreateContainerGroup \
--RegionId "$ECI_REGION" \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID" \
--VSwitchId "$ECI_VSWITCH_ID" \
--ContainerGroupName d8d-eci-registry \
--Container.1.Image registry:2 \
--Container.1.Name registry \
--Container.1.Cpu 0.5 \
--Container.1.Memory 1.0 \
--Container.1.Port.1.Protocol TCP \
--Container.1.Port.1.Port 5000 \
--Container.1.VolumeMount.1.Name nas-data \
--Container.1.VolumeMount.1.MountPath /var/lib/registry \
--Volume.1.Name nas-data \
--Volume.1.Type NFSVolume \
--Volume.1.NFSVolume.Server "$ECI_NAS_SERVER" \
--Volume.1.NFSVolume.Path "/" \
--EipInstanceId "$ECI_REGISTRY_EIP_ID" \
--SpotStrategy SpotAsPriceGo \
--RestartPolicy Always
```
### 步骤 3: 更新内网 IP
> Registry 重建后内网 IP 可能变化,必须更新 `eci.conf`
```bash
# 查询新的内网 IP
INTRANET_IP=$(aliyun eci DescribeContainerGroups \
--ContainerGroupName d8d-eci-registry \
| grep -o '"IntranetIp":"[^"]*"' \
| cut -d'"' -f4)
# 更新 eci.conf
sed -i "/^ECI_REGISTRY_INTRANET_IP=/d" eci.conf
sed -i "/^ECI_REGISTRY_INTRANET_URL=/d" eci.conf
echo "" >> eci.conf
echo "# 私有 Registry 内网 IP用于应用 ECI 内网拉取镜像)" >> eci.conf
echo "ECI_REGISTRY_INTRANET_IP=$INTRANET_IP" >> eci.conf
echo "ECI_REGISTRY_INTRANET_URL=${INTRANET_IP}:5000" >> eci.conf
```
### 步骤 4: 验证并输出
```bash
# 验证外网访问
curl -s http://$ECI_REGISTRY_URL/v2/_catalog
# 验证内网访问(通过临时 ECI 或同 VPC 内机器)
echo "Registry 内网地址: $ECI_REGISTRY_INTRANET_URL"
```
**输出报告**
```
✅ Registry 维护完成!
🔄 操作: 重建 / 重启 / 配置更新
🌐 外网地址: http://{REGISTRY_EIP}:5000
📍 内网地址: {NEW_INTRANET_IP}:5000
💾 镜像存储: NAS 持久化(数据未丢失)
⚠️ 注意: eci.conf 已更新内网 IP请重新部署应用 ECI 生效
```
---
## 常见维护任务
### 任务 A: 清理旧镜像
**AI 动态执行**
```bash
# 列出所有镜像
curl -s http://$ECI_REGISTRY_URL/v2/_catalog
# 列出镜像所有 tag
curl -s http://$ECI_REGISTRY_URL/v2/d8d-user-release/tags/list
# 清理策略AI 根据保留策略生成)
# - 保留最近 5 个 tag
# - 删除超过 30 天未使用的镜像
# - postgres:17-alpine 永久保留(预热镜像)
```
### 任务 B: 诊断连接问题
**检查清单**
1. 容器组状态 Running?
2. EIP 正确绑定?
3. 安全组 5000 端口开放?
4. NAS 挂载成功?
5. registry:2 镜像拉取成功?
6. 容器日志无错误?
**查看日志**
```bash
aliyun eci DescribeContainerLogs \
--ContainerGroupId "$REGISTRY_ID" \
--ContainerName registry
```
### 任务 C: 切换 Spot/按需实例
```bash
# Spot 实例(节省 90% 成本)
--SpotStrategy SpotAsPriceGo
# 按需实例(稳定不被抢占)
--SpotStrategy NoSpot
```
> 建议:开发环境用 Spot生产环境考虑按需 + 定期快照备份
---
## 注意事项
### 1. NAS 数据持久化
Registry 容器重建不会丢失镜像数据,因为:
- 所有镜像存储在 NAS `/var/lib/registry`
- 新容器启动时直接挂载同一份 NAS
- 重建后 Registry catalog 完整保留
### 2. EIP 固定不变
- Registry 始终绑定 `d8d-eci-eip-registry` EIP
- 外网地址永久不变
- 客户端配置无需更新
### 3. 内网 IP 变化处理
Registry 重建后内网 IP 通常会变化,必须:
1. ✅ 更新 `eci.conf` 中的 `ECI_REGISTRY_INTRANET_IP`
2. ✅ 重新部署应用 ECI使用新内网地址拉取镜像
### 4. Spot 抢占处理
Spot 实例被阿里云抢占后的自动恢复流程:
```
1. Registry 容器组被终止(通常 2 分钟预警)
2. AI 检测到容器消失
3. 自动重建新的 Spot 容器组
4. 绑定相同 EIP
5. 挂载相同 NAS镜像数据完整
6. 更新 eci.conf 内网 IP
7. 输出恢复报告,提示重新部署应用
```
---
→ 返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,77 @@
# Phase 00: 初始化 - 环境检查与配置
本阶段执行环境检查、CLI 安装配置、依赖检测。
## 前置条件
用户需提供:
- 阿里云 RAM AccessKey ID
- 阿里云 RAM AccessKey Secret
## 执行步骤
### 1. 检测并安装 aliyun CLI
**检查是否已安装**
```bash
aliyun --version
```
**如未安装则自动安装**
```bash
curl -o aliyun-cli.tgz https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz
tar -xzf aliyun-cli.tgz
chmod +x aliyun
sudo mv aliyun /usr/local/bin/
```
### 2. 配置阿里云凭证
使用用户提供的 AK/SK 配置 CLI
```bash
aliyun configure set --mode AK \
--access-key-id "$ALI_ACCESS_KEY_ID" \
--access-key-secret "$ALI_ACCESS_KEY_SECRET" \
--region cn-beijing
```
**默认区域**cn-beijing可根据用户需求调整
### 3. 验证配置有效性
调用一个简单的 API 验证凭证:
```bash
aliyun eci DescribeContainerGroups --limit 1
```
**失败处理**:如返回认证错误,提示用户检查 AK/SK 有效性。
### 4. 检测构建工具依赖
| 工具 | 检测命令 | 用途 |
|------|----------|------|
| `docker` | `docker --version` | 构建应用镜像 |
| `pnpm` | `pnpm --version` | Node.js 包管理器 |
| `git` | `git --version` | 版本控制和打 tag |
**缺失处理**:任何工具缺失都提示用户手动安装后继续。
### 5. 检测 PostgreSQL 客户端(可选)
如果后续需要数据同步,检测:
```bash
pg_dump --version
psql --version
```
**非强制**:数据同步时如缺失可临时安装。
## 本阶段输出
| 状态项 | 说明 |
|--------|------|
| ✅ aliyun CLI | 已安装并配置正确 |
| ✅ RAM 凭证 | 验证有效 |
| ✅ 构建工具 | docker/pnpm/git 可用 |
→ 进入 [Phase 01: 基础设施创建](phase-01-infra.md)

View File

@@ -0,0 +1,182 @@
# Phase 01: 基础设施创建
按名称标记 `d8d-eci-*`**不存在则创建,存在则复用**。所有资源复用同一账号下的项目。
## 资源清单
| 序号 | 资源 | 名称标记 | 关键配置 |
|------|------|----------|----------|
| 1 | VPC | `d8d-eci-vpc` | CIDR 192.168.0.0/16 |
| 2 | VSwitch | `d8d-eci-vswitch-{zone}` | CIDR 192.168.1.0/24 |
| 3 | Security Group | `d8d-eci-sg` | TCP 3001, 5000 入站 |
| 4 | NAS | `d8d-eci-nas` | NAS 挂载点 |
| 5 | EIP (App) | `d8d-eci-eip-app` | 5Mbps绑定应用 ECI |
| 6 | EIP (Registry) | `d8d-eci-eip-registry` | 5Mbps绑定 Registry ECI |
| 7 | ECI Registry | `d8d-eci-registry` | Spot 实例registry:2 |
---
## 1. VPC 创建
**查询是否存在**
```bash
aliyun vpc DescribeVpcs --VpcName d8d-eci-vpc
```
**如不存在则创建**
```bash
aliyun vpc CreateVpc \
--RegionId cn-beijing \
--VpcName d8d-eci-vpc \
--CidrBlock 192.168.0.0/16
```
**记录**`ECI_VPC_ID` → eci.conf
---
## 2. VSwitch 创建
**查询可用 Zone**
```bash
aliyun eci DescribeZones --RegionId cn-beijing
```
选择第一个可用 Zone 创建 VSwitch
```bash
aliyun vpc CreateVSwitch \
--VpcId $ECI_VPC_ID \
--VSwitchName d8d-eci-vswitch-${ZONE} \
--ZoneId ${ZONE} \
--CidrBlock 192.168.1.0/24
```
**记录**`ECI_VSWITCH_ID` → eci.conf
---
## 3. Security Group 创建
```bash
aliyun ecs CreateSecurityGroup \
--RegionId cn-beijing \
--VpcId $ECI_VPC_ID \
--SecurityGroupName d8d-eci-sg \
--Description "ECI deployment security group"
```
**添加入站规则**
```bash
# 应用端口 3001
aliyun ecs AuthorizeSecurityGroup \
--SecurityGroupId $ECI_SECURITY_GROUP_ID \
--IpProtocol TCP \
--PortRange "3001/3001" \
--SourceCidrIp "0.0.0.0/0"
# Registry 端口 5000
aliyun ecs AuthorizeSecurityGroup \
--SecurityGroupId $ECI_SECURITY_GROUP_ID \
--IpProtocol TCP \
--PortRange "5000/5000" \
--SourceCidrIp "0.0.0.0/0"
```
**记录**`ECI_SECURITY_GROUP_ID` → eci.conf
---
## 4. NAS 文件系统
**创建文件系统**
```bash
aliyun nas CreateFileSystem \
--RegionId cn-beijing \
--ProtocolType NFS \
--StorageType Performance \
--Description d8d-eci-nas
```
**创建挂载点**
```bash
aliyun nas CreateMountTarget \
--FileSystemId ${NAS_FILE_SYSTEM_ID} \
--MountTargetName d8d-eci-nas-mount \
--VpcId $ECI_VPC_ID \
--VSwitchId $ECI_VSWITCH_ID \
--AccessGroupName DEFAULT_VPC_GROUP_NAME
```
**记录 NAS 服务器地址**
```
ECI_NAS_SERVER=${MountTargetDomain}
```
→ eci.conf
---
## 5. 应用 EIP 创建
```bash
aliyun vpc AllocateEipAddress \
--RegionId cn-beijing \
--Name d8d-eci-eip-app \
--Bandwidth 5 \
--InternetChargeType PayByTraffic
```
**记录**
- `ECI_EIP_ID` → eci.conf
- `ECI_EIP_ADDRESS` → eci.conf
---
## 6. Registry EIP 创建
```bash
aliyun vpc AllocateEipAddress \
--RegionId cn-beijing \
--Name d8d-eci-eip-registry \
--Bandwidth 5 \
--InternetChargeType PayByTraffic
```
**记录**
- `ECI_REGISTRY_EIP_ID` → eci.conf
- `ECI_REGISTRY_EIP_ADDRESS` → eci.conf
- `ECI_REGISTRY_PORT=5000` → eci.conf
- `ECI_REGISTRY_URL=${ECI_REGISTRY_EIP_ADDRESS}:5000` → eci.conf
---
## 7. 私有 Registry ECI 容器组
执行脚本:`scripts/eci-registry.sh`
**关键配置**
- SpotStrategy: SpotAsPriceGo抢占式实例节省成本
- Image: registry:2
- CPU/内存: 0.5 vCPU / 1.0 GiB
- 端口: 5000/TCP
- NAS 挂载到 /var/lib/registry
- 绑定 Registry 专用 EIP
**创建后自动发现内网 IP**
```bash
aliyun eci DescribeContainerGroups \
--ContainerGroupName d8d-eci-registry \
| grep -o '"IntranetIp":"[^"]*"' | cut -d'"' -f4
```
**记录**
- `ECI_REGISTRY_INTRANET_IP` → eci.conf
- `ECI_REGISTRY_INTRANET_URL=${ECI_REGISTRY_INTRANET_IP}:5000` → eci.conf
---
## 本阶段输出
所有 7 项基础设施资源创建完成ID/地址已记录到 eci.conf。
→ 进入 [Phase 02: 配置生成](phase-02-config.md)

View File

@@ -0,0 +1,92 @@
# Phase 02: 配置生成
将 Phase 01 创建的所有基础设施 ID 填入 eci.conf 配置文件。
## 配置模板来源
```
.claude/skills/d8d-release/assets/eci.conf.template
```
## 完整配置清单
### 区域与基础网络
```bash
ECI_REGION=cn-beijing
ECI_VPC_ID=vpc-xxx
ECI_VSWITCH_ID=vsw-xxx
ECI_SECURITY_GROUP_ID=sg-xxx
ECI_NAS_SERVER=xxx.cn-beijing.nas.aliyuncs.com
```
### 应用 EIP固定不变
```bash
ECI_EIP_ID=eip-xxx
ECI_EIP_ADDRESS=x.x.x.x
```
### 私有 Registry EIP 与内网地址
```bash
ECI_REGISTRY_EIP_ID=eip-xxx
ECI_REGISTRY_EIP_ADDRESS=x.x.x.x
ECI_REGISTRY_PORT=5000
ECI_REGISTRY_URL=x.x.x.x:5000
# 内网 IP自动发现用于应用 ECI 内网拉取镜像)
ECI_REGISTRY_INTRANET_IP=172.29.xxx.xxx
ECI_REGISTRY_INTRANET_URL=${ECI_REGISTRY_INTRANET_IP}:5000
```
### 阿里云凭证
```bash
ALI_ACCESS_KEY_ID=RAM_AK_ID
ALI_ACCESS_KEY_SECRET=RAM_AK_SECRET
```
### 应用容器规格
```bash
ECI_CONTAINER_GROUP_NAME=map-collector
ECI_CPU=2.5
ECI_MEMORY=3.5
```
## 内网 IP 自动发现机制
> **关键优化**Registry 容器组创建后,自动查询 IntranetIp 并写入配置。
**查询命令**
```bash
aliyun eci DescribeContainerGroups \
--ContainerGroupName d8d-eci-registry \
| grep -o '"IntranetIp":"[^"]*"' | cut -d'"' -f4
```
**写入 eci.conf**
```bash
sed -i "/^ECI_REGISTRY_INTRANET_IP=/d" eci.conf
sed -i "/^ECI_REGISTRY_INTRANET_URL=/d" eci.conf
echo "" >> eci.conf
echo "# 私有 Registry 内网 IP用于应用 ECI 内网拉取镜像)" >> eci.conf
echo "ECI_REGISTRY_INTRANET_IP=$INTRANET_IP" >> eci.conf
echo "ECI_REGISTRY_INTRANET_URL=${ECI_REGISTRY_INTRANET_IP}:5000" >> eci.conf
```
## 配置验证
配置生成后,验证所有必填项已填充:
```bash
grep -E "^(ECI_REGION|ECI_VPC_ID|ECI_VSWITCH_ID|ECI_SECURITY_GROUP_ID|ECI_NAS_SERVER|ECI_EIP_ID|ECI_EIP_ADDRESS|ECI_REGISTRY_EIP_ID|ECI_REGISTRY_EIP_ADDRESS|ECI_REGISTRY_INTRANET_IP|ALI_ACCESS_KEY_ID)=.*" eci.conf
```
**确保**
- 无空值
- ID 格式正确(`vpc-`, `vsw-`, `sg-`, `eip-` 开头)
- NAS 域名格式正确(`*.nas.aliyuncs.com`
→ 进入 [Phase 03: CI 工作流部署](phase-03-ci-setup.md)

View File

@@ -0,0 +1,60 @@
# Phase 03: CI 工作流部署
复制技能模板中的 CI 配置和脚本到项目中。
## 模板文件清单
所有模板位于:`.claude/skills/d8d-release/assets/`
| 模板文件 | 部署目标 | 说明 |
|----------|----------|------|
| `release.yaml` | `.gitea/workflows/release.yaml` | CI 工作流配置 |
| `eci-deploy.sh` | `scripts/eci-deploy.sh` | ECI 应用部署脚本 |
| `eci-registry.sh` | `scripts/eci-registry.sh` | Registry 部署脚本 |
| `eci.conf.template` | `eci.conf` | 配置文件(已填充资源 ID
---
## 1. 复制 CI 工作流配置
```bash
mkdir -p .gitea/workflows
cp .claude/skills/d8d-release/assets/release.yaml .gitea/workflows/
```
**CI 工作流包含**
- Docker 多阶段构建standalone Next.js
- 推送应用镜像到私有 Registry
- **postgres:17-alpine 镜像预热**
- 调用 `scripts/eci-deploy.sh` 部署到 ECI
---
## 2. 复制部署脚本
```bash
mkdir -p scripts
cp .claude/skills/d8d-release/assets/eci-deploy.sh scripts/
cp .claude/skills/d8d-release/assets/eci-registry.sh scripts/
chmod +x scripts/eci-deploy.sh scripts/eci-registry.sh
```
---
## 3. 更新 eci.conf
确保 eci.conf 中已正确填写所有资源 ID来自 Phase 02
---
## 4. 提交并推送 Git
```bash
git add .gitea/workflows/release.yaml scripts/eci-deploy.sh scripts/eci-registry.sh eci.conf
git commit -m "feat: setup ECI deployment CI workflows"
git push origin main
```
> **重要**必须推送到远程仓库Gitea Actions 才能触发。
→ 进入 [Phase 04: 触发发布](phase-04-deploy.md)

View File

@@ -0,0 +1,116 @@
# Phase 04: 触发发布与 ECI 容器组部署
## 1. 创建 Git Tag 触发 CI
使用 `release_tag.sh` 脚本创建 tag
```bash
./scripts/release_tag.sh 0.1.0
```
**脚本自动执行**
1. 验证版本号格式
2. 检查工作目录是否干净
3. 拉取最新代码
4. 创建 `v{版本号}` git tag
5. 推送 tag 触发 Gitea Actions CI
---
## 2. CI 构建流程
Gitea Actions 自动执行以下步骤:
### 阶段 A: 构建应用镜像
1. **检出代码**actions/checkout@v4
2. **设置 Docker Buildx**docker/setup-buildx-action@v3
3. **提取版本信息**:解析 tag 名称、仓库名称
4. **构建并推送应用镜像**
- Docker 多阶段构建standalone Next.js
- 推送到私有 Registry: `$ECI_REGISTRY_URL/d8d-user-release:$TAG`
### 阶段 B: postgres 镜像预热
**从 Docker Hub 拉取官方镜像**
```bash
docker pull postgres:17-alpine
docker tag postgres:17-alpine $ECI_REGISTRY_URL/postgres:17-alpine
docker push $ECI_REGISTRY_URL/postgres:17-alpine
```
> **优化效果**:首次拉取慢(约 1 分钟),但 ECI 部署时内网拉取 < 1 秒。
---
## 3. ECI 容器组部署
CI 自动调用 `scripts/eci-deploy.sh`
### 删除旧容器组(如存在)
```bash
aliyun eci DeleteContainerGroup \
--RegionId "$ECI_REGION" \
--ContainerGroupId "$EXISTING_ID"
```
轮询等待删除完成。
### 创建新容器组
```bash
aliyun eci CreateContainerGroup \
--RegionId "$ECI_REGION" \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID" \
--VSwitchId "$ECI_VSWITCH_ID" \
--ContainerGroupName "$ECI_CONTAINER_GROUP_NAME" \
--Cpu "$ECI_CPU" \
--Memory "$ECI_MEMORY" \
--RestartPolicy Always \
--EipInstanceId "$ECI_EIP_ID" \
--PlainHttpRegistry "$ECI_REGISTRY_INTRANET_URL" \
--Volume.1.Name nas-data \
--Volume.1.Type NFSVolume \
--Volume.1.NFSVolume.Server "$ECI_NAS_SERVER" \
--Volume.1.NFSVolume.Path "/" \
... # 两个容器配置
```
#### Container 1: Next.js 应用
| 配置项 | 值 |
|--------|-----|
| Image | **内网拉取**`$ECI_REGISTRY_INTRANET_URL/d8d-user-release:$TAG` |
| CPU | 2 vCPU |
| Memory | 2 GiB |
| Port | TCP 3001 |
| 环境变量 | DATABASE_URL, ANTHROPIC_API_KEY, AMAP_API_KEY, etc. |
#### Container 2: PostgreSQL
| 配置项 | 值 |
|--------|-----|
| Image | **内网拉取**`$ECI_REGISTRY_INTRANET_URL/postgres:17-alpine` |
| CPU | 0.5 vCPU |
| Memory | 1.5 GiB |
| Port | TCP 5432 |
| 环境变量 | POSTGRES_DB, POSTGRES_USER, POSTGRES_PASSWORD |
| Volume Mount | NAS 挂载到 `/var/lib/postgresql/data` |
**总计**2.5 vCPU + 3.5 GiB 内存
### 等待容器组启动
**内网拉取优化后预估时间**30 秒 - 2 分钟
```bash
# 轮询查询状态
while true; do
STATUS=$(aliyun eci DescribeContainerGroups --ContainerGroupId $ID | grep Status)
if [ "$STATUS" = "Running" ]; then break; fi
sleep 5
done
```
→ 进入 [Phase 05: 部署验证](phase-05-verify.md)

View File

@@ -0,0 +1,78 @@
# Phase 05: 部署验证
验证 ECI 容器组成功启动,应用可访问。
## 1. 验证容器组状态
```bash
aliyun eci DescribeContainerGroups --ContainerGroupName map-collector
```
**预期状态**`Status: Running`
## 2. 健康检查端点
**调用 API**
```bash
curl http://$ECI_EIP_ADDRESS:3001/api/health
```
**预期返回**
```json
{ "status": "ok", "timestamp": ... }
```
## 3. 页面访问验证
**浏览器访问**
```
http://$ECI_EIP_ADDRESS:3001
```
**预期**
- 页面标题:地图门店采集平台
- 正常渲染页面内容
## 4. PostgreSQL 连接验证
**容器内验证**(可选,通过 ECI exec
```bash
pg_isready -h 127.0.0.1 -p 5432 -U postgres
```
或通过应用日志验证数据库连接正常。
## 5. 部署报告
验证完成后,输出最终部署信息:
```
✅ ECI 部署完成!
🌐 应用访问地址: http://{ECI_EIP_ADDRESS}:3001
🔐 私有 Registry: http://{ECI_REGISTRY_EIP_ADDRESS}:5000
📍 Registry 内网: {ECI_REGISTRY_INTRANET_IP}:5000
🐘 PostgreSQL: 已就绪(数据持久化在 NAS
💾 容器规格: 2.5 vCPU + 3.5 GiB
💡 后续部署: 打 tag 触发 CI 即可IP 地址永久不变
```
## 6. 可选:数据同步
如需同步本地开发数据到生产:
→ 进入 [数据同步模式](../modes/data-sync-mode.md)
---
## 全流程完成
恭喜d8d-release 全流程部署已完成。
**关键特性总结**
- ✅ 完全自托管,不依赖外部镜像仓库
- ✅ Registry 使用 Spot 实例,节省 90% 成本
- ✅ 两个固定 EIP永久不变
- ✅ 内网镜像拉取,速度 < 1
- NAS 持久化容器重建不丢失数据

View File

@@ -0,0 +1,256 @@
# 模板文件清单
所有可复用的脚本和配置模板位于 `.claude/skills/d8d-release/assets/`
---
## 资产文件总览
| 模板文件 | 部署目标 | 说明 |
|----------|----------|------|
| `eci.conf.template` | `eci.conf` | 配置文件模板AI 自动填充资源 ID |
| `eci-deploy.sh` | `scripts/eci-deploy.sh` | 应用 ECI 部署脚本 |
| `eci-registry.sh` | `scripts/eci-registry.sh` | Registry 部署脚本 |
| `release.yaml` | `.gitea/workflows/release.yaml` | CI 工作流配置 |
---
## 1. eci.conf.template
**模板内容**
```bash
# 区域
ECI_REGION=cn-beijing
# 基础网络
ECI_VPC_ID=
ECI_VSWITCH_ID=
ECI_SECURITY_GROUP_ID=
# NAS
ECI_NAS_SERVER=
# 应用 EIP固定不变
ECI_EIP_ID=
ECI_EIP_ADDRESS=
# 私有 Registry EIP
ECI_REGISTRY_EIP_ID=
ECI_REGISTRY_EIP_ADDRESS=
ECI_REGISTRY_PORT=5000
ECI_REGISTRY_URL=${ECI_REGISTRY_EIP_ADDRESS}:${ECI_REGISTRY_PORT}
# 私有 Registry 内网 IP用于应用 ECI 内网拉取镜像)
ECI_REGISTRY_INTRANET_IP=
ECI_REGISTRY_INTRANET_URL=${ECI_REGISTRY_INTRANET_IP}:5000
# 阿里云凭证
ALI_ACCESS_KEY_ID=
ALI_ACCESS_KEY_SECRET=
# 应用配置
ECI_CONTAINER_GROUP_NAME=map-collector
ECI_CPU=2.5
ECI_MEMORY=3.5
```
**填充时机**
- Phase 01 基础设施创建后填充资源 ID
- Phase 02 自动发现内网 IP 后补充内网配置
---
## 2. eci-deploy.sh
**功能**:删除旧应用容器组 → 创建新容器组 → 等待启动 → 输出访问地址。
**核心逻辑**
```bash
#!/bin/bash
source eci.conf
# 1. 删除旧容器组
aliyun eci DeleteContainerGroup \
--RegionId "$ECI_REGION" \
--ContainerGroupId "$EXISTING_ID"
# 2. 轮询等待删除完成
while true; do
STATUS=$(aliyun eci DescribeContainerGroups \
--ContainerGroupName "$ECI_CONTAINER_GROUP_NAME" \
| grep -o '"Status":"[^"]*"' | cut -d'"' -f4)
if [ -z "$STATUS" ]; then break; fi
sleep 3
done
# 3. 创建新容器组(关键:内网拉取配置)
aliyun eci CreateContainerGroup \
--RegionId "$ECI_REGION" \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID" \
--VSwitchId "$ECI_VSWITCH_ID" \
--ContainerGroupName "$ECI_CONTAINER_GROUP_NAME" \
--Cpu "$ECI_CPU" \
--Memory "$ECI_MEMORY" \
--RestartPolicy Always \
--EipInstanceId "$ECI_EIP_ID" \
--PlainHttpRegistry "$ECI_REGISTRY_INTRANET_URL" \
--Volume.1.Name nas-data \
--Volume.1.Type NFSVolume \
--Volume.1.NFSVolume.Server "$ECI_NAS_SERVER" \
--Volume.1.NFSVolume.Path "/" \
--Container.1.Name app \
--Container.1.Image "$ECI_REGISTRY_INTRANET_URL/d8d-user-release:$1" \
--Container.1.Cpu 2.0 \
--Container.1.Memory 2.5 \
--Container.1.Port.1.Protocol TCP \
--Container.1.Port.1.Port 3001 \
--Container.2.Name postgres \
--Container.2.Image "$ECI_REGISTRY_INTRANET_URL/postgres:17-alpine" \
--Container.2.Cpu 0.5 \
--Container.2.Memory 1.0 \
--Container.2.Port.1.Protocol TCP \
--Container.2.Port.1.Port 5432 \
--Container.2.VolumeMount.1.Name nas-data \
--Container.2.VolumeMount.1.MountPath "/var/lib/postgresql/data"
# 4. 等待容器组 Running
while true; do
STATUS=$(aliyun eci DescribeContainerGroups ... | grep ...)
if [ "$STATUS" = "Running" ]; then break; fi
sleep 5
done
# 5. 输出访问地址
echo "✅ 部署完成!访问: http://$ECI_EIP_ADDRESS:3001"
```
**关键参数说明**
| 参数 | 作用 |
|------|------|
| `--PlainHttpRegistry` | 允许使用 HTTP 协议的私有 Registry支持内网拉取 |
| `--EipInstanceId` | 绑定已有 EIP保证地址不变 |
| `--Volume.*` | NAS 挂载配置PostgreSQL 数据持久化 |
---
## 3. eci-registry.sh
**功能**:部署私有 Docker RegistrySpot 实例),自动发现内网 IP 并更新配置。
**核心逻辑**
```bash
#!/bin/bash
source eci.conf
# 1. 删除旧 Registry 容器组(如存在)
aliyun eci DeleteContainerGroup ...
# 2. 创建新 Registry 容器组Spot 实例)
aliyun eci CreateContainerGroup \
--ContainerGroupName d8d-eci-registry \
--SpotStrategy SpotAsPriceGo \
--Cpu 0.5 \
--Memory 1.0 \
--EipInstanceId "$ECI_REGISTRY_EIP_ID" \
--Container.1.Image registry:2 \
--Container.1.Name registry \
--Container.1.Port.1.Protocol TCP \
--Container.1.Port.1.Port 5000 \
--Container.1.VolumeMount.1.Name nas-registry \
--Container.1.VolumeMount.1.MountPath "/var/lib/registry" \
--Volume.1.Name nas-registry \
--Volume.1.Type NFSVolume \
--Volume.1.NFSVolume.Server "$ECI_NAS_SERVER" \
--Volume.1.NFSVolume.Path "/"
# 3. 等待容器组 Running
while true; do ... sleep 3; done
# 4. 关键:自动发现内网 IP 并更新 eci.conf
INTRANET_IP=$(aliyun eci DescribeContainerGroups \
--ContainerGroupName d8d-eci-registry \
| grep -o '"IntranetIp":"[^"]*"' | cut -d'"' -f4)
sed -i "/^ECI_REGISTRY_INTRANET_IP=/d" eci.conf
sed -i "/^ECI_REGISTRY_INTRANET_URL=/d" eci.conf
echo "" >> eci.conf
echo "# 私有 Registry 内网 IP用于应用 ECI 内网拉取镜像)" >> eci.conf
echo "ECI_REGISTRY_INTRANET_IP=$INTRANET_IP" >> eci.conf
echo "ECI_REGISTRY_INTRANET_URL=${INTRANET_IP}:5000" >> eci.conf
echo "✅ Registry 部署完成!内网 IP: $INTRANET_IP"
```
---
## 4. release.yamlCI 工作流)
**触发条件**:推送 tag `v*` 时自动触发。
**完整流程**
```yaml
name: ECI 发布
on:
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: 提取版本信息
id: extract_info
run: |
VERSION=$(echo "${{ gitea.ref_name }}" | sed 's/^v//')
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: 构建并推送应用镜像
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY_URL }}/d8d-user-release:${{ env.VERSION }}
# 关键优化postgres 镜像预热
- name: 预热 postgres:17-alpine 到私有 Registry
run: |
docker pull postgres:17-alpine
docker tag postgres:17-alpine ${{ env.REGISTRY_URL }}/postgres:17-alpine
docker push ${{ env.REGISTRY_URL }}/postgres:17-alpine
- name: 部署到 ECI
run: |
chmod +x scripts/eci-deploy.sh
./scripts/eci-deploy.sh ${{ env.VERSION }}
```
---
## 资产同步方式
**AI 自动执行**Phase 03
```bash
# 复制模板文件到项目
cp .claude/skills/d8d-release/assets/release.yaml .gitea/workflows/
cp .claude/skills/d8d-release/assets/eci-deploy.sh scripts/
cp .claude/skills/d8d-release/assets/eci-registry.sh scripts/
cp .claude/skills/d8d-release/assets/eci.conf.template eci.conf
# 添加执行权限
chmod +x scripts/eci-deploy.sh scripts/eci-registry.sh
```
**后续更新**:使用 `/d8d-skills-sync` 技能同步最新模板到所有使用 d8d-release 的项目。
---
→ 返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,401 @@
# 常用命令手册
---
## 1. 版本发布
### 创建发布标签
```bash
# 使用脚本创建 tag
./scripts/release_tag.sh 1.2.3
# 或手动创建
git tag v1.2.3
git push origin v1.2.3
```
**脚本自动执行**
1. 验证版本号格式(语义化版本)
2. 检查工作目录是否干净
3. 拉取最新代码确保本地是最新
4. 创建 `v{x.y.z}` git tag
5. 推送 tag 触发 Gitea Actions CI
### 查看已有标签
```bash
# 列出所有标签
./scripts/release_tag.sh -l
# 或
git tag -l
```
### 删除标签(谨慎使用)
```bash
# 删除本地标签
git tag -d v1.2.3
# 删除远程标签
git push origin :refs/tags/v1.2.3
```
---
## 2. Registry 操作
### 列出所有镜像
```bash
source eci.conf
curl -s http://$ECI_REGISTRY_URL/v2/_catalog | jq .
```
**响应示例**
```json
{
"repositories": [
"d8d-user-release",
"postgres"
]
}
```
### 查看镜像的所有标签
```bash
# 应用镜像标签
curl -s http://$ECI_REGISTRY_URL/v2/d8d-user-release/tags/list | jq .
# postgres 镜像标签(预热的基础镜像)
curl -s http://$ECI_REGISTRY_URL/v2/postgres/tags/list | jq .
```
**响应示例**
```json
{
"name": "d8d-user-release",
"tags": ["1.0.0", "1.1.0", "1.2.0"]
}
```
### 验证镜像存在
```bash
# 检查特定镜像标签是否存在
curl -s -o /dev/null -w "%{http_code}" \
http://$ECI_REGISTRY_URL/v2/d8d-user-release/manifests/1.2.0
# 200 = 存在404 = 不存在
```
### 查看镜像大小信息
```bash
# 获取镜像 manifest
curl -s http://$ECI_REGISTRY_URL/v2/d8d-user-release/manifests/1.2.0 \
| jq '.layers | map(.size) | add'
```
---
## 3. ECI 容器组操作
### 查看应用容器组状态
```bash
source eci.conf
aliyun eci DescribeContainerGroups \
--ContainerGroupName "$ECI_CONTAINER_GROUP_NAME"
```
**关键字段**
- `Status`: `Pending` / `Scheduling` / `Running` / `Failed`
- `IntranetIp`: 容器组内网 IP
- `EipAddress`: 绑定的公网 EIP
### 查看 Registry 容器组状态
```bash
aliyun eci DescribeContainerGroups \
--ContainerGroupName d8d-eci-registry
```
### 列出所有容器组
```bash
aliyun eci DescribeContainerGroups | jq '.ContainerGroups[].ContainerGroupName'
```
### 查看容器日志
```bash
# 应用容器日志
CONTAINER_ID=$(aliyun eci DescribeContainerGroups \
--ContainerGroupName "$ECI_CONTAINER_GROUP_NAME" \
| jq -r '.ContainerGroups[0].ContainerGroupId')
aliyun eci DescribeContainerLogs \
--ContainerGroupId "$CONTAINER_ID" \
--ContainerName app
# PostgreSQL 容器日志
aliyun eci DescribeContainerLogs \
--ContainerGroupId "$CONTAINER_ID" \
--ContainerName postgres
```
### 手动重启容器组
```bash
# 删除后重建
source eci.conf
CONTAINER_ID=$(aliyun eci DescribeContainerGroups \
--ContainerGroupName "$ECI_CONTAINER_GROUP_NAME" \
| jq -r '.ContainerGroups[0].ContainerGroupId')
aliyun eci DeleteContainerGroup \
--RegionId "$ECI_REGION" \
--ContainerGroupId "$CONTAINER_ID"
# 等待删除完成后重新部署
./scripts/eci-deploy.sh <version>
```
---
## 4. EIP 和网络操作
### 查看 EIP 绑定状态
```bash
source eci.conf
# 应用 EIP
aliyun ecs DescribeEipAddresses \
--AllocationId "$ECI_EIP_ID"
# Registry EIP
aliyun ecs DescribeEipAddresses \
--AllocationId "$ECI_REGISTRY_EIP_ID"
```
### 验证端口连通性
```bash
# 验证应用端口
curl -I http://$ECI_EIP_ADDRESS:3001
# 验证 Registry 端口
curl -I http://$ECI_REGISTRY_EIP_ADDRESS:5000/v2/
```
### 测试健康检查端点
```bash
source eci.conf
curl -s http://$ECI_EIP_ADDRESS:3001/api/health | jq .
```
**预期响应**
```json
{
"status": "ok",
"timestamp": "2026-04-24T14:30:00Z"
}
```
---
## 5. 安全组操作
### 查看安全组规则
```bash
source eci.conf
aliyun ecs DescribeSecurityGroupAttribute \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID"
```
### 临时开放 PostgreSQL 端口(谨慎使用)
```bash
source eci.conf
MY_IP=$(curl -s ifconfig.me)
# 开放端口(仅限当前 IP
aliyun ecs AuthorizeSecurityGroup \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID" \
--IpProtocol tcp \
--PortRange 5432/5432 \
--SourceCidrIp "$MY_IP/32" \
--Description "Temporary DB access $(date +%Y%m%d_%H%M%S)"
echo "端口已开放,请记住操作完成后关闭!"
```
### 关闭 PostgreSQL 端口
```bash
source eci.conf
MY_IP=$(curl -s ifconfig.me)
aliyun ecs RevokeSecurityGroup \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID" \
--IpProtocol tcp \
--PortRange 5432/5432 \
--SourceCidrIp "$MY_IP/32"
```
**重要**:数据同步完成后务必关闭端口!
---
## 6. NAS 操作
### 查看挂载点信息
```bash
source eci.conf
echo "$ECI_NAS_SERVER"
# 测试 NFS 挂载(需要有 nfs client
showmount -e "$ECI_NAS_SERVER"
```
### 估算 NAS 数据用量
通过 Registry API 估算镜像大小:
```bash
# 列出所有镜像标签
curl -s http://$ECI_REGISTRY_URL/v2/_catalog | jq -r '.repositories[]' | while read repo; do
echo "=== $repo ==="
curl -s http://$ECI_REGISTRY_URL/v2/$repo/tags/list | jq -r '.tags[]'
done
```
---
## 7. 数据备份与恢复
### 手动备份数据库
```bash
source eci.conf
# 临时开放端口
MY_IP=$(curl -s ifconfig.me)
aliyun ecs AuthorizeSecurityGroup ... # 见上文
# 备份数据库
mkdir -p backups
pg_dump -h "$ECI_EIP_ADDRESS" -p 5432 -U postgres -d map_stores \
-Fc -Z 6 -f "backups/map_stores_$(date +%Y%m%d_%H%M%S).dump"
# 立即关闭端口
aliyun ecs RevokeSecurityGroup ...
```
### 手动恢复数据库
```bash
source eci.conf
# 先备份当前数据(回滚点)
pg_dump -h "$ECI_EIP_ADDRESS" -p 5432 -U postgres -d map_stores \
-Fc -f "backups/before_restore_$(date +%Y%m%d_%H%M%S).dump"
# 恢复备份
pg_restore -h "$ECI_EIP_ADDRESS" -p 5432 -U postgres \
-d map_stores --clean --if-exists backups/map_stores_xxx.dump
```
### 验证备份文件完整性
```bash
# 列出归档内容(不实际恢复)
pg_restore -l backups/map_stores_xxx.dump
# 验证文件大小大于 0
if [ ! -s backups/map_stores_xxx.dump ]; then
echo "ERROR: 备份文件为空!"
exit 1
fi
```
---
## 8. 配置验证
### 检查 eci.conf 配置完整性
```bash
source eci.conf
# 检查必填字段
REQUIRED=(
"ECI_REGION" "ECI_VPC_ID" "ECI_VSWITCH_ID"
"ECI_SECURITY_GROUP_ID" "ECI_NAS_SERVER"
"ECI_EIP_ID" "ECI_EIP_ADDRESS"
"ECI_REGISTRY_EIP_ID" "ECI_REGISTRY_EIP_ADDRESS"
"ECI_REGISTRY_INTRANET_IP"
"ALI_ACCESS_KEY_ID" "ALI_ACCESS_KEY_SECRET"
)
for field in "${REQUIRED[@]}"; do
if [ -z "${!field}" ]; then
echo "MISSING: $field"
fi
done
echo "配置验证完成!"
```
### 验证阿里云 CLI 配置
```bash
# 验证凭证是否有效
aliyun sts GetCallerIdentity
# 验证区域配置
aliyun configure get | grep region
```
**有效响应示例**
```json
{
"UserId": "1234567890123456",
"AccountId": "1234567890123456",
"Arn": "acs:ram::1234567890123456:user/your-username"
}
```
---
## 9. 快速诊断清单
部署出现问题时,按以下顺序排查:
```bash
# 1. 检查容器组状态
aliyun eci DescribeContainerGroups --ContainerGroupName map-collector
# 2. 检查容器日志
aliyun eci DescribeContainerLogs --ContainerGroupId <id> --ContainerName app
# 3. 检查应用健康检查
curl http://<eip>:3001/api/health
# 4. 检查 Registry 可用性
curl http://<registry-eip>:5000/v2/_catalog
# 5. 检查内网 IP 配置
grep ECI_REGISTRY_INTRANET eci.conf
# 6. 验证 PostgreSQL 镜像是否预热
curl http://<registry-eip>:5000/v2/postgres/tags/list
```
---
→ 返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,219 @@
# ECI 容器规格说明
应用 ECI 容器组采用双容器架构Next.js 应用容器 + PostgreSQL 数据库容器。
---
## 容器组总体规格
| 配置项 | 值 | 说明 |
|--------|-----|------|
| 总 CPU | 2.5 vCPU | 应用 2.0 + PostgreSQL 0.5 |
| 总内存 | 3.5 GiB | 应用 2.5 + PostgreSQL 1.0 |
| 容器数量 | 2 | 应用 + 数据库 sidecar |
| 重启策略 | Always | 崩溃自动重启 |
| 外网模式 | 绑定已有 EIP | IP 固定不变 |
| 镜像仓库 | 私有 Registry 内网拉取 | <1 |
---
## Container 1: Next.js 应用
**镜像**`${ECI_REGISTRY_INTRANET_URL}/d8d-user-release:${VERSION}`
| 配置项 | |
|--------|-----|
| 容器名 | `app` |
| CPU | 2.0 vCPU |
| 内存 | 2.5 GiB |
| 端口 | 3001/TCP |
| 卷挂载 | 无状态应用 |
| 健康检查 | HTTP GET `/api/health` |
### 环境变量
```bash
# 数据库连接(同 Pod 内网访问)
DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@127.0.0.1:5432/map_stores
# API Keys
ANTHROPIC_API_KEY=...
AMAP_API_KEY=...
BAIDU_API_KEY=...
TENCENT_API_KEY=...
```
**注意**数据库通过 `127.0.0.1` 访问因为两个容器在同一 Pod 内共享网络命名空间
### 资源需求计算
**内存分析**
- Node.js runtime: ~200MB
- Next.js standalone: ~300MB
- Prisma connection pool: ~100MB
- 业务逻辑和缓存: ~500MB
- 峰值缓冲区: ~1.4GB
**合计**2.5 GiB安全冗余
**CPU 分析**
- 正常运行: 0.3-0.8 vCPU
- 批量任务处理: 1.0-1.8 vCPU
- 峰值缓冲区: 0.2 vCPU
**合计**2.0 vCPU
---
## Container 2: PostgreSQL 数据库
**镜像**`${ECI_REGISTRY_INTRANET_URL}/postgres:17-alpine`
| 配置项 | |
|--------|-----|
| 容器名 | `postgres` |
| CPU | 0.5 vCPU |
| 内存 | 1.0 GiB |
| 端口 | 5432/TCP |
| 卷挂载 | NAS `/var/lib/postgresql/data` |
| 监听地址 | `127.0.0.1` |
### 环境变量
```bash
POSTGRES_DB=map_stores
POSTGRES_USER=postgres
POSTGRES_PASSWORD=...
PGDATA=/var/lib/postgresql/data/pgdata
```
### NAS 持久化配置
```bash
--Volume.1.Name nas-data
--Volume.1.Type NFSVolume
--Volume.1.NFSVolume.Server "${ECI_NAS_SERVER}"
--Volume.1.NFSVolume.Path "/"
--Container.2.VolumeMount.1.Name nas-data
--Container.2.VolumeMount.1.MountPath "/var/lib/postgresql/data"
```
**效果**PostgreSQL 数据存储在 NAS容器重建Spot 抢占都不丢失数据
### 资源需求计算
**内存分析**
- PostgreSQL shared_buffers: 256MB
- 连接和工作内存: 128MB
- 系统缓存: 256MB
- 峰值缓冲区: 384MB
**合计**1.0 GiB
**CPU 分析**
- 正常查询: 0.1-0.2 vCPU
- 批量导入: 0.3-0.4 vCPU
- 峰值缓冲区: 0.1 vCPU
**合计**0.5 vCPU
---
## 成本估算(阿里云 ECI
**区域**华北 2北京
**计费方式**按量付费
| 资源 | 单价 | 用量 | 小时费用 | 月费用730 小时 |
|------|------|------|----------|-------------------|
| CPU | ¥0.00028//vCPU | 2.5 vCPU | ¥2.52 | ¥1,839.60 |
| 内存 | ¥0.00009//GiB | 3.5 GiB | ¥1.13 | ¥827.70 |
| **容器组合计** | | | **¥3.65** | **¥2,667.30** |
| EIP按流量计费 | | | ¥0.5 | ¥365 |
| NAS 容量型 | ¥0.35/GB/ | 50 GB | | ¥17.50 |
| **总计(月)** | | | | **约 ¥3,050** |
### 优化建议
**长期运行降本方案**
1. **包年包月 ECS**替代 ECI
- ecs.g7.large (2vCPU/8GiB): ¥800/
- 节省: ~73%
2. **Serverless 演进**
- 应用拆分为函数计算 + API 网关
- PostgreSQL 使用 RDS Serverless
- 按实际调用量计费低流量时成本极低
3. **开发/测试环境优化**
- 非工作时间自动停止 ECI
- 使用 Spot 实例运行非核心服务
- 预计可再节省 50-70%
---
## 端口和网络
### 暴露的端口
| 端口 | 协议 | 容器 | 外网可达 | 说明 |
|------|------|------|----------|------|
| 3001 | TCP | app | | 应用 Web 界面 |
| 5432 | TCP | postgres | | 仅同 Pod 127.0.0.1 访问 |
**安全设计**PostgreSQL 仅监听 `127.0.0.1`外网无法直接连接必须通过应用层访问
### 数据同步时临时开放
执行数据备份/恢复/迁移时AI 自动
1. 临时开放 5432 端口给当前公网 IP
2. 操作完成立即关闭trap 保证
3. 最大暴露窗口 < 5 分钟
---
## 健康检查配置
### 应用健康检查
```bash
# 端点
GET /api/health
# 预期响应
HTTP 200 OK
{ "status": "ok", "timestamp": "2026-04-24T14:30:00Z" }
```
### 数据库健康检查
容器内验证
```bash
pg_isready -h 127.0.0.1 -p 5432 -U postgres
```
---
## 扩展配置建议
### 高并发场景(>100 QPS
| 资源 | 建议值 |
|------|--------|
| 应用 CPU | 4.0 vCPU |
| 应用内存 | 8.0 GiB |
| PostgreSQL CPU | 1.0 vCPU |
| PostgreSQL 内存 | 4.0 GiB |
| **总计** | **5.0 vCPU / 12.0 GiB** |
### 大数据量场景(>100 万条记录)
- PostgreSQL 内存提升到 8 GiB
- 增加工作内存和维护工作内存配置
- 考虑只读副本分离查询负载
---
返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,161 @@
# 铁律
> **不可突破的原则**。d8d-release 所有执行模式必须严格遵守以下铁律,任何情况不得违反。
---
## 铁律 1: EIP 永久固定不变
**原则**:应用和 Registry 的外网 EIP 一旦分配,永久绑定,永不改变。
**执行保障**
1. 按名称 `d8d-eci-eip-app``d8d-eci-eip-registry` 查询 EIP
2. 已存在则直接复用,不存在才创建新的
3. 容器组重建时使用 `--EipInstanceId` 绑定已有 EIP不分配新地址
4. 绝不调用 `AllocateEipAddress` 创建重复 EIP
**用户价值**
- DNS 解析无需更新
- CI/CD 配置无需修改
- 客户端配置无需变更
- 业务连续性 100%
---
## 铁律 2: NAS 数据持久化优先
**原则**:所有有状态数据必须存储在 NAS容器重建不丢失。
**执行保障**
1. Registry 镜像存储 → NAS 挂载 `/var/lib/registry`
2. PostgreSQL 数据 → NAS 挂载 `/var/lib/postgresql/data`
3. NAS 文件系统按名称 `d8d-eci-nas` 查询,已存在直接复用
4. 绝不格式化或清空已有 NAS 数据
**数据清单(永不丢失)**
| 数据类型 | 存储位置 | 持久化保障 |
|----------|----------|------------|
| Docker 镜像 | NAS /var/lib/registry | ✅ Registry 重建保留 |
| PostgreSQL 数据 | NAS /var/lib/postgresql/data | ✅ 应用重建保留 |
| 用户采集数据 | PostgreSQL 表 | ✅ 数据库级持久化 |
| API Key 配置 | SystemConfig 表 | ✅ 数据库级持久化 |
---
## 铁律 3: 资源复用,不重复创建
**原则**:所有云资源按名称标记 `d8d-eci-*`,已存在则复用,绝不重复创建。
**资源查询优先级**
```
1. DescribeXxx 查询 → 按 Name = d8d-eci-xxx 过滤
2. 找到匹配资源 → 记录 ID跳过创建
3. 未找到 → 创建新资源并标记 Name = d8d-eci-xxx
```
**受保护资源清单**
| 资源类型 | 名称标记 |
|----------|----------|
| VPC | `d8d-eci-vpc` |
| VSwitch | `d8d-eci-vswitch-cn-beijing-a` |
| Security Group | `d8d-eci-sg` |
| NAS 文件系统 | `d8d-eci-nas` |
| EIP (应用) | `d8d-eci-eip-app` |
| EIP (Registry) | `d8d-eci-eip-registry` |
| ECI 容器组 (应用) | `map-collector` |
| ECI 容器组 (Registry) | `d8d-eci-registry` |
---
## 铁律 4: 增量部署 ≠ 重新创建基础设施
**原则**:增量部署仅重建 ECI 容器组,不触碰已存在的基础设施。
**首次部署 vs 增量部署**
| 操作 | 首次部署 | 增量部署 |
|------|----------|----------|
| 创建 VPC/VSwitch/SG/NAS | ✅ | ⏭️ 跳过(复用) |
| 分配 EIP | ✅ | ⏭️ 跳过(复用已有 EIP |
| 部署 Registry | ✅ | ⏭️ 跳过(已运行) |
| 发现 Registry 内网 IP | ✅ | ⏭️ 复用已有配置 |
| CI 构建镜像 | ✅ | ✅ |
| postgres 预热 | ✅ | ✅ |
| 删除旧应用容器 | ✅ | ✅ |
| 创建新应用容器 | ✅ | ✅ |
| 健康检查 | ✅ | ✅ |
**增量部署耗时**:约 2-3 分钟(首次 5-10 分钟)
---
## 铁律 5: 删除前必备份(写入操作安全)
**原则**:任何可能导致数据覆盖或丢失的操作,必须先创建备份。
**适用场景**
- 数据恢复操作 → 恢复前先备份目标数据库
- 数据库迁移 → 迁移前先备份目标数据库
- 大版本 Schema 变更 → 变更前全库备份
- 容器组重建 → NAS 数据无需备份但输出警告
**备份保留策略**
- 即时备份 → 保留 7 天
- 定期备份 → 保留 30 天
- 版本发布前备份 → 永久保留
---
## 铁律 6: 凭证不上传,配置不提交
**原则**:敏感凭证绝不提交到代码仓库。
**执行保障**
1. `eci.conf` 包含 `ALI_ACCESS_KEY_SECRET` → 必须加入 `.gitignore`
2. `.env` 包含数据库密码、API Key → 必须加入 `.gitignore`
3. CI 工作流中不硬编码任何密钥
4. 所有凭证通过环境变量或本地配置文件注入
**Git 安全检查**(每次提交前执行):
```bash
# 检查是否意外提交了敏感文件
git diff --cached --name-only | grep -E "(eci\.conf|\.env)"
# 检查是否硬编码了 AK/SK
git diff --cached | grep -E "ALI_ACCESS_KEY|sk-"
```
---
## 铁律 7: 端口最小开放原则
**原则**:安全组仅开放必要端口,数据同步端口用完即关。
**永久开放端口**
| 端口 | 用途 | 源 IP |
|------|------|--------|
| 3001/TCP | 应用访问 | 0.0.0.0/0 |
| 5000/TCP | Registry API | 0.0.0.0/0 |
**临时开放端口(用完自动关闭)**
| 端口 | 用途 | 开放时机 | 自动关闭 |
|------|------|----------|----------|
| 5432/TCP | PostgreSQL 数据同步 | 备份/恢复/迁移期间 | ✅ trap 保证 |
---
## 铁律 8: 增量部署不影响 NAS 数据
**原则**:应用 ECI 容器组重建时NAS 挂载保持不变PostgreSQL 数据完整保留。
**执行保障**
1. 容器组删除 → 仅删除计算资源NAS 存储独立存在
2. 新容器创建 → 挂载同一份 NAS数据目录完全相同
3. PostgreSQL 启动 → 使用已有数据目录,无需重新初始化
4. 应用连接 → 数据完整,无需重新同步
**结果**:容器重建 ≠ 数据重置,用户数据 100% 保留。
---
→ 返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,241 @@
# 内网优化详解
d8d-release 三大核心优化内网镜像拉取、postgres 镜像预热、Spot 实例降本。
---
## 优化 1: 内网镜像拉取
### 问题背景
**传统外网拉取痛点**
- 镜像拉取走公网,速度慢(大镜像需 30-60 秒)
- 产生外网流量费用
- 受 Docker Hub 限流影响
- 网络波动容易超时
### 解决方案架构
```
┌─────────────────────────────────────────────────────────────┐
│ 阿里云 VPC 内网 │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ 应用 ECI 容器组 │ 内网 │ Registry ECI 容器 │ │
│ │ │ ◄──────►│ │ │
│ │ - Next.js 应用 │ <1s │ - registry:2 │ │
│ │ - PostgreSQL │ 拉取 │ - NAS 持久化存储 │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │ │
│ │ 公网 EIP │ 公网 EIP │
└─────────┼───────────────────────────────┼───────────────────┘
│ │
▼ ▼
用户访问 CI 构建推送
```
### 关键技术实现
#### A. 内网 IP 自动发现
Registry 创建后,自动查询并记录内网 IP
```bash
# 查询容器组详情,提取 IntranetIp 字段
INTRANET_IP=$(aliyun eci DescribeContainerGroups \
--ContainerGroupName d8d-eci-registry \
| grep -o '"IntranetIp":"[^"]*"' \
| cut -d'"' -f4)
# 写入配置文件
echo "ECI_REGISTRY_INTRANET_IP=$INTRANET_IP" >> eci.conf
echo "ECI_REGISTRY_INTRANET_URL=${INTRANET_IP}:5000" >> eci.conf
```
#### B. ECI 内网拉取配置
应用容器组创建时指定 `--PlainHttpRegistry` 参数:
```bash
aliyun eci CreateContainerGroup \
# ... 其他参数 ...
--PlainHttpRegistry "$ECI_REGISTRY_INTRANET_URL" \
--Container.1.Image "$ECI_REGISTRY_INTRANET_URL/d8d-user-release:$TAG" \
--Container.2.Image "$ECI_REGISTRY_INTRANET_URL/postgres:17-alpine"
```
**`--PlainHttpRegistry` 作用**:告诉 ECI 该 Registry 使用 HTTP 协议而非 HTTPS允许跳过 TLS 验证,支持内网无证书访问。
### 优化效果对比
| 指标 | 外网拉取 | 内网拉取 | 提升 |
|------|----------|----------|------|
| 应用镜像拉取时间 | 30-60 秒 | < 1 | **30-60x** |
| postgres 镜像拉取时间 | 15-30 | < 1 | **15-30x** |
| 总拉取耗时 | 45-90 | < 2 | **22-45x** |
| 外网流量费用 | 500MB/ | 0 | **100% 节省** |
| Docker Hub 限流影响 | | | **完全消除** |
| 网络稳定性 | 受公网波动影响 | VPC 99.99% 可用 | **大幅提升** |
---
## 优化 2: postgres 镜像预热
### 问题背景
**传统拉取方式问题**
- ECI 部署时每次都从 Docker Hub 拉取 postgres:17-alpine
- 公网拉取慢15-30 且易超时
- Docker Hub 限流影响
- 同一镜像重复拉取浪费带宽和时间
### 预热架构
```
CI 构建阶段 ECI 部署阶段
─────────────── ───────────────
1. 构建应用镜像 1. 删除旧容器组
2. 推送到私有 Registry 2. 创建新容器组
↓ ↓ 内网拉取
3. 从 Docker Hub 拉取 postgres ────► 3. 应用镜像 <1s
↓ ↓ 内网拉取
4. 推送到私有 Registry 4. postgres 镜像 <1s
(NAS 持久化存储) ↓
5. 容器启动完成 ✓
```
### CI 实现细节
```yaml
# .gitea/workflows/release.yaml
- name: 构建并推送应用镜像
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ env.REGISTRY_URL }}/d8d-user-release:${{ env.VERSION }}
# 关键postgres 镜像预热步骤
- name: 预热 postgres:17-alpine 到私有 Registry
run: |
# 1. 从 Docker Hub 拉取官方镜像CI 阶段仅执行一次)
docker pull postgres:17-alpine
# 2. 重命名为私有 Registry 地址
docker tag postgres:17-alpine ${{ env.REGISTRY_URL }}/postgres:17-alpine
# 3. 推送到私有 RegistryNAS 持久化存储)
docker push ${{ env.REGISTRY_URL }}/postgres:17-alpine
echo "✅ postgres:17-alpine 预热完成,后续 ECI 内网拉取 <1 秒"
```
### 预热优势
| 优势 | 说明 |
|------|------|
| **一次拉取,无限复用** | CI 拉取一次推送到私有 Registry所有 ECI 部署复用 |
| **部署成功率提升** | ~70% 提升到 > 99%,消除公网拉取超时问题 |
| **部署速度提升** | postgres 拉取从 15-30 秒 → <1 |
| **NAS 持久化** | 镜像存在 NASRegistry 重建也不丢失 |
| **免受 Docker Hub 限流** | CI 拉取一次后续都是内网访问 |
---
## 优化 3: Spot 实例降本
### Spot 实例原理
阿里云**抢占式实例Spot Instance**
- 按需定价的折扣实例价格通常是按量付费的 10%-20%
- 阿里云资源紧张时可能被抢占回收提前 2 分钟通知
- 非常适合无状态可中断易恢复的服务
### Registry Spot 配置
```bash
aliyun eci CreateContainerGroup \
--ContainerGroupName d8d-eci-registry \
--SpotStrategy SpotAsPriceGo \ # 关键Spot 策略
--Cpu 0.5 \
--Memory 1.0 \
# ... 其他参数
```
**`SpotAsPriceGo` 策略**系统自动出价跟随当前市场价格优先保证运行不被释放
### 成本对比
| 实例类型 | CPU | 内存 | 单价北京 | 月费用730 小时 | 成本比例 |
|----------|-----|------|-------------|-------------------|----------|
| 按量付费 | 0.5 | 1 GiB | ¥0.07/小时 | ¥51.10 | 100% |
| Spot 实例 | 0.5 | 1 GiB | ¥0.007/小时 | ¥5.11 | **10%** |
**年节省**51.10 - ¥5.11) × 12 = **¥551.88 / **
### 抢占自动恢复
Registry Spot 实例被抢占后的全自动恢复流程
```
┌─────────────────────────────────────────────────────────────┐
│ 1. 阿里云发出抢占通知(提前 2 分钟) │
│ Registry 容器组即将被终止 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 2. d8d-release 检测到容器消失(下次执行时自动发现) │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 3. 自动创建新的 Spot 容器组 │
│ - 绑定相同 EIP → 外网地址不变 │
│ - 挂载相同 NAS → 镜像数据完整 │
│ - 分配新的内网 IP │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 4. 更新 eci.conf 中的内网 IP 配置 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 5. 输出恢复报告,提示重新部署应用 ECI 以使用新的内网地址 │
└─────────────────────────────────────────────────────────────┘
```
**注意**Spot 抢占是低概率事件通常 < 5%/即使发生也不影响数据仅需重建容器
---
## 优化协同效应
三大优化组合使用的整体效果
```
首次部署 增量部署
───────────── ─────────────
基础设施创建: 5-10 分钟 ⏭️ 跳过
CI 构建:
应用镜像: 1-2 分钟 1-2 分钟
postgres 预热: 15-30 秒 ⏭️ 跳过(已预热)
ECI 部署:
删旧容器: 5-10 秒 5-10 秒
拉取镜像: 内网 < 2 秒 内网 < 2 秒
启动容器: 10-20 秒 10-20 秒
─────────────────────────────────────────────────────
总计: 6-13 分钟 1-3 分钟
部署成功率: > 99% > 99%
(对比传统方式 ~70%
```
---
返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,258 @@
# 安全设计
d8d-release 内置多层安全机制,覆盖凭证管理、网络访问、数据操作全流程。
---
## 1. 凭证安全管理
### 凭证存储原则
| 凭证类型 | 存储位置 | 提交 Git | 说明 |
|----------|----------|----------|------|
| RAM AK/SK | `eci.conf` | ❌ 禁止 | 阿里云 API 访问密钥 |
| 数据库密码 | 环境变量 | ❌ 禁止 | PostgreSQL superuser 密码 |
| API Key | 数据库 `SystemConfig` | — | 应用运行时动态读取 |
| Docker Registry 认证 | 无 | — | 当前使用 HTTP 无认证 |
### Git 安全防护
**自动防护**
```bash
# .gitignore 必须包含
eci.conf
.env
.env.local
*.pem
*.key
backups/
```
**提交前检查**AI 自动执行):
```bash
# 检查是否意外提交敏感文件
git status --porcelain | grep -E "(eci\.conf|\.env)"
# 检查代码中是否硬编码密钥
grep -rE "ALI_ACCESS_KEY|sk-" src/ --include="*.ts" --include="*.tsx"
```
### 最小权限原则
RAM 用户权限最小化配置:
```json
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"vpc:DescribeVpcs",
"vpc:CreateVpc",
"vpc:DescribeVSwitches",
"vpc:CreateVSwitch",
"ecs:DescribeSecurityGroups",
"ecs:CreateSecurityGroup",
"ecs:AuthorizeSecurityGroup",
"ecs:RevokeSecurityGroup",
"nas:DescribeFileSystems",
"nas:CreateFileSystem",
"nas:CreateMountTarget",
"vpc:AllocateEipAddress",
"vpc:DescribeEipAddresses",
"eci:CreateContainerGroup",
"eci:DeleteContainerGroup",
"eci:DescribeContainerGroups",
"eci:DescribeContainerLogs"
],
"Resource": "*"
}
]
}
```
---
## 2. 网络安全
### 安全组端口策略
| 端口 | 协议 | 源 IP | 开放类型 | 说明 |
|------|------|--------|----------|------|
| 3001 | TCP | 0.0.0.0/0 | 永久开放 | 应用 Web 访问 |
| 5000 | TCP | 0.0.0.0/0 | 永久开放 | Docker Registry API |
| 5432 | TCP | 仅限当前公网 IP | 临时开放 | PostgreSQL 数据同步 |
### 端口自动关闭机制
**trap 保障**无论脚本正常退出、Ctrl+C 中断、还是异常崩溃,端口必定关闭。
```bash
# 注册清理钩子
trap '
echo "[$(date)] 正在关闭安全组 5432 端口..."
aliyun ecs RevokeSecurityGroup \
--SecurityGroupId "$ECI_SECURITY_GROUP_ID" \
--IpProtocol tcp \
--PortRange 5432/5432 \
--SourceCidrIp "$MY_IP/32"
echo "[$(date)] 端口已关闭"
' EXIT INT TERM HUP
```
**IP 白名单限制**
- 5432 端口绝不开放给 0.0.0.0/0
- 仅限执行同步操作的当前公网 IP
- 同步完成立即撤销,最大暴露窗口 < 5 分钟
---
## 3. 数据操作安全
### 二次确认机制
所有写入操作恢复迁移必须获得用户显式确认
```
⚠️ 危险操作确认 ⚠️
即将执行: 数据恢复
源文件: backups/map_stores_20260424_143022.dump (234 MB)
目标数据库: 47.95.xxx.xxx:5432/map_stores
回滚点: backups/before_restore_20260424_143500.dump 已创建
此操作将覆盖目标数据库所有数据!
请输入 "YES, I UNDERSTAND" 继续,或其他字符取消:
```
### 回滚点自动创建
执行任何覆盖操作前先对目标数据库创建即时备份
```bash
# 自动创建回滚点
ROLLBACK_FILE="backups/before_${OPERATION}_$(date +%Y%m%d_%H%M%S).dump"
pg_dump -h $TARGET_HOST -p 5432 -U postgres -d $TARGET_DB -Fc -f "$ROLLBACK_FILE"
# 记录到审计日志
echo "[$(date)] 回滚点已创建: $ROLLBACK_FILE" >> backups/sync_audit.log
```
### 幂等操作设计
所有数据操作可重复执行不破坏数据一致性
| 操作 | 幂等保障 |
|------|----------|
| 备份 | 新时间戳文件名不覆盖历史备份 |
| Schema 恢复 | `--clean --if-exists` 安全清理 |
| 数据恢复 | 事务包装失败自动回滚 |
| 端口开放 | 重复调用无副作用 |
| 端口关闭 | 不存在则静默跳过 |
---
## 4. 审计与可追溯
### 操作审计日志
所有数据同步操作记录到 `backups/sync_audit.log`
```
[2026-04-24 14:30:22] 操作类型: backup
[2026-04-24 14:30:22] 源数据库: 47.95.xxx.xxx:5432/map_stores
[2026-04-24 14:30:22] 目标文件: backups/map_stores_20260424_143022.dump
[2026-04-24 14:32:15] 总记录数: 125432
[2026-04-24 14:32:15] 文件大小: 234 MB
[2026-04-24 14:32:15] 耗时: 113 秒
[2026-04-24 14:32:15] 安全组状态: 5432 端口已关闭
[2026-04-24 14:32:15] 状态: SUCCESS
```
### 完整性校验
备份完成后自动验证文件完整性
```bash
# 检查文件大小 > 0
if [ ! -s "$BACKUP_FILE" ]; then
echo "ERROR: 备份文件为空,操作失败"
exit 1
fi
# 尝试列出归档内容验证格式
pg_restore -l "$BACKUP_FILE" > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "ERROR: 备份文件格式损坏"
exit 1
fi
# 记录校验和
md5sum "$BACKUP_FILE" >> backups/checksums.md5
```
---
## 5. Registry 安全
### 当前安全模式
**HTTP 无认证**
- Registry 当前使用 HTTP 协议 HTTPS
- 无用户名密码认证
- 适用于内部开发和测试环境
**安全边界**
- Registry 端口 5000 开放给公网
- 知道 EIP 即可拉取和推送镜像
- 生产环境建议添加认证
### 生产环境增强(可选)
如需生产级安全可启用
1. **HTTPS + Let's Encrypt 证书**
2. **Basic Auth 认证**htpasswd
3. **IP 白名单限制**
4. **只读镜像策略**
---
## 6. ECI 容器安全
### 环境变量隐藏
敏感环境变量不显示在容器详情中
```bash
aliyun eci CreateContainerGroup \
--EnvironmentVarHide true \
--Container.1.EnvironmentVar.1.Key POSTGRES_PASSWORD \
--Container.1.EnvironmentVar.1.Value "******"
```
### 容器隔离
- 应用容器和 PostgreSQL 容器运行在同一 Pod
- 容器间通信仅通过 localhost不暴露到外网
- PostgreSQL 仅监听 127.0.0.1外网无法直接连接
---
## 安全检查清单
每次执行部署或同步操作前AI 自动验证
`eci.conf` `.gitignore`
敏感文件未加入 git 暂存区
安全组端口配置正确
5432 端口当前处于关闭状态
备份目录存在且可写
凭证格式有效不为空
任何一项不通过则立即终止执行并报告问题
---
返回 [workflow.md](../workflow.md)

View File

@@ -0,0 +1,86 @@
# d8d-release ECI 部署流水线
**阿里云 ECI 容器化部署:基础设施自动创建 + 私有 RegistrySpot 实例)+ CI/CD 构建 + 内网镜像拉取 + 一键部署。
## 核心优势
| 优势 | 说明 |
|------|------|
| ✅ 完全自托管 | 不依赖 ACR/Docker Hub所有镜像私有存储 |
| ✅ Spot 实例成本 | Registry 使用抢占式实例,相比按量节省 90% |
| ✅ NAS 持久化 | 数据 + 镜像都存 NAS容器重建不丢失 |
| ✅ 双固定 EIP | 应用 (3001) + Registry (5000)IP 永久不变 |
| ✅ 内网拉取加速 | 应用 + postgres 从 VPC 内网拉取,速度提升 10-20 倍 |
| ✅ 镜像预热 | CI 构建时同步推送 postgres:17-alpine 到私有 Registry |
| ✅ 内网 IP 自动发现 | Registry 创建后自动保存内网 IP 到 eci.conf |
| ✅ AI 数据同步 | 动态执行备份/恢复/迁移,不依赖固定脚本 |
## 端到端流程
```
用户提供 RAM AK/SK
┌─────────────────────────────────────────────────────────────┐
│ ECI 部署流水线(全自动) │
│ │
│ PHASE 0: INIT PHASE 1: INFRA │
│ ┌───────────────┐ ┌──────────────────────┐ │
│ │ 环境检查 │ │ 创建云资源7项 │ │
│ │ 配置阿里云 CLI │ │ VPC/VSwitch/SG │ │
│ │ 检测构建工具 │ │ NAS/2xEIP/Registry│ │
│ └───────┬───────┘ └────────┬─────────────┘ │
│ ↓ ↓ │
│ PHASE 2: CONFIG PHASE 3: CI SETUP │
│ ┌───────────────┐ ┌──────────────────────┐ │
│ │ 填充 eci.conf │ │ 部署 CI 工作流 │ │
│ │ 内网 IP 自动发现│ │ 复制脚本模板 │ │
│ └───────┬───────┘ └────────┬─────────────┘ │
│ ↓ ↓ │
│ PHASE 4: DEPLOY PHASE 5: VERIFY │
│ ┌───────────────┐ ┌──────────────────────┐ │
│ │ 创建 tag 触发 CI │ │ 健康检查 │ │
│ │ 内网拉取双镜像 │ │ 输出访问地址 │ │
│ │ 部署 ECI 容器组 │ │ 数据同步(可选) │ │
│ └───────────────┘ └──────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
固定 EIP 可访问应用
```
## 运行模式选择
调用 `/d8d-release` 后,选择运行模式:
| 模式 | 文件 | 说明 |
|------|------|------|
| 🚀 **标准部署模式** | `modes/deployment-mode.md` | 完整端到端部署:基础设施 → CI → ECI 部署
| 💾 **数据同步模式** | `modes/data-sync-mode.md` | 备份/恢复/迁移AI 动态执行
| 🔄 **Registry 维护模式** | `modes/registry-only.md` | 仅部署/重启私有 Registry
## 阶段导航
| 阶段 | 文件 | 说明 |
|------|------|------|
| 00 | `phases/phase-00-init.md` | 初始化环境检查、CLI 配置
| 01 | `phases/phase-01-infra.md` | 基础设施创建7 项资源)
| 02 | `phases/phase-02-config.md` | 配置生成eci.conf 填充
| 03 | `phases/phase-03-ci-setup.md` | CI 工作流 + 脚本部署
| 04 | `phases/phase-04-deploy.md` | 触发发布 + ECI 容器组部署
| 05 | `phases/phase-05-verify.md` | 部署验证 + 健康检查
## 铁律与优化
| 分类 | 文件 | 说明 |
|------|------|------|
| 📜 | `rules/iron-laws.md` | 不可突破的铁律
| 🔒 | `rules/security.md` | 安全设计原则
| ⚡ | `rules/optimizations.md` | 内网优化详解
## 参考资料
| 文档 | 文件 |
|------|------|
| 📋 | `reference/asset-manifest.md` | 资产文件清单
| 📦 | `reference/eci-container-spec.md` | 容器规格说明
| 🔧 | `reference/common-commands.md` | 常用命令手册