Nautilus Trader Docker 服务与 PostgreSQL 测试环境搭建指南
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
本文基于仓库 .docker/README.md 编写,完整介绍 Nautilus Trader 的 Docker 开发服务(PostgreSQL、pgAdmin、Redis)的启动、初始化与清理流程,并深入解析其背后的docker-compose配置、Makefile 目标实现与 Rust 侧连接初始化源码。读者在阅读完本文后,将能够独立完成本仓库 Postgres 集成测试环境的搭建与运行,并理解测试环境与底层实现的对应关系。
一、Docker 服务概览:一次docker compose提供三套基础设施
Nautilus Trader 的本地开发与集成测试依赖一组 Docker 服务,全部定义在仓库的 .docker/docker-compose.yml 中。该 Compose 文件共声明了三个服务:
| 服务 | 容器名 | 镜像 | 默认端口(仅绑定 127.0.0.1) |
|---|---|---|---|
| postgres | nautilus-database | public.ecr.aws/docker/library/postgres | 5432 |
| pgadmin | nautilus-pgadmin | dpage/pgadmin4 | 5051(由PGADMIN_PORT控制) |
| redis | nautilus-redis | public.ecr.aws/docker/library/redis | 6379 |
值得注意的配置细节:
- 端口仅绑定回环地址:三个服务的端口映射均为
"127.0.0.1:xxxx:xxxx",服务不会被暴露到宿主机外部网络,只允许本机访问,这是本地测试场景下的安全默认值。 - 凭据通过环境变量注入:Postgres 的
POSTGRES_USER、POSTGRES_PASSWORD、POSTGRES_DB均支持${VAR:-default}语法,即未设置时使用默认值nautilus/pass/nautilus,设置后则覆盖默认值。 - 数据持久化:Postgres 数据目录被设为容器内
/data/postgres(PGDATA),并挂载到命名卷nautilus-database;pgAdmin 配置挂载到命名卷pgadmin。因此docker compose down(停止)不会丢失数据,只有显式删除卷才会清空。 - 安全加固:三个服务都配置了
security_opt: no-new-privileges:true,禁止容器内进程提升权限;pgAdmin 默认管理员邮箱为admin@mail.com、密码为admin。 - 网络与自愈:三个服务共享
nautilus-network网络,均设置了restart: unless-stopped。
各服务在项目中的用途
- Postgres:Nautilus Trader 的事件溯源与 Cache 数据库持久化后端,承载
nautilus-infrastructurecrate 的postgres特性(基于 sqlx)所依赖的数据库; - Redis:用于 Redis 缓存的集成测试(见 crates/infrastructure/tests/integration/test_cache_redis.rs),对应
redis特性; - pgAdmin:Postgres 的图形化管理界面,方便在开发时直接查看表结构与数据。
二、一键初始化:make init-services做了什么
根据 .docker/README.md,从仓库根目录运行以下命令即可完成全部初始化:
make init-services该命令内部依次执行三个步骤(对应 Makefile 中的init-services目标,见 L1287-L1293):
- 启动容器:调用
make start-services; - 等待就绪:输出 "Waiting for PostgreSQL to be ready..." 并
sleep 10,给 Postgres 留出启动时间; - 应用 Schema:调用
make init-db。
start-services:拉取镜像并启动全部服务
start-services目标(Makefile L1295-L1301)的真实执行逻辑是:
bash scripts/ci/docker-pull-retry.sh public.ecr.aws/docker/library/postgres bash scripts/ci/docker-pull-retry.sh dpage/pgadmin4 bash scripts/ci/docker-pull-retry.sh public.ecr.aws/docker/library/redis docker compose -f .docker/docker-compose.yml up -d其中scripts/ci/docker-pull-retry.sh是仓库自带的镜像拉取重试脚本,依次预拉取 postgres、pgadmin、redis 三个镜像(带重试机制,应对网络抖动),随后以守护模式(-d)启动 Compose 中定义的全部服务。因此start-services实际会一次性启动 Postgres、pgAdmin 与 Redis 三个容器,而不仅是 Postgres。
init-db:按序执行四个 SQL 文件
init-db目标(Makefile L1313-L1316)将 schema 目录下四个 SQL 文件拼接后通过docker exec管道送入 Postgres 容器执行:
cat schema/sql/types.sql schema/sql/tables.sql schema/sql/functions.sql schema/sql/partitions.sql | docker exec -i nautilus-database psql -U nautilus -d nautilus这里通过容器名nautilus-database直接定位 Postgres 容器,使用默认用户nautilus、默认数据库nautilus执行 SQL。四个文件的职责分别为:
- schema/sql/types.sql:创建 ENUM 与 DOMAIN 类型。例如
ACCOUNT_TYPE(CASH/MARGIN/BETTING/WALLET)、ORDER_STATUS(INITIALIZED 到 VOIDED 的完整生命周期)、BAR_AGGREGATION(TICK、VOLUME、MINUTE、RENKO 等)等枚举,以及I256、U256、U128、U160、I128等带范围约束 CHECK 的数值域类型(用于承载 Nautilus 的高精度 128/256 位整数语义); - schema/sql/tables.sql:创建各业务数据表;
- schema/sql/functions.sql:创建数据库函数;
- schema/sql/partitions.sql:创建分区表相关结构。
默认连接参数速查
| 参数 | 默认值 |
|---|---|
| 用户 | nautilus |
| 密码 | pass |
| 数据库 | nautilus |
| 端口 | 5432 |
三、运行 Postgres 集成测试
Python 侧:make test-postgres
make test-postgres该命令要求先执行过make init-services(或至少make start-services后再make init-db),即测试运行前必须存在已初始化 schema 的 Postgres 实例。Python 侧涉及 Postgres 的测试主要位于 python/tests/integration/test_live_node_cache.py,该文件在无法连通 Redis / Postgres 服务时会以Redis and Postgres infrastructure services are not reachable为原因跳过测试(见 L55 附近的reason),并同时覆盖PostgresCacheConfig与RedisCacheConfig两种缓存后端(L212 的pytest.param(PostgresCacheConfig, id="postgres"))。
Rust 侧:直接使用cargo test
POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_USERNAME=nautilus POSTGRES_PASSWORD=pass POSTGRES_DATABASE=nautilus \ cargo test -p nautilus-infrastructure --features postgres -- --test-threads=1这条命令说明了几件关键事实:
- 测试包:
nautilus-infrastructure(crates/infrastructure),其 Cargo.toml 中定义了postgres = ["dep:sqlx"]特性; - 必须显式启用
postgres特性,否则相关测试与代码不会被编译; - 必须串行执行(
--test-threads=1),因为 Postgres 测试共享同一个数据库实例,存在写冲突风险; - 通过环境变量注入连接参数。
四、环境变量如何驱动连接:源码级解析
上面的 Rust 测试命令注入的五个环境变量,正是nautilus-infrastructure连接 Postgres 的标准入口。在 crates/infrastructure/src/sql/pg.rs 的get_postgres_connect_options函数(L163-L191)中,连接参数的优先级为:显式传入的参数 > 环境变量 > 默认值。具体对应关系如下:
| 环境变量 | 含义 | 解析失败行为 |
|---|---|---|
POSTGRES_HOST | 主机地址 | 回退到默认 host |
POSTGRES_PORT | 端口 | 必须能被解析为u16,否则 panic |
POSTGRES_USERNAME | 用户名 | 回退到默认用户名 |
POSTGRES_PASSWORD | 密码 | 回退到默认密码 |
POSTGRES_DATABASE | 数据库名 | 回退到默认数据库 |
连接建立后,connect_pg(同文件 L198-L200)通过PgPool::connect_with创建连接池,而init_postgres(L237 起)则完成更完整的初始化:创建publicschema、创建对应角色的LOGIN用户(若已存在则跳过并记录日志)、将 schema 所有权与数据库所有权移交给该角色,最后执行 schema 目录下的 SQL 文件。
从源码结构看,这套初始化流程同时被 CLI 复用:crates/cli/src/database/postgres.rs 的run_database_command在收到DatabaseCommand::Init时,同样调用get_postgres_connect_options→connect_pg→init_postgres,因此也可以使用 CLI 的database init命令完成相同工作(参考 crates/cli/src/opt.rs 中的DatabaseOpt定义)。
五、Postgres 集成测试的验证依据
仓库中实际的 Postgres 集成测试位于 crates/infrastructure/tests/integration/test_cache_database_postgres.rs。该测试文件在模块级声明了三个#[cfg]门控:
#[cfg(test)] #[cfg(feature = "postgres")] #[cfg(target_os = "linux")] // Databases only tested and supported on Linux即:只有在启用postgres特性、且在 Linux 系统上运行时,测试才会被编译执行——这解释了为什么 .docker/README.md 开头强调 "Postgres integration tests run on Linux when a Postgres instance is available"。测试内容覆盖CashAccount、各类 Instrument(含audusd_sim、crypto_perpetual_ethusdt、futures_contract_es等测试桩)、Order/Position、Signal、自定义数据等对象在PostgresCacheDatabase中的写入与查询,并通过wait_until/wait_until_async等待异步就绪。
另外,仓库 CI 侧的引导脚本 scripts/ci/test-postgres-bootstrap.bash 展示了另一种更完整的验证路径:动态启动一个一次性 Postgres 容器(镜像固定为public.ecr.aws/docker/library/postgres:16.4-alpine),等待pg_isready,用管理员角色执行database init --schema "$PWD/schema/sql"初始化 schema 与角色,再以nautilus用户运行测试,最后还会校验角色创建结果。
六、仅启动 Postgres 或清理环境
只启动 Postgres(不初始化 schema)
如果只想运行数据库容器、稍后再手动初始化,可以使用:
docker compose -f .docker/docker-compose.yml up -d postgres然后从仓库根目录执行make init-db来应用 schema。
停止与清理
| 命令 | 行为 |
|---|---|
make stop-services | 停止全部容器(docker compose down),数据保留在命名卷中 |
make purge-services | 停止并删除容器与卷(docker compose down -v),彻底清除数据 |
两个目标分别对应 Makefile 中的stop-services(L1303-L1306)与purge-services(L1308-L1311)实现。若开发过程中需要彻底重置数据库状态,应在stop-services之后使用purge-services删除命名卷,再重新执行make init-services。
七、其他 Docker 资源
本仓库的 .docker 目录除docker-compose.yml与本文档外,还包含多个可用于构建开发/发布镜像的 Dockerfile:
- .docker/nautilus_trader.dockerfile:主镜像,多阶段构建,基于
rust:1.98.0-slim-bookworm与python:3.13-slim(均按 digest 锁定版本以保障供应链安全),安装 clang、capnproto 等构建依赖; - .docker/DockerfileUbuntu:Ubuntu 开发环境镜像,配合 .docker/entrypoint.sh 使用,entrypoint 会打印 Rust/UV 版本并设置
PYO3_PYTHON环境变量,支持交互式 shell 或直接执行传入命令; - .docker/jupyterlab.dockerfile:JupyterLab 镜像(Makefile 中通过
make docker-build-jupyter构建); - .docker/preload-base-image.dockerfile:基础镜像预热。
这些文件与本文的测试服务编排共同构成了完整的本地开发与集成测试基础设施。
八、常见问题与排查思路
- 测试提示服务不可达:确认容器已启动(
docker compose -f .docker/docker-compose.yml ps),并确认端口映射正常;注意端口只绑定127.0.0.1,若在容器内或远程访问需要自行调整映射。 - schema 未初始化:
make init-db会静默拼接执行 SQL,若报错请检查容器名是否为nautilus-database、用户/数据库是否为nautilus,或直接改用make init-services全流程。 - Rust 测试未运行:检查是否传入了
--features postgres,且当前系统是否为 Linux(测试文件对target_os = "linux"有硬性要求);同时确认五个POSTGRES_*环境变量与 Compose 中的默认凭据一致。 - 重复初始化:
init_postgres对已存在的角色、schema 会记录日志并跳过("already exists" 分支),因此重复执行make init-db是安全的;如需完全重置,使用make purge-services删除卷后重新初始化。
【免费下载链接】nautilus_traderProduction-grade Rust-native trading engine with deterministic event-driven architecture项目地址: https://gitcode.com/GitHub_Trending/na/nautilus_trader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考