Crunchy Postgres Operator v5 の紹介

以前の記事 Crunchy Postgres Operatorの紹介 では、バージョン 4 を対象に Crunchy Postgres Operator のインストール方法、主な機能について紹介しました。2021 年にリリースされた バージョン 5 (v5) では、インストールや管理方法が大幅に変更され、より簡単に構築・運用できるようになりました。

本記事では、 Crunchy Postgres Operator (PGO) v5 のインストール方法と主な機能の使い方について紹介します。

Operator を使う理由や Crunchy Postgres Operator の概要については以前の記事 Kubernetes PostgreSQL Operatorの紹介Crunchy Postgres Operatorの紹介 を参照してください。

PGO v5 の主な変更点

以前 v4 で紹介した以下の機能は、v5 でも引き続き利用できます。利用する際の設定方法は v4 までと異なるので、詳しい設定方法については後述します。

  • PostgreSQL HA クラスタのデプロイ・管理
  • ストリーミングレプリケーション (同期・非同期)
  • フェイルオーバー
  • バックアップ
  • WALアーカイブ
  • リストア・PITR
  • アップグレード
  • コネクションプール
  • モニタリング

PGO v5 における構築・運用の主な変更点は以下の通りです。

  • PGO v5 では、Kustomize または Helm を利用し、PGO をインストールします。v4 までの方法に比べて、より簡単にインストールできるようになりました。
  • PGO v4 では基本的に pgo クライアントコマンドを利用して各種操作を行っていましたが、PGO v5 では pgo クライアントコマンドが廃止されました。PGO v5では、Kubernetes で一般的によく使われるコマンドラインツール kubectl を利用して PostgreSQL の構築および管理を行います。
  • PostgreSQL インスタンスは StatefulSet を使用するようになりました。
  • PGO v4 までは PostgreSQL のマイナーバージョンアップを行うために、「PGO 自身のアップデート」と「PostgreSQL のアップデート」の2段階のアップデートを実行する必要がありましたが、PGO v5 では、PGO のバージョンを更新せず、PostgreSQL のイメージをアップデートするだけでマイナーバージョンアップができるようになりました。

その他の改良や変更点についてはPGO 5.0.0 のリリースノートを参照してください。

PGO v5 の動作環境

本記事執筆時点 (2022 年 3 月) では、以下の環境で動作確認が行われています。

  • Kubernetes 1.19 以降
  • OpenShift 4.6 以降
  • Rancher
  • Google Kubernetes Engine (GKE), Anthos を含む
  • Amazon EKS
  • Microsoft AKS
  • VMware Tanzu

検証環境およびバージョン情報

本記事では Kubernetes のマネージドサービスである Amazon EKS 上で PGO v5 のデプロイ・動作確認を行います。

環境の構成およびバージョン情報は以下のとおりです。各ソフトウェアは 2022 年 3 月時点での最新バージョンを利用しています。

  • PGO 5.0.5
  • Amazon EKS
    • Kubernetes バージョン: 1.21
    • ノード数: 3ノード
    • ノードタイプ: t3.medium
  • オブジェクトストレージ: Amazon S3
  • ローカル作業環境: CentOS 7.9 (VirtualBox 上の VM)

検証内容

本記事では、PGO v5 を用いて PostgreSQL クラスタを構築し、以下の機能について動作確認を行います。

  • PGO のデプロイ
  • PostgreSQL クラスタの作成
  • psql による接続確認
  • PostgreSQL の設定
  • フェイルオーバー
  • スケーリング
  • ボリュームサイズの拡張
  • バックアップ
  • リストア・PITR
  • モニタリング

前提条件

PGO をデプロイする前に、次の準備が完了していることが前提となります。

    • EKS クラスタの作成 (こちらを参照)
    • Amazon S3 バケットの作成 (こちらを参照)
      ※作成したバケットの名前は PostgresCluster (後述) の YAML ファイルに設定します。

PGO のデプロイ

PGO v5 以降、PGO のデプロイは Kustomize または Helm を使うことになりました。本記事では、Kustomize を用いて PGO をデプロイします。

各種サンプル設定ファイルを含むpostgres-operator-examplesリポジトリを作業環境に複製します。

$ git clone https://github.com/CrunchyData/postgres-operator-examples.git
$ cd postgres-operator-examples/

PGO をデプロイします。

$ kubectl apply -k kustomize/install

上記コマンドを実行すると、postgres-operator という名前空間が作成され、PGO のデプロイに必要なすべてのオブジェクトがこの名前空間に作成されます。

$ kubectl get pods -n postgres-operator
NAME                   READY   STATUS    RESTARTS   AGE
pgo-59c4f987b6-cd6pn   1/1     Running   0          29s

ストレージクラスの作成

Amazon EKS ではデフォルトで gp2 という名前のストレージクラスが作成されていますが、本記事では以下の属性を持つ PGO 専用のストレージクラスを作成します。

  • allowVolumeExpansion: ボリュームの拡張を許可するように true を設定する。
  • reclaimPolicy: PVC が削除された際に PV を残すように Retain を設定する。
$ cat > ~/pgo-sc.yaml <<EOF
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: pgo
provisioner: kubernetes.io/aws-ebs
parameters:
  type: gp2
  fsType: ext4
allowVolumeExpansion: true
reclaimPolicy: Retain
EOF

$ kubectl apply -f ~/pgo-sc.yaml

作成したストレージクラス pgo を確認します。

$ kubectl get sc pgo
NAME   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
pgo    kubernetes.io/aws-ebs   Retain          Immediate           true                   6m36s

PostgreSQL クラスタの作成

PostgreSQL クラスタをデプロイします。

今回は以下のような構成の PostgreSQL クラスタを作成します。

  • master 1台、replica 2台
  • 同期レプリケーション
  • 自動フェイルオーバー (デフォルト)
  • WAL アーカイブ (デフォルト)
  • 永続ボリュームと Amazon S3 にバックアップと WAL アーカイブを保存

取得したバックアップや WAL アーカイブは永続ボリュームのほか、S3 や GCS、Azure Blob Storage などのオブジェクトストレージにも保存できます。今回は、Amazon S3 を使います。GCS や Azure Blob Storage の設定については、こちらを参照してください。

postgres-operator-examples リポジトリに各種構成のサンプル設定ファイルが用意されているので、それらのファイルを元に設定ファイルを作成します。今回は S3 を利用するために、kustomize/s3 配下のサンプルファイルをコピーし、必要に応じて編集します。

$ cp -r  kustomize/s3 kustomize/my-postgres

PGO v5 では PostgresCluster リソース (CRD) を使って、PostgreSQL クラスタのデプロイ・管理を行います。 PostgresCluster 設定のサンプルは postgres.yaml ファイルに記述されているので、必要に応じて編集します。

今回は以下のような YAML ファイルを利用して PostgresCluster をデプロイします。

$ cat kustomize/my-postgres/postgres.yaml
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: my-postgres
spec:
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-14.2-0
  postgresVersion: 14
  instances:
    - name: pg-1
      replicas: 3
      dataVolumeClaimSpec:
        accessModes:
        - "ReadWriteOnce"
        resources:
          requests:
            storage: 10Gi
        storageClassName: pgo
  backups:
    pgbackrest:
      image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:centos8-2.36-1
      configuration:
      - secret:
          name: pgo-s3-creds
      global:
        repo2-path: /pgbackrest/postgres-operator/my-postgres/repo2
      repos:
      - name: repo1
        volume:
          volumeClaimSpec:
            accessModes:
            - "ReadWriteOnce"
            resources:
              requests:
                storage: 10Gi
            storageClassName: pgo
      - name: repo2
        s3:
          bucket: "my-postgres"
          endpoint: "s3.us-west-2.amazonaws.com"
          region: "us-west-2"
  patroni:
    dynamicConfiguration:
      synchronous_mode: true
  users:
    - name: postgres
    - name: testuser
      databases:
        - testdb

パラメータの意味は以下のとおりです。

  • spec.instances.replicas: レプリカの数を指定します。上記設定では、master 1台、replica 2台の PostgreSQL クラスタが作成されます。
  • spec.backups.pgbackrest.repos.name: バックアップと WAL アーカイブの保存先を設定します。repoN 形式で複数の保存先を指定可能です。上記設定では、永続ボリュームを repo1、S3 を repo2 と設定しています。
  • spec.backups.pgbackrest.repos.s3: Amazon S3 バケット情報を設定します。今回は前述の「前提条件」であらかじめ作成したバケットを利用します。
  • spec.instances.dataVolumeClaimSpec: PostgreSQL データ保存用の PVC を設定します。
  • spec.backups.pgbackrest.repos.volume.volumeClaimSpec: バックアップと WAL アーカイブ保存用の PVC を設定します。
  • spec.patroni.dynamicConfiguration.synchronous_mode: 同期レプリケーションを利用するか否かを true/false で設定します。
  • spec.users: デプロイ時に作成する PostgreSQL ユーザとデータベースを指定します。上記設定では、postgres と testuser ユーザ、testdb データベースが作成され、testuser に testdb へのアクセス権限が付与されます。

次に、Amazon S3 にアクセスするためのクレデンシャルを登録します。repoN には、上記 postgres.yaml に設定した S3 のリポジトリ名を指定します。

$ cp kustomize/my-postgres/s3.conf{.example,}
$ vi kustomize/my-postgres/s3.conf
[global]
repo2-s3-key=<アクセスキー>
repo2-s3-key-secret=<シークレットアクセスキー>

マニフェストの設定が完了したら、PostgreSQL クラスタをデプロイします。

$ kubectl apply -k kustomize/my-postgres/

PostgreSQL クラスタの状態を確認します。3つの Pod が作成されます。各 Pod が master か replica かはラベルで確認できます。

$ kubectl get pods -n postgres-operator --selector=postgres-operator.crunchydata.com/instance-set \
  -L postgres-operator.crunchydata.com/role
NAME                      READY   STATUS    RESTARTS   AGE     ROLE
my-postgres-pg-1-45cl-0   3/3     Running   0          7m14s   master
my-postgres-pg-1-ctj8-0   3/3     Running   0          7m14s   replica
my-postgres-pg-1-rgc4-0   3/3     Running   0          7m14s   replica

psql による接続確認

Kubernetes クラスタ内のノードから、または他の Pod からは上記の PostgreSQL の Pod に接続できますが、作業環境から psql でアクセスしたい場合は kubectlport-forward 機能を利用します。
ここでは master Pod の 5432 ポートをローカルの 5432 へポートフォワードを行います。
PG_CLUSTER_MASTER_POD には master の Pod 名が入ります。

$ PG_CLUSTER_MASTER_POD=$(kubectl get pod -n postgres-operator -o name -l postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/role=master)
$ kubectl -n postgres-operator port-forward "${PG_CLUSTER_MASTER_POD}" 5432:5432 &

psql で接続します。testuser ユーザの情報を格納する Secret からデータベースの接続情報を取得します。

$ PG_CLUSTER_USER_SECRET_NAME=my-postgres-pguser-testuser
$ PGPASSWORD=$(kubectl get secrets -n postgres-operator "${PG_CLUSTER_USER_SECRET_NAME}" -o go-template='{{.data.password | base64decode}}') \
PGUSER=$(kubectl get secrets -n postgres-operator "${PG_CLUSTER_USER_SECRET_NAME}" -o go-template='{{.data.user | base64decode}}') \
PGDATABASE=$(kubectl get secrets -n postgres-operator "${PG_CLUSTER_USER_SECRET_NAME}" -o go-template='{{.data.dbname | base64decode}}') \
psql -h localhost
Handling connection for 5432
psql (14.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-ECDSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

testdb=> \du+
                                            List of roles
  Role name   |                         Attributes                         | Member of | Description 
--------------+------------------------------------------------------------+-----------+-------------
 _crunchyrepl | Replication                                                | {}        | 
 postgres     | Superuser, Create role, Create DB, Replication, Bypass RLS | {}        | 
 testuser     |                                                            | {}        |

次に、postgres スーパーユーザで接続し、レプリケーションの状態を確認します。

$ PG_CLUSTER_USER_SECRET_NAME=my-postgres-pguser-postgres
$ PGPASSWORD=$(kubectl get secrets -n postgres-operator "${PG_CLUSTER_USER_SECRET_NAME}" -o go-template='{{.data.password | base64decode}}') \
PGUSER=$(kubectl get secrets -n postgres-operator "${PG_CLUSTER_USER_SECRET_NAME}" -o go-template='{{.data.user | base64decode}}') \
psql -h localhost
Handling connection for 5432
psql (14.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-ECDSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# select application_name, state, sync_state from pg_stat_replication;
    application_name     |   state   | sync_state 
-------------------------+-----------+------------
 my-postgres-pg-1-rgc4-0 | streaming | sync
 my-postgres-pg-1-ctj8-0 | streaming | async
(2 rows)

PostgreSQL の設定

PostgreSQL のパラメータは spec.patroni.dynamicConfiguration.postgresql.parameters で設定します。
例えば、log_statement: all の設定を追加して適用すると、自動的に PostgreSQL に反映されます。

vi kustomize/my-postgres/postgres.yaml
(以下を追加)
spec:
  patroni:
    dynamicConfiguration:
      synchronous_mode: true
      postgresql:
        parameters:
          log_statement: all

$ kubectl apply -k kustomize/my-postgres/

設定値が変更されていることを確認します。

$ PG_CLUSTER_MASTER_POD=$(kubectl get pod -n postgres-operator -o name -l postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/role=master)
$ kubectl exec ${PG_CLUSTER_MASTER_POD} -n postgres-operator -it -- psql
Handling connection for 5432
psql (14.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-ECDSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# show log_statement;
 log_statement 
---------------
 all
(1 row)

ただし、PostgreSQL の Pod の再起動が必要になるパラメータもあります。
例えば、max_connections の設定値を変更した場合、Pod の再起動が必要になります。
以下のコマンドを実行し、アノテーションを編集することで、PGO が自動的に変更を検出し、1つずつ Pod の再起動を行います。

$ kubectl patch postgrescluster/my-postgres -n postgres-operator --type merge \
  --patch '{"spec":{"metadata":{"annotations":{"restarted":"'"$(date)"'"}}}}'

フェイルオーバー

PostgreSQL の master Pod に故障が発生した場合は、自動的にフェイルオーバーしていずれかの replica が master に昇格します。また、定義した「あるべき状態」を満たすために、新たな replica が起動します。この節では、PGO のフェイルオーバーの動作を検証してみます。

まず、master Pod を削除します。

$ PG_CLUSTER_MASTER_POD=$(kubectl get pod -n postgres-operator -o name -l postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/role=master)
$ kubectl delete ${PG_CLUSTER_MASTER_POD} -n postgres-operator
pod "my-postgres-pg-1-45cl-0" deleted

すると、replica Pod「my-postgres-pg-1-ctj8-0」が master に昇格し、旧 master Pod が replica として再起動されることを確認できます。

$ kubectl get pods -n postgres-operator --selector=postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/instance-set \
  -L postgres-operator.crunchydata.com/role
NAME                      READY   STATUS    RESTARTS   AGE   ROLE
my-postgres-pg-1-45cl-0   3/3     Running   0          74s   replica
my-postgres-pg-1-ctj8-0   3/3     Running   0          52m   master
my-postgres-pg-1-rgc4-0   3/3     Running   0          52m   replica

スケーリング

スケーリングとは、replica を追加または削除することです。
PostgreSQL クラスタに replica を追加することをクラスタのスケールアップ、クラスタから replica を削除することをクラスタのスケールダウンといいます。
スケーリングすることにより、データベースの負荷分散や耐障害性の向上を実現できます。

YAML ファイルの replicas の数を変更し、適用するだけで、簡単にクラスタのスケールアップ/スケールダウンを行えます。
以下のように replicas の数を 3 から 4 に変更して適用すると、新しい replica Pod が起動します。

$ vi kustomize/my-postgres/postgres.yaml
(replicas を 3 から 4 に変更)
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: my-postgres
spec:
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-14.2-0
  postgresVersion: 14
  instances:
    - name: pg-1
      replicas: 4
(省略)

$ kubectl apply -k kustomize/my-postgres/

新しい replica が起動することを確認します。

$ kubectl get pods -n postgres-operator --selector=postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/instance-set \
  -L postgres-operator.crunchydata.com/role
NAME                      READY   STATUS    RESTARTS   AGE     ROLE
my-postgres-pg-1-2fzw-0   3/3     Running   0          27s     replica
my-postgres-pg-1-45cl-0   3/3     Running   0          8m14s   replica
my-postgres-pg-1-ctj8-0   3/3     Running   0          59m     master
my-postgres-pg-1-rgc4-0   3/3     Running   0          59m     replica

$ PG_CLUSTER_MASTER_POD=$(kubectl get pod -n postgres-operator -o name -l postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/role=master)
$ kubectl exec ${PG_CLUSTER_MASTER_POD} -n postgres-operator -it -- psql
Handling connection for 5432
psql (14.2)
SSL connection (protocol: TLSv1.2, cipher: ECDHE-ECDSA-AES256-GCM-SHA384, bits: 256, compression: off)
Type "help" for help.

postgres=# select application_name, state, sync_state from pg_stat_replication;
    application_name     |   state   | sync_state 
-------------------------+-----------+------------
 my-postgres-pg-1-45cl-0 | streaming | async
 my-postgres-pg-1-rgc4-0 | streaming | sync
 my-postgres-pg-1-2fzw-0 | streaming | async
(3 rows)

元の設定に戻したい場合には、replicas の数を 4 から 3 に減らすと、いずれかの replica Pod が削除されます。

$ vi kustomize/my-postgres/postgres.yaml
(replicas を 4 から 3 に変更)
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: my-postgres
spec:
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-14.2-0
  postgresVersion: 14
  instances:
    - name: pg-1
      replicas: 3
(省略)

$ kubectl apply -k kustomize/my-postgres/

replica Pod が削除されていることを確認します。

$ kubectl get pods -n postgres-operator --selector=postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/instance-set \
  -L postgres-operator.crunchydata.com/role
NAME                      READY   STATUS    RESTARTS   AGE   ROLE
my-postgres-pg-1-45cl-0   3/3     Running   0          15m   replica
my-postgres-pg-1-ctj8-0   3/3     Running   0          67m   master
my-postgres-pg-1-rgc4-0   3/3     Running   0          67m   replica

ボリュームサイズの拡張

Kubernetes では、ボリュームの拡張が許可される場合のみ、既存の永続ボリューム (PV) のサイズを拡張できます。ボリュームの拡張を許可するかはストレージクラスの allowVolumeExpansion 属性で設定します。

ボリュームの拡張を行う前に、デプロイ済みの PostgreSQL クラスタが利用しているストレージクラスの allowVolumeExpansion の設定値が true であることを確認します。

$ kubectl get sc pgo
NAME   PROVISIONER             RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
pgo    kubernetes.io/aws-ebs   Retain          Immediate           true                   97m

PV と PVC のサイズを確認します。

$ kubectl get pv,pvc -n postgres-operator
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                            STORAGECLASS   REASON   AGE
persistentvolume/pvc-04060f68-9590-4af1-a341-2dd8e1b42055   10Gi       RWO            Delete           Bound    postgres-operator/my-postgres-repo1              pgo                     91m
persistentvolume/pvc-1e13602d-1455-46b7-bfb0-7dee549ac70f   10Gi       RWO            Delete           Bound    postgres-operator/my-postgres-pg-1-rgc4-pgdata   pgo                     91m
persistentvolume/pvc-4dde75fe-c392-4f75-b483-326dcd6e5d46   10Gi       RWO            Delete           Bound    postgres-operator/my-postgres-pg-1-45cl-pgdata   pgo                     91m
persistentvolume/pvc-ff62508e-e743-482d-a67b-19726c075c2c   10Gi       RWO            Delete           Bound    postgres-operator/my-postgres-pg-1-ctj8-pgdata   pgo                     91m

NAME                                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/my-postgres-pg-1-45cl-pgdata   Bound    pvc-4dde75fe-c392-4f75-b483-326dcd6e5d46   10Gi       RWO            pgo            91m
persistentvolumeclaim/my-postgres-pg-1-ctj8-pgdata   Bound    pvc-ff62508e-e743-482d-a67b-19726c075c2c   10Gi       RWO            pgo            91m
persistentvolumeclaim/my-postgres-pg-1-rgc4-pgdata   Bound    pvc-1e13602d-1455-46b7-bfb0-7dee549ac70f   10Gi       RWO            pgo            91m
persistentvolumeclaim/my-postgres-repo1              Bound    pvc-04060f68-9590-4af1-a341-2dd8e1b42055   10Gi       RWO            pgo            91m

PostgreSQL のデータ保存用の PVC とバックアップ保存用の PVC の要求サイズを 20GB に変更してみます。

$ vi kustomize/my-postgres/postgres.yaml
(省略)
spec:
  image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:centos8-14.2-0
  postgresVersion: 14
  instances:
    - name: pg-1
      replicas: 3
      dataVolumeClaimSpec:
        accessModes:
        - "ReadWriteOnce"
        resources:
          requests:
            storage: 20Gi
        storageClassName: pgo
  backups:
    pgbackrest:
      - name: repo1
        volume:
          volumeClaimSpec:
            accessModes:
            - "ReadWriteOnce"
            resources:
              requests:
                storage: 20Gi
            storageClassName: pgo
(省略)

$ kubectl apply -k kustomize/my-postgres/

PVC と PV のサイズが拡張されていることを確認します。

$ kubectl get pv,pvc -n postgres-operator
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                            STORAGECLASS   REASON   AGE
persistentvolume/pvc-04060f68-9590-4af1-a341-2dd8e1b42055   20Gi       RWO            Delete           Bound    postgres-operator/my-postgres-repo1              pgo                     103m
persistentvolume/pvc-1e13602d-1455-46b7-bfb0-7dee549ac70f   20Gi       RWO            Delete           Bound    postgres-operator/my-postgres-pg-1-rgc4-pgdata   pgo                     103m
persistentvolume/pvc-4dde75fe-c392-4f75-b483-326dcd6e5d46   20Gi       RWO            Delete           Bound    postgres-operator/my-postgres-pg-1-45cl-pgdata   pgo                     103m
persistentvolume/pvc-ff62508e-e743-482d-a67b-19726c075c2c   20Gi       RWO            Delete           Bound    postgres-operator/my-postgres-pg-1-ctj8-pgdata   pgo                     103m

NAME                                                 STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/my-postgres-pg-1-45cl-pgdata   Bound    pvc-4dde75fe-c392-4f75-b483-326dcd6e5d46   20Gi       RWO            pgo            103m
persistentvolumeclaim/my-postgres-pg-1-ctj8-pgdata   Bound    pvc-ff62508e-e743-482d-a67b-19726c075c2c   20Gi       RWO            pgo            103m
persistentvolumeclaim/my-postgres-pg-1-rgc4-pgdata   Bound    pvc-1e13602d-1455-46b7-bfb0-7dee549ac70f   20Gi       RWO            pgo            103m
persistentvolumeclaim/my-postgres-repo1              Bound    pvc-04060f68-9590-4af1-a341-2dd8e1b42055   20Gi       RWO            pgo            103m

allowVolumeExpansion 機能に対応していないストレージでは、既存の永続ボリュームのサイズを拡張できないため、ボリュームサイズが大きい、新しい PostgreSQL クラスタを作成し、既存のクラスタと同期させる必要があります。詳細についてはこちらを参照してください。

バックアップ

PGO のバックアップ/リストアは内部的に pgBackRest のバックアップ/リストアユーティリティを利用しています。定期的にフルバックアップまたは差分バックアップを取得することができます。バックアップのスケジュールは cron 表記で指定します。

定期バックアップのスケジュールは spec.backups.pgbackrest.repos.schedules で設定します。
例えば、以下のように設定すると、毎日午前1時にフルバックアップ、4時間ごとに差分バックアップを実行するようになります。

$ vi kustomize/my-postgres/postgres.yaml
(省略)
spec:
  backups:
    pgbackrest:
      image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:centos8-2.36-1
      configuration:
      - secret:
          name: pgo-s3-creds
      global:
        repo2-path: /pgbackrest/postgres-operator/my-postgres/repo2
      repos:
      - name: repo1
        volume:
          volumeClaimSpec:
            accessModes:
            - "ReadWriteOnce"
            resources:
              requests:
                storage: 10Gi
      - name: repo2
        schedules:
          full: "0 1 * * *"
          incremental: "0 */4 * * *"
        s3:
          bucket: "crunchy-pg-backup"
          endpoint: "s3.us-west-2.amazonaws.com"
          region: "us-west-2"
(省略)

リストア

PostgreSQL クラスタの複製

PGO v5 では、既存の PostgreSQL クラスタを複製できます。
既存のクラスタを複製するには、spec.dataSource.postgresCluster にコピー元のクラスタ名、バックアップの格納先のリポジトリ名を指定します。

spec:
  dataSource:
    postgresCluster:
      clusterName: my-postgres
      repoName: repo2

デフォルトでは最新のデータにリストアしますが、spec.dataSource.postgresCluster.options を指定して、特定の時点までリストア可能です。

spec:
  dataSource:
    postgresCluster:
      clusterName: my-postgres
      repoName: repo2
      options:
      - --type=time
      - --target="2022-03-16 14:00:00+09"

既存の PostgreSQL クラスタの PITR

起動中の PostgreSQL クラスタを以前の時点にリストアするには spec.backups.pgbackrest.restore を設定します。既存のデータディレクトリでリストアを実行するので、クラスタが一旦停止され、作成しなおされます。

例えば、2022-03-16 14:00:00 時点までリストアする場合は、以下の手順を実行します。

まず、PostgresCluster リソースを設定する YAML ファイル postgres.yaml を編集し、適用します。
※この時点ではリストアが実行されません。

$ vi kustomize/my-postgres/postgres.yaml
(省略)
spec:
  backups:
    pgbackrest:
      restore:
        enabled: true
        repoName: repo2
        options:
        - --type=time
        - --target="2022-03-16 14:00:00+09"
(省略)

$ kubectl apply -k kustomize/my-postgres/

次に、PostgresCluster のアノテーションを設定します。
PostgresCluster のアノテーションを設定すると、リストアが実行されます。

$ kubectl annotate postgrescluster my-postgres  -n postgres-operator --overwrite \
  postgres-operator.crunchydata.com/pgbackrest-restore=id1

リストア完了後、リストアの設定を無効にしておきます。

$ vi kustomize/my-postgres/postgres.yaml
(省略)
spec:
  backups:
    pgbackrest:
      restore:
        enabled: false
(省略)

$ kubectl apply -k kustomize/my-postgres/

モニタリング

PGO は Prometheus によるモニタリング機能を提供しています。Kubernetes では Prometheus を利用した監視がよく使われています。Prometheus がクラスタの各種メトリクスを収集し、Grafana と連携して、Grafana のダッシュボードで Kubernetes クラスタと PostgreSQL を監視することができます。

モニタリング機能を利用するために、まず、各  Pod に Exporter サイドカーを追加する必要があります。

$ vi kustomize/my-postgres/postgres.yaml
(省略)
spec:
  monitoring:
    pgmonitor:
      exporter:
        image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-5.0.5-0
(省略)

$ kubectl apply -k kustomize/my-postgres

$ kubectl get pods -n postgres-operator --selector=postgres-operator.crunchydata.com/cluster=my-postgres,postgres-operator.crunchydata.com/instance-set \
  -L postgres-operator.crunchydata.com/role
NAME                      READY   STATUS    RESTARTS   AGE     ROLE
my-postgres-pg-1-45cl-0   4/4     Running   0          3m4s    master
my-postgres-pg-1-ctj8-0   4/4     Running   0          3m35s   replica
my-postgres-pg-1-rgc4-0   4/4     Running   0          2m14s   replica

次に、監視アプリケーション Prometheus、Grafana、Alertmanager をデプロイします。

Amazon EKS のデフォルトのストレージクラス gp2 を利用しても良いですが、今回は前述の手順で作成したストレージクラスを利用します。Prometheus、Grafana、Alertmanager の PVC を定義する YAML ファイルを編集します。
※ デフォルトのストレージクラスを利用する場合は編集不要です。

$ vi kustomize/monitoring/pvcs.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app.kubernetes.io/name: pgo-monitoring
    vendor: crunchydata
  name: alertmanagerdata
spec:
(省略)
  storageClassName: pgo
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app.kubernetes.io/name: pgo-monitoring
    vendor: crunchydata
  name: grafanadata
spec:
(省略)
  storageClassName: pgo
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  labels:
    app.kubernetes.io/name: pgo-monitoring
    vendor: crunchydata
  name: prometheusdata
spec:
(省略)
  storageClassName: pgo

Prometheus、Grafana、Alertmanager をデプロイします。

$ kubectl apply -k kustomize/monitoring

Prometheus、Grafana、Alertmanager の Pod が起動していることを確認できます。

$ kubectl get pods -n postgres-operator --selector=app.kubernetes.io/name=postgres-operator-monitoring
NAME                                   READY   STATUS    RESTARTS   AGE
crunchy-alertmanager-f7b79cbd8-lfv7s   1/1     Running   0          4m47s
crunchy-grafana-548446d646-lqw4l       1/1     Running   0          4m45s
crunchy-prometheus-556f754797-98lcn    1/1     Running   0          4m44s

作業環境のブラウザから Grafana にアクセスするために、Grafana Pod の 3000 ポートをローカルの 3000 へポートフォワードを行います。

$ GRAFANA_POD=$(kubectl get pod -n postgres-operator -o name -l app.kubernetes.io/name=postgres-operator-monitoring,name=crunchy-grafana)
$ kubectl -n postgres-operator port-forward ${GRAFANA_POD} 3000:3000 &

ブラウザで http://localhost:3000 にアクセスすると、Grafana のログイン画面が表示されます。Grafana のログインユーザ名とパスワードは以下のコマンドで確認できます。

$ kubectl get secret grafana-secret -n postgres-operator -ojsonpath='{.data.username}' | base64 --decode ; echo 
$ kubectl get secret grafana-secret -n postgres-operator -ojsonpath='{.data.password}' | base64 --decode ; echo

Grafana にログインし、ダッシュボードで Kubernetes クラスタや PostgreSQL の稼働状況を確認できます。

おわりに

今回は Crunchy Postgres Operator v5 について紹介しました。

インストールや管理方法が大幅に変更され、kubectl が使用できるようになり、以前のバージョンより構築・管理がさらに容易になりました。さらに詳しく知りたい方は、公式ドキュメントをご覧ください。