You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

M1 Mac下Docker run与Compose的Postgres-API连接差异排查

问题描述

我在M1 Mac上通过Docker运行Postgres 14.2镜像,端口映射为5432:5432,主机端应用可通过localhost:5432连接数据库。现在尝试将API应用部署到Docker中,出现异常情况:

可行的docker run命令

使用以下命令可成功连接数据库:

docker run --name api --add-host host.docker.internal:host-gateway -e DB_HOST=host.docker.internal -p 8000:8000

第一种docker-compose配置(连接失败)

复制相同配置到docker-compose却无法连接:

services:
  postgres:
    image: postgres:14.2
    ports:
      - "5432:5432"
    networks:
      - my-network
  
  api:
    image: api
    environment:
      DB_HOST: host.docker.internal
    extra_hosts:
      - "host.docker.internal:host-gateway"

报错信息:

failed to connect to host=host.docker.internal user=postgres database=postgres: failed to receive message (unexpected EOF)

第二种docker-compose配置(连接失败)

尝试将API容器加入Postgres所在的my-network网络,将DB_HOST改为容器名:

api:
    image: api
    environment:
      DB_HOST: postgres
    networks:
      - my-network

报错信息:

failed to connect to host=postgres user=postgres database=postgres: dial error (dial tcp 192.168.192.2:5432: connect: connection refused)

已知数据库监听地址为IPv4 address "0.0.0.0", port 5432和IPv6 address "::", port 5432,为何docker run命令可行,而另外两种docker-compose配置却无法正常连接?


原因分析与解决方案

1. docker run可行的核心原因

docker run启动的API容器默认使用Docker默认的bridge网络,通过--add-host host.docker.internal:host-gateway配置后,host.docker.internal会指向宿主机网关地址。此时API容器通过宿主机的端口映射5432:5432访问Postgres,和主机端应用的连接逻辑完全一致,因此可以成功建立连接。

2. 第一种docker-compose配置失败的原因

你的Postgres容器加入了自定义网络my-network,但API容器未指定网络,默认会使用docker-compose自动创建的项目名_default网络。此时API容器的host.docker.internal虽指向宿主机,但受M1 Mac的Docker网络特性限制,或Postgres的pg_hba.conf访问控制规则影响,通过宿主机转发连接时出现EOF错误。

修复方案:给API容器也指定my-network网络,确保网络环境一致:

services:
  postgres:
    image: postgres:14.2
    ports:
      - "5432:5432"
    networks:
      - my-network
  
  api:
    image: api
    environment:
      DB_HOST: host.docker.internal
    extra_hosts:
      - "host.docker.internal:host-gateway"
    networks:
      - my-network  # 添加该行,让API容器和Postgres在同一网络

3. 第二种docker-compose配置失败的原因

这种容器间直接通信的方式报错connection refused,通常由两个问题导致:

  • Postgres未完全就绪:API容器启动速度快于Postgres,尝试连接时数据库服务还未初始化完成。
  • Postgres访问控制限制:虽然监听地址设为0.0.0.0,但pg_hba.conf可能仅允许本地(127.0.0.1)或宿主机IP段连接,拒绝了Docker自定义网络的IP段(如192.168.192.0/24)。

修复方案:

方案A:添加健康检查与启动依赖

确保Postgres完全就绪后再启动API:

services:
  postgres:
    image: postgres:14.2
    ports:
      - "5432:5432"
    networks:
      - my-network
    environment:
      POSTGRES_PASSWORD: your_db_password  # 必须设置密码,否则Postgres可能拒绝连接
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 5s
      timeout: 5s
      retries: 5
  
  api:
    image: api
    environment:
      DB_HOST: postgres
      DB_PASSWORD: your_db_password  # 和Postgres配置的密码一致
    networks:
      - my-network
    depends_on:
      postgres:
        condition: service_healthy  # 等待Postgres健康检查通过后再启动

方案B:修改Postgres访问控制规则

在pg_hba.conf中添加允许Docker自定义网络连接的规则:

  1. 本地创建pg_hba.conf文件,添加一行:
host    all             all             192.168.0.0/16          scram-sha-256
  1. 在docker-compose中挂载该配置文件:
services:
  postgres:
    image: postgres:14.2
    ports:
      - "5432:5432"
    networks:
      - my-network
    environment:
      POSTGRES_PASSWORD: your_db_password
    volumes:
      - ./pg_hba.conf:/var/lib/postgresql/data/pg_hba.conf  # 挂载自定义配置

内容的提问来源于stack exchange,提问作者BeetleJuice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 22:05:46