在Docker Compose中指定PostgreSQL参数以进行健康检查的安全方法

26

我正在使用 secrets 管理用户名、密码和数据库名称,在使用 Postgres 作为数据库的 Docker stack 中。现在,我想使用 Docker 提供的健康检查功能。

docker-compose.yml

x-db-secrets: &db_secrets
    - psql_user
    - psql_pass
    - psql_dbname

services:
  db:
    image: postgres:13.1
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - POSTGRES_USER_FILE=/run/secrets/psql_user
      - POSTGRES_DB_FILE=/run/secrets/psql_dbname
      - POSTGRES_PASSWORD_FILE=/run/secrets/psql_pass
    secrets: *db_secrets
    healthcheck:
      test: pg_isready -U myuser -d db_prod
      interval: 10s
      timeout: 3s
      retries: 3

(... other services...)

volumes:
  postgres_data:
  static_content:
  media_content:

secrets:
  psql_user:
    external: true
  psql_pass:
    external: true
  psql_dbname:
    external: true

正如在healthcheck:部分中所述,我正在使用健康检查公开数据库用户名和数据库名称。我的问题(根据答案进行了一些后续):

  • 这重要吗? docker-compose.yml文件不在将运行此代码的主机上。因此,潜在攻击者将无法从那里访问此信息...但是,由于docker将执行健康检查,因此可能会在该容器中留下痕迹(shell?存储该过程的docker配置文件?)
  • 如果很重要,我怎样才能在不暴露该信息的情况下执行健康检查?我必须直接使用secrets,但我无法弄清楚如何在docker-compose中使用secrets...

想法?解决方法?

其他详细信息:

  • 我目前正在检查(flask)容器是否能够使用它。例如,flask服务会检查是否可以ping DB。但是,我宁愿对事物进行隔离,而DB容器应该是告诉它是否健康的人(im o)。


将您的凭据写入容器中的 .pgpass 文件 - larsks
4个回答

36

您可以使用pg_isready而无需任何用户名/密码来检查容器是否处于“健康”状态。

  bbepostgres:
    image: postgres:14.2
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres
      - POSTGRES_DB=pangea_local_dev
      - PGUSER=postgres
    healthcheck:
      test: ["CMD-SHELL", "pg_isready", "-d", "db_prod"]
      interval: 30s
      timeout: 60s
      retries: 5
      start_period: 80s  

这将会做和你想要的一样的事情。

参考: https://github.com/peter-evans/docker-compose-healthcheck


我认为这确实有效。但是,如果需要执行更多涉及的检查,它仍然能够工作吗?无论如何,这将是一个可以接受的答案。我接受另一个答案,因为它更加通用。 - logicOnAbstractions
4
为什么在环境变量中需要 PGUSER?另外,为什么要在 db_prod 上执行 pg_ready 而不是 pangea_local_dev - tuk
1
PGUSER 的原因在这里有解释。 - tuk
超时持续时间不应该这么长。我觉得不应该超过10秒。 - Pravind Kumar

28

可以通过使用 .env 文件并略微修改您的 docker-compose.yml 文件来实现此操作。

POSTGRES_HOST=db
POSTGRES_USER=root
POSTGRES_PASSWORD=password
POSTGRES_DB=dev
services:
  db:
    image: postgres:13.1
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    env_file:
      - .env
    secrets: *db_secrets
    healthcheck:
      test: ["CMD-SHELL", "sh -c 'pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}'"]
      interval: 10s
      timeout: 3s
      retries: 3

@parity3 这不是真的:当 pg_isready 无法连接时,它会以状态 2 退出。即使它确实 exit 0,在 || 后面的代码也不会被调用。 - Zubin
你可能是对的,但我已经继续前进了。我可能有些困惑。我们需要让其他人来验证。感谢你澄清了我的评论可能不准确的事实。 - parity3
1
pg_ready 不适用于特定于数据库的健康检查 - 它无论数据库是否存在或凭据是否正确,都会返回 0。请参见: https://dev59.com/1Mn6oIgBc1ULPQZFZVpj - L.M
文件一定要命名为.env吗?我可以将其命名为.env.dev.env.prod吗?当我运行docker compose convert时,环境变量没有被加载,除非文件名是.env。如果我想为开发和生产使用不同的.env文件,这似乎是个问题。 - Jarad
嘿 @Jarad,Docker文档中指定了: https://docs.docker.com/compose/environment-variables/set-environment-variables/#substitute-with---env-file,你可以使用CLI来替换环境变量,例如 docker compose --env-file ./config/.env.dev up。我不确定除了.env之外是否可以使用其他文件后缀名的env_file属性,但我知道你可以将它们命名为 prod.envdev.env等。如果这样做不起作用,至少CLI给你提供了一个选项,然后您可以将其与CI / CD工作流程结合使用。希望能帮到你。 - Josh Dando
显示剩余2条评论

1

由于Docker套接字在主机上,因此可以通过docker inspect查看healthcheck命令,因此只有当密码可以从用户名和/或数据库名中检索时,它才(真正)重要。

有人可能会说它总是很重要,但这取决于您的需求。


0

如上所述,您可以使用pg_isready

我还添加了一个“虚拟”容器来等待多个容器,例如postgresmysql

version: "3.2"

services:
  postgres-db:
    image: postgres:13
    restart: always
    environment:   
      POSTGRES_PASSWORD: password   
    ports:
      - "5432:5432"
    networks:
      - default
    healthcheck:
      test: pg_isready -U postgres
    enter code here

  mysql-db:
    platform: linux/x86_64
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
    ports:
      - "3306:3306"
    healthcheck:
      test: 'mysql --user=root --password=password --execute "SHOW DATABASES;"'
    
  ready:
    image: hello-world
    depends_on:
      postgres-db:
        condition: service_completed_successfully
      mysql-db:
        condition: service_healthy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接