ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Polar 多实例本地开发环境:用 `dev docker` 并行管理多个隔离工作树

Polar 多实例本地开发环境:用 `dev docker` 并行管理多个隔离工作树 Polar 多实例本地开发环境用dev docker并行管理多个隔离工作树【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar导读Polar 是一个面向智能时代的计费billing平台其代码库横跨 FastAPI 后端server/、Next.js 前端clients/apps/web/与 Python SDK 等大量模块。为了让开发者可以在同一台机器上同时维护多个 git worktree例如并行对比两个分支、同时跑长测试与日常开发仓库提供了基于 Docker 的多实例本地开发模型每台机器只启动一套重量级共享基础设施每个 worktree 各自运行一套轻量应用栈彼此通过端口偏移、独立数据库、独立 Redis 索引与独立 S3 桶实现完全隔离。本文以 .agents/skills/local-environment/rules/manage-instances.md 为骨架结合 dev/cli/commands/docker.py、dev/docker/docker-compose.dev.yml、dev/docker/docker-compose.shared.yml 等源码讲清楚实例instance是什么、端口如何映射、如何固定与检查实例、如何对指定实例执行命令以及资源消耗与回收策略。读完本文你将能在一台开发机上并排运行多个隔离的 Polar 环境并准确判断每个实例暴露在哪些端口、数据落在哪里。什么是实例共享基础设施之上的一套隔离应用栈在 Polar 的本地开发模型中一个instance实例就是一个 git worktree 所拥有的、隔离的应用栈对应的 Docker Compose 项目名称为polar-app-NN为实例号。所有实例共享同一套每机器一份的基础设施栈polar-shared包括 postgres、redis、minio、tinybird以及可选的 prometheus / grafana。每个实例从共享基础设施中拿到以下四类专属资源均按实例号隔离api / web 主机端口按实例号偏移避免不同 worktree 的容器在宿主机上撞端口独立数据库polar_dev_N位于共享 postgres 内一个实例一个逻辑库Redis DB 索引N位于共享 redis 内一个实例一个 DB indexS3 桶对polar-s3-N/polar-s3-public-N位于共享 MinIO 内。因此实例是廉价的只有 api / worker / web 三个应用容器被复制重型的 postgres、redis、MinIO 等基础设施每台机器只付一次成本。这正是 .agents/skills/local-environment/SKILL.md 中所描述的两段式模型two-part model共享基础设施每机器一份每实例应用栈每 worktree 一份。从 Compose 文件也能印证这一点dev/docker/docker-compose.shared.yml 中dbpostgres:15.1-bullseye、redisredis:alpine、minio、tinybird都属于polar-shared项目而 dev/docker/docker-compose.dev.yml 中只有api、worker、web三个服务且通过环境变量插值按实例号生成资源名POLAR_POSTGRES_DATABASE: polar_dev_${POLAR_DOCKER_INSTANCE:-1} POLAR_REDIS_DB: ${POLAR_REDIS_DB:-0} POLAR_S3_FILES_BUCKET_NAME: polar-s3-${POLAR_DOCKER_INSTANCE:-1} POLAR_S3_FILES_PUBLIC_BUCKET_NAME: polar-s3-public-${POLAR_DOCKER_INSTANCE:-1}这些变量由dev dockerCLI 在启动时通过_build_compose_env()注入见 dev/cli/commands/docker.py从而实现一套 Compose 文件、多实例并行。端口映射只有 api 和 web 暴露主机端口多实例模型中最容易混淆的一点是只有 api 和 web 两个服务发布宿主机端口其余服务全部通过容器名在polar-shared网络内部访问。因此并不存在每实例一个 5532 / 6479 / 9100这种端口——这些端口在该模型下根本不存在。数据库和 Redis 没有宿主机端口想连它们必须通过dev docker exec db ...或dev docker exec redis ...进入容器内操作。官方文档给出的端口映射如下ServiceInstance 0Instance 1Instance 2API (host)800081018102Web (host)300031013102DB (logical)polar_dev_0polar_dev_1polar_dev_2Redis (DB index)012S3 bucketpolar-s3-0polar-s3-1polar-s3-2主机端口公式仅 api/webPort Base Instance。api 的 Base 是 8100、web 的 Base 是 3100适用于实例 199实例 0 使用遗留默认端口 8000 / 3000。所以实例 5 的 api 是8105、web 是3105。该公式在源码中有精确实现dev/cli/commands/docker.pyAPI_PORT_BASE 8100 # instance 1..99 → 8101..8199 WEB_PORT_BASE 3100 # instance 1..99 → 3101..3199 DOCS_PORT_BASE 3300 # instance 0..99 → 3300..3399 def api_port(instance: int) - int: return DEFAULT_API_PORT if instance 0 else API_PORT_BASE instance def web_port(instance: int) - int: return DEFAULT_WEB_PORT if instance 0 else WEB_PORT_BASE instance其中DEFAULT_API_PORT 8000、DEFAULT_WEB_PORT 3000定义在 dev/cli/shared.py。另有一个细节文档预览服务mintlify原生非 Docker 进程的端口公式是3300 实例号刻意避开了 web 的 3100 段和 mintlify 默认的 3000。端口段还避开了所有保留端口。源码中RESERVED_HOST_PORTS集合dev/cli/commands/docker.py包含了 8000、3000、3001grafana、5432postgres、6379redis、7181/7182tinybird、9000/9001minio、9090prometheus_assert_ports_free()会在启动前做冲突校验一旦某个实例的 api/web 端口落到保留端口上会立即报错提示。查看当前 worktree 实际解析出的实例号与地址只需运行dev docker ports # 人类可读输出 dev docker ports --json # 机器可读 JSON供工具/脚本消费ports --json会输出instance、api_port、web_port、docs_port、api_url、web_url、database、redis_db、s3_bucket、s3_public_bucket等完整字段见 dev/cli/commands/docker.py是工具化集成的首选入口。实例的自动检测与固定dev docker的大多数命令会自动检测当前 worktree 对应的实例号因此日常很少需要手动指定-i。自动检测的优先级见 dev/cli/commands/docker.py 的_detect_instance()POLAR_DOCKER_INSTANCE写入dev/docker/.env.docker的显式固定值由dev docker set-instance N写入CONDUCTOR_PORT环境变量按(port - 55000) / 10 1推导实例号跨 worktree 注册表~/.config/polar/docker-instances.json中本路径已有的条目兜底分配当前最小的空闲实例号并写入注册表。其中跨 worktree 注册表是多实例不互相冲突的关键机制它存放在用户主目录~/.config/polar/docker-instances.json所有 worktree包括 conductor 检出都能看到同一份分配表从而避免两个 worktree 抢同一个实例号dev/cli/commands/docker.py。自动检测通常够用但把 worktree 接入.claude/launch.json等工具链时端口需要确定性因此提供了固定的命令dev docker set-instance 5 # 将当前 worktree 固定到实例 5写入 .env.docker 注册表 dev docker clear-instance # 清除固定值恢复自动检测 dev docker list # 列出每个已注册实例编号、状态、路径 dev docker prune # 删除已不存在 worktree 的注册条目及其数据set-instance会做两件事把POLAR_DOCKER_INSTANCEN写入dev/docker/.env.docker对应源码_write_stored_instance()并把当前路径登记进注册表_upsert_registry()。同时它还会做冲突检查——如果该实例号已被其他 worktree 占用命令会拒绝并提示先运行dev docker list查看分配情况dev/cli/commands/docker.py。实例号合法范围是 099其中 199 用于偏移端口0 表示使用默认端口redis 以 100 个 DB 启动正好与 1:1 的索引映射对应。dev docker list会展示每个实例的编号、运行状态running / stopped / path missing、创建时间与路径当前 worktree 会高亮标注。dev docker prune则遍历注册表删除路径已不存在的条目并连带回收其数据详见下文资源回收一节。另外还有一个与实例固定配套的工具化命令dev docker launch-json它会根据当前实例的端口生成.claude/launch.json该文件被 gitignore因此每次set-instance之后都需要重新生成把 Claude Code 的 preview 端口指向当前 worktree 的真实端口避免硬编码端口在其他 worktree 中失效dev/cli/commands/docker.py。面向指定实例的显式命令大多数命令自动检测实例但也可以用-i N显式指定目标实例dev docker up -i 1 -d # 启动实例 1后台 dev docker ps -i 1 # 查看实例 1 状态 dev docker logs -i 1 api # 查看实例 1 的 api 日志 dev docker down -i 1 # 停止实例 1 dev docker shell -i 1 api # 进入实例 1 的 api 容器 shell在 CLI 实现中-i/--instance是注册在docker命令组 callback 上的全局选项dev/cli/commands/docker.py显式传入时直接使用该值未传时按上文优先级自动检测并把是否显式记录到上下文用于在提示信息中展示对应的-i N片段。除了上面五个命令dev docker还提供以下与实例相关的操作dev docker exec -i 1 db psql -U polar -d polar_dev_1—— 在共享基础设施的 db 容器里执行 SQL注意 db 属于polar-shared服务名会自动路由到共享项目dev docker exec -i 1 redis redis-cli -n 1 dbsize—— 查看实例 1 的 Redis DB 大小dev docker restart service/dev docker build—— 服务路由遵循同一套规则api、worker、web路由到本实例的polar-app-Ndb、redis、minio、tinybird、prometheus、grafana路由到polar-shared见_route()dev/cli/commands/docker.py。命令的服务感知路由是这套 CLI 的贴心设计你不需要记住某个服务属于哪个 Compose 项目CLI 会根据服务名自动选择正确的 compose 文件与项目名。典型使用场景多实例模型针对的真实开发痛点如下原文档列出的三大场景分支 A 与分支 B 并排运行每个分支在自己的 worktree 中各自拥有独立的实例互不干扰地启动、验证、对比一个实例跑长测试套件另一个实例继续开发长时间运行的任务不会阻塞日常开发环境前后对比before/after例如验证一次数据库迁移或一次依赖升级的效果无需先拆掉主环境再重建。配合dev docker up -d --wait阻塞到应用服务健康与dev docker ports拿到真实端口这些场景可以完全脚本化在 worktree A 里dev docker up -d --wait dev docker ports --json在 worktree B 里做同样操作即可获得两套并行的服务地址。资源开销与数据回收每个实例的应用栈大约增加~2 GB内存占用并拥有自己的一套构建/缓存卷server_uv_cache、api_venv、worker_venv、pnpm_store、web_node_modules、web_next_cache见 dev/docker/docker-compose.dev.yml而共享基础设施postgres、minio 等数据卷只支付一次。官方文档的建议是典型开发机上同时运行 23 个实例比较舒适。当你删除一个 worktree 后它的实例注册条目与数据不会自动消失需要用dev docker prune回收。prune针对每个路径已不存在的实例依次执行dev/cli/commands/docker.pydocker compose down -v --remove-orphans—— 移除该实例的容器与构建/缓存卷_drop_instance_data()—— 删除共享基础设施中属于该实例的数据DROP DATABASE IF EXISTS polar_dev_N WITH (FORCE)、redis-cli -n N FLUSHDB、通过 MinIO 客户端mc rb --force删除两个 S3 桶dev/cli/commands/docker.py从注册表中移除该条目。prune需要共享基础设施处于运行状态才能完成数据清理如果共享栈未启动命令会提示先dev docker up再重试。另外还有粒度更大的dev docker cleanup不带--all时清理当前实例的应用栈及其数据共享基础设施保持运行带--all -f时连共享卷一并清空——这会销毁机器上所有实例的 postgres 数据、MinIO 对象与监控状态属于高风险操作执行前会有强确认提示。关于每个实例的数据库和桶何时创建首次启动时 api 容器内的引导脚本 dev/docker/scripts/startup.sh 会执行createdb创建polar_dev_N并创建该实例的 MinIO 桶完成标记.bootstrap-db.done存放在api_venv卷中因此dev docker cleanup移除该卷后下次启动会重新引导。源码级要点速查实例号范围199MIN_INSTANCE/MAX_INSTANCE0 为遗留默认端口模式Redis 以--databases 100启动使实例号与 DB 索引 1:1 对应、无取模冲突dev/docker/docker-compose.shared.yml。每实例命名函数app_project(N)→polar-app-N、db_name(N)→polar_dev_N、redis_db(N)→N、s3_bucket(N)/s3_public_bucket(N)全部集中在 dev/cli/commands/docker.py并被 compose 文件与启动脚本共同引用是命名的单一事实来源。网络隔离共享网络polar-shared由 CLI 幂等创建_ensure_network()应用容器同时加入实例私有的polar-app-N-default桥接网络与polar-sharedweb 容器只留在私有网络因此http://api:8000在 web 内部解析到的必然是本实例的 apidev/docker/docker-compose.dev.yml。数据层面的隔离保证每个实例使用独立 postgres 逻辑库组织 UUID 全局不重叠因此共享 Tinybird 上的查询按organization_id过滤也不会跨实例泄漏dev/docker/docker-compose.dev.yml。结语Polar 的多实例模型把重量级基础设施每机器一份、轻量应用栈每 worktree 一份做到了极致端口按Base Instance偏移、数据按polar_dev_N/ Redis 索引N/polar-s3-N隔离再配合跨 worktree 注册表与set-instance固定机制让多个 git worktree 在同一台机器上并排运行既安全又省资源。掌握dev docker ports、set-instance、list、prune与-i N这组命令你就能在日常开发中随时开出第二个、第三个隔离环境而无需担心端口冲突或数据串扰。更多背景可继续阅读 .agents/skills/local-environment/SKILL.md 中的服务清单与快速参考表以及 .agents/skills/local-environment/rules/service-architecture.md 对polar-shared/polar-app-N两个项目的详细拆解。【免费下载链接】polarPolar — A billing platform for the intelligence era项目地址: https://gitcode.com/GitHub_Trending/po/polar创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进