検索
連載

Kubernetesでもデータベースを本格運用――「PostgreSQL Operator PGO」を使い倒すCloud Nativeチートシート(19)

Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する連載。今回は、Operatorを利用して、Kubernetes上でデータベースを動作させる方法とその利点を紹介します。

Share
Tweet
LINE
Hatena

 Kubernetesやクラウドネイティブをより便利に利用する技術やツールの概要、使い方を凝縮して紹介する本連載「Cloud Nativeチートシート」。連載第8回では、「Operator」を利用してKubernetesを拡張する方法を紹介しました。

 今回はそのOperatorを利用して、Kubernetes上で「PostgreSQL」を動作させる方法とその利点を紹介します。

なぜDBをKubernetes上で動かすのか

DBマスター:え、Kubernetesでデータベース(DB)を動かしたことないの?

DB初心者:Kubernetes上でDBを動かしていいんですか? じっちゃんは「Kubernetes上でDBは絶対動かすな」って言ってましたよ。

DBマスター:それは、おじいちゃんだけに情報が古いね。今は、Operatorっていう便利なもんがあって、安心安全にDBを運用できるんだよ。

 さて、そもそもなぜDBをKubernetes上で動かすのでしょうか?

 いろいろなモチベーションがあるとは思いますが、オンプレミスでKubernetesを利用している場合、「Amazon Relational Database Service」(RDS)のようなクラウドベンダーが提供するDBサービスを利用できないので、自分でPostgreSQLのようなオープンソースソフトウェア(OSS)のDBを構築したいというケースがあるでしょう。

 もちろんPostgreSQLのPodを起動するだけなら、DBのコンテナイメージを利用できますが、耐障害性やスケーラビリティ、バックアップやモニタリングまで考えると、PostgreSQL本体以外にもさまざまなプロダクトや、その設計と管理が必要になります。

 また、アプリケーションのマイクロサービス化が進むにつれて、単一の巨大なDBではなく、サービスごとに小分けにしたDBを作成する構成も取られるようになります。この場合、多数のDBを管理する必要がありますが、従来の運用ではDBの数が増加するにつれて運用コストも増加しがちです。

 このような背景の下、多数のDBを効率的に管理するために、DBも「Deployment」「StatefulSet」などのようにKubernetesのリソースとして扱いたいというのも耳にするケースです。

 この点、「PostgreSQL Operator」を利用すれば、運用に必要なものが一通りそろったPostgreSQLクラスタを簡単に構築でき、他のアプリケーション同様Kubernetes上のリソースとして運用できます。

PostgreSQL Operatorにもいろいろある

 本記事では、PostgreSQL Operatorを動かして例示しながら、その手軽さやKubernetes上でDBを動かす利点を見ていきます。

 2022年7月現在、PostgreSQL Operatorは、PostgreSQL開発コミュニティーが公式に開発、提供しているものはありません。さまざまな企業やコミュニティーがそれぞれ開発したPostgreSQL Operatorを提供している状況です(※1)。

※1:PostgreSQL Operatorの比較については、NTTデータが公開しているスライドがあるので、PostgreSQL Operatorごとの機能の違いや、PGO以外のOperatorに興味のある方はこちらもご参照ください。

 今回は数あるPostgreSQL Operatorの中でも、PostgreSQLコア開発者が複数在籍する企業Crunchy Dataが開発を主導している「PGO」を取り上げます。

PGOが作成するPostgreSQL環境のアーキテクチャ

 今回利用する機能の範囲で、PGOが作成するPostgreSQLクラスタの概要を図示します。

 PostgreSQLのインスタンスには、参照/更新両方のリクエストが処理可能なプライマリと、参照のみ処理可能なリードレプリカ(PostgreSQLでは通常「ホットスタンバイ」といいます)がありますが、上の図では、プライマリとリードレプリカ用のPodがそれぞれ1インスタンスずつ起動している状態を示しています。後ほど解説するように、PGOでは2台以上のリードレプリカを追加することも可能です。

 PostgreSQLが動作するPod内には、PostgreSQLのdatabaseコンテナの他、設定ファイルや証明書を更新するコンテナ、バックアップサーバと通信するサイドカーコンテナが起動します。オプションでモニタリング用の情報をエクスポートするサイドカーコンテナも起動できます。

 PostgreSQLのPodはStatefulSetから管理されます。リードレプリカを追加した場合、新しいStatefulSetを作成します。通常1つのStatefulSetのレプリカ数を増やすところですが、PGOではそれぞれ独立したStatefulSetを作成します。これも後ほど見るように、PGOが各StatefulSetを管理することで、StatefulSet組み込みのローリングアップデートよりも、PostgreSQLの状態を踏まえた柔軟なアップデートになります。

 プライマリとリードレプリカそれぞれにアクセスするServiceが作成されます。ユーザーはこれらのServiceにアクセスすることで、系切り替えなどPostgreSQLクラスタ内部の状態が変化しても、それを意識することなくPostgreSQLにアクセスできます。

 バックアップサーバ用のStatefulSetも作成されます。バックアップにはPostgreSQL用のバックアップソフトウェア「pgBackRest」が組み込まれています。

PGOを使ってオールインワンのPostgreSQL環境を作る

Kubernetesクラスタの準備

 動作確認用のKubernetesクラスタを準備します。

 PGOはKubernetes、OpenShift、Rancher、Google Kubernetes Engine(GKE)、Amazon Elastic Kubernetes Service(EKS)といったプラットフォーム上で動作確認されています。Kubernetesは、1.20以上のバージョンがサポート対象となっています。サポートされている各プラットフォームのバージョンは公式サイトを参照してください。

 これらのプラットフォームの他、「minikube」「kind」などでも利用可能です。

 用意した環境でデフォルトの「Storage Class」が存在することを確認します。これは、PostgreSQLクラスタを作成するときなど、ボリュームを動的に払い出すために利用されます。なお、カスタムリソースのマニフェストで指定すれば、デフォルト以外のStorage Classを利用することもできます。

 こちらはminikubeを利用した場合の例です。

$ kubectl get sc
NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  54s

PGOのインストール

 PGOは執筆時点の最新バージョン、5.1.1を利用します。Crunchy DataがPGOのインストール資材や、PostgreSQLクラスタを作成する「Kustomize」のサンプルなどをGitHub上に公開しているので、こちらをクローンします。

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

 クローンしたディレクトリに移動します。以降の作業は本ディレクトリ内で実施します。

$ cd postgres-operator-examples

 本記事を執筆した時点のコミットIDを指定してブランチを作成、チェックアウトします。

 HEADを利用する場合、本記事とは動作が変わる可能性があるので、公式ドキュメントも参照してください。

$ git checkout -b pgotest 12085b73c85f3c30f0a0b1d3f5fe17b22c3eede3

 KustomizeによってOperatorのインストールやPostgreSQLクラスタの構成がテンプレート化されているので、これを利用します。次の通り実行することで、PGO用のNamespace「postgres-operator」が作成され、PGOがインストールされます。

$ kubectl apply -k kustomize/install/namespace
$ kubectl apply --server-side -k kustomize/install/default

コラム 「--server-side」オプションについて

 「kubectl apply」コマンドを実行すると、内部ではマニフェストとKubernetesクラスタの差分を確認し、差分がある場合その部分を変更しますが、この処理は従来kubectlでのみ可能でした。

 このため、自分の知らないところで別の人がKubernetesを変更した場合、その変更に気付くことなくkubectl applyで上書きしてしまったり、kubectl以外のクライアントからこの仕組みを利用しようとしたりすると、kubectlのコードを利用するといった対応が必要となる問題がありました。

 本問題の対応として、この処理をkubectlではなくKubernetesで実施する仕組みが開発されました。それを有効化するオプションが--server-sideです。


 OperatorのPodの「STATUS」が「Running」になるまで待ちます。

$ kubectl -n postgres-operator get pods \
  --selector=postgres-operator.crunchydata.com/control-plane=postgres-operator \
  --field-selector=status.phase=Running
 
NAME                   READY   STATUS    RESTARTS   AGE
pgo-548fdd84bd-8hkhw   1/1     Running   0          12m

PostgreSQLクラスタの作成

 PGOが動作を開始したら、PostgreSQLクラスタを作成できます。具体的には、カスタムリソースとして「PostgresCluster」を利用できるようになります。これを利用することで、PostgreSQLのクラスタを作成します。

 サンプルとして幾つかのPostgreSQLクラスタの構成が用意されていますが、今回は最もシンプルなPostgreSQL1台構成のKustomize「kustomize/postgres」を利用します。

 マニフェストを適用する前に、中身を確認してみましょう。以下「kustomize/postgres/postgres.yaml」に簡単な説明を追記していますが、詳細やその他に設定可能な項目については、PGOのCRD Referenceを参照してください。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster # カスタムリソース名
  3 metadata:
  4   name: hippo # PostgreSQLクラスタの名前
  5 spec:
  6   image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.3-0 # PostgreSQLのコンテナイメージ
  7   postgresVersion: 14 # PostgreSQLのバージョン
  8   instances: # PostgreSQL Podの設定
  9     - name: instance1
 10       dataVolumeClaimSpec: # PostgreSQLのデータを保存するPVC(Persistent Volume Claim)の設定。本記述の場合、defaultのStorage Classを利用してPVCを動的に作成
 11        accessModes:
 12        - "ReadWriteOnce"
 13          resources:
 14          requests:
 15            storage: 1Gi
 16  backups:
 17    pgbackrest: # PostgreSQLのバックアップソフトウェア、pgBackRestの設定
 18      image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.38-1 # pgBackRestのコンテナイメージ
 19      repos: # バックアップの保存先を指定
 20      - name: repo1
 21        volume:
 22          volumeClaimSpec: # バックアップを保存するPVCの設定。本記述の場合、defaultのStorage Classを利用してPVCを動的に作成
 23            accessModes:
 24            - "ReadWriteOnce"
 25            resources:
 26              requests:
 27                storage: 1Gi

 PostgresClusterリソースをKubernetes上に作成します。KustomizeでYAMLが定義されているので、次のように「-k」オプションを利用して適用します。

$ kubectl apply -k kustomize/postgres

 PostgreSQLのPodのSTATUSがRunningになるまで待ちます。YAMLファイルで確認したコンテナイメージのサイズが1GB程度とそれなりに大きいので、初回はPod起動までに時間がかかることもあります(※2)が、気長に待ちましょう。

※2:筆者の環境では1時間ほどかかることもありました。

 PostgreSQLのサービスを提供するコンテナの他、先述した設定ファイルや証明書の更新、バックアップサーバと通信するコンテナが起動し、合計4つのコンテナが起動します。

 次のコマンドでは、「hippo」という名前のPostgreSQLクラスタについて、PostgreSQLインスタンスの情報を取得しています。

$ kubectl -n postgres-operator get pods \
  --selector=postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/instance
 
  NAME                     READY   STATUS    RESTARTS   AGE
hippo-instance1-w7sc-0   4/4     Running   0          85m
 
$ kubectl -n postgres-operator get pod hippo-instance1-w7sc-0 \
  -o jsonpath="{.spec.containers[*].name}"
database replication-cert-copy pgbackrest pgbackrest-config

 PostgreSQLのデータ保存用、およびバックアップデータ保存用のPVCが払い出されていることを確認できます。

$ kubectl -n postgres-operator get pvc
 
NAME                          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
hippo-instance1-w7sc-pgdata   Bound    pvc-d56cff2f-da29-4e0f-b317-0632c49d8731   1Gi        RWO            standard       47s
hippo-repo1                   Bound    pvc-94850c1e-ab9f-4f5e-aa22-d7cda2b60266   1Gi        RWO            standard       47s

 これでPostgreSQLのサービスが利用可能になりました。

PostgreSQLへの接続確認

 作成したPostgreSQLに接続して、サービスが利用できることを確認します。接続時にパスワード認証が必要になるので、「Secret」からパスワードを確認します。次の例パスワードを環境変数「HIPPO_CLIENT_PGPASSWORD」に保存しています。

$ HIPPO_CLIENT_PGPASSWORD=`kubectl -n postgres-operator get secret hippo-pguser-hippo -o json | jq -r .data.password | base64 --decode`

 クライアントとしてDocker公式イメージの「postgresコンテナ」を利用して、テスト用にテーブル「t1」を作成して、1件データを投入してみます。接続先は、「hippo-ha」というServiceが払い出されているので、これを利用します。

$ kubectl -n postgres-operator run --image=postgres:14 --restart=Never --rm -it client --env="PGPASSWORD=${HIPPO_CLIENT_PGPASSWORD}" --command -- bash
If you don't see a command prompt, try pressing enter.
root@client:/# psql -h hippo-ha -U hippo -d postgres
psql (14.4 (Debian 14.4-1.pgdg110+1))
SSL connection (protocol: TLSv1.3, cipher: TLS_AES_256_GCM_SHA384, bits: 256, compression: off)
Type "help" for help.
 
postgres=> CREATE TABLE t1(t TEXT);
CREATE TABLE
postgres=> INSERT INTO t1 VALUES ('test');
INSERT 0 1
postgres=> \q
root@client:/# exit
exit

 無事PostgreSQLに接続し、サービスを利用できることを確認できました。

Kubernetes上で動かしたPostgreSQLの運用性を確認する

 機能的にPostgreSQLの動作を確認できましたが、これだけだと、PGOを利用せず、普通にPostgreSQLのコンテナを立ち上げたの大きな違いはありません。

 ここからはモニタリングやリードレプリカの追加、バージョンアップなどPGOで提供される運用機能を見ていきましょう。

 いずれもマニフェストを編集して適用することで、PostgreSQLクラスタの構成変更や運用操作を実現できます。

モニタリング機能の追加と確認

 kustomize/postgres/postgres.yamlを編集します。28〜31行目を新規に追加しています。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster
  3 metadata:
  4   name: hippo
  5 spec:
    ...(略)...
 16   backups:
    ...(略)...
 28   monitoring:
 29     pgmonitor:
 30       exporter:
 31         image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres-exporter:ubi8-5.1.1-0

 変更を適用します。

$ kubectl apply -k kustomize/postgres

 先ほどまでは「4/4」だった「READY」が、「5/5」になり、コンテナが1つ追加されたことを確認します。追加されたコンテナは、Prometheusサーバからのリクエストに応じてPostgreSQLのメトリクスを返す「Exporter」です。

$ kubectl -n postgres-operator get pods --selector=postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/instance
NAME                     READY   STATUS    RESTARTS   AGE
hippo-instance1-w7sc-0   5/5     Running   0          28m

 メトリクスを取得、表示するには、モニタリングツールが必要です。PGOには、組み込みのモニタリングツールが用意されているので、ここでは、PGO組み込みのモニタリングツール(「Prometheus」「Grafana」「Alertmanager」)をインストールします。

$ kubectl apply -k kustomize/monitoring

 こちらも各PodのSTATUSがRunningになるまで待ちます。

$ kubectl -n postgres-operator get pods --selector=app.kubernetes.io/name=postgres-operator-monitoring
NAME                                    READY   STATUS    RESTARTS   AGE
crunchy-alertmanager-5fdb768b96-zf46n   1/1     Running   0          10m
crunchy-grafana-85799958b8-cbw8q        1/1     Running   0          10m
crunchy-prometheus-67b84d64b9-b29c6     1/1     Running   0          10m

 これでPrometheus、Grafana、Alertmanagerが利用可能になりました。試しにポートフォワードを利用してローカル端末のブラウザからGrafanaにアクセスしてみます。

 下記のコマンドをローカル端末上で実行し、ローカルの3000番ポートをGrafanaが起動しているPodの3000番ポートにポートフォワードします。

$ GRAFANA=$(kubectl -n postgres-operator get pods -o name --selector=name=crunchy-grafana)
$ kubectl -n postgres-operator port-forward ${GRAFANA} 3000:3000

 ブラウザで「http://localhost:3000」にアクセスすると、ログイン画面が表示されます。初期ユーザー名とパスワードは、いずれも「admin」ですが、初回ログイン後パスワードの変更を促されるので変更しておきます。

※Webブラウザから利用しているクラウドコンソール上でkubectlコマンドを実行している場合は、ポートフォワードしてローカルのWebブラウザで画面を表示できません。ローカルの端末にkubectlを入れてコマンドを実行してください。

 ダッシュボードを幾つか見てみましょう。

 「PostgreSQLDetails」ダッシュボードでは、PostgreSQLの内部の状況が確認できます。例えば、PostgreSQLへの接続の状況(Active Connections、Idle In Transactionなど)、キャッシュヒット率(Cache Hit Ratio)、DBサイズ(database size)、ロックの取得状況(Locks)などが確認可能です。

 「Query Statistics」ダッシュボードでは、PostgreSQLが実行したクエリの情報を確認できます。例えば、DBクラスタ全体のクエリ総実行回数(Queries Executed)や実行時間(Query Runtime)などのマクロな情報の他、SQLクエリごとの平均/最大/総実行時間(Query Mean Runtime、Query Max Runtime、Query Total Runtime)を確認できます。

 この他にも幾つかダッシュボードが用意されており、バックアップの状況やPodのリソース利用状況などが確認可能です。

リードレプリカの追加

 ここまでPostgreSQLは1台だけでしたが、リードレプリカを追加し、参照性能をスケールアウトしてみましょう。

 先ほど確認した通り、現在PostgreSQLインスタンスのPodは1つだけです。これはプライマリが1台だけ動作していることを意味します。

$ kubectl -n postgres-operator get pods \
  --selector=postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/instance
 
NAME                     READY   STATUS    RESTARTS   AGE
hippo-instance1-w7sc-0   5/5     Running   0          15m

 kustomize/postgres/postgres.yamlを編集し、プライマリ1台、リードレプリカ1台の合計2台構成に変更してみます。10行目を新規に追加しています。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster
  3 metadata:
  4   name: hippo
  5 spec:
  6   image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.3-0
  7   postgresVersion: 14
  8   instances:
  9     - name: instance1
 10       replicas: 2 
 11       dataVolumeClaimSpec:
    ...(略)...

 変更を適用します。

$ kubectl apply -k kustomize/postgres

 しばらくするとリードレプリカが起動し、Podも2つ起動します。

$ kubectl -n postgres-operator get pods \
  --selector=postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/instance
 
NAME                     READY   STATUS    RESTARTS   AGE
hippo-instance1-w7sc-0   5/5     Running   0          16m
hippo-instance1-f8m4-0   5/5     Running   0          22s

 起動したリードレプリカにSQLクエリを発行してみます。リードレプリカ接続用に、「hippo-replicas」というServiceが用意されているので、これを利用します。

$ kubectl -n postgres-operator  run --image=postgres:14 --restart=Never \
    --rm -i client --env="PGPASSWORD=${HIPPO_CLIENT_PGPASSWORD}" \
    --command -- psql -h hippo-replicas -U hippo -d postgres -c "SELECT * FROM t1"
 
  t
------
 test
(1 row)

 リードレプリカはその名の通り、読み込み専用インスタンスなので、参照リクエストのスケールアウトには有効ですが、更新系のクエリを実行できません。下記の例ではテーブルを新規作成しようとしていますが、失敗しています。

 $ kubectl -n postgres-operator  run --image=postgres:14 --restart=Never \
    --rm -i client --env="PGPASSWORD=${HIPPO_CLIENT_PGPASSWORD}" \
    --command -- psql -h hippo-replicas -U hippo -d postgres \
    -c "CREATE TABLE t2(t TEXT)"
 
ERROR:  cannot execute CREATE TABLE in a read-only transaction

 リードレプリカの台数を2台以上に増やしたり、任意の台数に減らしたりすることも、マニフェストを変更することで簡単に実現できます。例えば、先ほどマニフェストに追加した「replicas」を「3」に変更して(下記コードの10行目)マニフェストを適用すると、プライマリ1台、リードレプリカ2台の合計3台のPostgreSQLクラスタに拡張されます。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster
  3 metadata:
  4   name: hippo
  5 spec:
  6   image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.3-0
  7   postgresVersion: 14
  8   instances:
  9     - name: instance1
 10       replicas: 3
 11       dataVolumeClaimSpec:
    ...(略)...

 リードレプリカを作成するには、一般的には次のような作業が必要になります。

  • リードレプリカ用のノードを作成
  • PostgreSQLをインストール
  • プライマリのデータを取得
  • レプリケーションを開始

 KubernetesとOperatorを利用することで、マニフェストを編集、適用するだけで簡単にリードレプリカを追加、削除できました。

自己修復の確認

 障害などによって、宣言した状態と実際の状態が乖離(かいり)した場合、KubernetesとOperatorが宣言した状態に修復してくれます。この自己修復の様子を、何らかの障害が発生したと想定し、プライマリのPostgreSQLを停止して確認してみます。

 「pg_ctl stop -m immediate」コマンドを実行し、PostgreSQLを即時停止します。

$ PG_CLUSTER_PRIMARY_POD=`kubectl get pod -n postgres-operator -o name \
  -l postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/role=master`
 
$ kubectl -n postgres-operator exec ${PG_CLUSTER_PRIMARY_POD} -- /usr/pgsql-14/bin/pg_ctl stop -m immediate
 
waiting for server to shut down.... done
server stopped

 停止直後にプライマリに接続すると、エラーになります。

$ kubectl  -n postgres-operator  run --image=postgres:14 --restart=Never \
    --rm -i client --env="PGPASSWORD=${HIPPO_CLIENT_PGPASSWORD}" \
    --command -- psql -h hippo-ha -U hippo -d postgres -c "select 1"
 
psql: error: connection to server at "hippo-ha" (10.96.72.178), port 5432 failed: Connection refused
        Is the server running on that host and accepting TCP/IP connections?

 しばらくすると通常通り接続可能になり、自己修復されていることを確認できます。

$ kubectl  -n postgres-operator  run --image=postgres:14 --restart=Never \
    --rm -i client --env="PGPASSWORD=${HIPPO_CLIENT_PGPASSWORD}" \
    --command -- psql -h hippo-ha -U hippo -d postgres -c "select 1"
 
 ?column?
----------
        1
(1 row)

 このような自己修復は、Kubernetesだけでも可能なようにも思えますが、HA(ハイアベイラビリティ)ソフトウェア「Patroni」も活躍しています。興味のある方は下記コラムをご覧ください。

コラム HAソフトウェア「Patroni」って何?

 Patroniは、OSSのPostgreSQL向けHAソフトウェアです。

 3ノード以上のPostgreSQLクラスタも容易に作成可能で、オンプレミスやKubernetes上でも動作することから、「StackGres」などPGO以外のPostgreSQL Operatorでも利用されています。

 Patroniの大まかな仕組みは、次の通りです。

  1. PostgreSQLの台数分Patroniデーモンが動作し、各PostgreSQLの稼働状況をモニタリング
  2. モニタリング結果を「etcd」「Zookeeper」など高い一貫性を持つデータストアに保存。このデータストアを参照することで、各Patroniは、PostgreSQLクラスタ全体の状態を把握
  3. 把握した状態に応じてPostgreSQLを操作

 Kubernetesも自己修復能力があることから、Patroniまで必要かどうか疑問に感じる方もいるかもしれません。しかし、DBの可用性を高めるには、DBMS内部の情報も踏まえた上で、正常性や系切り替えを判断する必要があるので、Kubernetesだけでは十分な対応が難しいのです。

 例えば、プライマリのダウン後、複数台存在するリードレプリカから新プライマリを選出する場合、他の条件が同じならば、各リードレプリカのうち最もレプリケーションの遅延が小さいものを新プライマリとして選出すべきでしょう。このような判断がPatroniによって実現されます。


PostgreSQLのバージョンアップ

 PostgreSQLは年に一度のメジャーバージョンアップで新機能が追加される他、マイナーバージョンを少なくとも3カ月に一度リリースします。バージョンの表記は、X.Yの形式をとり、Xがメジャーバージョン番号、Yがマイナーバージョンです。「14.3」の場合、メジャーバージョンは14、マイナーバージョンは3です。

 マイナーバージョンはバグやセキュリティ上の問題を修正するリリースなので、基本的に利用しているメジャーバージョン内の最新のマイナーバージョンにアップデートしておくことが望ましいです。

 このマイナーバージョンアップ対応もマニフェストから簡単にできます(※3)。具体的には、マニフェスト上のPostgreSQLのコンテナイメージを変更すれば、PGOがローリングアップデートをしてくれます。ローリングアップデート中は系切り替えが発生するので、PostgreSQLのサービスが一時的に停止します。

※3:メジャーバージョンアップもPGO 5.1からサポートされています。マイナーバージョンアップに比べると手順が増えますが、興味のある方はCrunchy Dataのブログを参照してください。

 PGOが利用可能なコンテナイメージはCrunchy Dataが公開しています。ここまで、2022年7月の執筆時点でメジャー、マイナーともにコンテナイメージが存在する最新バージョン14.3を利用しているので、いったん14.2に変更した後、14.3にアップデートします。

 kustomize/postgres/postgres.yamlの6行目を編集し、コンテナイメージを「ubi8-14.3-0」から「ubi8-14.2-1」に変更します(※4)。

※4:2022年7月の執筆時点では、14.2-1コンテナイメージが利用可能なことを確認していますが、イメージのダウンロードに失敗するなどした場合、Crunchy Data社が公開している最新のPostgreSQL14のコンテナイメージを公式サイトから確認し、適宜利用可能なイメージを選択してください。

 ハイフンより前はPostgreSQLのバージョン、ハイフンの後の数字はPGOのコンテナイメージのバージョンです。6行目を変更しています。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster
  3 metadata:
  4   name: hippo
  5 spec:
  6   image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.2-1
    ...(略)...

 変更を適用します。

$ kubectl apply -k kustomize/postgres

 下記コマンドを実行し、コンテナイメージがubi8-14.2-1に変更されたことを確認します。

$ kubectl -n postgres-operator get pods \
  --selector=postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/instance \
  -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.postgres-operator\.crunchydata\.com/role}{"\t"}{.status.phase}{"\t"}{.spec.containers[].image}{"\n"}{end}'
 
hippo-instance1-w7sc-0  replica Running registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.2-1
hippo-instance1-f8m4-0  master  Running registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.2-1

 クラスタ初期構築時同様コンテナイメージのサイズがそれなりに大きいので、Pod起動までに時間がかかることもありますが、やはり気長に待ちましょう。

 さて、先ほど変更したkustomize/postgres/postgres.yamlの6行目のコンテナイメージをubi8-14.3-0に戻し、マニフェストを適用してみましょう。今度はコンテナイメージが既にダウンロード済みなので、短時間で完了するはずです。

 ローリングアップデートは、まずレプリカから実施されます。その後スイッチオーバし、旧プライマリがアップデートされ完了となります。アップデートが完了すると、以下のようにコンテナイメージのバージョンがubi8-14.3-0に更新されます。

$ kubectl -n postgres-operator get pods \
  --selector=postgres-operator.crunchydata.com/cluster=hippo,postgres-operator.crunchydata.com/instance \
  -o=jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.labels.postgres-operator\.crunchydata\.com/role}{"\t"}{.status.phase}{"\t"}{.spec.containers[].image}{"\n"}{end}'
 
hippo-instance1-w7sc-0  master  Running registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.3-0
hippo-instance1-f8m4-0  replica Running registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.3-0

 ローリングアップデートによってプライマリ(master)とリードレプリカ(replica)が入れ替わっていることを確認できます。

自動バックアップとリストア

 PostgreSQLに格納したデータのバックアップの運用も、マニフェスト上に宣言することで実現できます。例えば、開始時刻を指定した自動バックアップの定義や、取得したバックアップデータを利用して新規にPostgreSQLクラスタを作成できます。

 今回は自動バックアップの設定とリストアの手順を確認してみます。バックアップの保存先は、Amazon S3やS3互換のオブジェクトストレージなどが選択可能です(※5)。今回はdefaultのStorage Classが新規に作成するPVCを利用します。

※5:その他対応しているストレージは、公式ドキュメントを参照してください。

 kustomize/postgres/postgres.yamlを編集し、毎時5分にフルバックアップを取得するように設定します(※6)。

※6:1時間に1回という高頻度でのフルバックアップを取得することになりますが、今回は動作を確認するための設定です。利用する際は、バックアップ要件を踏まえて設定してください。

 実行時刻の指定様式はcronと同様です。22〜23行目を新規に追加しています。手元の環境で確認する際は、数分後にバックアップが開始するように設定するとよいでしょう。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster
   ..(略)..
 17   backups:
 18     pgbackrest:
 19       image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.38-1
 20       repos:
 21       - name: repo1
 22       schedules:
 23         full: "5 * * * *"

 変更を適用すると、対応するcronjobが追加されます。

$ kubectl apply -k kustomize/postgres
 
$ kubectl -n postgres-operator get cronjobs
NAME               SCHEDULE     SUSPEND   ACTIVE   LAST SCHEDULE   AGE
hippo-repo1-full   5 * * * *   False     0        <none>          8s

 指定した時刻になるとバックアップを実行するPodが起動します。しばらくするとバックアップが完了し、STATUSがCompletedに変化します。

$ kubectl -n postgres-operator get pods --selector=postgres-operator.crunchydata.com/pgbackrest-cronjob=full
NAME                              READY   STATUS      RESTARTS   AGE
hippo-repo1-full-27617165-kl87w   0/1     Completed   0          9m16s

 これでバックアップが取得できました。続いてリストアしましょう。バックアップデータを利用して、新しいPostgreSQLクラスタを構築するために、下記の「restore.yaml」というマニフェストを新規に作成します。4〜10行目で、「elephant」というPostgreSQLクラスタを作成し、データとして先ほど取得したバックアップを利用するよう定義しています。

  1 apiVersion: postgres-operator.crunchydata.com/v1beta1
  2 kind: PostgresCluster
  3 metadata:
  4   name: elephant # PostgreSQLクラスタの名前
  5   namespace: postgres-operator
  6 spec:
  7   dataSource:
  8     postgresCluster:
  9       clusterName: hippo
 10       repoName: repo1
 11   image: registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-14.3-0
 12   postgresVersion: 14
 13   instances:
 14     - dataVolumeClaimSpec:
 15         accessModes:
 16         - "ReadWriteOnce"
 17         resources:
 18           requests:
 19             storage: 1Gi
 20   backups:
 21     pgbackrest:
 22       image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.38-1
 23       repos:
 24       - name: repo1
 25         volume:
 26           volumeClaimSpec:
 27             accessModes:
 28             - "ReadWriteOnce"
 29             resources:
 30               requests:
 31                 storage: 1Gi

 マニフェストを適用します。

$ kubectl apply -f restore.yaml

 しばらくするとリストアが完了し、新しいPostgreSQLクラスタが起動します。下記のselectorで指定したPodのSTATUSがRunningになるまで待ちましょう。

$ kubectl -n postgres-operator get pods --selector=postgres-operator.crunchydata.com/cluster=elephant,postgres-operator.crunchydata.com/instance
NAME                 READY   STATUS    RESTARTS   AGE
elephant-00-79cc-0   4/4     Running   0          2m16s

 リストアしたPostgreSQLクラスタ用のServiceである「elephant-ha」に接続すると、バックアップを取得したPostgreSQLクラスタに投入したデータが存在することを確認できます。

$ kubectl -n postgres-operator run --image=postgres:14 --restart=Never \
    --rm -i client --env="PGPASSWORD=${HIPPO_CLIENT_PGPASSWORD}" \
    --command -- psql -h elephant-ha -U hippo -d postgres \
  -c 'select * FROM t1'
  t
------
 test
(1 row)

 バックアップ/リストアについては、今回紹介した内容以外にも次のような機能もサポートされています。

  • 差分/増分バックアップ
  • 保存するバックアップ世代数の管理
  • 任意のタイミングでの手動バックアップ
  • リカバリーポイントを指定したリストア(PITR)
  • バックアップを取得したPostgreSQLクラスタへのリストア

 興味のある方はこちらも公式ドキュメントの「バックアップ」「リストア」を参照してください。

PGOを利用する際の注意点

 以下、幾つかPGOを利用する際の注意点を記載します。

書き込み可能なプライマリはスケールアウトしない

 リードレプリカは簡単に追加できましたが、先ほど確認した通り、これはあくまで参照リクエストのみ可能なインスタンスです。更新リクエストが処理できるプライマリは1台のままなので、書き込み性能をスケールアウトさせることはできません。

マニフェストから実施できない操作もある

 Kubernetesのマニフェストを編集、適用することでさまざまな運用ができることを見てきましたが、マニフェストからは実施できない操作もあります。例えば、取得したバックアップの一覧を確認する操作は、マニフェスト経由ではできません。下記のようにコンテナ上で直接バックアップツールのコマンドで実行するなどが必要です。

$ kubectl -n postgres-operator exec hippo-repo-host-0 -c pgbackrest -- pgbackrest info
 
stanza: db
    status: ok
    cipher: none
    db (current)
        wal archive min/max (14): 000000010000000000000001/00000003000000000000005D
 
        full backup: 20220705-115307F
            timestamp start/stop: 2022-07-07 11:53:07 / 2022-07-07 11:53:51
            wal start/stop: 000000010000000000000004 / 000000010000000000000004
            database size: 33.9MB, database backup size: 33.9MB
            repo1: backup set size: 4.3MB, backup size: 4.3MB
        ..(略)
        full backup: 20220705-120802F
            timestamp start/stop: 2022-07-07 12:08:02 / 2022-07-05 12:08:10

ストレージソフトウェアは同梱されていない

 監視/HAなどさまざまな周辺ソフトウェアや機能が同梱(どうこん)されているPGOですが、ストレージについては特に用意されていません。DBが利用するストレージの性能や信頼性は、サービス品質に直結します。今回defaultのstorage classを利用しましたが、PGOでは任意のstorage classを指定できるので、DBの用途や利用する環境に応じて適切なストレージを選択しましょう。

まとめ

 今回は、PostgreSQL OperatorとしてPGOを紹介し、幾つか主要な機能を解説しました。Kubernetes上でPostgreSQLを動作させることで、宣言的にPostgreSQLクラスタを管理可能になり、運用性が向上することを実感できたのではないでしょうか。

 注意点も踏まえた上で、オンプレミスのKubernetesでのDB構築や、KubernetesのリソースとしてDBを運用する手段として、PostgreSQL Operatorの利用を検討してみではいかがでしょうか。

Copyright © ITmedia, Inc. All Rights Reserved.

[an error occurred while processing this directive]
ページトップに戻る