CloudNativePG を使用して PostgreSQL を GKE にデプロイする


このガイドでは、CloudNativePG オペレーターを使用して PostgreSQL クラスタを Google Kubernetes Engine(GKE)にデプロイする方法について説明します。

PostgreSQL は、数十年にわたって積極的に開発され、安定したクライアント パフォーマンスを実現するオープンソースのオブジェクト リレーショナル データベースです。レプリケーション、ポイントインタイム リカバリ、セキュリティ機能、拡張性など、幅広い機能を備えています。PostgreSQL は主要なオペレーティング システムと互換性があり、ACID(アトミック性、整合性、独立性、耐久性)標準に完全に準拠しています。

このガイドは、GKE に Postgres クラスタをデプロイすることに関心があるプラットフォーム管理者、クラウド アーキテクト、運用担当者を対象としています。Cloud SQL を使用する代わりに GKE で Postgres を実行すると、経験豊富なデータベース管理者がより柔軟に構成を管理できるようになります。

利点

CloudNativePG は、Apache 2 ライセンスの下で EDB が開発したオープンソースのオペレーターです。これにより、PostgreSQL のデプロイメントに次の機能が追加されます。

  • 宣言型および Kubernetes ネイティブの方法による PostgreSQL クラスタの管理と構成
  • Volume Snapshots または Cloud Storage を使用したバックアップ管理
  • 転送中の暗号化された TLS 接続、独自の認証局を使用する機能、TLS 証明書の自動発行とローテーションのための Certificate Manager とのインテグレーション
  • PostgreSQL のマイナー リリースに対するローリング アップデート
  • Kubernetes API サーバーを使用して、追加のツールなしで PostgreSQL クラスタのステータス維持と、高可用性のためのフェイルオーバーを実現
  • SQL で記述されたユーザー定義指標による組み込みの Prometheus エクスポータ構成

目標

  • Postgres 向けに GKE インフラストラクチャを計画して、デプロイする
  • Helm を使用して CloudNativePG Postgres オペレーターをデプロイして構成する
  • PostgreSQL クラスタをデプロイする
  • PostgreSQL の認証とオブザーバビリティを構成する

デプロイ アーキテクチャ

PostgreSQL には、スタンドアロン データベース サーバーからレプリケーションされた高可用性クラスタまで、さまざまなデプロイ オプションがあります。このチュートリアルでは、GKE への高可用性クラスタのデプロイに焦点を当てています。

このデプロイでは、リージョン GKE クラスタ内の複数のアベイラビリティ ゾーンに PostgreSQL クラスタのワークロードが分散され、高可用性と冗長性が確保されます。詳細については、リージョン クラスタをご覧ください。

次の図は、GKE クラスタ内の複数のノードとゾーンで実行されている Postgres クラスタを示しています。

GKE 上の Postgres クラスタ

  • デフォルトの設定には、1 つのメイン PostgreSQL サーバーと、メインサーバーが故障した場合に引き継ぐ準備ができている 2 つのバックアップ サーバーが含まれています。これにより、データベースの可用性が継続的に確保されます。

  • CloudNativePG オペレーター リソースは、GKE クラスタの別の Namespace を使用してリソースの隔離を強化し、PostgreSQL クラスタごとに 1 つのデータベースという推奨されるマイクロサービス アプローチを遵守します。データベースとそれに対応するユーザー(アプリユーザー)は、クラスタを表す Kubernetes カスタム リソースで定義されます。

  • ストレージは、データベースについて議論する際に重要な要素となります。ストレージを効率的に実行し、継続的な可用性を確保して、データの整合性を保証する必要があります。このような理由から、SSD ディスクに基づく premium-rwo ストレージ クラスをおすすめします。CloudNativePG オペレーターは、PostgreSQL クラスタの Pod を設定すると、必要に応じて PersistentVolumeClaims を自動的に作成します。

費用

このドキュメントでは、Google Cloud の次の課金対象のコンポーネントを使用します。

料金計算ツールを使うと、予想使用量に基づいて費用の見積もりを生成できます。 新しい Google Cloud ユーザーは無料トライアルをご利用いただける場合があります。

このドキュメントに記載されているタスクの完了後、作成したリソースを削除すると、それ以上の請求は発生しません。詳細については、クリーンアップをご覧ください。

始める前に

Cloud Shell には、このチュートリアルに必要なソフトウェア(kubectlgcloud CLIHelmTerraform など)がプリインストールされています。Cloud Shell を使用しない場合は、gcloud CLI をインストールする必要があります。

  1. Install the Google Cloud CLI.
  2. To initialize the gcloud CLI, run the following command:

    gcloud init
  3. Google Cloud プロジェクトを作成または選択します

    • Google Cloud プロジェクトを作成します。

      gcloud projects create PROJECT_ID

      PROJECT_ID は、作成する Google Cloud プロジェクトの名前に置き換えます。

    • 作成した Google Cloud プロジェクトを選択します。

      gcloud config set project PROJECT_ID

      PROJECT_ID は、実際の Google Cloud プロジェクト名に置き換えます。

  4. Google Cloud プロジェクトで課金が有効になっていることを確認します

  5. Compute Engine, IAM, GKE, Resource Manager API を有効にします。

    gcloud services enable compute.googleapis.comiam.googleapis.comcontainer.googleapis.comcloudresourcemanager.googleapis.com
  6. Grant roles to your user account. Run the following command once for each of the following IAM roles: roles/compute.securityAdmin, roles/compute.viewer, roles/container.clusterAdmin, roles/container.admin, roles/iam.serviceAccountAdmin, roles/iam.serviceAccountUser

    gcloud projects add-iam-policy-binding PROJECT_ID --member="USER_IDENTIFIER" --role=ROLE
    • Replace PROJECT_ID with your project ID.
    • Replace USER_IDENTIFIER with the identifier for your user account. For example, user:[email protected].

    • Replace ROLE with each individual role.

環境を設定する

環境の設定手順は次のとおりです。

  1. 環境変数を設定します。

    export PROJECT_ID=PROJECT_ID
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    

    PROJECT_ID は、実際の Google Cloud プロジェクト ID に置き換えます。

  2. GitHub リポジトリのクローンを作成します。

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  3. 作業ディレクトリを変更します。

    cd kubernetes-engine-samples/databases/postgresql-cloudnativepg
    

クラスタ インフラストラクチャを作成する

このセクションでは、Terraform スクリプトを実行して、限定公開の高可用性リージョン GKE クラスタを作成します。

オペレーターは、Standard または Autopilot クラスタを使用してインストールできます。

Standard

次の図は、3 つの異なるゾーンにデプロイされた限定公開のリージョン GKE Standard クラスタを示しています。

このインフラストラクチャをデプロイするには、次のコマンドを実行します。

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-standard init
terraform -chdir=terraform/gke-standard apply \
-var project_id=${PROJECT_ID}   \
-var region=${REGION}  \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

プロンプトが表示されたら、「yes」と入力します。このコマンドが完了し、クラスタが準備完了ステータスになるまでに数分かかることがあります。

Terraform が次のリソースを作成します。

  • Kubernetes ノード用の VPC ネットワークとプライベート サブネット
  • NAT 経由でインターネットにアクセスするためのルーター
  • us-central1 リージョンの限定公開 GKE クラスタ
  • 自動スケーリングが有効になっているノードプール(ゾーンあたり 1~2 ノード、最小ゾーンあたり 1 ノード)

出力は次のようになります。

...
Apply complete! Resources: 14 added, 0 changed, 0 destroyed.
...

Autopilot

次の図は、限定公開のリージョン GKE Autopilot クラスタを示しています。

このインフラストラクチャをデプロイするには、次のコマンドを実行します。

export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
terraform -chdir=terraform/gke-autopilot init
terraform -chdir=terraform/gke-autopilot apply \
-var project_id=${PROJECT_ID} \
-var region=${REGION} \
-var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}

プロンプトが表示されたら、「yes」と入力します。このコマンドが完了し、クラスタが準備完了ステータスになるまでに数分かかることがあります。

Terraform が次のリソースを作成します。

  • Kubernetes ノード用の VPC ネットワークとプライベート サブネット
  • NAT 経由でインターネットにアクセスするためのルーター
  • us-central1 リージョンの限定公開 GKE クラスタ
  • ロギングとモニタリングの権限を持つ ServiceAccount
  • Google Cloud Managed Service for Prometheus(クラスタ モニタリング用)

出力は次のようになります。

...
Apply complete! Resources: 12 added, 0 changed, 0 destroyed.
...

クラスタに接続する

クラスタと通信を行うように kubectl を構成します。

gcloud container clusters get-credentials ${KUBERNETES_CLUSTER_PREFIX}-cluster --region ${REGION}

CloudNativePG オペレーターをデプロイする

Helm チャートを使用して、CloudNativePG を Kubernetes クラスタにデプロイします。

  1. CloudNativePG オペレーターの Helm チャート リポジトリを追加します。

    helm repo add cnpg https://cloudnative-pg.github.io/charts
    
  2. Helm コマンドライン ツールを使用して CloudNativePG オペレーターをデプロイします。

    helm upgrade --install cnpg \
        --namespace cnpg-system \
        --create-namespace \
        cnpg/cloudnative-pg
    

    出力は次のようになります。

    Release "cnpg" does not exist. Installing it now.
    NAME: cnpg
    LAST DEPLOYED: Fri Oct 13 13:52:36 2023
    NAMESPACE: cnpg-system
    STATUS: deployed
    REVISION: 1
    TEST SUITE: None
    ...
    

Postgres をデプロイする

次のマニフェストは、CloudNativePG オペレーターのカスタム リソースで定義された PostgreSQL クラスタを記述しています。

apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
  name: gke-pg-cluster
spec:
  description: "Standard GKE PostgreSQL cluster"
  imageName: ghcr.io/cloudnative-pg/postgresql:16.2
  enableSuperuserAccess: true
  instances: 3
  startDelay: 300
  primaryUpdateStrategy: unsupervised
  postgresql:
    pg_hba:
      - host all all 10.48.0.0/20 md5
  bootstrap:
    initdb:
      database: app
  storage:
    storageClass: premium-rwo
    size: 2Gi
  resources:
    requests:
      memory: "1Gi"
      cpu: "1000m"
    limits:
      memory: "1Gi"
      cpu: "1000m"
  affinity:
    enablePodAntiAffinity: true
    tolerations:
    - key: cnpg.io/cluster
      effect: NoSchedule
      value: gke-pg-cluster
      operator: Equal
    additionalPodAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app.component
              operator: In
              values:
              - "pg-cluster"
          topologyKey: topology.kubernetes.io/zone
  monitoring:
    enablePodMonitor: true

このマニフェストには次のフィールドがあります。

  • spec.instances: クラスタ Pod の数
  • spec.primaryUpdateStrategy: ローリング アップデート戦略。
    • Unsupervised: レプリカノードの後にプライマリ クラスタノードを自律的に更新します。
    • Supervised: プライマリ クラスタノードの手動切り替えが必要です
  • spec.postgresql: postgres.conf ファイル パラメータのオーバーライド(pg-hba ルール、LDAP、満たすべき同期レプリカの要件など)。
  • spec.storage: ストレージ関連の設定(ストレージ クラス、Volume のサイズ、write-ahead log の設定など)。
  • spec.bootstrap: クラスタで作成された初期データベースのパラメータ、ユーザー認証情報、データベースの復元オプション
  • spec.resources: クラスタ Pod のリクエストと上限
  • spec.affinity: クラスタ ワークロードのアフィニティと反アフィニティ ルール

基本的な Postgres クラスタを作成する

  1. Namespace を作成します。

    kubectl create ns pg-ns
    
  2. カスタム リソースを使用して PostgreSQL クラスタを作成します。

    kubectl apply -n pg-ns -f manifests/01-basic-cluster/postgreSQL_cluster.yaml
    

    このコマンドの完了までに数分かかることがあります。

  3. クラスタのステータスを確認します。

    kubectl get cluster -n pg-ns --watch
    

    出力に Cluster in healthy state のステータスが表示されるまで待ってから、次のステップに進みます。

    NAME             AGE     INSTANCES   READY   STATUS                     PRIMARY
    gke-pg-cluster   2m53s   3           3       Cluster in healthy state   gke-pg-cluster-1
    

リソースを検査する

GKE がクラスタのリソースを作成したことを確認します。

kubectl get cluster,pod,svc,pvc,pdb,secret,cm -n pg-ns

出力は次のようになります。

NAME                                        AGE   INSTANCES   READY   STATUS                     PRIMARY
cluster.postgresql.cnpg.io/gke-pg-cluster   32m   3           3       Cluster in healthy state   gke-pg-cluster-1

NAME                   READY   STATUS    RESTARTS   AGE
pod/gke-pg-cluster-1   1/1     Running   0          31m
pod/gke-pg-cluster-2   1/1     Running   0          30m
pod/gke-pg-cluster-3   1/1     Running   0          29m

NAME                        TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)    AGE
service/gke-pg-cluster-r    ClusterIP   10.52.11.24   <none>        5432/TCP   32m
service/gke-pg-cluster-ro   ClusterIP   10.52.9.233   <none>        5432/TCP   32m
service/gke-pg-cluster-rw   ClusterIP   10.52.1.135   <none>        5432/TCP   32m

NAME                                     STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/gke-pg-cluster-1   Bound    pvc-bbdd1cdd-bdd9-4e7c-8f8c-1a14a87e5329   2Gi        RWO            standard       32m
persistentvolumeclaim/gke-pg-cluster-2   Bound    pvc-e7a8b4df-6a3e-43ce-beb0-b54ec1d24011   2Gi        RWO            standard       31m
persistentvolumeclaim/gke-pg-cluster-3   Bound    pvc-dac7f931-6ac5-425f-ac61-0cfc55aae72f   2Gi        RWO            standard       30m

NAME                                                MIN AVAILABLE   MAX UNAVAILABLE   ALLOWED DISRUPTIONS   AGE
poddisruptionbudget.policy/gke-pg-cluster           1               N/A               1                     32m
poddisruptionbudget.policy/gke-pg-cluster-primary   1               N/A               0                     32m

NAME                                TYPE                       DATA   AGE
secret/gke-pg-cluster-app           kubernetes.io/basic-auth   3      32m
secret/gke-pg-cluster-ca            Opaque                     2      32m
secret/gke-pg-cluster-replication   kubernetes.io/tls          2      32m
secret/gke-pg-cluster-server        kubernetes.io/tls          2      32m
secret/gke-pg-cluster-superuser     kubernetes.io/basic-auth   3      32m

NAME                                DATA   AGE
configmap/cnpg-default-monitoring   1      32m
configmap/kube-root-ca.crt          1      135m

オペレーターが次のリソースを作成します。

  • オペレーターが制御する PostgreSQL クラスタを表すクラスタ カスタム リソース
  • 対応する永続ボリュームがある PersistentVolumeClaim リソース
  • データベースへのアクセスや Postgres ノード間のレプリケーションのためのユーザー認証情報を含むシークレット。
  • クラスタに接続する <name>-rw<name>-ro<name>-r の 3 つのデータベース エンドポイント サービス。詳細については、PostgreSQL のアーキテクチャをご覧ください。

Postgres に対する認証を行う

PostgreSQL データベースに接続し、オペレーターによって作成されたさまざまなサービス エンドポイントを介してアクセスを確認できます。これを行うには、PostgreSQL クライアントがあり、同期アプリケーション ユーザー認証情報が環境変数としてマウントされた追加の Pod を使用します。

  1. クライアント Pod を実行して Postgres クラスタとやり取りします。

    kubectl apply -n pg-ns -f manifests/02-auth/pg-client.yaml
    
  2. pg-client Pod で exec コマンドを実行し、gke-pg-cluster-rw Service にログインします。

    kubectl wait --for=condition=Ready -n pg-ns pod/pg-client --timeout=300s
    kubectl exec -n pg-ns -i -t pg-client -- /bin/sh
    
  3. gke-pg-cluster-rw Service を使用してデータベースにログインし、読み取り / 書き込み権限で接続を確立します。

    psql postgresql://$CLIENTUSERNAME:[email protected]/app
    

    ターミナルはデータベース名で始まります。

    app=>
    
  4. テーブルを作成します。

    CREATE TABLE travel_agency_clients (
    client VARCHAR ( 50 ) UNIQUE NOT NULL,
    address VARCHAR ( 50 ) UNIQUE NOT NULL,
    phone VARCHAR ( 50 ) UNIQUE NOT NULL);
    
  5. テーブルにデータを挿入します。

    INSERT INTO travel_agency_clients(client, address, phone)
    VALUES ('Tom', 'Warsaw', '+55555')
    RETURNING *;
    
  6. 作成したデータを表示します。

    SELECT * FROM travel_agency_clients ;
    

    出力は次のようになります。

    client | address |  phone
    --------+---------+---------
    Tom    | Warsaw  | +55555
    (1 row)
    
  7. 現在のデータベース セッションからログアウトします。

    exit
    
  8. gke-pg-cluster-ro Service を使用してデータベースにログインし、読み取り専用アクセス権を確認します。この Service ではデータのクエリは許可されますが、書き込みオペレーションは制限されます。

    psql postgresql://$CLIENTUSERNAME:[email protected]/app
    
  9. 新しいデータの挿入を試みます。

    INSERT INTO travel_agency_clients(client, address, phone)
    VALUES ('John', 'Paris', '+55555')
    RETURNING *;
    

    出力は次のようになります。

    ERROR:  cannot execute INSERT in a read-only transaction
    
  10. データの読み取りを試みます。

    SELECT * FROM travel_agency_clients ;
    

    出力は次のようになります。

    client | address |  phone
    --------+---------+---------
    Tom    | Warsaw  | +55555
    (1 row)
    
  11. 現在のデータベース セッションからログアウトします。

    exit
    
  12. Pod のシェルを終了します。

    exit
    

Prometheus が Postgres クラスタの指標を収集する仕組みを理解する

次の図は、Prometheus 指標の収集の仕組みを示しています。

この図では、GKE 限定公開クラスタに次のものが存在します。

  • パス / とポート 9187 の指標を収集する Postgres Pod
  • Postgres Pod の指標を処理する Prometheus ベースのコレクタ
  • Cloud Monitoring に指標を送信する PodMonitoring リソース

Pod から指標を収集できるようにするには、次の手順で操作します。

  1. PodMonitoring リソースを作成します。

    kubectl apply -f manifests/03-observability/pod-monitoring.yaml -n pg-ns
    
  2. Google Cloud コンソールで、[Metrics Explorer] ページに移動します。

    [Metrics Explorer] に移動

    ダッシュボードにゼロ以外の指標の取り込み率が表示されます。

  3. [指標を選択] で、「Prometheus Target」と入力します。

  4. [有効な指標カテゴリ] セクションで、[Cnpg] を選択します。

指標のダッシュボードを作成する

エクスポートされた指標を可視化するには、指標ダッシュボードを作成します。

  1. ダッシュボードをデプロイします。

    gcloud --project "${PROJECT_ID}" monitoring dashboards create --config-from-file manifests/03-observability/gcp-pg.json
    
  2. Google Cloud コンソールで [ダッシュボード] ページに移動します。

    ダッシュボードに移動する

  3. [PostgresQL Prometheus Overview] ダッシュボードを選択します。

    ダッシュボードがどのように関数をモニタリングしているかを確認するには、[データベース認証] セクションのアクションを再利用し、データベースに読み取り / 書き込みリクエストを適用した後、収集した指標の可視化をダッシュボードで確認します。

  4. クライアント Pod に接続します。

    kubectl exec -n pg-ns -i -t pg-client -- /bin/sh
    
  5. ランダムなデータを挿入します。

    psql postgresql://$CLIENTUSERNAME:[email protected]/app -c "CREATE TABLE test (id serial PRIMARY KEY, randomdata VARCHAR ( 50 ) NOT NULL);INSERT INTO test (randomdata) VALUES (generate_series(1, 1000));"
    
  6. ダッシュボードを更新します。グラフは実際の指標で更新されます。

  7. Pod のシェルを終了します。

    exit
    

クリーンアップ

プロジェクトを削除する

    Delete a Google Cloud project:

    gcloud projects delete PROJECT_ID

リソースを個別に削除する

  1. 環境変数を設定します。

    export PROJECT_ID=${PROJECT_ID}
    export KUBERNETES_CLUSTER_PREFIX=postgres
    export REGION=us-central1
    
  2. terraform destroy コマンドを実行します。

    export GOOGLE_OAUTH_ACCESS_TOKEN=$(gcloud auth print-access-token)
    terraform  -chdir=terraform/FOLDER destroy \
      -var project_id=${PROJECT_ID} \
      -var region=${REGION} \
      -var cluster_prefix=${KUBERNETES_CLUSTER_PREFIX}
    

    FOLDER は、gke-autopilot または gke-standard に置き換えます。

    プロンプトが表示されたら、「yes」と入力します。

  3. アタッチされていないすべてのディスクを検索します。

    export disk_list=$(gcloud compute disks list --filter="-users:* AND labels.name=${KUBERNETES_CLUSTER_PREFIX}-cluster" --format "value[separator=|](name,zone)")
    
  4. ディスクを削除します。

    for i in $disk_list; do
      disk_name=$(echo $i| cut -d'|' -f1)
      disk_zone=$(echo $i| cut -d'|' -f2|sed 's|.*/||')
      echo "Deleting $disk_name"
      gcloud compute disks delete $disk_name --zone $disk_zone --quiet
    done
    

次のステップ