Docker Hub公式PostgreSQLで初期化ずみデータベースコンテナを作る

# はじめに
Docker Hub公式PostgreSQL( https://hub.docker.com/_/postgres/ )で初期化ずみデータベースコンテナを作ります。
意外と日本語で書かれたものがなかったのでメモを残します。


# 環境
 * Ubuntu 16.04
 * Docker 1.12.1

 PostgreSQLはすでに別環境にあって、それをまるっと載せたデータベースコンテナを作る想定。

# 手順
## Dockerfileの準備
 * 空ディレクトリ``~docker/postgres``を作成し、その中に以下のように記述したDockerfileを配置。

 ```:Dockerfile
FROM postgres:9.3
RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
COPY *.sql /docker-entrypoint-initdb.d/
```
Postgresのバージョンは今別環境で使っているものとあわせたので9.3に。
RUNとENVはlocaleのために必要。
COPYで同じディレクトリにおいた初期化用の *.sql を imageの所定のディレクトリにコピーしている。

 * 初期化用SQLを``docker/postgres`` に配置。

公式Docker HubのPostgreSQLを使うと、 コンテナを作ったときに ``/docker-entrypoint-initdb.d/`` の下の *.sh, *.sql, *.sql.gz　が実行されます。なので、初期化用のsqlを一緒においておきます。

こちらはユーザ作成とデータベース作成だけやるSQL。

```sql:01_createdb.sql
create role user1 login password 'user1';
create database user1;
grant all privileges on database user1 to user1;
```

こちらは既存のPostgreSQLのDBから``$ pg_dump -U postgres -Fp user1 `` して作成したダンプ。ファイルの先頭にuser1 dbにつなぐための ``\c user1;`` だけ追加してある。

```sql:02_restore.sql
\c user1;
--
-- PostgreSQL database dump
--

SET statement_timeout = 0;
SET lock_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SET check_function_bodies = false;
SET client_min_messages = warning;

--
-- Name: user1; Type: SCHEMA; Schema: -; Owner: user1
--

CREATE SCHEMA user1;


ALTER SCHEMA user1 OWNER TO user1;
-- 後略
```

ちなみにsqlファイル名の先頭に01とか02をつけているのは、実行を順番にしたいため。コンテナ作成時の初期化スクリプトで `` for f in /docker-entrypoint-initdb.d/*; do `` しているので、数字をつけたら順番にやってくれるかな、という姑息な手段。

## docker builld
``~/docker/postgres``でビルド

```
$ docker build -t xxx/postgres .
Sending build context to Docker daemon 6.656 kB
Step 1 : FROM postgres:9.3
 ---> db4057098cc9
Step 2 : RUN localedef -i ja_JP -c -f UTF-8 -A /usr/share/locale/locale.alias ja_JP.UTF-8
 ---> Running in 3ad41c50d769
 ---> 427b06ac1197
Removing intermediate container 3ad41c50d769
Step 3 : ENV LANG ja_JP.UTF-8
 ---> Running in 27b2492a5365
 ---> ff2f1175f3d5
Removing intermediate container 27b2492a5365
Step 4 : COPY *.sql /docker-entrypoint-initdb.d/
 ---> 1e02da327a9f
Removing intermediate container 23e15cd44a29
Successfully built 1e02da327a9f
```
## コンテナ作成

```
$ docker run -d --name postgres xxx/postgres
2131dcf553de2f67c0d9ae990d99c811de1b2dd8aa399204a34e65a6760e0947
```

コンテナ作成時に docker-entrypoint.sh が実行されて、initdb等が行われます。同時に、 /docker-entrypoint-initdb.d の下にある、 *.sh, *.sql, *.sql.gz が順次実行されています。
なので長いsqlを流した場合は docker run でプロンプトが返ってきてもまだ初期化中だったりします。
``docker logs -f postgres`` で見ていました。

コンテナの起動を確認。

```
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
2131dcf553de        xxx/postgres        "/docker-entrypoint.s"   10 minutes ago      Up 10 minutes       5432/tcp            postgres
```

初期化スクリプトで失敗するとコンテナはstopします。logsで原因を調べてファイルを修正して、コンテナとイメージを削除して(`` docker rm コンテナ名``, ``docker rmi イメージ名``) build からやり直し。

## 接続確認

user1 データベースに user1ユーザで接続して確認。

```
$ docker run -it --rm --link postgres:postgres xxx/postgres psql -h postgres -U user1 -n user1
psql (9.3.14)
Type "help" for help.

user1=> \d
            List of relations
 Schema |     Name     |   Type   | Owner 
--------+--------------+----------+-------
 user1  | distributors | table    | user1
 user1  | films        | table    | user1
 user1  | serial       | sequence | user1
(3 rows)
```

無事初期化できたようです。

# そのほか
ちょっと困ったことなど。

## psqlで日本語入らない
docker run で動かしたpsqlで日本語が入らなかったのですよ。
軽くぐぐったら
https://pasero.net/~mako/blog/s/229
にあたって、とりあえずpsqlに-nオプションを足したら日本語入りました。でもreadlineないと超不便。

## create role多すぎた
これは元のDBの構成のせいですが、roleが多すぎて、記憶に頼ってcreate roleをぽちぽち書いていたら足りないユーザがいて、何度も初期化の途中でコケるはめに。最後は面倒になってpg_dumpのファイルから以下のようにしてcreate role文を作りました。

```
$ grep -e '^GRANT' 02_restore.sql |awk '{print $7}'|sort |uniq| sed -e "s/\(.*\);/create role \1 login password '\1';/g"
```

PUBLICとかpostgresとかが入っちゃったら削る必要があります。