如何定制官方PostgreSQL Docker镜像的配置文件?

213

我正在使用官方Postgres Docker镜像,尝试自定义其配置。为此,我使用sed命令来更改max_connections,例如:

sed -i -e"s/^max_connections = 100.*$/max_connections = 1000/" /var/lib/postgresql/data/postgresql.conf

我尝试了两种方法来应用这个配置信息:
  • 第一种是将命令添加到脚本中,并将其复制到init文件夹:/docker-entrypoint-initdb.d
  • 第二种方法是通过“RUN”命令在我的Dockerfile中直接运行命令(这个方法与一个非官方的PostgreSQL镜像使用不同路径 /etc/postgres/... 的配置文件正常工作)。
无论哪种方法,由于缺少配置文件(我认为它还没有被创建),更改都失败了。
我应该如何更改配置?
下面是用于创建镜像的Dockerfile:
# Database (http://www.cs3c.ma/)

FROM postgres:9.4
MAINTAINER Sabbane <contact@cs3c.ma>

ENV TERM=xterm

RUN apt-get update
RUN apt-get install -y nano

ADD scripts /scripts
# ADD scripts/setup-my-schema.sh /docker-entrypoint-initdb.d/

# Allow connections from anywhere.
RUN sed -i -e"s/^#listen_addresses =.*$/listen_addresses = '*'/" /var/lib/postgresql/data/postgresql.conf
RUN echo "host    all    all    0.0.0.0/0    md5" >> /var/lib/postgresql/data/pg_hba.conf

# Configure logs
RUN sed -i -e"s/^#logging_collector = off.*$/logging_collector = on/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_directory = 'pg_log'.*$/log_directory = '\/var\/log\/postgresql'/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_filename = 'postgresql-\%Y-\%m-\%d_\%H\%M\%S.log'.*$/log_filename = 'postgresql_\%a.log'/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_file_mode = 0600.*$/log_file_mode = 0644/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_truncate_on_rotation = off.*$/log_truncate_on_rotation = on/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_rotation_age = 1d.*$/log_rotation_age = 1d/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_min_duration_statement = -1.*$/log_min_duration_statement = 0/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_checkpoints = off.*$/log_checkpoints = on/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_connections = off.*$/log_connections = on/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_disconnections = off.*$/log_disconnections = on/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^log_line_prefix = '\%t \[\%p-\%l\] \%q\%u@\%d '.*$/log_line_prefix = '\%t \[\%p\]: \[\%l-1\] user=\%u,db=\%d'/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_lock_waits = off.*$/log_lock_waits = on/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#log_temp_files = -1.*$/log_temp_files = 0/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#statement_timeout = 0.*$/statement_timeout = 1800000        # in milliseconds, 0 is disabled (current 30min)/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^lc_messages = 'en_US.UTF-8'.*$/lc_messages = 'C'/" /var/lib/postgresql/data/postgresql.conf

# Performance Tuning
RUN sed -i -e"s/^max_connections = 100.*$/max_connections = 1000/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^shared_buffers =.*$/shared_buffers = 16GB/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#effective_cache_size = 128MB.*$/effective_cache_size = 48GB/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#work_mem = 1MB.*$/work_mem = 16MB/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#maintenance_work_mem = 16MB.*$/maintenance_work_mem = 2GB/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#checkpoint_segments = .*$/checkpoint_segments = 32/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#checkpoint_completion_target = 0.5.*$/checkpoint_completion_target = 0.7/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#wal_buffers =.*$/wal_buffers = 16MB/" /var/lib/postgresql/data/postgresql.conf
RUN sed -i -e"s/^#default_statistics_target = 100.*$/default_statistics_target = 100/" /var/lib/postgresql/data/postgresql.conf


VOLUME ["/var/lib/postgresql/data", "/var/log/postgresql"]

CMD ["postgres"]

使用这个Dockerfile进行构建过程时,出现了错误:

sed: 无法读取 /var/lib/postgresql/data/postgresql.conf: 没有那个文件或目录


1
一种方法是使用官方镜像,启动它,内部连接使用 docker exec -it container_id bash,然后进行修改,最后使用 docker commit container_id myuser/myimage_myPostegresql:myversion 进行提交。请参考文档 http://docs.docker.com/reference/commandline/cli/#commit - user2915097
2
我认为Docker范式的一个优点是自动化整个构建过程。正如另一张镜像paintedfox/postgresql所提到的,可以直接在Dockerfile中更改配置。我认为这也应该适用于官方镜像。 - Sabbane
sed方法应该可以工作,你能否发布你的Dockerfile或完整的复现程序? - user2915097
11个回答

167

使用Docker Compose

在使用Docker Compose时,您可以在docker-compose.yml中使用command: postgres -c option=value来配置Postgres。

例如,这将使Postgres记录到文件中:

command: postgres -c logging_collector=on -c log_destination=stderr -c log_directory=/logs

参考 Vojtech Vitek的回答,您可以使用以下方法:

command: postgres -c config_file=/etc/postgresql.conf

要更改Postgres将使用的配置文件,您需要使用卷挂载自定义配置文件:

volumes:
   - ./customPostgresql.conf:/etc/postgresql.conf

这是我的应用程序的 docker-compose.yml 文件,展示了如何配置 Postgres:

# Start the app using docker-compose pull && docker-compose up to make sure you have the latest image
version: '2.1'
services:
  myApp:
    image: registry.gitlab.com/bullbytes/myApp:latest
    networks:
      - myApp-network
  db:
     image: postgres:9.6.1
     # Make Postgres log to a file.
     # More on logging with Postgres: https://www.postgresql.org/docs/current/static/runtime-config-logging.html
     command: postgres -c logging_collector=on -c log_destination=stderr -c log_directory=/logs
     environment:
       # Provide the password via an environment variable. If the variable is unset or empty, use a default password
       # Explanation of this shell feature: https://unix.stackexchange.com/questions/122845/using-a-b-for-variable-assignment-in-scripts/122848#122848
       - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-4WXUms893U6j4GE&Hvk3S*hqcqebFgo!vZi}
     # If on a non-Linux OS, make sure you share the drive used here. Go to Docker's settings -> Shared Drives
     volumes:
       # Persist the data between container invocations
       - postgresVolume:/var/lib/postgresql/data
       - ./logs:/logs
     networks:
       myApp-network:
         # Our application can communicate with the database using this hostname
         aliases:
           - postgresForMyApp
networks:
  myApp-network:
    driver: bridge
# Creates a named volume to persist our data. When on a non-Linux OS, the volume's data will be in the Docker VM
# (e.g., MobyLinuxVM) in /var/lib/docker/volumes/
volumes:
  postgresVolume:

写入日志目录的权限

请注意,在Linux上,主机上的日志目录必须具有正确的权限。 否则,您将收到略微误导的错误:

FATAL:无法打开日志文件"/logs/postgresql-2017-02-04_115222.log":权限被拒绝

我说这是误导性的,因为错误消息表明容器中的目录权限有问题,而实际上主机上的目录不允许写入。

要解决这个问题,我使用以下命令在主机上设置了正确的权限:

chgroup ./logs docker && chmod 770 ./logs

2
我还没有决定是将日志记录到文件还是使用Docker日志更可取,jpic。 - Matthias Braun
3
@vidstige:我不会把这个随机字符串当作密码使用,它只是用于演示。 - Matthias Braun
1
@Gherman:你在 Dockerfile 中添加了这个吗?因为你不应该这样做。我的答案使用 docker-compose.yml - Matthias Braun
1
@BennyChan:我自己没有尝试过,但是这里有一些重新加载Postgres配置的方法,例如pg_ctl reload - Matthias Braun
1
实际上,您可以从command:中省略postgres部分,因为官方镜像将自动转发任何命令选项到postgres。入口脚本是这样制作的,以便将传递给docker命令的任何选项传递给postgres服务器守护程序。 - phoenix
显示剩余7条评论

111

你继承的postgres:9.4镜像在/var/lib/postgresql/data声明了一个卷。这意味着你不能将任何文件复制到镜像中该路径下,因为更改将被抛弃。

你有几个选择:

  • 你可以在运行时使用docker run -v postgresql.conf:/var/lib/postgresql/data/postgresql.conf ...将自己的配置文件作为卷添加。但是,我不确定这会如何与现有卷交互。

  • 你可以在容器启动时将文件复制过去。为此,请将文件复制到构建不在卷下方的位置,然后从entrypointcmd调用脚本,该脚本将文件复制到正确的位置并启动Postgres。

  • 克隆Postgres官方镜像背后的项目,并编辑Dockerfile,在声明VOLUME之前添加你自己的配置文件(在VOLUME指令之前添加的任何内容都会在运行时自动复制)。

  • 在docker-compose文件中的命令选项中传递所有配置更改。

像这样:

services:
  postgres:
    ...
    command:
      - "postgres"
      - "-c"
      - "max_connections=1000"
      - "-c"
      - "shared_buffers=3GB"
      - "-c"
      ...

4
就是这样,更改不应该在Dockerfile中进行。我将它们移动到一个脚本中,并从入口点调用它,现在工作得很好。感谢您的答复。 - Sabbane
6
我刚刚复制了"/docker-entrypoint-initdb.d/"文件夹中的脚本,这次也成功了。虽然有点奇怪,但看来我太专注于使用Dockerfile设置镜像(和我通常做的大多数镜像一样),以至于在最初尝试使用初始化脚本时错过了一些东西。 - Sabbane
这基本上意味着您无法将任何文件复制到图像中的该路径;更改将被丢弃。请问为什么会这样? - isco
@isco 在官方文档中查找卷。基本上,卷不存储在镜像中,而是存储在主机上,因此数据将保存在主机上,而不是镜像中。您需要使用相同的卷启动容器以保留数据。 - Adrian Mouat
我在 /var/lib/postgres/data/pg_hba.conf 中进行了更改,只接受来自主机 192.168.0.0/0 的连接,并注释掉了默认的 host all all all md5。但是更改没有生效。 - Lucas Resende
这对我很有效。将以下内容添加到docker-compose文件中:command: - "postgres" - "-c" - "work_mem=4GB" - "-c" - "shared_buffers=8GB"使得查询(使用默认值)从16分钟缩短到30秒。 - JoeAC

59

当您运行官方入口点(即启动容器时),它会在$PGDATA中(默认为/var/lib/postgresql/data)运行initdb,然后在该目录中存储两个文件:

  • postgresql.conf带有默认手动设置。
  • postgresql.auto.conf带有使用ALTER SYSTEM命令自动覆盖的设置。

入口点还执行任何/docker-entrypoint-initdb.d/*.{sh,sql}文件。

所有这些意味着您可以在该文件夹中提供一个Shell/SQL脚本,以便为下次引导(将立即进行DB初始化或下次引导容器时)配置服务器。

例如:

conf.sql文件:

ALTER SYSTEM SET max_connections = 6;
ALTER SYSTEM RESET shared_buffers;

Dockerfile文件:

FROM posgres:9.6-alpine
COPY *.sql /docker-entrypoint-initdb.d/
RUN chmod a+r /docker-entrypoint-initdb.d/*

然后您将需要在已经存在的数据库中手动执行 conf.sql。由于配置存储在卷中,它将在重建时保持不变。


另一种选择是根据需要多次传递-c选项:

docker container run -d postgres -c max_connections=6 -c log_lock_waits=on

这种方式不需要构建新的镜像,也不用担心已经存在或不存在的数据库,所有数据库都会受到影响。


8
我最喜欢的是最后一个选项,即传递“-c”。这种方法非常干净简单,可以为不同的环境生成相应的设置。 - epic_fil

51

将自定义postgresql.conf注入到Postgres Docker容器中

postgresql.conf文件默认位于PGDATA目录(/var/lib/postgresql/data)中,这使得事情变得更加复杂,尤其是在首次运行Postgres容器时,因为docker-entrypoint.sh包装器会调用initdb步骤来初始化PGDATA目录。

为了一致地在Docker中自定义PostgreSQL配置,建议使用config_file Postgres选项和Docker卷,像这样:

生产数据库(PGDATA目录作为持久化卷)

docker run -d \
-v $CUSTOM_CONFIG:/etc/postgresql.conf \
-v $CUSTOM_DATADIR:/var/lib/postgresql/data \
-e POSTGRES_USER=postgres \
-p 5432:5432 \
--name postgres \
postgres:9.6 postgres -c config_file=/etc/postgresql.conf

测试数据库(PGDATA目录将在docker rm后被丢弃)

docker run -d \
-v $CUSTOM_CONFIG:/etc/postgresql.conf \
-e POSTGRES_USER=postgres \
--name postgres \
postgres:9.6 postgres -c config_file=/etc/postgresql.conf

调试

  1. docker run 命令中移除 -d(分离选项),以直接查看服务器日志。

  2. 使用psql客户端连接到Postgres服务器并查询配置:

  3. docker run -it --rm --link postgres:postgres postgres:9.6 sh -c 'exec psql -h $POSTGRES_PORT_5432_TCP_ADDR -p $POSTGRES_PORT_5432_TCP_PORT -U postgres'
    
    psql (9.6.0)
    Type "help" for help.
    
    postgres=# SHOW all;
    

21

您可以将自定义的postgresql.conf放入容器内的临时文件中,并在运行时覆盖默认配置。

操作步骤如下:

  • 将自定义的postgresql.conf复制到容器内
  • updateConfig.sh文件复制到/docker-entrypoint-initdb.d/目录下

Dockerfile

FROM postgres:9.6

COPY postgresql.conf      /tmp/postgresql.conf
COPY updateConfig.sh      /docker-entrypoint-initdb.d/_updateConfig.sh

updateConfig.sh

#!/usr/bin/env bash

cat /tmp/postgresql.conf > /var/lib/postgresql/data/postgresql.conf

在运行时,容器将执行 /docker-entrypoint-initdb.d/ 中的脚本,并用您自定义的配置文件覆盖默认配置。


1
为什么第二个复制命令中要使用"_"?难道不应该是:/docker-entrypoint-initdb.d/updateConfig.sh吗? - Fernando Castilla Ospina
7
这是因为 docker-entrypoint-initdb.d/ 文件夹按字母顺序执行脚本。我想在其它脚本之前应用这个脚本。 - alphayax
谢谢,我忘了复制时重命名文件使用了下划线。 - Fernando Castilla Ospina
我建议将 updateConfig.sh 更改为首先复制 postgresql.conf.sample (cp /usr/share/postgresql/postgresql.conf.sample /var/lib/postgresql/data/postgresql.conf),然后再附加 postgresql.conf (cat /tmp/postgresql.conf >> /var/lib/postgresql/data/postgresql.conf)。postgresql.conf.sample 中至少有一行(设置 listen_addresses)是您可能需要的。 - Tony

18

我查看了所有答案,还有另一种选择:您可以在Dockerfile中更改CMD值(这不是最好的选择,但仍然是实现目标的可能方法)。

基本上我们需要:

  • 将配置文件复制到Docker容器中
  • 覆盖Postgres启动选项

Dockerfile示例:

FROM postgres:9.6
USER postgres

# Copy Postgres config file into container
COPY postgresql.conf /etc/postgresql

# Override default Postgres config file
CMD ["postgres", "-c", "config_file=/etc/postgresql/postgresql.conf"]

虽然我认为在您的docker-compose.yml文件中使用Matthias Braun建议的command: postgres -c config_file=/etc/postgresql/postgresql.conf是最佳选择。


6

我也在使用官方镜像 (FROM postgres),通过执行以下命令,我成功地修改了配置文件。

首先需要找到 PostgreSQL 的配置文件。您可以在正在运行的数据库中执行以下命令来完成此操作。

SHOW config_file;

在我的情况下,它返回/data/postgres/postgresql.conf

下一步是找出您运行的PostgreSQL docker容器的哈希值。

docker ps -a

这将返回所有正在运行的容器列表。在我的情况下,它看起来像这样。

...
0ba35e5427d9    postgres    "docker-entrypoint.s…" ....
...

现在,您需要执行以下命令来切换到容器内的bash:

docker exec -it 0ba35e5427d9 /bin/bash

在容器内检查配置是否位于正确的路径并显示它。

cat /data/postgres/postgresql.conf

我想把最大连接数从100改为1000,将共享缓冲区从128MB改为3GB。 使用sed命令,我可以在配置文件中通过搜索和替换相应的变量来实现。
sed -i -e"s/^max_connections = 100.*$/max_connections = 1000/" /data/postgres/postgresql.conf
sed -i -e"s/^shared_buffers = 128MB.*$/shared_buffers = 3GB/" /data/postgres/postgresql.conf

我们需要做的最后一件事是在容器内重新启动数据库。 找出您正在使用的 PostGres 版本。
cd /usr/lib/postgresql/
ls 

在我的情况下,它是12。因此,您现在可以通过在正确的版本中执行以下命令来重新启动数据库。
su - postgres -c "PGDATA=$PGDATA /usr/lib/postgresql/12/bin/pg_ctl -w restart"

4
我的解决方案是为那些需要在启动docker-entrypoint-initdb.d之前进行配置更改的同事们准备的。
我需要更改'shared_preload_libraries'设置,这样在它的工作期间,postgres已经预加载了新库,并且在docker-entrypoint-initdb.d中的代码可以使用它。
所以我只需在Dockerfile中修补postgresql.conf.sample文件即可:
RUN echo "shared_preload_libraries='citus,pg_cron'" >> /usr/share/postgresql/postgresql.conf.sample
RUN echo "cron.database_name='newbie'" >> /usr/share/postgresql/postgresql.conf.sample

通过这个补丁,可以在docker-entrypoint-initdb.d/中添加.sql文件扩展名的扩展:

CREATE EXTENSION pg_cron;

3
一个相对低技术的解决方案是使用持久化卷来声明服务(我在AWS上使用swarm和yaml文件),将数据库文件挂载到持久化卷上(这里使用cloudstor:aws驱动程序规范表示AWS EFS)。
  version: '3.3'
  services:
    database:
      image: postgres:latest
      volumes:
        - postgresql:/var/lib/postgresql
        - postgresql_data:/var/lib/postgresql/data
    volumes:
       postgresql:
         driver: "cloudstor:aws" 
       postgresql_data:
         driver: "cloudstor:aws"
  1. 数据库以默认设置初始化。
  2. 您可以在容器内编辑配置设置,例如,如果您想增加最大并发连接数,则需要重新启动。
  3. 停止运行的容器(或将服务缩小为零,然后再次调整为1)。
  4. Swarm生成一个新的容器,这一次选择您持久化的配置设置,并愉快地应用它们。

持久化配置的一个愉快的副作用是它也持久化了您的数据库(或者反过来);-)


2
使用Docker Compose,您可以挂载一个带有postgresql.auto.conf的卷。 示例:
version: '2'

services:
  db:
    image: postgres:10.9-alpine
    volumes:
      - postgres:/var/lib/postgresql/data:z
      - ./docker/postgres/postgresql.auto.conf:/var/lib/postgresql/data/postgresql.auto.conf
    ports:
      - 5432:5432

3
不要编辑 postgresql.auto.conf,因为它会被覆盖。请使用同一位置下的 postgresql.conf 文件。 - Baschdl

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