如何在Postgres容器的Dockerfile中从共享卷复制SQL脚本?
问题描述
我有两个容器,第一个容器会生成一个文件,第二个Postgres容器需要使用该文件,二者通过共享卷持久化这个文件。请问如何在第二个容器的Dockerfile中使用COPY指令复制该文件?这是一个SQL脚本,若放置在正确位置,Postgres启动时会自动执行它。
现有配置
compose.yml
services: fetch: build: fetch/. env_file: fetch/.env volumes: - data:/dump/data db: build: db/. restart: always environment: POSTGRES_PASSWORD: postgres ports: - "5432:5432" volumes: - data:/dump/data depends_on: fetch: condition: service_completed_successfully volumes: data:
Postgres容器的Dockerfile
FROM postgres:latest # copy in form of volume_name:path/file destination COPY data:/dump/data/restore.sql /docker-entrypoint-initdb.d/ EXPOSE 5432 CMD ["postgres"]
问题分析
你当前的Dockerfile写法是错误的:Dockerfile中的COPY指令在镜像构建阶段执行,这个阶段完全独立于容器运行时,无法访问Docker卷(卷是容器启动后才挂载的),所以根本没办法从data卷中复制文件。
解决方案
不需要在Dockerfile里用COPY,直接利用Postgres的初始化机制和Docker卷的挂载特性,有两种常用方法:
方法一:直接挂载卷到Postgres初始化目录
修改compose.yml中db服务的volumes配置,把共享卷中存放SQL脚本的目录直接挂载到Postgres的初始化目录/docker-entrypoint-initdb.d/(Postgres启动时会自动执行这个目录下的SQL脚本):
services: # ... fetch服务保持不变 db: build: db/. restart: always environment: POSTGRES_PASSWORD: postgres ports: - "5432:5432" volumes: - data:/dump/data # 保留原挂载,可选(如果不需要在db容器里访问原路径的话可以去掉) - data:/docker-entrypoint-initdb.d/ # 新增:把卷挂载到初始化目录 depends_on: fetch: condition: service_completed_successfully
注意:如果fetch容器生成的restore.sql存放在卷的/dump/data子目录下,可调整挂载路径为data:/dump/data:/docker-entrypoint-initdb.d/,这样该目录下的所有脚本都会被执行。
方法二:通过启动脚本复制文件
如果需要保留原卷挂载路径,或者需要对文件做额外处理,可以自定义启动脚本:
- 在Postgres容器的构建目录下创建
entrypoint.sh脚本:
#!/bin/bash # 复制卷中的SQL脚本到初始化目录 cp /dump/data/restore.sql /docker-entrypoint-initdb.d/ # 调用Postgres官方的入口脚本,保证原初始化流程正常执行 exec docker-entrypoint.sh "$@"
- 修改Postgres的Dockerfile:
FROM postgres:latest # 复制自定义启动脚本 COPY entrypoint.sh /usr/local/bin/ # 赋予执行权限 RUN chmod +x /usr/local/bin/entrypoint.sh EXPOSE 5432 # 使用自定义入口脚本 ENTRYPOINT ["entrypoint.sh"] CMD ["postgres"]
compose.yml保持原配置即可,容器启动时共享卷已经挂载,脚本能正常读取到/dump/data/restore.sql并复制到初始化目录。
注意事项
- 确保
fetch容器生成的restore.sql文件权限正确,Postgres容器内的postgres用户需要有读取权限(可在fetch容器生成文件时设置权限为644)。 - Postgres的初始化脚本只会在第一次启动(数据库未初始化时)执行,如果需要每次启动都执行,需调整初始化逻辑或使用其他方式。
内容的提问来源于stack exchange,提问作者sam
相关产品推荐
相关产品推荐

