diff --git a/mixapi/Dockerfile b/mixapi/Dockerfile new file mode 100644 index 0000000..a35781d --- /dev/null +++ b/mixapi/Dockerfile @@ -0,0 +1,43 @@ +# ============================================================ +# MIXAPI 镜像 — 官方无 Docker Hub 镜像,本文件从 GitHub Release +# 下载官方编译好的二进制构建(与上游仓库 Dockerfile 思路一致)。 +# +# build:: docker build --build-arg MIXAPI_VER=v2.5.1cl -t mixapi:v2.5.1cl . +# run:: docker run --name mixapi -d --restart always -p 3000:3000 \ +# -v /data/volumes/mixapi/data:/data -e TZ=Asia/Shanghai mixapi:v2.5.1cl +# +# disc:: MIXAPI_VER 需与 GitHub release tag 同名,二进制命名规则: +# mixapi-${MIXAPI_VER}-linux-${amd64|arm64} +# disc:: 数据目录:容器内工作目录为 /data(不配置 SQL_DSN 时默认使用 +# /data/one-api.db 的 SQLite;--log-dir 未指定时日志在 /data/logs) +# ============================================================ + +FROM alpine:3.21 + +# wget 下载二进制 + 健康检查;tzdata 保证 TZ=Asia/Shanghai 时区生效;ca-certificates 保证 HTTPS +RUN apk add --no-cache wget ca-certificates tzdata \ + && rm -rf /var/cache/apk/* + +WORKDIR /app + +# 版本号(与 release tag 同名),可用 --build-arg 覆盖 +ARG MIXAPI_VER=v2.5.1cl +# 架构后缀,留空则按构建机架构自动识别(amd64/arm64) +ARG MIXAPI_ARCH= +RUN set -eux; \ + if [ -z "${MIXAPI_ARCH}" ]; then \ + case "$(apk --print-arch)" in \ + x86_64) MIXAPI_ARCH=amd64 ;; \ + aarch64) MIXAPI_ARCH=arm64 ;; \ + *) echo "不支持的架构: $(apk --print-arch)" >&2; exit 1 ;; \ + esac; \ + fi; \ + wget -O mixapi "https://github.com/aiprodcoder/MIXAPI/releases/download/${MIXAPI_VER}/mixapi-${MIXAPI_VER}-linux-${MIXAPI_ARCH}"; \ + chmod +x mixapi + +# 数据/日志工作目录(与上游 Dockerfile 约定一致) +WORKDIR /data +EXPOSE 3000 + +# 用 ENTRYPOINT 便于 compose 追加参数(如 --log-dir /app/logs) +ENTRYPOINT ["/app/mixapi"] diff --git a/mixapi/env.cfg.example b/mixapi/env.cfg.example new file mode 100644 index 0000000..61e353d --- /dev/null +++ b/mixapi/env.cfg.example @@ -0,0 +1,66 @@ +# ============================================================ +# MIXAPI 部署 — 环境变量模板(不含敏感信息) +# 复制为 env.cfg 后填入真实值,env.cfg 已在 .gitignore 中忽略 +# ============================================================ +# +# 使用方式(在 deploy.stack 仓库根目录执行): +# cp mixapi/env.cfg.example mixapi/env.cfg +# $EDITOR mixapi/env.cfg # 填入真实密码后保存 +# docker build --build-arg MIXAPI_VER=${IMAGE_TAG_VER} -t ${IMAGE_TAG} ./mixapi +# docker compose -p mixapi --env-file ./mixapi/env.cfg -f ./mixapi/stack.yml up -d +# +# 说明:MIXAPI 官方未发布 Docker Hub 镜像(GitHub 仅提供 release 二进制), +# 故 IMAGE_TAG 指向本地构建的镜像,需先按上面命令 docker build。 +# 本栈为全容器化独立部署(MIXAPI + Redis + PostgreSQL), +# 容器内 PostgreSQL 会自动初始化库和账号,无需在宿主机手动建库或配置 pg_hba。 +# 容器 PG 未映射宿主机端口,不会与宿主机已有的 PostgreSQL 服务产生端口冲突。 +# +# 复用宿主机 PG/Redis 的单容器方案见 stack.host.yml。 +# +# 注意: +# - SQL_DSN / REDIS_CONN_STRING 为 URL 格式,密码建议纯字母+数字, +# 含 @ : / # 等字符时需做百分号转义 +# - MIXAPI 首次启动无管理员账号:浏览器打开后走 /setup 页, +# 创建的第一个账号即为 root(同 new-api 行为) +# - 如已把镜像推到私有仓库(如 hub.tp229.com:3500/mixapi), +# 把 IMAGE_TAG 改为仓库地址即可跳过本地 docker build + +# ---------- 端口 ---------- +# MIXAPI Web 服务对外暴露端口(与 newapi 默认同为 3000,同机部署需改其一) +PORT=3000 + +# ---------- 镜像(本地构建)---------- +# MIXAPI 版本号(与 GitHub release tag 同名,如 v2.5.1cl) +IMAGE_TAG_VER=v2.5.1cl +# 完整镜像引用(默认本地镜像;已推送私有仓库则改为 仓库地址/mixapi:${IMAGE_TAG_VER}) +IMAGE_TAG=mixapi:${IMAGE_TAG_VER} +# Redis 镜像 +REDIS_IMAGE_TAG=redis:8.2 +# PostgreSQL 镜像 +PGSQL_IMAGE_TAG=postgres:16-alpine + +# ---------- 数据卷 ---------- +# 宿主机持久化目录,挂载到容器 /data、/app/logs、redis 数据以及 pgsql 数据 +# 部署前手动创建:mkdir -pv /data/volumes/mixapi/{data,logs,redis,pgsql_data} +Volumes_Path=/data/volumes/mixapi + +# ---------- 数据库(容器内 PostgreSQL)---------- +# 容器内网络互通,通过服务名 postgres 访问 +DB_HOST=postgres +DB_PORT=5432 +DB_NAME=mixapi +DB_USER=mixapi +DB_PASSWORD= + +# ---------- Redis(容器内 Redis)---------- +REDIS_HOST=redis +REDIS_PASSWORD= + +# ---------- MIXAPI 应用 ---------- +# 会话密钥:留空则每次重启随机生成(导致重启后登录态失效); +# 多节点/多容器部署时必须设为同一随机串 +SESSION_SECRET= +# 渠道密钥加密密钥:默认取 SESSION_SECRET;存量数据加密后不可随意变更 +# CRYPTO_SECRET= +# 节点类型:master 主节点(默认)/ slave 从节点(多节点时从节点设为 slave) +# NODE_TYPE=master diff --git a/mixapi/readme.md b/mixapi/readme.md new file mode 100644 index 0000000..e70e7ad --- /dev/null +++ b/mixapi/readme.md @@ -0,0 +1,111 @@ +# mixapi + +MIXAPI(https://github.com/aiprodcoder/MIXAPI)大模型 API 网关部署。 +MIXAPI 基于 New-API/One-API 二次开发,配置语义与 `../newapi` 同源 +(`SQL_DSN` / `REDIS_CONN_STRING` / `SESSION_SECRET` / `--log-dir` 等一致), +本目录按 `../newapi` 的结构提供两套部署方案。 + +## 镜像说明(重要) + +MIXAPI 官方**没有发布 Docker Hub 镜像**:仓库 CI 与 README 的分发方式是 +GitHub Release 二进制(或自行 `docker build`)。本目录 `Dockerfile` 即采用 +官方方式——构建时从 GitHub Release 下载与 `IMAGE_TAG_VER` 同名的二进制: + +```bash +docker build --build-arg MIXAPI_VER=v2.5.1cl -t mixapi:v2.5.1cl ./mixapi +``` + +- 版本号:`env.cfg` 中 `IMAGE_TAG_VER`(默认 `v2.5.1cl`),需与 + https://github.com/aiprodcoder/MIXAPI/releases 的 tag 同名; +- 架构:构建机 x86_64 → amd64,aarch64 → arm64,跨架构构建可用 + `--build-arg MIXAPI_ARCH=amd64` 显式指定; +- 若已把镜像推送到私有仓库(如 `hub.tp229.com:3500/mixapi`),把 `env.cfg` + 的 `IMAGE_TAG` 改为仓库地址,即可跳过本地构建,走常规 `pull`。 + +## 部署方式 A:全容器化独立栈(stack.yml) + +自带 PostgreSQL + Redis,与宿主机已有服务互不冲突: + +```bash +# 1. 数据目录 + 环境变量 +mkdir -pv /data/volumes/mixapi/{data,logs,redis,pgsql_data} +cp mixapi/env.cfg.example mixapi/env.cfg && vim mixapi/env.cfg # 填密码 + +# 2. 构建镜像(首次或升级版本时) +docker build --build-arg MIXAPI_VER=v2.5.1cl -t mixapi:v2.5.1cl ./mixapi + +# 3. 启动 / 状态 / 日志 +docker compose -p mixapi --env-file ./mixapi/env.cfg -f ./mixapi/stack.yml up -d +docker compose -p mixapi -f ./mixapi/stack.yml ps +docker compose -p mixapi -f ./mixapi/stack.yml logs -f mixapi +``` + +## 部署方式 B:单容器复用 prod-dbs PG + Dev-Redis(stack.host.yml) + +场景与 `../newapi/stack.host.yml` 一致:本机已有 `prod-dbs` 项目的 +PostgreSQL 和 `dbSer/redis` 的 Dev-Redis(172.22.10.205,固定 IP,无密码)。 + +前置条件: + +1. `prod-dbs` 已启动,`docker network ls` 能看到 `prod-dbs_default`; +2. 在 prod-postgres 上为 mixapi 建库建号(与 new_api 库隔离): + +```sql +CREATE USER mixapi WITH PASSWORD ''; +CREATE DATABASE mixapi OWNER mixapi; +``` + +之后: + +```bash +mkdir -pv /data/volumes/mixapi/{data,logs} +cp mixapi/env.cfg.example mixapi/env.cfg && vim mixapi/env.cfg # 填 prod-postgres 密码 +docker build --build-arg MIXAPI_VER=v2.5.1cl -t mixapi:v2.5.1cl ./mixapi +docker compose -p mixapi-host --env-file ./mixapi/env.cfg -f ./mixapi/stack.host.yml up -d +``` + +> 若 prod-postgres 已有可复用的专用账号,直接改 `env.cfg` 的 +> `DB_USER` / `DB_NAME` / `DB_PASSWORD` 即可,无需执行上面 SQL。 + +## 首次初始化 + +1. 浏览器访问 `http://:`(默认 3000); +2. 走 `/setup` 初始化页:创建的第一个账号即 root 管理员 + (勾选「自用模式」可关闭注册/充值等对外功能); +3. 登录后先在「设置」里配置渠道(上游 API Key)并开启缓存 + (栈内已内置 Redis,无需额外配置)。 + +健康检查:`curl http://127.0.0.1:${PORT}/api/status` + +## 端口与同机部署冲突 + +- MIXAPI 与 newapi 默认端口同为 **3000**,同机部署两者时必须把其中一个 + 的 `PORT` 改掉(`env.cfg`)。 +- 全容器栈 A 的容器 PG/Redis 不映射宿主机端口,无冲突。 + +## 环境变量要点 + +| 变量 | 说明 | +|------|------| +| `SQL_DSN` | `postgresql://user:pass@host:port/db`;**不设置**则回落使用 `/data` 下的 SQLite(文件名随版本,v2.5.1cl 为 `mix-api.db`) | +| `REDIS_CONN_STRING` | `redis://:密码@host:6379`;Dev-Redis 无密码时不带 `:密码@` | +| `SESSION_SECRET` | 会话密钥。设为字面量 `random_string` 会**启动失败**;留空则重启随机(登录态失效);多节点必须同值 | +| `CRYPTO_SECRET` | 渠道密钥加密密钥,默认取 `SESSION_SECRET`;**存量数据加密后不可变更** | +| `NODE_TYPE` | 多节点:主节点默认 `master`,从节点设 `slave` | +| `STREAMING_TIMEOUT` | 流式无响应超时(秒),默认 120,出现空补全时调大 | + +## 升级与备份 + +- 升级:改 `env.cfg` 的 `IMAGE_TAG_VER` → 重新 `docker build` → + `docker compose ... up -d`(数据卷保留,无需迁移); +- 备份:全栈 A 备份 `/data/volumes/mixapi/pgsql_data`;若走 SQLite 回退 + 则备份 `/data/volumes/mixapi/data` 下的数据库文件;`SESSION_SECRET` / + `CRYPTO_SECRET` 变更会导致会话与已加密渠道失效,升级时保持原值。 + +## 停止与清理 + +```bash +docker compose -p mixapi -f ./mixapi/stack.yml down # 全栈 A +docker compose -p mixapi-host -f ./mixapi/stack.host.yml down # 方案 B +# 数据目录不随 down 删除,需要彻底清理时手动 rm -rf /data/volumes/mixapi +``` diff --git a/mixapi/stack.host.yml b/mixapi/stack.host.yml new file mode 100644 index 0000000..1d20b53 --- /dev/null +++ b/mixapi/stack.host.yml @@ -0,0 +1,57 @@ +# build:: docker build --build-arg MIXAPI_VER=v2.5.1cl -t mixapi:v2.5.1cl ./mixapi +# path:: mkdir -pv /data/volumes/mixapi/{data,logs} +# run:: docker compose -p mixapi-host --env-file ./mixapi/env.cfg -f ./mixapi/stack.host.yml up -d +# disc:: 单容器部署:复用 prod-dbs 项目里的 prod-postgres(join 同一 docker 网络解析) +# disc:: 需先在 prod-postgres 上建好库与账号(SQL 模板见 mixapi/readme.md), +# disc:: 库名/账号与 newapi 分开(默认 mixapi),避免共用 new_api 库 +# disc:: Redis 复用 dbSer/redis 的 Dev-Redis(172.22.10.205,固定 IP,无密码) +# disc:: 项目名 mixapi-host,与 stack.yml 全栈部署互不冲突 +# disc:: 启动顺序:先 up prod-dbs(确保 prod-dbs_default 网络存在),再 up mixapi-host +# disc:: 所有连接变量已内置默认值,复用 env.cfg 只需填密码即可 +# disc:: 首次启动后浏览器访问 /setup 创建的第一个账号即为 root 管理员 + +services: + mixapi: + image: ${IMAGE_TAG} + container_name: mixapi_host_app + restart: always + command: --log-dir /app/logs + ports: + - "${PORT:-3000}:3000" + networks: + - prod-dbs_default # 加入 prod-dbs 默认网络,用 service 名 postgres 解析 prod-postgres + volumes: + - ${Volumes_Path:-/data/volumes/mixapi}/data:/data + - ${Volumes_Path:-/data/volumes/mixapi}/logs:/app/logs + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + environment: + # 默认值:DB_HOST=postgres 指向 prod-dbs 项目的 postgres service; + # DB_NAME/DB_USER 默认 mixapi,需在 prod-postgres 预先创建 + # 本机 PG 无 TLS 时显式关闭 SSL(取消下行注释并注释上行): + # - SQL_DSN=postgresql://${DB_USER:-mixapi}:${DB_PASSWORD}@${DB_HOST:-postgres}:${DB_PORT:-5432}/${DB_NAME:-mixapi}?sslmode=disable + - SQL_DSN=postgresql://${DB_USER:-mixapi}:${DB_PASSWORD}@${DB_HOST:-postgres}:${DB_PORT:-5432}/${DB_NAME:-mixapi} + # 默认值:REDIS_HOST=172.22.10.205 指向 Dev-Redis(dbSer/redis.yml部署,DevNet 网段固定 IP,无密码) + # - REDIS_CONN_STRING=redis://:${REDIS_PASSWORD}@${REDIS_HOST:-172.22.10.205}:${REDIS_PORT:-6379} + - REDIS_CONN_STRING=redis://${REDIS_HOST:-172.22.10.205}:${REDIS_PORT:-6379} + - TZ=Asia/Shanghai + - BATCH_UPDATE_ENABLED=true + - ERROR_LOG_ENABLED=true + - SESSION_SECRET=${SESSION_SECRET:-} + # 渠道密钥加密密钥:默认取 SESSION_SECRET,存量数据加密后不可随意变更 + # - CRYPTO_SECRET=${CRYPTO_SECRET:-} + # 可选:独立日志库(PG 或 ClickHouse),不设则日志与业务同库 + # - LOG_SQL_DSN=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST:-postgres}:${DB_PORT:-5432}/${DB_NAME:-mixapi}_log + # 流式无响应超时(秒),默认 120,出现空补全时调大: + # - STREAMING_TIMEOUT=300 + healthcheck: + test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:3000/api/status >/dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + +networks: + prod-dbs_default: + # 引用 prod-dbs 项目自动创建的默认网络,不归本 compose 管 + external: true + name: prod-dbs_default diff --git a/mixapi/stack.yml b/mixapi/stack.yml new file mode 100644 index 0000000..8dd59aa --- /dev/null +++ b/mixapi/stack.yml @@ -0,0 +1,93 @@ +# path:: mkdir -pv /data/volumes/mixapi/{data,logs,redis,pgsql_data} +# build:: docker build --build-arg MIXAPI_VER=v2.5.1cl -t mixapi:v2.5.1cl ./mixapi +# run:: docker compose -p mixapi --env-file ./mixapi/env.cfg -f ./mixapi/stack.yml up -d +# disc:: 全栈容器化部署:包含 MIXAPI、Redis 缓存及 PostgreSQL 数据库 +# disc:: 容器间处于自定义桥接网络 mixapi 内,内部通过服务名 postgres:5432 / redis:6379 互通 +# disc:: 容器 PG 不暴露宿主机端口,与宿主机已有 PostgreSQL 服务互不冲突 +# disc:: MIXAPI 无官方 Docker Hub 镜像:需先按 build:: 命令本地构建(版本见 env.cfg) +# disc:: 首次启动后浏览器访问 /setup 创建的第一个账号即为 root 管理员 +# disc:: 生产务必修改 env.cfg 中的全部密码与 SESSION_SECRET + +services: + mixapi: + image: ${IMAGE_TAG} + container_name: mixapi_app + restart: always + command: --log-dir /app/logs + ports: + - "${PORT:-3000}:3000" + networks: + - mixapi + depends_on: + - redis + - postgres + volumes: + - ${Volumes_Path}/data:/data + - ${Volumes_Path}/logs:/app/logs + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + environment: + - SQL_DSN=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME} + # 本机无 TLS 的 PG 也可显式关闭 SSL: + # - SQL_DSN=postgresql://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?sslmode=disable + # 不设 SQL_DSN(删除上行)则 MIXAPI 回落使用 /data 下的 SQLite(文件名随版本,v2.5.1cl 为 mix-api.db) + - REDIS_CONN_STRING=redis://:${REDIS_PASSWORD}@redis:6379 + - TZ=Asia/Shanghai + - BATCH_UPDATE_ENABLED=true + - ERROR_LOG_ENABLED=true + - SESSION_SECRET=${SESSION_SECRET} + # 渠道密钥加密密钥:默认取 SESSION_SECRET,存量数据加密后不可随意变更 + # - CRYPTO_SECRET=${CRYPTO_SECRET} + # 多节点部署时从节点设为 slave: + # - NODE_TYPE=slave + # 可选:独立日志库(PG 或 ClickHouse),不设则日志与业务同库 + # - LOG_SQL_DSN=postgresql://${DB_USER}:${DB_PASSWORD}@postgres:5432/mixapi_log + # 流式无响应超时(秒),默认 120,出现空补全时调大: + # - STREAMING_TIMEOUT=300 + healthcheck: + test: ["CMD-SHELL", "wget -q -O - http://127.0.0.1:3000/api/status > /dev/null || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + + redis: + image: ${REDIS_IMAGE_TAG} + container_name: mixapi_redis + restart: always + networks: + - mixapi + volumes: + - ${Volumes_Path}/redis:/data + command: ["redis-server", "--requirepass", "${REDIS_PASSWORD}"] + healthcheck: + test: ["CMD-SHELL", "redis-cli -a ${REDIS_PASSWORD} ping | grep -q PONG"] + interval: 10s + timeout: 5s + retries: 5 + + postgres: + image: ${PGSQL_IMAGE_TAG} + container_name: mixapi_pgsql + restart: always + shm_size: 256mb + environment: + - POSTGRES_USER=${DB_USER} + - POSTGRES_PASSWORD=${DB_PASSWORD} + - POSTGRES_DB=${DB_NAME} + - POSTGRES_HOST_AUTH_METHOD=scram-sha-256 + - TZ=Asia/Shanghai + volumes: + - ${Volumes_Path}/pgsql_data:/var/lib/postgresql/data + - /etc/timezone:/etc/timezone:ro + - /etc/localtime:/etc/localtime:ro + networks: + - mixapi + healthcheck: + test: ["CMD-SHELL", "pg_isready -U ${DB_USER} -d ${DB_NAME}"] + interval: 10s + timeout: 5s + retries: 5 + +networks: + mixapi: + driver: bridge