内部 / 测试环境部署方案(单台阿里云 ECS)

内部 / 测试环境部署方案(单台阿里云 ECS)

一台阿里云 ECS 用 Docker Compose 部署 agentgateway v1.5.0 与 SkillHub v0.2.20,PostgreSQL / Redis / 对象存储用阿里云 RDS / Tair / OSS:部署图 · 资源清单 · 网络与安全组 · 部署包(compose / 网关配置模板 / .env)· 实施步骤与验收命令 · 待业务侧输入与风险。

v1.0 · 2026-09-15 · 面向 DevOps · 阶段 2(Keycloak 飞书登录)以叠加文件给出 · 本页不含内部域名与凭据

日期:2026-09-15 | 对象:DevOps 工程师 | 范围:内部试点与测试环境(不含外部 SIT / PROD、不含公网入口)| 依据:《公司 AI 网关方案》§4 多环境、《Skill 中心 · SkillHub 整合评估》

0. 一页结论

  • 形态:一台阿里云 ECS(4 vCPU / 16 GiB)用 Docker Compose 跑 agentgateway v1.5.0 与 SkillHub v0.2.20(server / web / scanner 三个容器);PostgreSQL、Redis、对象存储不装在机器上,分别用阿里云 RDS PostgreSQL 16、Tair(Redis 7 兼容)、OSS。
  • 入口:只有内网。两个内网域名(网关 API、SkillHub Web)都指向 ECS 内网 IP,TLS 在 agentgateway 终止,443 端口只对办公网 / VPN 网段开放;SkillHub Web 只经网关按主机名转发,不直接暴露。
  • 身份:阶段 1 用 agentgateway 管理界面签发的个人 Key(模型与 MCP 同一把 Key)+ SkillHub 首登管理员与本地账号;阶段 2 再加 Keycloak(AI IdP,飞书登录)并让 SkillHub 走 OIDC。阶段 2 的容器与路由已在部署包里以叠加文件给出。
  • 与正式环境的关系:这是公司版方案"内部 1 套"的试点前身。配置文件、策略格式、镜像版本与后续落生产主集群命名空间的形态一致,试点验收后可平迁。
  • DevOps 可直接开工的材料:§1 部署图、§2 资源清单、§3 网络与安全组、§4 部署包(deploy/internal-test/compose.yamlcompose.idp.yamlgateway/config.yaml.tmpl.env.examplerender.sh)、§5 实施步骤与验收命令。需要业务侧先给的 5 个输入见 §6。

1. 部署图

内部 / 测试环境部署图(单台阿里云 ECS · agentgateway + SkillHub) 使用方 · 办公网 / VPN · 范围外 开发者 · Claude Code / Codex / Cursor个人 Key · OpenAI / Anthropic 兼容接口 浏览器 · SkillHub Web发布 · 审核 · 安装技能 存量GitLab CI · 技能发布扫描 · 签名清单 → /api/v1/publish 存量内部应用 · Agent 测试实例base_url 指向网关 · 应用 Key 运维 · 堡垒机 / 运维 IPSSH 22 · 管理界面 15000 内网 DNS · 443 阿里云 VPC · 内部测试环境(与生产主集群同地域,仅内网) ECS ×1 · 4 vCPU / 16 GiB · Docker Compose · 内网 IP · 无公网入站 开源复用agentgateway v1.5.0统一入口 :443 TLS/v1 模型 · /mcp · skills 主机路由 开源复用SkillHub Webnginx :80静态资源 · /api 反代 开源复用SkillHub ServerJava 21 · :8080技能库 · 审核 · 审计 · 令牌 可选skill-scannerPython :8000上传模式 · 可选 阶段 2 · 可选Keycloak 26AI IdP · :8080阶段 2 · 飞书登录 skills 主机名 → web → server(容器网络) JWT 校验 · OIDC 登录(阶段 2) 阿里云托管中间件 · 同 VPC · 仅内网 · 白名单 ECS 内网 IP 云服务复用RDS PostgreSQL 16基础版 2c4g · 50 GiB库:agentgateway · skillhub · keycloak 云服务复用Tair / Redis 71 GiB 主从SkillHub 会话 · 锁 云服务复用OSS Bucket私有 · 版本控制技能包 · 内网 endpoint 可选SLS 日志Logtail 采 stdout可选 可选云监控ECS · RDS告警到飞书 VPC 内网 · 5432 / 6379 / OSS 443 · 白名单 → RDS:Key · 预算 · 请求日志 → RDS · Redis · OSS → RDS(阶段 2) 出站目标 · 范围外 存量模型厂商 API百炼 · 火山 · 存量 new-api 代理 存量MCP server公司内网登记 · 公网试点(context7) 存量飞书开放平台OIDC 联邦 · Keycloak 阶段 2 存量镜像仓库 ACR转存 cr.agentgateway.dev · ghcr.io 存量NAT 网关 / 公网出口仅出站 · 无入站 · 存量或新建 NAT 出站 · 厂商 Key 只在网关 · 换发后转发 MCP 存量 / 范围外 开源或云服务直接复用 需配置或增强 需新开发(本环境无) 安全组:443 ← 办公网 / VPN 网段;15000(管理界面,无鉴权)与 22 ← 运维 IP / 堡垒机;其余端口不发布到宿主;容器网段 10.207.0.0/24 避开公司 172.x;出站经 NAT 到模型厂商与镜像仓库 中间件:RDS / Tair / OSS 仅内网访问并白名单 ECS 内网 IP;RDS 三库三账号最小权限、自动备份 7 天;OSS 私有 Bucket + RAM 子账号只授该 Bucket;厂商 Key 与数据库口令只在 ECS 的 .env(0600) 版本锁定:agentgateway v1.5.0 · SkillHub v0.2.20 · Keycloak 26.7.3(阶段 2);配置目录 /opt/ai-gateway(compose.yaml · gateway/config.yaml · certs/ · .env);本环境为公司版方案 §4"内部 1 套"的试点前身,策略与配置格式与生产一致
内部 / 测试环境部署图:使用方经内网 DNS 到 ECS 上的 agentgateway 统一入口;ECS 内 Docker Compose 跑 agentgateway、SkillHub Web / Server / scanner 与阶段 2 的 Keycloak;PostgreSQL / Redis / 对象存储用阿里云 RDS / Tair / OSS;出站经 NAT 到模型厂商、MCP server、镜像仓库 · 下载:SVG PNG

读图要点:

  • 左列是使用方,全部来自办公网或 VPN;四类流量汇聚到网关 443,运维走堡垒机 SSH 与管理界面 15000。
  • 中间是阿里云 VPC。上方虚线框是那台 ECS 与其上的五个容器(Keycloak 为阶段 2);下方虚线框是托管中间件,二者之间是 VPC 内网,中间件只放行 ECS 内网 IP。
  • 右列是出站目标:模型厂商与 MCP server 由网关经 NAT 访问,厂商 Key 只在网关;镜像仓库用公司 ACR 转存;飞书开放平台只在阶段 2 由 Keycloak 访问。
  • 颜色:蓝 = 开源或云服务直接复用;青 = 需要配置或增强(Keycloak 联邦);灰 = 存量或范围外。本环境没有新开发组件。

2. 资源清单(阿里云)

# 资源 规格建议 数量 用途 说明
1 ECS ecs.g8i.xlarge(4 vCPU / 16 GiB);系统盘 ESSD 100 GiB;Alibaba Cloud Linux 3 或 Ubuntu 24.04;只分配内网 IP 1 跑全部容器 官网目录价 502.32 元/月(2026-09-14)。不装 Keycloak 与 scanner 时可降到 ecs.c8i.xlarge(4 vCPU / 8 GiB,391.39 元/月)。倚天 c8y 系列便宜约 35%,但需拉 arm64 镜像(SkillHub 有多架构镜像,agentgateway 需确认)
2 RDS PostgreSQL 16 版本,基础版 2 vCPU / 4 GiB,ESSD 50 GiB,内网地址 1 实例 3 库 agentgateway(Key、预算、请求日志)、skillhub(技能库)、keycloak(阶段 2) 三库三账号,各自只有本库权限;白名单只放 ECS 内网 IP;自动备份保留 7 天
3 Tair(Redis 7 兼容) 社区版 1 GiB 标准版(主从),内网地址,开密码 1 SkillHub 会话与分布式锁 白名单只放 ECS 内网 IP;不开公网
4 OSS 标准存储,私有读写,同地域,开版本控制 1 Bucket SkillHub 技能包 RAM 子账号 AK,策略只授该 Bucket;服务端走内网 endpoint,浏览器下载链接走公网 endpoint
5 NAT 网关 存量或新建,绑定 ECS 所在交换机 1 ECS 出站到模型厂商、公网 MCP、镜像仓库 ECS 不绑 EIP;若出站要走代理,改网关的 provider baseUrl 或系统代理
6 内网 DNS(PrivateZone) A 记录 → ECS 内网 IP 2 条(阶段 2 加 1 条) ai-gw-test.<内网域>skills-test.<内网域>idp-test.<内网域> 域名由业务侧定,见 §6
7 TLS 证书 一张证书含三个 SAN;公司内部 CA 签发,或公有 CA 用 DNS 验证签发 1 网关 443 内部 CA 时开发机需信任根证书;证书放 /opt/ai-gateway/certs/
8 容器镜像仓库(ACR) 个人版或企业版,命名空间 ai-gateway 1 转存 cr.agentgateway.devghcr.io/iflytek 的镜像 避免 ECS 直接拉境外仓库不稳定;镜像与版本锁定见 §4.1
9 云监控 + SLS(可选) ECS / RDS 基础监控免费;SLS 一个 Logstore,Logtail 采容器 stdout 告警到飞书群;日志集中查 阶段 1 可只用 docker compose logs

3. 网络与安全组

方向 端口 来源 / 目标 用途 备注
入站 443/TCP 办公网与 VPN 网段 agentgateway 统一入口(模型 API、MCP、SkillHub Web) TLS 在网关终止;SkillHub 与阶段 2 的 Keycloak 按主机名转发
入站 15000/TCP 运维 IP / 堡垒机 agentgateway 管理界面(创建 Key、看日志、改预算) 界面无鉴权,必须限源;后续可用网关自身 ui.policies 加认证
入站 22/TCP 堡垒机 SSH 密钥登录,禁密码
入站 其他 全部拒绝;容器端口 18080 / 18081 / 15020 / 15021 / 18180 / 19000 只绑 127.0.0.1
出站 5432 / 6379 RDS / Tair 内网地址 数据库与缓存 RDS / Tair 白名单放 ECS 内网 IP
出站 443 OSS 内网 endpoint 技能包读写 oss-<region>-internal.aliyuncs.com
出站 443 模型厂商、公网 MCP、镜像仓库、存量代理 经 NAT 厂商 Key 只在网关配置文件
容器网络 10.207.0.0/24 Compose 默认网络 避开 Docker 默认地址池与公司 172.x 内网重叠

域名与路由(同一张证书、同一个 443):

主机名 网关路由 后端 鉴权
ai-gw-test.<内网域> /v1/*(模型,OpenAI / Anthropic 兼容)、/mcp/<name>(MCP) 模型厂商 / 存量代理;登记的 MCP server 个人 Key(Authorization: Bearer),模型与 MCP 同一把
skills-test.<内网域> /(主机名匹配) skillhub-web:80skillhub-server:8080 SkillHub 自身登录(首登管理员、本地账号;阶段 2 OIDC)
idp-test.<内网域>(阶段 2) /(主机名匹配) keycloak:8080 Keycloak 自身

4. 部署包与配置

部署包在本目录 deploy/internal-test/,放到 ECS 的 /opt/ai-gateway/

/opt/ai-gateway/
├── compose.yaml              # agentgateway + skillhub-server + skillhub-web + skill-scanner
├── compose.idp.yaml          # 阶段 2 叠加:keycloak + SkillHub OIDC 环境变量
├── .env                      # 由 .env.example 复制并填写;chmod 600;不入库
├── render.sh                 # 用 .env 渲染 gateway/config.yaml 并重启网关
├── gateway/
│   ├── config.yaml.tmpl      # agentgateway 配置模板(入库)
│   └── config.yaml           # 渲染产物(含口令;0600;属主 65532;不入库)
└── certs/
    ├── fullchain.pem         # SAN:ai-gw-test / skills-test / idp-test
    └── privkey.pem           # 0600;属主 65532

4.1 镜像与版本锁定

组件 镜像 版本 说明
agentgateway cr.agentgateway.dev/agentgateway v1.5.0 无 shell 的最小镜像,以 uid 65532 运行;配置与证书文件属主要改为 65532
SkillHub Server ghcr.io/iflytek/skillhub-server v0.2.20 Java 21;Flyway 启动时自动建表
SkillHub Web ghcr.io/iflytek/skillhub-web v0.2.20 nginx,/api/*/oauth2/*/.well-known/* 反代到 server
skill-scanner ghcr.io/iflytek/skillhub-scanner v0.2.20 可选;发布技能时做规则 / 行为扫描,AI 扫描可经网关调模型
Keycloak(阶段 2) quay.io/keycloak/keycloak 26.7.3 与 POC 同版本;start 生产模式,数据库用 RDS

转存到公司 ACR 后,把 .env 里的 AGW_IMAGESKILLHUB_*_IMAGEKC_IMAGE 改成 ACR 地址;升级只改版本号,并按 §5.7 回滚。

4.2 Compose(compose.yaml

要点:PostgreSQL / Redis 不在本机,全部指向 RDS / Tair;OSS 需要显式传 SKILLHUB_STORAGE_S3_DISABLE_CHUNKED_ENCODING=true(阿里云 OSS 不接受 aws-chunked 编码,上游发布模板没有透传这个变量);SkillHub Web 只经网关访问,所以 SKILLHUB_TRUST_FORWARDED_PROTO=true 且网关在该路由上覆写 X-Forwarded-Proto;每个容器有 CPU / 内存上限与日志滚动。

name: ai-gateway-test
x-common: &common
  restart: unless-stopped
  logging: { driver: json-file, options: { max-size: "50m", max-file: "5" } }
services:
  agentgateway:
    <<: *common
    image: ${AGW_IMAGE:-cr.agentgateway.dev/agentgateway:v1.5.0}
    command: ["-f", "/config/config.yaml"]
    environment: { RUST_LOG: "${AGW_LOG_LEVEL:-info}" }
    volumes: ["./gateway:/config:ro", "./certs:/certs:ro"]
    ports: ["443:3000", "15000:15000", "127.0.0.1:15020:15020", "127.0.0.1:15021:15021"]
    deploy: { resources: { limits: { cpus: "2", memory: 1G } } }
  skillhub-server:
    <<: *common
    image: ${SKILLHUB_SERVER_IMAGE:-ghcr.io/iflytek/skillhub-server}:${SKILLHUB_VERSION:-v0.2.20}
    environment:
      SPRING_PROFILES_ACTIVE: docker
      JAVA_TOOL_OPTIONS: -XX:MaxRAMPercentage=70 -Duser.timezone=Asia/Shanghai
      SPRING_DATASOURCE_URL: jdbc:postgresql://${RDS_HOST}:${RDS_PORT:-5432}/skillhub
      SPRING_DATASOURCE_USERNAME: ${SKILLHUB_DB_USER:-skillhub}
      SPRING_DATASOURCE_PASSWORD: ${SKILLHUB_DB_PASSWORD:?required}
      SPRING_DATA_REDIS_HOST: ${TAIR_HOST:?required}
      SPRING_DATA_REDIS_PORT: ${TAIR_PORT:-6379}
      SPRING_DATA_REDIS_PASSWORD: ${TAIR_PASSWORD:-}
      SKILLHUB_PUBLIC_BASE_URL: https://${SKILLS_HOST:?required}
      SESSION_COOKIE_SECURE: "true"
      SKILLHUB_DOWNLOAD_ANON_COOKIE_SECRET: ${SKILLHUB_DOWNLOAD_ANON_COOKIE_SECRET:?required}
      SKILLHUB_STORAGE_PROVIDER: s3
      SKILLHUB_STORAGE_S3_ENDPOINT: https://oss-${ALIYUN_REGION:?required}-internal.aliyuncs.com
      SKILLHUB_STORAGE_S3_PUBLIC_ENDPOINT: https://oss-${ALIYUN_REGION}.aliyuncs.com
      SKILLHUB_STORAGE_S3_BUCKET: ${OSS_BUCKET:?required}
      SKILLHUB_STORAGE_S3_ACCESS_KEY: ${OSS_ACCESS_KEY:?required}
      SKILLHUB_STORAGE_S3_SECRET_KEY: ${OSS_SECRET_KEY:?required}
      SKILLHUB_STORAGE_S3_REGION: ${ALIYUN_REGION}
      SKILLHUB_STORAGE_S3_FORCE_PATH_STYLE: "false"
      SKILLHUB_STORAGE_S3_DISABLE_CHUNKED_ENCODING: "true"
      SKILLHUB_SECURITY_SCANNER_ENABLED: ${SKILLHUB_SECURITY_SCANNER_ENABLED:-true}
      SKILLHUB_SECURITY_SCANNER_URL: http://skill-scanner:8000
      SKILLHUB_SECURITY_SCANNER_MODE: upload
      SKILLHUB_BUILTIN_SKILLS_ENABLED: "false"
      SKILLHUB_LOG_FORMAT: json
      BOOTSTRAP_ADMIN_ENABLED: ${BOOTSTRAP_ADMIN_ENABLED:-true}
      BOOTSTRAP_ADMIN_USERNAME: ${BOOTSTRAP_ADMIN_USERNAME:-admin}
      BOOTSTRAP_ADMIN_PASSWORD: ${BOOTSTRAP_ADMIN_PASSWORD:?required}
      BOOTSTRAP_ADMIN_EMAIL: ${BOOTSTRAP_ADMIN_EMAIL:?required}
    ports: ["127.0.0.1:18081:8080"]
    depends_on: { skill-scanner: { condition: service_healthy } }
    deploy: { resources: { limits: { cpus: "2", memory: 3G } } }
    healthcheck: { test: ["CMD", "wget", "-qO-", "http://localhost:8080/actuator/health"], interval: 15s, timeout: 5s, retries: 12, start_period: 90s }
  skillhub-web:
    <<: *common
    image: ${SKILLHUB_WEB_IMAGE:-ghcr.io/iflytek/skillhub-web}:${SKILLHUB_VERSION:-v0.2.20}
    environment:
      SKILLHUB_API_UPSTREAM: http://skillhub-server:8080
      SKILLHUB_PUBLIC_BASE_URL: https://${SKILLS_HOST}
      SKILLHUB_TRUST_FORWARDED_PROTO: "true"
    ports: ["127.0.0.1:18080:80"]
    depends_on: { skillhub-server: { condition: service_healthy } }
    deploy: { resources: { limits: { cpus: "0.5", memory: 256M } } }
    healthcheck: { test: ["CMD", "wget", "-qO-", "http://127.0.0.1/nginx-health"], interval: 15s, timeout: 5s, retries: 12 }
  skill-scanner:
    <<: *common
    image: ${SKILLHUB_SCANNER_IMAGE:-ghcr.io/iflytek/skillhub-scanner}:${SKILLHUB_VERSION:-v0.2.20}
    environment:
      SKILL_SCANNER_LLM_API_KEY: ${SCANNER_LLM_API_KEY:-}
      SKILL_SCANNER_LLM_BASE_URL: ${SCANNER_LLM_BASE_URL:-}
      SKILL_SCANNER_LLM_MODEL: ${SCANNER_LLM_MODEL:-}
      SKILLHUB_SCANNER_MAX_CONCURRENT_SCANS: "1"
    deploy: { resources: { limits: { cpus: "1", memory: 1500M } } }
    healthcheck: { test: ["CMD", "wget", "-qO-", "http://127.0.0.1:8000/health"], interval: 15s, timeout: 5s, retries: 10 }
networks:
  default: { ipam: { config: [{ subnet: 10.207.0.0/24 }] } }

完整文件(含注释与阶段 2 的 compose.idp.yaml)见部署包。

4.3 agentgateway 配置模板(gateway/config.yaml.tmpl

要点:config.database.url 指向 RDS,storage.mode: hybrid 让管理界面创建的 Key、预算、模型白名单落库;请求日志入库只存元数据(不存提示词);gateways.default 在 3000 端口终止 TLS(宿主发布为 443);llm 段是模型入口并强制个人 Key;/mcp/context7 是验证"同一把 Key 接 MCP"的公网试点路由;skills-test 主机名转发到 SkillHub Web。

config:
  adminAddr: 0.0.0.0:15000
  statsAddr: 0.0.0.0:15020
  readinessAddr: 0.0.0.0:15021
  database:
    url: postgresql://${AGW_DB_USER}:${AGW_DB_PASSWORD}@${RDS_HOST}:${RDS_PORT}/agentgateway
    maxConnections: 5
  storage:
    mode: hybrid
  logging:
    format: json
    database:
      llm: metadata
gateways:
  default:
    port: 3000
    tls: { cert: /certs/fullchain.pem, key: /certs/privkey.pem, minTlsVersion: "1.2" }
llm:
  gateways: [default]
  policies:
    apiKey: { mode: strict, keys: [] }          # Key 在管理界面创建(hybrid 落 RDS);也可写 keyHash: sha256:<hex>
    localRateLimit:
    - { type: requests, maxTokens: 300, tokensPerFill: 300, fillInterval: 60s }
  providers:
  - name: bailian
    provider: openAI
    params: { baseUrl: https://dashscope.aliyuncs.com/compatible-mode/v1, apiKey: "${BAILIAN_API_KEY}" }
  - name: newapi
    provider: openAI
    params: { baseUrl: "${NEWAPI_BASE_URL}", apiKey: "${NEWAPI_API_KEY}" }
  models:
  - { name: coding-default, provider: { reference: bailian }, params: { model: "${BAILIAN_CODING_MODEL}" } }
  - { name: chat-default,   provider: { reference: newapi },  params: { model: "${NEWAPI_DEFAULT_MODEL}" } }
routes:
- name: mcp-context7
  matches: [{ path: { exact: /mcp/context7 } }]
  policies:
    apiKey: { mode: strict, keys: [] }
    cors: { allowOrigins: ['*'], allowHeaders: ['*'], exposeHeaders: [Mcp-Session-Id, Mcp-Protocol-Version] }
  backends:
  - mcp: { targets: [{ name: context7, mcp: { host: https://mcp.context7.com/mcp } }] }
- name: skillhub-web
  hostnames: ["${SKILLS_HOST}"]
  matches: [{ path: { pathPrefix: / } }]
  policies:
    requestHeaderModifier: { set: { X-Forwarded-Proto: https, X-Forwarded-Host: "${SKILLS_HOST}" } }
  backends:
  - host: skillhub-web:80

两处待试点第一天实测的字段(05 §2.4 已注明以 v1.5.0 文档为准):管理界面创建的 Key 是否自动被 apiKey 策略接受(否则改为在模板里写 keyHash);就绪端点路径(按 /healthz/ready 试)。

4.4 .env 变量(只列名字,值不进文档)

变量 说明
域名 GW_HOSTSKILLS_HOSTIDP_HOST 三个内网域名
地域与镜像 ALIYUN_REGIONAGW_IMAGESKILLHUB_VERSIONSKILLHUB_SERVER_IMAGESKILLHUB_WEB_IMAGESKILLHUB_SCANNER_IMAGEKC_IMAGE 转存 ACR 后改地址
RDS RDS_HOSTRDS_PORTAGW_DB_USER/PASSWORDSKILLHUB_DB_USER/PASSWORDKC_DB_USER/PASSWORD 三库三账号
Tair TAIR_HOSTTAIR_PORTTAIR_PASSWORDTAIR_DATABASE
OSS OSS_BUCKETOSS_ACCESS_KEYOSS_SECRET_KEY RAM 子账号
SkillHub SKILLHUB_DOWNLOAD_ANON_COOKIE_SECRETopenssl rand -hex 32)、BOOTSTRAP_ADMIN_*SKILLHUB_SECURITY_SCANNER_ENABLEDSCANNER_LLM_*
模型 BAILIAN_API_KEYBAILIAN_CODING_MODELNEWAPI_BASE_URLNEWAPI_API_KEYNEWAPI_DEFAULT_MODEL 只出现在 .env 与渲染后的网关配置
阶段 2 KC_ADMIN_USER/PASSWORDKC_REALMSKILLHUB_OIDC_CLIENT_ID/SECRET

4.5 容器资源与 ECS 容量

容器 CPU 上限 内存上限 依据
agentgateway 2 1 GiB POC 实测空载 14 MiB、压测 30 MiB 以内;上限留给日志与连接
skillhub-server 2 3 GiB Java 21,MaxRAMPercentage=70
skillhub-web 0.5 256 MiB nginx
skill-scanner 1 1.5 GiB Python,扫描时峰值
keycloak(阶段 2) 2 2 GiB POC 实测稳态 0.8 GiB
合计 7.5 7.8 GiB 16 GiB 机型留有余量;8 GiB 机型需去掉 Keycloak 与 scanner

5. 实施步骤与验收

5.1 开通资源(云平台)

  1. 按 §2 开通 ECS、RDS、Tair、OSS;RDS 建三个库与三个账号(agentgateway / skillhub / keycloak),各库 owner 为对应账号;Tair 开密码;OSS 建私有 Bucket 与 RAM 子账号(策略只授该 Bucket 的 oss:GetObject / PutObject / DeleteObject / ListObjects)。
  2. RDS、Tair 白名单加 ECS 内网 IP;确认 ECS 所在交换机有 NAT 出站。
  3. PrivateZone 加两条 A 记录(阶段 2 加第三条);签发含三个 SAN 的证书。
  4. 安全组按 §3 配置。

5.2 ECS 初始化

# Docker Engine 27+ 与 compose 插件(>= 2.24);时区;目录
sudo timedatectl set-timezone Asia/Shanghai
sudo mkdir -p /opt/ai-gateway && sudo chown $USER /opt/ai-gateway
# 镜像转存到 ACR(在有境外访问的机器上执行一次;<acr> 为公司 ACR 地址)
for i in cr.agentgateway.dev/agentgateway:v1.5.0 ghcr.io/iflytek/skillhub-server:v0.2.20 ghcr.io/iflytek/skillhub-web:v0.2.20 ghcr.io/iflytek/skillhub-scanner:v0.2.20; do
  docker pull $i && docker tag $i <acr>/ai-gateway/${i##*/} && docker push <acr>/ai-gateway/${i##*/}; done

5.3 落配置并启动

cd /opt/ai-gateway            # 放入部署包内容
cp .env.example .env && chmod 600 .env && vi .env      # 填 §4.4 的值;镜像改为 ACR 地址
cp /path/to/fullchain.pem certs/ && cp /path/to/privkey.pem certs/
./render.sh                   # 渲染 gateway/config.yaml,修正属主为 65532
docker compose config -q      # 语法与变量检查(缺必填变量会直接报错)
docker compose up -d && docker compose ps

5.4 健康检查

curl -s http://127.0.0.1:15021/healthz/ready            # 网关就绪
curl -s http://127.0.0.1:15020/metrics | head -3         # 指标
curl -s http://127.0.0.1:18081/actuator/health           # SkillHub server:{"status":"UP"}
curl -s http://127.0.0.1:18080/nginx-health              # SkillHub web
docker compose logs --since 10m agentgateway | grep -i -E 'error|reload' # 配置装载无错误

5.5 初始化

  1. 运维 IP 打开 http://<ECS 内网 IP>:15000,在管理界面创建第一把个人 Key(填元数据:用户、部门;设日 / 月预算),记录 Key。
  2. 打开 https://skills-test.<内网域>,用首登管理员登录并立即改密;创建第一个命名空间(如 platform),关闭公开注册或开启准入审批。
  3. 在 SkillHub 管理界面把发布审核策略设为"必须审核",扫描失败不批准(SkillHub 默认不阻断,靠审核兜底)。
  4. 用一个示例技能(SKILL.md + 目录)走一遍:CI 或本机 skillhub publish → 扫描 → 审核通过 → 发布。

5.6 验收命令(开发者视角)

KEY=<管理界面创建的个人 Key>; GW=https://ai-gw-test.<内网域>
curl -s $GW/v1/models -H "Authorization: Bearer $KEY"                                  # 列出别名
curl -s $GW/v1/chat/completions -H "Authorization: Bearer $KEY" -H 'content-type: application/json' \
  -d '{"model":"coding-default","messages":[{"role":"user","content":"ping"}]}'          # 经百炼返回
curl -s -o /dev/null -w '%{http_code}\n' $GW/v1/models                                   # 无 Key → 401
# Claude Code / Codex 接入
export ANTHROPIC_BASE_URL=$GW ANTHROPIC_AUTH_TOKEN=$KEY        # Claude Code(/v1/messages 由网关转换)
export OPENAI_BASE_URL=$GW/v1 OPENAI_API_KEY=$KEY               # Codex / 其他 OpenAI 兼容工具
claude mcp add --transport http context7 $GW/mcp/context7 --header "Authorization: Bearer $KEY"   # 同一把 Key 接 MCP
# SkillHub CLI(npm 包 @astron-team/skillhub)
npx @astron-team/skillhub login --server https://skills-test.<内网域> --token <SkillHub 令牌>
npx @astron-team/skillhub install <命名空间>/<技能> --agent claude       # 装到 ~/.claude/skills
# 管理界面"日志"页应能看到上述请求的模型、token 与费用;RDS agentgateway 库有请求日志表

验收通过标准:三条模型请求(有 Key、无 Key、超预算)行为正确;Claude Code 与 Codex 各完成一次对话;MCP tools/list 经网关成功;一个技能完成发布并被 CLI 安装到 Claude Code;重启 ECS 后所有容器自动拉起且 Key 仍有效(落库验证)。

5.7 运维约定

  • 备份:RDS 自动备份 7 天;OSS 版本控制;/opt/ai-gateway 整目录纳入 ECS 快照或每日打包到 OSS(含 .env,按密文处理)。
  • 升级:改 .env 里的版本号 → docker compose pulldocker compose up -d;回滚改回版本号再 up -d。SkillHub 升级前看上游变更说明(Flyway 迁移不可逆)。
  • 改网关配置:改 gateway/config.yaml.tmpl./render.sh(含重启)。管理界面里的改动落在 RDS,不会写回文件。
  • 日志:容器 stdout 滚动 50 MB × 5;接 SLS 时用 Logtail 采 Docker 标准输出。
  • 口令轮换:厂商 Key、数据库口令、OSS AK 每季度轮换,只改 .env./render.sh && docker compose up -d

6. 需要业务侧先给的输入

# 输入 默认假设
1 地域与 VPC(与生产主集群同 VPC,还是独立 VPC 打通) 同地域同 VPC
2 三个内网域名与证书方式(内部 CA / 公有 CA) ai-gw-testskills-testidp-test + 公司内部 CA
3 办公网 / VPN 出口网段与运维 IP 由网络组提供
4 模型出口:百炼账号 Key 与存量代理地址;是否还要火山方舟 百炼 + 存量代理
5 阶段 2 时间:是否在试点第一周就上 Keycloak 与飞书登录 试点第 2 周

7. 风险与对冲

风险 对冲
单机无高可用;ECS 故障即中断 试点可接受;配置全在 RDS 与 /opt/ai-gateway,新 ECS 30 分钟内重建;正式环境按公司版方案落 K8s
管理界面无鉴权 安全组只放运维 IP;第二步用网关 ui.policies 加 basicAuth 或 OIDC
境外镜像仓库拉取不稳定 ACR 转存并锁版本
SkillHub 扫描失败默认不阻断发布 审核规则设为不批准;CI 侧再扫一道
阿里云 OSS 与 S3 细节差异(分块编码、路径风格) 已在 compose 固定 DISABLE_CHUNKED_ENCODING=trueFORCE_PATH_STYLE=false;上线前上传一个 20 MB 技能包验证
agentgateway v1.5.0 的 Key 落库与策略衔接细节 试点第一天实测;不通则改为文件内 keyHash