内容提要
为用 pgrx 构建 PostgreSQL 扩展的 Rust 项目配置 VS Code 开发容器时,需解决三个问题:PostgreSQL 拒绝以 root 运行,应创建非 root 用户;以 root 安装 cargo-pgrx 会导致缓存目录权限错误,应提前设置 CARGO_HOME;镜像构建时生成的 pgrx config.toml 会被挂载覆盖,应在 postCreateCommand 中运行 cargo pgrx init。
延伸解读
非 root 用户:不仅是安全要求
PostgreSQL 设计上拒绝以 root 运行,因此 pgrx 的集成测试(cargo pgrx test)会启动 PostgreSQL 实例,若容器以 root 运行则测试失败。作者最初在 devcontainer.json 中设置 remoteUser: "root" 并安装所有内容,结果构建时测试报错。解决方案是提前创建并使用非 root 用户。这提醒我们,设计容器用户时需考虑运行时行为,而不仅仅是构建阶段。
Cargo 工具安装顺序与权限陷阱
官方 Rust 镜像将 cargo 全局路径设为 /usr/local/cargo。若以 root 安装 cargo-pgrx 等工具,会在该目录生成 root 拥有的缓存和注册表文件。之后切换到非 root 用户时,该用户无法写入这些位置,导致权限错误。正确做法是提前设置 CARGO_HOME 和 PATH 指向非 root 用户的家目录(如 /home/vscode/.cargo),确保下载、缓存和二进制文件都属于运行时用户。
初始化时机:构建时 vs 运行时
作者最初在 Docker 构建阶段运行 cargo pgrx init,生成 /home/vscode/.pgrx/config.toml。但启动 devcontainer 时,挂载或覆盖(如绑定挂载、工作区文件夹)可能隐藏该文件,尤其在 Windows 上路径解析脆弱。最终方案是在 postCreateCommand 中运行 cargo pgrx init,让初始化在容器运行时进行,运行时用户可写入家目录。这强调了某些工具必须在运行时初始化,而非镜像构建时。
测试容器与 Docker 套接字复用
如果测试套件使用 testcontainers 启动 Docker 容器进行集成测试,devcontainer 内需要 Docker 访问权限。作者倾向于通过 docker-outside-of-docker 特性复用宿主机的 Docker 套接字,而非 Docker-in-Docker,以简化主机配置并避免嵌套 Docker 的复杂性。同时需确保容器用户有权访问 Docker 套接字,在 Linux 上通常意味着将用户加入 docker 组。
Q&A
为什么在开发容器中以 root 用户运行 pgrx 测试会失败?
PostgreSQL 设计上不允许以 root 用户运行。cargo pgrx test 会启动 PostgreSQL 实例进行集成测试,如果开发容器以 root 运行,测试将失败。
如何正确设置 CARGO_HOME 以避免 cargo-pgrx 的权限错误?
应在安装 cargo-pgrx 之前设置 CARGO_HOME 环境变量,例如指向非 root 用户的家目录(如 /home/vscode/.cargo),并确保该目录由非 root 用户拥有。这样下载的缓存和二进制文件都属于该用户,避免权限错误。
为什么在镜像构建时运行 cargo pgrx init 会导致 config.toml 找不到?
在镜像构建时运行 cargo pgrx init 会生成 /home/vscode/.pgrx/config.toml,但启动开发容器时,挂载(如绑定挂载)可能会覆盖该目录,导致容器内看不到该文件。
如何正确初始化 pgrx 配置以避免 config.toml 被覆盖?
应在 postCreateCommand 中运行 cargo pgrx init,这样初始化发生在容器启动后,运行时用户可以写入其家目录,避免被挂载覆盖。
在开发容器中如何让测试套件访问 Docker 来运行 testcontainers?
可以使用 docker-outside-of-docker 开发容器特性来复用宿主机的 Docker 套接字,而不是运行 Docker-in-Docker。同时确保容器用户有权限访问 Docker 套接字(例如在 Linux 上将用户加入 docker 组)。
为 pgrx 配置开发容器时,需要解决哪三个核心问题?
三个核心问题是:1) PostgreSQL 拒绝以 root 运行,应创建非 root 用户;2) 以 root 安装 cargo-pgrx 会导致缓存目录权限错误,应提前设置 CARGO_HOME;3) 镜像构建时生成的 pgrx config.toml 会被挂载覆盖,应在 postCreateCommand 中运行 cargo pgrx init。