Docker PostgreSQLイメージを利用する(https://qiita.com/kimullaa/items/70eaec61c02d2513e76c)

PostgreSQLDocker

目的

この記事はDockerで開発環境用のPostgreSQLを用意することを目的にしています。運用レベルの考慮はしていません。

検証環境

$ cat /etc/redhat-release CentOS Linux release 7.1.1503 (Core) $ docker -v Docker version 18.03.1-ce, build 9ee9f40 $ docker-compose -v docker-compose version 1.21.2, build a133471

PostgreSQLを起動する

$ docker run --name my-db -p 5432:5432 -e POSTGRES_USER=dev -e POSTGRES_PASSWORD=secret -d postgres:9.6

オプション役割

--name my-db コンテナ名
-p 5432:5432 ホストのポート:コンテナのポート
-e POSTGRES_USER スーパユーザ名(省略時は"postgres")
-e POSTGRES_PASSWORD スーパユーザのパスワード(省略時はパスワードなしでログイン可)
-e POSTGRES_DB データベース名(省略時はPOSTGRES_USERと同じ)デフォルトだとユーザ名と同じデータベースが存在しないとエラーになるため、変える機会は少なそう
-e PGDATA PostgreSQLのデータの格納先ディレクトリ(省略時は/var/lib/postgresql/data)

起動しているコンテナの一覧はdocker psで確認できる。

# --name my-dbで指定した値がNAMESに表示される $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ebb9cf2ff57f postgres:9.6 "docker-entrypoint.s…" 14 seconds ago Up 10 seconds 0.0.0.0:5432->5432/tcp my-db

PostgreSQLに接続する

ホストからアクセスする

-p xxxx:5432で指定したホストのポートにアクセスすると、コンテナにパケットが転送される。PostgreSQLがコンテナで動いていることを意識する必要はない。ローカルで動くアプリケーションから接続するときは、この方法になる。

$ psql -h localhost -U dev ユーザ dev のパスワード: psql (9.6.9) "help" でヘルプを表示します. dev=#

コンテナを利用してアクセスする パターン1

psqlクライアント用にコンテナを立ち上げ、--linkを利用して接続する。

$ docker run -it --rm --link my-db:db postgres:9.6 psql -h db -U dev Password for user dev: psql (9.6.9) Type "help" for help. dev=#

オプション役割

--rm 実行が終わったらpsqlのコンテナを破棄する
--link my-db:db コンテナの/etc/hostsに、my-dbにアクセス可能なIPを持つホスト名(db)が設定される
psql -h db -U dev コンテナ内で実行するコマンド。 接続先には --linkで設定したホスト名を指定する

ただし --linkは古い機能のため扱いに注意。
参考 Legacy container links

コンテナを利用してアクセスする パターン2

現在立ち上がっているコンテナで別プロセスを立ち上げる。

$ docker exec -it my-db psql -U dev psql (9.6.9) Type "help" for help. dev=#

PostgreSQLコンテナの停止

docker stopするだけ。

$ docker stop my-db

PostgreSQLコンテナの再開

docker rmしてなければ、docker startできる。
DBデータは消えずに利用できる。

$ docker start my-db

初期データを設定する

コンテナの/docker-entrypoint-initdb.dディレクトリに*.sql, *.sql.gz, or *.shのファイルを配置すると、コンテナ起動時に実行される。アプリケーション用のスキーマを毎回実行したいときに利用すると便利。

$ cat sql/schema.sql CREATE TABLE SAMPLE(); $ docker run -it --name my-db -v $(pwd)/sql:/docker-entrypoint-initdb.d -e POSTGRES_PASSWORD=secret -d postgres:9.6 56fdad8741465fd86b618b62ed2ca7bf613b715b73e3ccf90e3618155d58d5d3 [kimura@localhost docker]$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 56fdad874146 postgres:9.6 "docker-entrypoint.s…" 5 seconds ago Up 2 seconds 5432/tcp my-db [kimura@localhost docker]$ docker exec -it my-db psql -U postgres psql (9.6.9) Type "help" for help. postgres=# \d List of relations Schema | Name | Type | Owner --------+--------+-------+---------- public | sample | table | postgres (1 row)

PostgreSQLコンテナの削除

docker rmするだけ。

$ docker rm my-db

docker runすると、DBデータはまっさらな状態になる。

PostgreSQLコンテナを消すとDBデータはどうなる?

PostgreSQLのDockerfileに、以下のような記述がある。
参考 Dockerfile

VOLUME /var/lib/postgresql/data

VOLUMEの指定があると、コンテナ起動時にボリュームが自動で作成される。
作成されたボリューム名は、docker inspectで確認できる。

$ docker inspect my-db ... "Mounts": [ { "Type": "volume", "Name": "813a6def1e8a1e07e6e5cb317bd4577a83ecce8d6d2846449014ea3e32e56fd4", "Source": "/var/lib/docker/volumes/813a6def1e8a1e07e6e5cb317bd4577a83ecce8d6d2846449014ea3e32e56fd4/_data", "Destination": "/var/lib/postgresql/data", "Driver": "local", "Mode": "", "RW": true, "Propagation": "" } ], ...

コンテナを削除するだけではボリュームは削除されない

Removing the service does not remove any volumes created by the service. Volume removal is a separate step.

参考 Manage data in Docker

そのため、ボリュームを指定すれば昔のDBデータにアクセスできる。

$ docker run -it -v 813a6def1e8a1e07e6e5cb317bd4577a83ecce8d6d2846449014ea3e32e56fd4:/var/lib/postgresql/data --name my-db -e POSTGRES_PA SSWORD=secret -d postgres:9.6

逆に、明示的にボリュームを消さないとデータは残り続ける。
コンテナを削除するときにボリュームも消す場合は、-vオプションを使う。

$ docker rm -v my-db

また、以下のコマンドで不要なボリュームを一括で削除できる。

$ docker volume prune

DBデータを永続化する

ボリュームに名前を付けておけば、手軽にコンテナにアタッチできる。
参考 Use volumes

$ docker volume create --name pgdata pgdata $ docker volume inspect pgdata [ { "CreatedAt": "2018-06-14T03:55:37+09:00", "Driver": "local", "Labels": {}, "Mountpoint": "/var/lib/docker/volumes/pgdata/_data", "Name": "pgdata", "Options": {}, "Scope": "local" } ]

起動時にData Volumeを指定してテーブルを作成する。

$ docker run -it --name my-db -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret -d postgres:9.6 $ docker exec -it my-db -U postgres psql (9.6.9) Type "help" for help. postgres=# create table book(); CREATE TABLE

コンテナを停止して削除する。

$ docker stop my-db $ docker rm my-db

起動時に同じData Volumeを指定すれば、永続化したデータが参照できる。

$ docker run -it --name my-db -v pgdata:/var/lib/postgresql/data -e POSTGRES_PASSWORD=secret -d postgres:9.6 $ docker exec -it my-db -U postgres psql (9.6.9) Type "help" for help. postgres=# \d List of relations Schema | Name | Type | Owner --------+------+-------+---------- public | book | table | postgres (1 row)

ただしボリュームにデータを永続化した場合、/docker-entrypoint-initdb.dのスクリプトはコンテナ起動ごとに実行されない点に注意。

ロケール設定

デフォルトではサーバロケールがen_US.utf8になっている。
ja_JP.utf8にしたい人は以下のように、Dockerfileを作成する必要がある。

You can also extend the image with a simple Dockerfile to set a different locale. The following example will set the default locale to de_DE.utf8:

FROM postgres:9.4
RUN localedef -i de_DE -c -f UTF-8 -A /usr/share/locale/locale.alias de_DE.UTF-8
ENV LANG de_DE.utf8

Since database initialization only happens on container startup, this allows us to set the language before it is created.

参考 OFFICIAL REPOSITORY

日本語化する

ロケールを追加し、LANG環境変数に設定する。

$ cat Dockerfile FROM postgres:9.6 RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8 ENV LANG ja_JP.utf8

Dockerfileをビルドし、起動すると日本語になる。

$ docker build -t dev-postgres -f Dockerfile . $ docker run -it --name my-db -e POSTGRES_PASSWORD=secret -d dev-postgres $ docker exec -it my-db -U postgres psql (9.6.9) Type "help" for help. postgres=# \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -----------+----------+----------+------------+------------+----------------------- postgres | postgres | UTF8 | ja_JP.utf8 | ja_JP.utf8 | template0 | postgres | UTF8 | ja_JP.utf8 | ja_JP.utf8 | =c/postgres + | | | | | postgres=CTc/postgres

なぜユーザ名やパスワードが起動引数で変えられるのか?

-eオプションでコンテナの環境変数を設定できる仕組みがDockerにある。
参考 Docker run リファレンス

以下のようなコマンドを実行すると -e SAMPLE=sampleで指定した値が環境変数に設定されることがわかる。

$ docker run -it -e SAPMLE=sample --rm centos:7 env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=c650fe32fc3b TERM=xterm SAPMLE=sample HOME=/root

また、既に設定された環境変数を上書きすることもできる。
例えば、HOME=/rootをHOME=/tmpで上書きしてみる。

$ docker run -it -e HOME=/tmp --rm centos:7 env PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin HOSTNAME=4c5e7303b0fa TERM=xterm HOME=/tmp

上記の仕組みを利用し、起動スクリプトの動きを動的に変えている様子。

起動スクリプトの詳細を確認するときはdocker inspectを利用する。

$ docker inspect postgres:9.6 ... "WorkingDir": "", "Entrypoint": [ // 起動時に呼ばれるコマンド "docker-entrypoint.sh" ], ...

起動スクリプト(docker-entrypoint.sh)は以下のような処理になっている。
参考 docker-entrypoint.sh

docker-entrypoint.sh

file_env 'POSTGRES_USER' 'postgres' file_env 'POSTGRES_DB' "$POSTGRES_USER" psql=( psql -v ON_ERROR_STOP=1 ) if [ "$POSTGRES_DB" != 'postgres' ]; then "${psql[@]}" --username postgres <<-EOSQL CREATE DATABASE "$POSTGRES_DB" ; EOSQL echo fi if [ "$POSTGRES_USER" = 'postgres' ]; then op='ALTER'

PostgreSQLコンテナの構成を知る

起動しているコンテナにbashで入ることができる。

スクリプトやディレクトリ構成を確認できる。

$ docker exec -it my-db /bin/bash root@f43f50193fa6:/# ls bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var boot docker-entrypoint-initdb.d etc lib media opt root sbin sys usr

PostgreSQLの設定の変更

設定ファイルの編集

まず、コンテナからpostgresql.confをコピーする。

$ docker cp my-db:/var/lib/postgresql/data/postgresql.conf .

次に、ホスト側で設定ファイルを編集する。
(今回はmax_connectionsを100から5にする)

$ cat postgresql.conf ... max_connections = 5 ...

編集した設定ファイルの配置

コンテナ側にファイルを配置するだけだと、以下のようにエラーになる。
参考 Unable to replace postgresql.conf

$ docker run -it --name my-db -v $(pwd)/postgresql.conf:/var/lib/postgresql/data/postgresql.conf -e POSTGRES_PASSWORD=secret postgres:9.6 The files belonging to this database system will be owned by user "postgres". This user must also own the server process. The database cluster will be initialized with locale "en_US.utf8". The default database encoding has accordingly been set to "UTF8". The default text search configuration will be set to "english". Data page checksums are disabled. initdb: directory "/var/lib/postgresql/data" exists but is not empty If you want to create a new database system, either remove or empty the directory "/var/lib/postgresql/data" or run initdb with an argument other than "/var/lib/postgresql/data".

以下のように、$PGDATAに設定ファイルを配置しないでPostgreSQLの起動時にconfig_fileで設定ファイルのパスを指定すればよい。

$ docker run -it --name my-db -v $(pwd)/postgresql.conf:/etc/postgresql.conf -e POSTGRES_PASSWORD=secret -d postgres:9.6 -c config_file=/etc/postgresql.conf bf55391afe2ae709e84efd81025b4d7f542f3db4bb42d13c348807cc9342b47f $ $ docker exec -it my-db psql -U postgres psql (9.6.9) Type "help" for help. postgres=# SHOW max_connections; max_connections ----------------- 5 (1 row)

設定をまとめる

docker-composeを使ってコマンドをまとめる。

$ ll 合計 8 -rw-rw-r--. 1 kimura kimura 119 6月 14 07:02 Dockerfile drwxrwxr-x. 2 kimura kimura 28 6月 14 06:58 conf -rw-rw-r--. 1 kimura kimura 438 6月 14 13:11 docker-compose.yml drwxrwxr-x. 2 kimura kimura 23 6月 14 13:41 sql

Dockerfile

FROM postgres:9.6 RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8 ENV LANG ja_JP.utf8

docker-compose.yml

version: "3" volumes: pgdata: driver: "local" services: my-db: build: . container_name: "my-db" ports: - "5432:5432" environment: POSTGRES_USER: "dev" POSTGRES_PASSWORD: "secret" command: 'postgres -c config_file="/etc/postgresql.conf"' volumes: - "pgdata:/var/lib/postgresql/data" - "./conf/postgresql.conf:/etc/postgresql.conf" - "./sql:/docker-entrypoint-initdb.d"

これでDockerfileのビルドから起動までコマンド一発。

$ docker-compose up -d Creating network "postgres_default" with the default driver Creating volume "postgres_pgdata" with local driver Building my-db Step 1/3 : FROM postgres:9.6 ---> d92dad241eff Step 2/3 : RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8 ---> Running in 4ae5282dc582 Removing intermediate container 4ae5282dc582 ---> ae2a5b8d31fe Step 3/3 : ENV LANG ja_JP.utf8 ---> Running in aa9c01cdc114 Removing intermediate container aa9c01cdc114 ---> 117a670283ca Successfully built 117a670283ca Successfully tagged postgres_my-db:latest WARNING: Image for service my-db was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating my-db ... done

PostgreSQLに接続したい場合は以下のようにする。

$ docker-compose exec my-db psql -U dev psql (9.6.9) "help" でヘルプを表示します. dev=# \d リレーションの一覧 スキーマ | 名前 | 型 | 所有者 ----------+--------+----------+-------- public | sample | テーブル | dev (1 行) dev=# show max_connections; max_connections ----------------- 5 (1 行)

docker execでもアクセスできる。

$ docker exec -it my-db psql -U dev psql (9.6.9) "help" でヘルプを表示します. dev=# show max_connections ; max_connections ----------------- 5 (1 行)

コンテナとデータのどちらも削除したい場合は、-vオプションを付ける。

$ docker-compose down -v Removing network postgres_default Removing volume postgres_pgdata

参考 docker-compose down

関連記事

+ Recent posts