#14 PostgreSQLコンテナの紹介

本記事は[PostgreSQL on Kubernetes Advent Calendar 2018](https://qiita.com/advent-calendar/2018/postgresonk8s)の14日目です。
昨日は「[Recap: PGConf.Asia 2018](https://qiita.com/tzkoba/private/9b37f3151f92408ae554)」ということで、3日間にかけて行われたカンファレンスの振り返りをしました。

12/25までのAdvent Calenderも今日でようやく半分を超えました。
本日からはPostgreSQL on Kubernetesの検証内容の投稿を再開します。

#TL;DR

+ 公式のpostgresイメージを使って、コンフィグは外から与えるように設定するよ。
+ データとアーカイブログは保存先を分けよう。
+ Probe設定もわすれずに。

# PostgreSQLのStatefulSet設定
これまで話している通り、今回のPostgreSQL on Kubernetesでは**StatefulSet**を使っています。
今回のPostgreSQL on Kubernetesで求められる要件は以下となります。

+ postgresql.confを外出しして、設定を自由に変えられること。
+ アーカイブログモードで動くこと。
+ 外部ディスクとして、データとアーカイブログの領域を分けること。

実際にどのような設定で使っているかを順に記していきます。YAML全体は[こちら](https://github.com/tzkoba/postgres-on-k8s/blob/master/postgres-install/pg-rook/pg-rook-sf.yaml)をご覧下さい。

### imageの指定
まずコンテナイメージですが、Docker Hub上の[Postgres公式のもの](https://hub.docker.com/_/postgres/)を使っています。

```YAML:pg-rook-sf.yaml（image部分抜粋）
    spec:
      containers:
        - name: pg-rook-sf
          image: postgres:10.4
          imagePullPolicy: "IfNotPresent"
```
PostgreSQLのバージョンは**10.4**、OSはDebianベースになります。公式のコンテナイメージとして提供されているのは2018年時点ではDebianとAlpineになっています。

### postgresql.confの指定
公式のコンテナイメージでは、今回検証に必要なアーカイブログモードの設定がOFFになっています。さらにログ出力も少しカスタマイズしたかったので、事前に[pg-rook-postgresql_conf.yaml](https://github.com/tzkoba/postgres-on-k8s/blob/master/postgres-install/pg-rook/pg-rook-postgresql_conf.yaml)を作りました。

```text:pg-rook-postgresql_conf.yaml（アーカイブ部分を抜粋）
archive_mode = on                                   # enables archiving; off, on, or always
                                                    # (change requires restart)
archive_command = 'cp %p /mnt/postgresql/xlog/%f'   # command to use to archive a logfile segment
                                                    # placeholders: %p = path of file to archive
                                                    #               %f = file name only
archive_timeout = 60                                # force a logfile segment switch after this
                                                    # number of seconds; 0 disables
```


さらに起動時にこのコンフィグをマウントしたConfigMapから与えるために、コンテナにargsを与えています。

```YAML:pg-rook-sf.yaml（コンフィグ部分を抜粋）
          args:
          - -c 
          - config_file=/etc/postgresql/postgresql.conf
```

上記により、ConfigMapで指定したファイルがドキュメントにある以下の形で与えられ、想定したパラメータでPostgreSQLを起動することができます。

```$ docker run -d --name some-postgres -v "$PWD/my-postgres.conf":/etc/postgresql/postgresql.conf postgres -c 'config_file=/etc/postgresql/postgresql.conf'```

### PGDATAディレクトリとTimeZoneの設定
公式のコンテナイメージでは環境変数：**PGDATA**でPostgreSQLのデータディレクトリを指定することができます。これを使うのはドキュメントにあるとおり、データディレクトリがマウントポイントになるケースです。

>This optional environment variable can be used to define another location - like a subdirectory - for the database files. The default is /var/lib/postgresql/data, but if the data volume you're using is a fs mountpoint (like with GCE persistent disks), Postgres initdb recommends a subdirectory (for example /var/lib/postgresql/data/pgdata ) be created to contain the data.
>（Google翻訳）
>このオプションの環境変数を使用して、データベースファイルの別の場所（サブディレクトリなど）を定義することができます。デフォルトは/ var / lib / postgresql / dataですが、使用するデータボリュームがfsマウントポイント（GCE永続ディスクなど）の場合、Postgres initdbはサブディレクトリ（たとえば/ var / lib / postgresql / data / pgdata）を作成してデータを格納することができます。

今回のPostgreSQL on Rook構成でもPVでマウントした箇所が/var/lib/postgresql/dataとなるため、このPGDATAを指定しないと、PostgreSQLが起動できません。


```YAML:pg-rook-sf.yaml（env部分を抜粋）
          env:
            - name: PGDATA
              value: /var/lib/postgresql/data/pgdata
            - name: TZ
              value: Asia/Tokyo
```

さらに、TimeZoneについては今回検証するまで知らなかったのですが、コンテナの時刻はホストOSのそれを踏襲するのですね。TimeZoneを明示するために環境変数でTZ：Asia/Tokyoを与えています。


### ボリュームのマウント
これまでの設定を踏まえて、今回のPostgreSQLコンテナでは3つのボリュームをマウントします。

+ PGDATA、PostgreSQLのデータ格納ディレクトリ。Cephのブロックデバイス。
+ アーカイブログの保存先。Cephのブロックデバイス。
+ postgresql.conf。ConfigMap。

```YAML:pg-rook-sf.yaml（volumeMount部分の抜粋）
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: pg-rook-vol
            - mountPath: /mnt/postgresql/xlog
              name: pg-rook-vol-xlog
            - mountPath: /etc/postgresql
              readOnly: true
              name: postgres-conf
```

データベースの設計としてはデータとアーカイブログの領域を分けて配置するのは当然のことです。後日、バックアップ/リストアのケースで詳細は確認しますが、リストアされるデータ領域と継続的に保管されるだけのアーカイブログ領域はライフサイクルが異なるためです。

今回はデータ、アーカイブログ領域のどちらもCephのブロックデバイスをマウントしていますが、アーカイブログ側は特にブロックデバイスである必要はありません。NFSのようなファイルストレージで十分なケースも多いので、いずれそこは修正できればと思います。

ただ、この構成で問題になってくるのがアーカイブログを出力する```/mnt/postgresql/xlog```の権限です。コンテナを普通に起動するとPostgreSQLコンテナから権限不足で書き込みができなくなってしまいます。
これを避けるため、今回は**postStart**で該当ディレクトリの書き込み権限を付与をしています。

```YAML:pg-rook-sf.yaml（postStart部分の抜粋）
          lifecycle:
            postStart:
              exec:
                command:
                  - sh
                  - -c
                  - chmod a+w /mnt/postgresql/xlog/
```

これもファイルストレージを事前に用意しておけば、回避することができるはずです。

### Probeの設定
最後にliveness/readinessProbeの設定です。
ここではPostgreSQLが使う5432ポートに対して、tcpソケットでヘルスチェックを投げる設定を入れています。


```YAML:pg-rook-sf.yaml（postStart部分の抜粋）
          livenessProbe:
              tcpSocket:
                 port: 5432
              initialDelaySeconds: 10
              periodSeconds: 3
              timeoutSeconds: 3
              failureThreshold: 2
          readinessProbe:
              tcpSocket:
                 port: 5432
              initialDelaySeconds: 5
              periodSeconds: 3
              timeoutSeconds: 1
              successThreshold: 1
```

ただ、ポッド障害のケースで見たようにPostgreSQLのリカバリを待つケースなどでは上のヘルスチェックでは意味がないため、もう少し修正が必要そうです。

#まとめ
とりあえず動く形にはなりましたが、アーカイブログやProbe設定でまだまだ修正が必要な部分は残っています。PostgreSQL on Kubernetesしたいという方もこちらを参考に独自のYAMLを書いてみて下さい。

よろしくお願いします。