ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

使用 Docker 与 Docker Compose 容器化部署 PostGraphile 与 PostgreSQL 实战指南

使用 Docker 与 Docker Compose 容器化部署 PostGraphile 与 PostgreSQL 实战指南 使用 Docker 与 Docker Compose 容器化部署 PostGraphile 与 PostgreSQL 实战指南【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal本文是一份面向 V5 版本的完整 Docker 部署教程讲解如何在本机用 Docker Compose 编排一个 PostgreSQL 数据库容器和一个 PostGraphile GraphQL API 容器从环境准备、SQL 初始化脚本、Dockerfile 与graphile.config.ts配置到镜像构建、容器启动、数据库重建以及自定义wrapPlans插件的完整流程。读完本文你将能够在本机一键拉起一套论坛示例GraphQL API并理解 PostGraphile 容器化部署中连接串、端口、数据卷与插件加载的底层原理。重要提示来自官方文档本指南已针对 PostGraphile V5 更新但尚未经过完整测试。请谨慎操作并在遇到问题时反馈 issues。文中方案已在 Linux、Windows Pro、Windows Home 三种系统上开发和测试。前置要求与 Docker 安装需要准备什么本教程要求在本地工作站安装Docker与Docker Compose。Docker Compose 的价值在于它能通过配置文件一次性编排一组容器网络而不是在命令行里堆砌大量参数——当容器参数很多时命令行会变得冗长且难以阅读这正是 Compose 存在的意义。如果你已经安装 Docker Desktop for Windows它自动附带 Docker Compose无需单独安装。Linux安装 Docker 与 Docker Compose先添加 Docker 官方仓库以 Ubuntu 系为例sudo apt-get update sudo apt-get install apt-transport-https ca-certificates curl software-properties-common curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - sudo add-apt-repository deb [archamd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable安装 Docker 社区版sudo apt-get update sudo apt-get install docker-ce将当前用户加入docker组以获取权限执行后务必重启机器sudo usermod -a -G docker username验证安装。下面的命令会自动下载hello-world镜像如果本地不存在并运行docker run hello-world验证完毕后清理该镜像docker image ls docker rmi -f hello-world接着安装 Docker Composesudo apt install docker-composeWindows Pro安装 Docker Desktop for Windows从官方渠道下载 Docker Desktop for WindowsDocker 社区版 Windows 版本按默认设置安装即可。它自带 Docker Compose。Windows Home安装 Docker Toolbox for WindowsWindows Home 无法运行 Docker Desktop 的 Hyper-V 方案可改用 Docker Toolbox for Windows同样按默认设置安装也会自动附带 Docker Compose。注意在 Windows Home 的 Docker Toolbox 环境下容器地址不再是localhost而是 Docker Machine 的 IP。可用docker-machine ip default命令获取该 IP见下文运行容器章节的地址对照表。创建 PostgreSQL 数据库容器编写.env环境变量文件在仓库根目录新建.env文件Docker 会把它作为环境变量注入容器。本教程中数据库容器用到了三个关键变量POSTGRES_DBPostgreSQL 容器启动时要创建的数据库名POSTGRES_USER数据库初始化时创建的默认管理员用户POSTGRES_PASSWORD默认管理员用户的密码。# DB # Parameters used by db container POSTGRES_DBforum_example POSTGRES_USERpostgres POSTGRES_PASSWORDchange_me建议更安全的管理方式是用 Docker Secrets 管理数据库密码避免明文写入配置文件。编写数据库初始化 SQL新建db目录存放数据库容器所需文件再在其中新建db/init子目录存放 SQL 初始化脚本。PostgreSQL 在首次初始化数据库时会按文件名的顺序依次执行init目录下的所有内容——这是官方postgres镜像的docker-entrypoint-initdb.d约定。本教程以一个简单论坛为例数据库包含user与post两张表二者是一对多关系一个用户可有多篇帖子post.author_id作为外键引用user.id。创建db/init/00-database.sql定义表结构\connect forum_example; /*Create user table in public schema*/ CREATE TABLE public.user ( id SERIAL PRIMARY KEY, username TEXT, created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); COMMENT ON TABLE public.user IS Forum users.; /*Create post table in public schema*/ CREATE TABLE public.post ( id SERIAL PRIMARY KEY, title TEXT, body TEXT, created_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP, author_id INTEGER NOT NULL REFERENCES public.user(id) ); COMMENT ON TABLE public.post IS Forum posts written by a user.;再创建db/init/01-data.sql填充示例数据\connect forum_example; /*Create some dummy users*/ insert into public.user (username) values (Benjie), (Singingwolfboy), (Lexius); /*Create some dummy posts*/ insert into public.post (title, body, author_id) values (First post example, Lorem ipsum dolor sit amet, 1), (Second post example, Consectetur adipiscing elit, 2), (Third post example, Aenean blandit felis sodales, 3);编写 PostgreSQL DockerfileDockerfile 是构建 Docker 镜像的蓝图容器则由镜像创建而来。官方 PostgreSQL 镜像的 Dockerfile 极其简单。在db目录注意不是db/init下新建DockerfileFROM postgres:14-alpine COPY ./init/ /docker-entrypoint-initdb.d/第一行FROM postgres:14-alpine基于运行在 Alpine Linux 上的官方 PostgreSQL 镜像构建第二行COPY ./init/ /docker-entrypoint-initdb.d/把初始化 SQL 复制进容器内的docker-entrypoint-initdb.d目录。PostgreSQL 初始化数据库时会读取该目录并执行其中的全部内容按文件名排序。编写 Docker Compose 编排文件在仓库根目录新建docker-compose.ymlversion: 3.3 services: db: container_name: forum-example-db restart: always image: forum-example-db build: context: ./db volumes: - db:/var/lib/postgresql/data env_file: - ./.env networks: - network ports: - 5432:5432 networks: network: volumes: db:参数说明参数说明dbDocker Compose 中服务的名称。container_name容器名称。image用于运行容器的镜像名称。build提供 build context 时Docker Compose 会用 context 目录中的 Dockerfile 构建自定义镜像。context指定查找 Dockerfile 以构建镜像的目录。volumesDocker 卷与容器内 PostgreSQL 数据目录的映射格式为docker_volume:container_folder。container_folder中生成的所有文件都会写入docker_volume从而在容器停止/重启后保留数据。首次运行 db 容器时 Docker 会自动创建该卷。env_file容器环境变量配置文件的路径即上文 .env 文件。networks网络用于把一组容器归入同一网络并相互连接。ports宿主机端口与容器端口的映射格式为host_port:container_port。command容器启动后要执行的命令每个参数需单独占一个列表项。此时仓库结构应为/ ├─ db/ │ ├─ init/ │ │ ├─ 00-database.sql │ │ └─ 01-data.sql │ └─ Dockerfile ├─ .env └─ docker-compose.yml创建 PostGraphile 容器扩展环境变量添加 DATABASE_URL更新.env追加DATABASE_URLPostGraphile 将用它连接 PostgreSQL 数据库[...] # GRAPHQL # Parameters used by graphql container DATABASE_URLpostgres://postgres:change_medb:5432/forum_example注意DATABASE_URL的语法为postgres://user:passworddb:5432/db_name。其中主机名写的是db——这正是 docker-compose 中数据库服务的名称。在 Compose 创建的自定义网络network内服务名db会作为容器间可解析的 DNS 主机名因此 PostGraphile 容器无需知道数据库容器的 IP 就能连接它。创建 graphql 目录与 npm 配置新建graphql目录存放 PostGraphile 容器所需的文件。先创建package.json与其锁文件如package-lock.json安装 PostGraphile{ name: postgraphile-docker, private: true, type: module, dependencies: { postgraphile: ^5.0.0 } }注意type: module与 V5 的 ESM 生态保持一致private: true表明该包不用于发布。创建 graphile.config.ts 配置文件PostGraphile V5 采用基于 preset预设的配置体系。创建graphql/graphile.config.tsimport { PostGraphileAmberPreset } from postgraphile/presets/amber; import { makePgService } from postgraphile/adaptors/pg; export default { extends: [PostGraphileAmberPreset], pgServices: [makePgService({ connectionString: process.env.DATABASE_URL })], grafserv: { host: 0.0.0.0, port: 5678, }, };配置拆解PostGraphileAmberPresetPostGraphile V5 的官方推荐预设extends继承它即可获得全套默认行为。从源码可见它聚合了QueryQueryPlugin、PgBasicsPlugin、PgIntrospectionPlugin、PgTablesPlugin、PgAllRowsPlugin、PgRelationsPlugin、PgMutationCreatePlugin、PgMutationUpdateDeletePlugin、NodePlugin等一系列插件的顺序编排见 amber.ts并挂载SwallowErrorsPlugin统一吞并记录但不抛出操作执行中的错误makePgService来自postgraphile/adaptors/pg导出路径映射到dataplan/pg的 pg 适配器负责根据连接串创建 PostgreSQL 服务配置。其接口定义于 pgServices.tsPgAdaptor.makePgService接收connectionString等选项并返回PgServiceConfiguration。这里直接读取容器环境变量process.env.DATABASE_URL而该变量由 compose 的env_file注入grafserv.host/grafserv.portHTTP 服务监听地址与端口。0.0.0.0表示监听容器内所有网卡这样宿主机才能通过端口映射访问到容器里的服务。端口5678与后面 Dockerfile 的EXPOSE 5678以及 compose 的5678:5678相互对应。创建 PostGraphile Dockerfile在graphql目录新建DockerfileFROM node:24-alpine LABEL descriptionInstant high-performance GraphQL API for your PostgreSQL database https://github.com/graphile/postgraphile # Set app folder WORKDIR /app # Install dependencies COPY package.json package-lock.json ./ RUN npm install # Copy config and plugins COPY graphile.config.ts ./ COPY plugins ./plugins EXPOSE 5678 ENTRYPOINT [npx, --no-install, postgraphile]要点基于node:24-alpine运行 PostGraphile。仓库中 PostGraphile 的 package.json 声明engines: { node: 22 }见 package.jsonNode 24 完全满足要求先复制package.json与锁文件并npm install利用镜像分层缓存加速后续构建COPY plugins ./plugins为可选的插件目录预留挂载点插件内容见下文添加自定义插件一节EXPOSE 5678声明容器对外端口ENTRYPOINT [npx, --no-install, postgraphile]容器启动后执行 PostGraphile CLI。--no-install强制 npx 只使用本地已安装的postgraphile避免它去网络下载这正是前面必须生成package-lock.json的原因——没有锁文件时npm install可能生成不一致的依赖树也可能导致 npx 行为不可预期。从 CLI 源码cli.ts可以看到postgraphile命令会加载graphile.config.ts中的 presetloadConfig解析pgServices后通过 grafserv 创建 HTTP 服务器若未配置任何 presetCLI 会提示使用--preset postgraphile/presets/amber并退出。更新 docker-compose.yml 加入 GraphQL 服务在docker-compose.yml的services段追加graphql服务version: 3.3 services: db: [...] graphql: container_name: forum-example-graphql restart: always image: forum-example-graphql build: context: ./graphql env_file: - ./.env depends_on: - db networks: - network ports: - 5678:5678 [...]新增配置解读depends_on: - db声明 graphql 服务依赖 db 服务Compose 会先启动数据库容器再启动 GraphQL 容器两个容器共享network网络graphql 容器通过服务名db访问 PostgreSQL对应DATABASE_URL中的主机名5678:5678把容器内 5678 端口映射到宿主机 5678 端口与graphile.config.ts中的grafserv.port及 Dockerfile 的EXPOSE保持一致。此时完整仓库结构为/ ├─ db/ │ ├─ init/ │ │ ├─ 00-database.sql │ │ └─ 01-data.sql │ └─ Dockerfile ├─ graphql/ │ ├─ graphile.config.ts │ ├─ package.json │ ├─ package-lock.json │ ├─ plugins/ │ └─ Dockerfile ├─ .env └─ docker-compose.yml构建镜像并运行容器构建镜像在仓库根目录执行# Build images for all services in docker-compose.yml docker-compose build # You can also build images one by one # For instance you can build the database image like this docker-compose build db # And build the graphql image like this docker-compose build graphql运行容器# Run containers for all services in docker-compose.yml docker-compose up # Run containers as daemon (in background) docker-compose up -d # Run only the database container as daemon docker-compose up -d db # Run only the GraphQL container as daemon docker-compose up -d graphql首次运行数据库容器时Docker 会自动创建一个数据卷用于持久化数据库数据卷名自动命名为your_repository_name_db。各容器访问地址如下容器Docker on Linux / Windows ProDocker on Windows HomeGraphQL API 文档http://localhost:5678/graphiqlhttp://your_docker_machine_ip:5678/graphiqlGraphQL APIhttp://localhost:5678/graphqlhttp://your_docker_machine_ip:5678/graphqlPostgreSQL 数据库host:localhost, port:5432host:your_docker_machine_ip, port:5432若在 Windows Home 上运行 Docker Toolbox可用docker-machine ip default获取 Docker Machine 的 IP 地址。数据库重建重新初始化初始化 SQL 只在数据库卷为空时执行一次。如果你修改了db/init下的文件需要删除数据卷与数据库镜像并重建改动才会生效# Stop running containers docker-compose down # List Docker volumes docker volume ls # Delete volume docker volume rm your_repository_name_db # Delete database image to force rebuild docker rmi db # Run containers (will automatically rebuild the image) docker-compose updocker-compose up会检测到 db 镜像已不存在而自动重建数据卷删除后PostgreSQL 初始化脚本会在新卷上重新执行从而应用你修改过的 schema 与数据。添加自定义插件wrapPlans本节为可选内容演示如何包装 PostGraphile 生成的 plan 以定制行为——这正是 V5 中替代 V4 wrap resolver 的官方推荐方式对应旧版makeWrapResolversPlugin见仓库 wrap-plans.md 与 customization-overview.md。新建graphql/plugins目录并添加wrap-plans.tsimport { sideEffect } from postgraphile/grafast; import { wrapPlans } from postgraphile/utils; export default wrapPlans({ Mutation: { createUser(plan) { const $result plan(); const $user $result.get(user); sideEffect($user, (user) { console.info(Created user:, user?.username); }); return $result; }, }, });代码解析wrapPlans来自postgraphile/utils其导出路径映射到graphile-utils。它接收一个按类型名 - 字段名分组的规则对象为匹配的字段包装 plan resolver从实现上看makeWrapPlansPlugin.ts每个包装函数接收plan原始 plan resolver、$source、fieldArgs、info等参数返回替换后的 plan。wrapPlans()调用后会生成一个 PostGraphile 插件对象Mutation.createUser包装 PostGraphile 为createUser变更自动生成的 plan。先调用原始plan()拿到结果 step$result再通过$result.get(user)取出其中的user字段 step$usersideEffect来自postgraphile/grafast导出Grafast 步骤库。从源码sideEffect.ts可见它创建一个SideEffectStep该 step 将上游值逐个喂给回调函数并在构造时设置this.hasSideEffects true确保其副作用不会被 Grafast 计划优化器随意剪裁或合并allowMultipleOptimizations false。这里的回调在创建用户后把用户名打印到日志最后返回$result保持变更原有的返回结构不变只是顺带加了日志副作用。随后更新graphile.config.ts导入并注册该插件import { PostGraphileAmberPreset } from postgraphile/presets/amber; import { makePgService } from postgraphile/adaptors/pg; import WrapPlansPlugin from ./plugins/wrap-plans.ts; export default { extends: [PostGraphileAmberPreset], pgServices: [makePgService({ connectionString: process.env.DATABASE_URL })], plugins: [WrapPlansPlugin], };最后重建并重启 GraphQL 容器# Shut down containers docker-compose down # Rebuild the GraphQL container docker-compose build graphql # Rerun containers docker-compose up由于graphql/Dockerfile中有COPY plugins ./plugins插件目录会被打进镜像重建后执行createUser变更时容器终端即可看到插件打印的日志。查询与变更示例查询获取全部帖子及其作者query { allPosts { nodes { id title body userByAuthorId { username } } } }allPosts来自 Amber 预设中的PgAllRowsPluginuserByAuthorId则是PgRelationsPlugin依据post.author_id外键自动生成的关联字段——PostGraphile 会为外键关系自动生成通过作者查用户的嵌套查询入口无需手写任何 resolver。变更创建新用户mutation { createUser(input: { user: { username: Bob } }) { user { id username createdDate } } }createUser由PgMutationCreatePlugin依据user表自动生成createdDate对应created_date列V5 默认采用 camelCase 命名。执行此变更时如果已按上文加载了wrap-plans.ts插件容器日志中会打印Created user: Bob。排查与提示首次启动顺序depends_on只保证容器启动顺序PostGraphile 连接数据库通常在 db 初始化完成后才能成功若 GraphQL 容器先于数据库初始化完成就绪restart: always会让它持续重试。端口冲突宿主机 5432PostgreSQL或 5678GraphQL已被占用时可修改 compose 左侧的host_port例如5679:5678后重新docker-compose up。数据持久化与重建数据库数据存放在命名卷your_repository_name_db中需要清库重来时按上文数据库重建章节操作即可删除镜像与卷不会影响宿主机其他目录。配置文件与 CLI 的关系容器内通过npx --no-install postgraphile启动 CLICLI 会加载graphile.config.ts中的 preset你也可以直接用--preset postgraphile/presets/amber --connection 连接串 --port 5678等参数运行CLI 支持的完整选项见 cli.ts包括--connection/-c、--schema/-s、--watch/-w、--subscriptions、--allow-explain/-e等两者可以互相替代或叠加。【免费下载链接】crystal Graphiles Crystal Monorepo; home to Grafast, PostGraphile, pg-introspection, pg-sql2 and much more!项目地址: https://gitcode.com/gh_mirrors/cry/crystal创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表