PostgreSQL 9.6 on RHEL の Docker を作成する

PostgreSQL on RHEL の Docker を作成します。
Red Hat Enterprise Linux は開発目的であれば無料で利用することができます。
https://redhat.sios.jp/red-hat-developer-program

これを利用して、RHEL 上に PostgreSQL をインストールした Docker イメージを作成したいと思います。
Developer Program への登録は上記記事を参照してください。

intra-mart の検証用として利用したいので、intra-mart が要求する設定をあらかじめ実施します。
これによって、docker run 後何も考えずにすぐに検証用として利用できるような PostgreSQL 9.6 の環境が作れるようになります。

```dockerfile:Dockerfile
FROM registry.access.redhat.com/rhel7.6

EXPOSE 22 5432
ENV DEBIAN_FRONTEND noninteractive

# subscription
COPY subscribe_rhel.sh /subscribe_rhel.sh
RUN chmod +x /subscribe_rhel.sh \
 && /subscribe_rhel.sh

# yum
RUN yum -y update \
 && yum provides -y '*/applydeltarpm' \
 && yum groupinstall -y 'Development Tools' \
 && yum install -y initscripts curl tar unzip mlocate openssh-server openssl-devel \
 && yum localinstall -y https://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-7-x86_64/pgdg-redhat96-9.6-3.noarch.rpm \
 && yum install -y postgresql96-server epel-release multitail ncurses-devel ncurses-static ncurses-term \
 && rm -rf /var/cache/yum/* \
 && yum clean all

# locale
RUN yum reinstall -y glibc-common \
 && localedef -i ja_JP -f UTF-8 ja_JP.utf8 \
 && touch /etc/sysconfig/i18n \
 && echo 'LANG="ja_JP.UTF-8"' >> /etc/sysconfig/i18n \
 && echo 'LANG="ja_JP.UTF-8"' > /etc/locale.conf
ENV LANG ja_JP.UTF-8
ENV LC_ALL ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja

# timezone
RUN yum install -y tzdata \
 && echo 'ZONE="Asia/Tokyo"' > /etc/sysconfig/clock \
 && echo 'UTC=false' >> /etc/sysconfig/clock \
 && ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

# root passwd
RUN bash -c 'echo "root:password" | chpasswd'

# ssh
RUN sed -i -e "s/#PasswordAuthentication yes/PasswordAuthentication yes/g" /etc/ssh/sshd_config \
 && sed -i -e "s/#PermitRootLogin yes/PermitRootLogin yes/g" /etc/ssh/sshd_config \
 && sed -i -e "s/UsePAM yes/UsePAM no/g" /etc/ssh/sshd_config

# PostgreSQL
COPY setup_postgresql.sh /setup_postgresql.sh
RUN chmod +x /setup_postgresql.sh \
 && /setup_postgresql.sh \
 && rm -f /setup_postgresql.sh

COPY run.sh /run.sh
RUN chmod +x /run.sh

RUN updatedb

CMD ["/run.sh"]
```

yum が使えないので subscription-manager register を実行します。
username と password はあなたのアカウントを設定してください。

```shell:subscribe_rhel.sh
#!/bin/sh

subscription-manager register --username 'FIXME' --password 'FIXME' --auto-attach

exit 0
```

さて setup_postgresql.sh ですが、CentOS 6/RHEL 6 では以下のようにしてセットアップできました。

```shell:setup_postgresql.sh
#!/bin/bash

service postgresql-9.6 initdb

echo "listen_addresses = '*'" >> /var/lib/pgsql/9.6/data/postgresql.conf
sed -i -e "s/ident$/trust/g" /var/lib/pgsql/9.6/data/pg_hba.conf
sed -i -e "s/127\.0\.0\.1\/32/0\.0\.0\.0\/0/g" /var/lib/pgsql/9.6/data/pg_hba.conf

sed -i -e "s/max_connections = 100/max_connections = 200/g" /var/lib/pgsql/9.6/data/postgresql.conf
sed -i -e "s/shared_buffers = 128MB/shared_buffers = 512MB/g" /var/lib/pgsql/9.6/data/postgresql.conf

echo "postgres:postgres" | chpasswd

service postgresql-9.6 start
sleep 1

su postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD 'postgres'\""
su postgres -c "psql -c \"CREATE ROLE imart WITH LOGIN PASSWORD 'imart'\""
su postgres -c "psql -c \"CREATE DATABASE imart OWNER=imart encoding 'utf8' TEMPLATE template0\""
su postgres -c "psql -c \"CREATE DATABASE iap_db OWNER=imart encoding 'utf8' TEMPLATE template0\""
su postgres -c "psql -c \"CREATE DATABASE \\\"default\\\" OWNER=imart encoding 'utf8' TEMPLATE template0\""
su postgres -c "psql -d imart -c \"CREATE SCHEMA acceldocuments AUTHORIZATION imart\""
su postgres -c "psql -d iap_db -c \"CREATE SCHEMA acceldocuments AUTHORIZATION imart\""
su postgres -c "psql -d default -c \"CREATE SCHEMA acceldocuments AUTHORIZATION imart\""

service postgresql-9.6 stop
```

ここで問題は、RHEL 7 では service ではなく systemctl を利用しますが、docker build では systemctl は利用できない事です。

```shell
# service postgresql-9.6 initdb
The service command supports only basic LSB actions (start, stop, restart, try-restart, reload, force-reload, status). For other actions, please try to use systemctl.
```

無理やり実行しようとしても、

```shell
# /usr/pgsql-9.6/bin/postgresql96-setup initdb
Failed to get D-Bus connection: Operation not permitted
failed to find PGDATA setting in postgresql-9.6.service
```

問題となっているのは以下の箇所です。

```shell:/usr/pgsql-9.6/bin/postgresql96-setup
# this parsing technique fails for PGDATA pathnames containing spaces,
# but there's not much I can do about it given systemctl's output format...
PGDATA=`systemctl show -p Environment "${SERVICE_NAME}.service" |
                sed 's/^Environment=//' | tr ' ' '\n' |
                sed -n 's/^PGDATA=//p' | tail -n 1`
```

以下のようにして、

```shell
sed -i -e "s/if \[ x\"\$PGDATA\" = x \]; then/PGDATA=\/var\/lib\/pgsql\/9.6\/data\/\nif \[ x\"\$PGDATA\" = x \]; then/g" /usr/pgsql-9.6/bin/postgresql96-setup
```

```shell
if [ x"$PGDATA" = x ]; then
```

を

```shell
PGDATA=/var/lib/pgsql/9.6/data/
if [ x"$PGDATA" = x ]; then
```

に置き換えてから /usr/pgsql-9.6/bin/postgresql96-setup initdb をする方法もありますが、無理やり感がぬぐえません。
結論としては、以下のように pg_ctl を利用して initdb が可能です。

```shell
su postgres -c "/usr/pgsql-9.6/bin/pg_ctl -D /var/lib/pgsql/9.6/data initdb"
```

次に問題となるのは service start と service stop です。

```shell:/usr/lib/systemd/system/postgresql-9.6.service
# It's not recommended to modify this file in-place, because it will be
# overwritten during package upgrades.  If you want to customize, the
# best way is to create a file "/etc/systemd/system/postgresql-9.6.service",
# containing
#	.include /lib/systemd/system/postgresql-9.6.service
#	...make your changes here...
# For more info about custom unit files, see
# http://fedoraproject.org/wiki/Systemd#How_do_I_customize_a_unit_file.2F_add_a_custom_unit_file.3F

# Note: changing PGDATA will typically require adjusting SELinux
# configuration as well.

# Note: do not use a PGDATA pathname containing spaces, or you will
# break postgresql-setup.
[Unit]
Description=PostgreSQL 9.6 database server
Documentation=https://www.postgresql.org/docs/9.6/static/
After=syslog.target
After=network.target

[Service]
Type=notify

User=postgres
Group=postgres

# Note: avoid inserting whitespace in these Environment= lines, or you may
# break postgresql-setup.

# Location of database directory
Environment=PGDATA=/var/lib/pgsql/9.6/data/

# Where to send early-startup messages from the server (before the logging
# options of postgresql.conf take effect)
# This is normally controlled by the global default set by systemd
# StandardOutput=syslog

# Disable OOM kill on the postmaster
OOMScoreAdjust=-1000
Environment=PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj
Environment=PG_OOM_ADJUST_VALUE=0

ExecStartPre=/usr/pgsql-9.6/bin/postgresql96-check-db-dir ${PGDATA}
ExecStart=/usr/pgsql-9.6/bin/postmaster -D ${PGDATA}
ExecReload=/bin/kill -HUP $MAINPID
KillMode=mixed
KillSignal=SIGINT
 

# Do not set any timeout value, so that systemd will not kill postmaster
# during crash recovery.
TimeoutSec=0

[Install]
WantedBy=multi-user.target
```

このようになっているため、以下のようにして起動/終了します。

```shell
# 起動
su postgres -c "PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj; PG_OOM_ADJUST_VALUE=0; /usr/pgsql-9.6/bin/pg_ctl -D /var/lib/pgsql/9.6/data -l /var/lib/pgsql/9.6/data/pg_log/postgresql.log start"
# 終了
su postgres -c "/usr/pgsql-9.6/bin/pg_ctl -D /var/lib/pgsql/9.6/data stop"
```

まとめると以下のようにしてセットアップが可能です。

```shell:setup_postgresql.sh
#!/bin/bash

export PGSQL=/usr/pgsql-9.6
export PGDATA=/var/lib/pgsql/9.6/data
su postgres -c "${PGSQL}/bin/pg_ctl -D $PGDATA initdb"

echo "listen_addresses = '*'" >> ${PGDATA}/postgresql.conf
sed -i -e "s/ident$/trust/g" ${PGDATA}/pg_hba.conf
sed -i -e "s/127\.0\.0\.1\/32/0\.0\.0\.0\/0/g" ${PGDATA}/pg_hba.conf

sed -i -e "s/max_connections = 100/max_connections = 200/g" ${PGDATA}/postgresql.conf
sed -i -e "s/shared_buffers = 128MB/shared_buffers = 512MB/g" ${PGDATA}/postgresql.conf

echo "postgres:postgres" | chpasswd

su postgres -c "mkdir -p ${PGDATA}/pg_log"
su postgres -c "PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj; PG_OOM_ADJUST_VALUE=0; ${PGSQL}/bin/pg_ctl -D $PGDATA -l ${PGDATA}/pg_log/postgresql.log start"
sleep 2

su postgres -c "psql -c \"ALTER USER postgres WITH PASSWORD 'postgres'\""
su postgres -c "psql -c \"CREATE ROLE imart WITH LOGIN PASSWORD 'imart'\""
su postgres -c "psql -c \"CREATE DATABASE imart OWNER=imart encoding 'utf8' TEMPLATE template0\""
su postgres -c "psql -c \"CREATE DATABASE iap_db OWNER=imart encoding 'utf8' TEMPLATE template0\""
su postgres -c "psql -c \"CREATE DATABASE \\\"default\\\" OWNER=imart encoding 'utf8' TEMPLATE template0\""
su postgres -c "psql -d imart -c \"CREATE SCHEMA acceldocuments AUTHORIZATION imart\""
su postgres -c "psql -d iap_db -c \"CREATE SCHEMA acceldocuments AUTHORIZATION imart\""
su postgres -c "psql -d default -c \"CREATE SCHEMA acceldocuments AUTHORIZATION imart\""

su postgres -c "${PGSQL}/bin/pg_ctl -D $PGDATA stop"
```

```run.sh
#!/bin/bash

/subscribe_rhel.sh
/usr/sbin/sshd-keygen
/usr/sbin/sshd
su postgres -c "PG_OOM_ADJUST_FILE=/proc/self/oom_score_adj; PG_OOM_ADJUST_VALUE=0; /usr/pgsql-9.6/bin/pg_ctl -D /var/lib/pgsql/9.6/data restart"

multitail -M 0 --follow-all --retry-all -q 1 "/var/lib/pgsql/9.6/data/pg_log/*.log"
```

ビルドします。

```shell
docker build -t mypostgresql:9.6 .
```

# 起動

```shell
docker run -itd -p 5432:5432 -p 2222:22 mypostgresql:9.6
```

# ssh

```shell
ssh -p 2222 root@localhost
```

docker run 時に -p 2222:22 とした場合、2222 が ssh のポートです。
root/password でログインできます。
localhost の部分は、docker run しているマシンの IP アドレスに置き換えてください。

というわけで簡単にですが PostgreSQL 9.6 intra-mart 用構成済みの Docker についての紹介でした。