# 支持的运行时依赖

## 接线闭环

托管依赖不是“创建成功”就结束。部署应用前必须完成下面闭环：

1. 获取私网 endpoint，确保依赖和 ECS/VKE 在同一 VPC。
2. 创建应用数据库/账号或应用 Redis 账号，生成或收集密码。
3. 将 ECS/VKE 所在子网 CIDR、安全组来源或节点来源加入依赖 allowlist。
4. 组装 `DATABASE_URL`、`REDIS_URL` 等运行时变量，交给 `volcengine-deploy` 的 env/Secret 注入阶段。
5. 如果 `migration_paths` 非空，先跑迁移，再做最终健康检查。

不要使用公网 endpoint 作为默认接线方式；除非用户明确要求公网暴露，并接受安全组/白名单风险。

## 检测规则

### MySQL
**检测关键词**（配置文件/代码/依赖）：
- `mysql`, `mysql2`, `mysqlclient`, `pymysql`, `MYSQL_HOST`, `3306`, `mysql://`, `jdbc:mysql`
- `docker-compose.yml` 中 `image: mysql`

**火山引擎服务**：RDS MySQL (`ve rdsmysql`)

**创建参数**：
```bash
# RDS swagger discovery can return 404; use CLI help for the body schema.
ve rdsmysql CreateDBInstance --help
```

**推荐规格**（入门级）：
- 实例类型: `HA`（高可用）
- 规格: `rds.mysql.2c4g`（2核4G）
- 存储: 20GB ESSD PL0
- 版本: MySQL 8.0

**注意事项**：
- 必须和 VKE 在同一 VPC
- 创建后需要创建数据库和账号
- 白名单需添加 VKE 子网 CIDR
- 将私网 endpoint、数据库名、账号、密码组装为 `DATABASE_URL`，不要把账号密码写入日志

---

### PostgreSQL
**检测关键词**：
- `pg`, `postgres`, `postgresql`, `psycopg2`, `pg-promise`, `POSTGRES_HOST`, `5432`, `postgres://`, `jdbc:postgresql`
- `docker-compose.yml` 中 `image: postgres`

**火山引擎服务**：RDS PostgreSQL (`ve rdspostgresql`)

**推荐规格**：
- 实例类型: `HA`
- 规格: `rds.postgres.2c4g`
- 存储: 20GB ESSD PL0
- 版本: PostgreSQL 15

**接线**：
- HA 实例创建时 `NodeInfo` 必须同时包含 `Primary` 和 `Secondary`
- 应用账号权限使用 `Inherit,Login`，不要用 `ReadWrite`
- 创建数据库时优先把 `Owner` 设为应用账号；迁移仍缺 `public` schema 权限时，对 `public` 执行 `ModifySchemaOwner`
- `CreateDatabase` 先省略 `CharacterSetName`；不要传未经验证的 uppercase `UTF8`
- 实例进入 `Running` 后，账号/库/schema 操作仍可能遇到短暂 exclusive status，等待后重试
- 创建数据库和应用账号后，使用私网 endpoint 组装 `DATABASE_URL`
- 白名单加入 ECS/VKE 子网 CIDR 或安全组来源
- 有迁移目录时先执行迁移，再放量健康检查

---

### Redis
**检测关键词**：
- `redis`, `ioredis`, `redis-py`, `REDIS_HOST`, `REDIS_URL`, `6379`, `redis://`
- `docker-compose.yml` 中 `image: redis`

**火山引擎服务**：Redis (`ve redis`)

**推荐规格**：
- 类型: 主备版
- 规格: 1GB 内存
- 版本: Redis 6.0

**创建示例**：
```bash
ve redis CreateDBInstance --body '{
  "InstanceName": "deploy-<repo>-redis",
  "RegionId": "<region>",
  "ZoneIds": ["<zone-id>"],
  "ShardedCluster": 0,
  "NodeNumber": 2,
  "ShardCapacity": 1024,
  "ShardNumber": 1,
  "EngineVersion": "6.0",
  "SubnetId": "<subnet-id>",
  "VpcId": "<vpc-id>",
  "Password": "<auto-generated>",
  "Tags": [{"Key": "project", "Value": "<repo>"}]
}'
```

**接线**：
- 使用私网 endpoint 和应用账号/密码组装 `REDIS_URL`
- 白名单加入 ECS/VKE 子网 CIDR 或安全组来源
- 先从应用运行环境执行连通性检查，再做公网健康检查

---

### MongoDB
**检测关键词**：
- `mongodb`, `mongoose`, `pymongo`, `mongoclient`, `MONGO_URI`, `MONGODB_URL`, `27017`, `mongodb://`
- `docker-compose.yml` 中 `image: mongo`

**火山引擎服务**：MongoDB (`ve mongodb`)

**推荐规格**：
- 类型: 副本集
- 规格: `mongo.2c4g`（2核4G）
- 存储: 20GB
- 版本: MongoDB 5.0

---

### Kafka
**检测关键词**：
- `kafka`, `kafkajs`, `kafka-python`, `confluent-kafka`, `KAFKA_BROKERS`, `KAFKA_BOOTSTRAP_SERVERS`, `9092`
- `docker-compose.yml` 中 `image: *kafka*`

**火山引擎服务**：Kafka (`ve kafka`)

**推荐规格**：
- 版本: 2.8
- 规格: `kafka.20xrate.hw`（入门级）
- 存储: 100GB
- 分区数: 根据 topic 配置

---

### RabbitMQ
**检测关键词**：
- `rabbitmq`, `amqplib`, `amqp`, `pika`, `RABBITMQ_HOST`, `AMQP_URL`, `5672`, `amqp://`
- `docker-compose.yml` 中 `image: rabbitmq`

**火山引擎服务**：无托管服务

**部署方式**：在 VKE 集群中部署官方 Docker 镜像

```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: rabbitmq
spec:
  serviceName: rabbitmq
  replicas: 1
  selector:
    matchLabels:
      app: rabbitmq
  template:
    metadata:
      labels:
        app: rabbitmq
    spec:
      containers:
      - name: rabbitmq
        image: rabbitmq:3.12-management-alpine
        ports:
        - containerPort: 5672
          name: amqp
        - containerPort: 15672
          name: management
        env:
        - name: RABBITMQ_DEFAULT_USER
          valueFrom:
            secretKeyRef:
              name: rabbitmq-secret
              key: username
        - name: RABBITMQ_DEFAULT_PASS
          valueFrom:
            secretKeyRef:
              name: rabbitmq-secret
              key: password
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: "1"
            memory: 1Gi
        volumeMounts:
        - name: data
          mountPath: /var/lib/rabbitmq
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
---
apiVersion: v1
kind: Service
metadata:
  name: rabbitmq
spec:
  selector:
    app: rabbitmq
  ports:
  - port: 5672
    name: amqp
  - port: 15672
    name: management
```

---

### TOS（对象存储）
**检测关键词**：
- `@volcengine/tos`, `tos-sdk`, `TOS_ENDPOINT`, `TOS_BUCKET`, `tos.volces.com`
- 代码中引用了 S3 兼容 API 且 endpoint 指向火山

**火山引擎服务**：TOS (`tosutil` / Terraform IaC)

当前 `ve` CLI build 可能没有 `tos` 服务；不要生成 `ve tos` 命令。需要对象传输时优先使用独立的 `volcengine-tosutil` skill 或 Terraform/IaC 创建桶。`volcengine-deploy` 中的 `tosutil` 只能是可选能力，必须保留 SSH/scp 或用户提供 artifact URL 降级。

```bash
tosutil mb tos://deploy-<repo>-assets -acl=private -sc=STANDARD
tosutil cp ./dist/app.tar.gz tos://deploy-<repo>-assets/artifacts/app.tar.gz
tosutil presign tos://deploy-<repo>-assets/artifacts/app.tar.gz -vp=15min
```

---

## 通用依赖处理（不在上述列表中）

对于其他服务依赖（如 Elasticsearch、MinIO、Memcached 等），采用以下策略：

1. 在 VKE 集群中以 **StatefulSet** 部署官方 Docker 镜像
2. 使用 **PVC** 持久化数据
3. 通过 **ClusterIP Service** 暴露给应用

```yaml
# 通用模板
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: <dependency-name>
spec:
  serviceName: <dependency-name>
  replicas: 1
  selector:
    matchLabels:
      app: <dependency-name>
  template:
    metadata:
      labels:
        app: <dependency-name>
    spec:
      containers:
      - name: <dependency-name>
        image: <official-docker-image>
        ports:
        - containerPort: <port>
        resources:
          requests:
            cpu: 250m
            memory: 256Mi
          limits:
            cpu: "1"
            memory: 1Gi
        volumeMounts:
        - name: data
          mountPath: <data-path>
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
```
