Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -292,3 +292,5 @@ static/frontend/dist/

# GUI Launcher user config (contains user-specific settings)
gui/user_config.json

.multi-instance-runtime/
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ cp .env.example .env
- [认证轮转与 Cookie 刷新](docs/auth-rotation-cookie-refresh.md)
- [排障指南](docs/troubleshooting.md)
- [开发、测试与发布](docs/development-and-release.md)
- [多实例 Docker 管理器](scripts/multi-instance-manager/README.md)

---

Expand Down
12 changes: 12 additions & 0 deletions docs/deployment-and-operations.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,18 @@ bash update.sh
docker compose exec ai-studio-proxy /bin/bash
```

## 3.5 多实例扩展

如需为多个已保存认证同时启动隔离容器,可使用外部管理脚本 [`scripts/multi-instance-manager/README.md`](../scripts/multi-instance-manager/README.md)。

当前 multi-instance 管理器会为每个容器显式设置独立的 `ACTIVE_AUTH_JSON_PATH`,并把选中的 profile 复制到 [` .multi-instance-runtime/active/`](../.multi-instance-runtime/) 后再挂载为容器内的 [`auth_profiles/active/`](../auth_profiles/active/) 单文件。默认同时关闭启动时自动轮转与运行时自动轮转,这样既能维持一容器一认证隔离,也能满足 Docker headless 启动阶段对 active-dir 的运行时要求。
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s an extra leading space inside the inline-code link text ([ .multi-instance-runtime/active/]), which renders as a different path and looks like a typo. Remove the leading space so the path displays consistently as .multi-instance-runtime/active/.

Copilot uses AI. Check for mistakes.

镜像名方面,管理器默认仍优先查找 `ai-studio-proxy:latest`。但如果你是通过 `cd docker && docker compose build` 构建,Compose 常见产物会是 `docker-ai-studio-proxy:latest`;在未显式覆盖镜像名时,脚本会自动回退到该 Compose 镜像并打印提示。若你通过 `--image` 或 `IMAGE_NAME` 指定了其他镜像名,则脚本会严格使用该值,不会静默改写。

如需让单个容器重新参与跨 profile 轮转,可在脚本中使用 `--enable-auth-rotation`,但这会放宽默认的一容器一认证隔离约束。

该方案面向高级用户,不修改主应用、Dockerfile 或默认 Docker 启动流程。

---

## 4. 生产配置建议(重点)
Expand Down
246 changes: 246 additions & 0 deletions scripts/multi-instance-manager/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
# Multi-instance manager

External helper for advanced users who want to run multiple isolated Docker containers from a pool of saved auth profiles.

## What it does

The manager launches one container per auth profile from [`auth_profiles/saved/`](../../auth_profiles/saved/). Each container gets:

- its own host API port
- its own host stream port
- its own dedicated startup profile via [`ACTIVE_AUTH_JSON_PATH`](../../launcher/runner.py)
- the same Docker image and shared API key unless overridden

By default, the manager enforces strict profile isolation at startup. A container starts with its matching file from [`auth_profiles/saved/`](../../auth_profiles/saved/) and does not auto-rotate into other profiles unless you explicitly enable that behavior.

This component is intentionally isolated from the main application. It does not modify the core server code, the existing Docker setup, or the standard launch flow.

## Resource warning

> ⚠️ Each instance can consume around 1 GB RAM or more.
> Running many containers at once may cause memory pressure or OOM kills.
> Review available system memory before launching a large profile pool.

The script shows a visible warning and asks for confirmation before startup unless auto-confirm is enabled.

## Requirements

- Docker installed and running
- Bash 3.2+ on macOS or a newer Bash on Linux
- built image available locally
- default expected name: `ai-studio-proxy:latest`
- if you built via `cd docker && docker compose build`, Docker Compose may create `docker-ai-studio-proxy:latest`; the launcher now detects this and falls back automatically when the default image is still configured
- if you use another tag, pass it explicitly with `--image ...` or set `IMAGE_NAME=...`
- prepared auth files in [`auth_profiles/saved/`](../../auth_profiles/saved/)
- optional [`docker/.env`](../../docker/.env) file if you want containers to inherit the usual Docker configuration

## Files

- [`run-multi-instance.sh`](run-multi-instance.sh) — launcher script
- [`README.md`](README.md) — usage guide

## Startup contract

The launcher now follows a strict startup contract:

1. It mounts the shared [`auth_profiles/`](../../auth_profiles/) tree into the container.
2. It sets `ACTIVE_AUTH_JSON_PATH=/app/auth_profiles/saved/<profile>.json` for that specific container.
3. It copies the selected host profile into [` .multi-instance-runtime/active/`](../../.multi-instance-runtime/) and bind-mounts that copy as `/app/auth_profiles/active/<profile>.json` inside the container.
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s an extra leading space inside the inline-code link text ([ .multi-instance-runtime/active/]), which renders as a different path and looks like a typo. Remove the leading space so the path displays consistently as .multi-instance-runtime/active/.

Copilot uses AI. Check for mistakes.
4. It disables `AUTO_AUTH_ROTATION_ON_STARTUP` and `AUTO_ROTATE_AUTH_PROFILE` by default.

This dual-path startup is intentional. Although [`launcher/runner.py::_resolve_auth_file_path()`](../../launcher/runner.py:270) and [`browser_utils/initialization/core.py::initialize_page_logic()`](../../browser_utils/initialization/core.py:48) can consume [`ACTIVE_AUTH_JSON_PATH`](../../launcher/runner.py:413), real Docker headless startup may still traverse logic that expects at least one file under [`auth_profiles/active/`](../../auth_profiles/active/). The runtime copy avoids polluting the shared host [`auth_profiles/active/`](../../auth_profiles/active/) pool while still satisfying that container-local requirement.

Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These Markdown links use runner.py:270 / core.py:48 style suffixes, which GitHub won’t interpret as line anchors (it expects #L270, etc.). Update the links to proper GitHub line anchors or remove the line-specific suffix so the references don’t 404.

Copilot uses AI. Check for mistakes.
If you really want cross-profile rotation inside each container, use [`--enable-auth-rotation`](run-multi-instance.sh).

## Quick start

Build the image first if needed:

```bash
cd docker
docker compose build
```

After a regular Compose build, the local image is often named `docker-ai-studio-proxy:latest`. The manager still starts from its default `ai-studio-proxy:latest`, but now automatically falls back to `docker-ai-studio-proxy:latest` when that Compose image exists and no custom image override was requested.

Then run the manager from the repository root:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh
```

## Common examples

Use defaults:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh
```

Use another image and custom container prefix:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh \
--image docker-ai-studio-proxy:latest \
--container-prefix team-a
```

Use an explicit image override via environment variable:

```bash
IMAGE_NAME=docker-ai-studio-proxy:latest \
bash scripts/multi-instance-manager/run-multi-instance.sh
```

Shift ports upward to avoid collisions:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh \
--base-api-port 3048 \
--base-stream-port 4120
```

Skip confirmation for automation:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh --auto-confirm
```

Preview actions without launching containers:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh --dry-run
```

Allow auth rotation inside each launched container:

```bash
bash scripts/multi-instance-manager/run-multi-instance.sh --enable-auth-rotation
```

## Options

| Option | Description | Default |
| --- | --- | --- |
| `--project-root PATH` | Repository root path | auto-detected |
| `--profile-dir PATH` | Directory with saved auth profiles | `auth_profiles/saved` |
| `--docker-env-file PATH` | Path to mounted Docker env file | `docker/.env` |
| `--image NAME` | Docker image name | `ai-studio-proxy:latest` with automatic fallback to `docker-ai-studio-proxy:latest` after a standard `docker compose build` |
| `--container-prefix PREFIX` | Prefix for launched containers | `ai-proxy` |
| `--api-key KEY` | Shared API key for all containers | `sk-dummy` |
| `--log-level LEVEL` | `SERVER_LOG_LEVEL` value | `INFO` |
| `--base-api-port PORT` | Starting host API port | `2048` |
| `--base-stream-port PORT` | Starting host stream port | `3120` |
Comment on lines +127 to +133
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doc describes --api-key / API_KEY as the shared API key for launched containers, but the current codebase enforces API keys via auth_profiles/key.txt (and doesn’t appear to read an API_KEY env var). Please either adjust the manager to actually provision key.txt (and document that), or update the README to direct users to configure auth_profiles/key.txt for API-key auth.

Copilot uses AI. Check for mistakes.
| `--memory-per-instance-gb N` | Estimated RAM per container | `1` |
| `--auto-confirm` | Skip confirmation prompt | disabled |
| `--dry-run` | Show commands without running Docker | disabled |
| `--no-docker-env` | Do not mount [`docker/.env`](../../docker/.env) | disabled |
| `--enable-auth-rotation` | Re-enable auth rotation inside launched containers | disabled |
| `--help` | Show help | disabled |

## Environment variables

All major parameters can also be set with environment variables before launch:

```bash
export IMAGE_NAME=docker-ai-studio-proxy:latest
export BASE_API_PORT=3048
export BASE_STREAM_PORT=4120
export AUTO_CONFIRM=true
bash scripts/multi-instance-manager/run-multi-instance.sh
```

When `IMAGE_NAME` or `--image` is set explicitly, the launcher treats that as an intentional override and does not silently replace it with the Compose image name.

Supported variables:

- `PROJECT_ROOT`
- `PROFILE_DIR`
- `DOCKER_ENV_FILE`
- `IMAGE_NAME`
- `CONTAINER_PREFIX`
- `API_KEY`
- `SERVER_LOG_LEVEL`
- `BASE_API_PORT`
- `BASE_STREAM_PORT`
- `MEMORY_PER_INSTANCE_GB`
- `AUTO_CONFIRM`
- `DRY_RUN`
- `MOUNT_DOCKER_ENV`
- `DISABLE_AUTH_ROTATION`
- `EXTRA_DOCKER_ARGS`

## How the manager maps profiles

If the saved pool contains:

- `auth_profiles/saved/user-a.json`
- `auth_profiles/saved/user-b.json`

The script will launch containers similar to:

- `ai-proxy-user-a` on API `2048` and stream `3120`
- `ai-proxy-user-b` on API `2049` and stream `3121`

Inside each container, startup resolves through two aligned paths:

- `/app/auth_profiles/saved/user-a.json` and `/app/auth_profiles/active/user-a.json`
- `/app/auth_profiles/saved/user-b.json` and `/app/auth_profiles/active/user-b.json`

The saved-path mapping is still passed through `ACTIVE_AUTH_JSON_PATH`, and the same host file is also mounted into the container-local [`auth_profiles/active/`](../../auth_profiles/active/) directory. This keeps one-container-one-profile isolation while avoiding the Docker headless startup failure that reports no active profile.

## Port collision behavior

The launcher checks host ports twice:

1. before removing a same-named container
2. again after removal and before starting the replacement container

This prevents a false success path where an old container was removed even though the target port actually belonged to another listener.

If a host port is occupied by another process or container, the script stops before launching replacements.

## Safety checks

Before launch the script validates:

- Docker CLI availability
- Docker daemon availability
- Docker image existence
- profile directory existence
- presence of `*.json` profiles
- host port collisions for all planned instances
- optional [`docker/.env`](../../docker/.env) existence when mounting is enabled

## Stop and inspect containers

List launched containers:

```bash
docker ps --filter "name=^ai-proxy-"
```

Stop one container:

```bash
docker rm -f ai-proxy-user-a
```

Stop all containers for the default prefix:

```bash
containers=$(docker ps -aq --filter "name=^ai-proxy-")
[ -n "$containers" ] && docker rm -f $containers
```

Check logs for one instance:

```bash
docker logs -f ai-proxy-user-a
```

## Notes

- The manager is intended for advanced operators.
- It is an external wrapper, not a replacement for the standard single-instance flow.
- For the normal Docker path, continue using [`docker/README.md`](../../docker/README.md) and [`docs/deployment-and-operations.md`](../../docs/deployment-and-operations.md).
Loading
Loading