接続プーリング

CloudNativePG は、Pooler CRD を通じて、PostgreSQL の最も一般的なオープン ソース接続プーラーの 1 つである PgBouncer 設定オプション による接続プーリングのネイティブ サポートを提供します。

簡単に言うと、CloudNativePG の Pooler は、アプリケーションと PostgreSQL サービス (rw サービスなど) の間にある PgBouncer ポッドのデプロイであり、スケーラブルで構成可能で可用性の高い個別の データベースアクセスレイヤー を作成します。

建築

次の図は、PgBouncer に基づくデータベース アクセス レイヤーの導入によって CloudNativePG のアーキテクチャがどのように変化するかを示しています。アプリケーションは、PostgreSQL プライマリ サービスに直接接続する代わりに、PgBouncer の同等のサービスに接続できるようになりました。これにより、既存の接続を再利用して、PostgreSQL 側のパフォーマンスを高速化し、リソース管理を改善できます。

Applications writing to the single primary via PgBouncer

Applications writing to the single primary via PgBouncer

クイックスタート

CloudNativePG が PgBouncer プーラーを実装する方法を説明する最も簡単な方法は、例を使用することです。

apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
  name: pooler-example-rw
spec:
  cluster:
    name: cluster-example

  instances: 3
  type: rw
  pgbouncer:
    poolMode: session
    parameters:
      max_client_conn: "1000"
      default_pool_size: "10"

重要

プーラー名は、同じ名前空間内のクラスター名と決して一致してはなりません。

これにより、pooler-example-rw (名前は任意) と呼ばれる新しい Pooler リソースが作成されます。これは、cluster-example と呼ばれる Postgres Cluster リソースに厳密に関連付けられ、読み取り/書き込みサービス (rw 、したがって cluster-example-rw ) によって識別されるプライマリを指します。

Pooler は、Postgres クラスターの同じ名前空間に存在する必要があります。これは、 session で構成された latest stable image of PgBouncer を実行する 3 つのポッドの Kubernetes デプロイメントで構成されています

それぞれ最大 1000 の接続を受け入れます。デフォルトのプール サイズは、PostgreSQL に対して 10 のユーザー/データベース ペアです。

重要

Pooler は、PgBouncer で * フォールバック データベースのみを設定します。つまり、クライアントから渡された接続文字列のすべてのパラメーターが PostgreSQL サーバーに中継されます (["Section [databases]" in PgBouncer's documentation](https://www.pgbouncer.org/config.html#section-databases) を参照してください)。

さらに、CloudNativePG は、PgBouncer で使用される構成ファイルを含むプーラーと同じ名前のシークレットを自動的に作成します。

API リファレンスで。

Pooler リソースのライフサイクル

Pooler リソースは、Cluster が管理するリソースではありません。必要に応じて、プーラーを手動で作成することになっています。さらに、PostgreSQL クラスターごとに複数のプーラーをデプロイできます。

注意すべき重要なことは、Cluster リソースと Pooler リソースのライフサイクルが現在独立していることです。

重要

Pooler がどのように機能するかがわかったので、考えられるアーキテクチャに関して完全な自由が得られます。プーラーのないクラスター、単一のプーラーを持つクラスター、または複数のプーラー (つまり、アプリケーションごとに 1 つ) を持つクラスターを持つことができます。

安全

すべての PgBouncer プーラーは、プールのクライアント (アプリケーション) 側とサーバー (PostgreSQL) 側の両方で、 TLS接続 を介して転送中の暗号化をサポートする CloudNativePG と透過的に統合されます。

具体的には、PgBouncer は PostgreSQL サーバーの証明書を自動的に再利用します。さらに、TLS クライアント証明書認証を使用して PostgreSQL サーバーに接続し、クライアントのパスワード認証のために auth_query を実行します (以下の ユーザー名/パスワード認証 を参照)。

コンテナーは pgbouncer システム ユーザーとして実行され、pgbouncer データベースへのアクセスは、peer 認証によるローカル接続経由でのみ許可されます。

証明書

デフォルトでは、PgBouncer プーラーはクラスター自体が使用するものと同じ証明書を使用しますが、ユーザーがこれらの証明書を提供すると、プーラーは次の形式のシークレットを受け入れます。

1.基本認証

2.TLS

3.不透明

Opaque の場合、使用する必要がある特定のキーを探します。これらのキーは次のとおりです。

  • tls.crt

  • tls.key

したがって、このシークレットを TLS シークレットとして扱い、そこから開始できます。

認証

パスワードベースの認証 は、CloudNativePG の PgBouncer のクライアントでサポートされている唯一の方法です。

内部的には、実装は PgBouncer の auth_user および auth_query オプションに依存しています。具体的には、オペレーター:

  • PostgreSQL サーバーに cnpg_pooler_pgbouncer という標準ユーザーを作成します。

  • postgres データベースにルックアップ関数を作成し、cnpg_pooler_pgbouncer ユーザーに実行権限を付与します (PoLA)

  • このユーザーの TLS 証明書を発行します

  • cnpg_pooler_pgbouncer を auth_user として設定します

  • TLS 証明書を使用して cnpg_pooler_pgbouncer を PostgreSQL サーバーに対して認証するように PgBouncer を構成します。

  • クラスターにプーラーが関連付けられていないことが検出されると、上記のすべてが削除されます

重要

独自のシークレットを指定すると、オペレーターはプーラーを自動的に統合しません。

プーラーを手動で統合するには、独自のシークレットを指定した場合、クラスター内から次のクエリを実行する必要があります。

まず、ロールを作成する必要があります。

CREATE ROLE cnpg_pooler_pgbouncer WITH LOGIN;

次に、アプリケーション データベースごとに、cnpg_pooler_pgbouncer に接続するためのアクセス許可を付与します。

GRANT CONNECT ON DATABASE { database name here } TO cnpg_pooler_pgbouncer;

最後に、各アプリケーション データベースに接続し、各アプリケーション データベース内に認証関数を作成します。

CREATE OR REPLACE FUNCTION user_search(uname TEXT)
  RETURNS TABLE (usename name, passwd text)
  LANGUAGE sql SECURITY DEFINER AS
  SELECT usename, passwd FROM pg_shadow WHERE usename=$1;;

REVOKE ALL ON FUNCTION user_search(text)
  FROM public;

GRANT EXECUTE ON FUNCTION user_search(text)
  TO cnpg_pooler_pgbouncer;

ポッドテンプレート

Pooler リソースの template セクションでポッド テンプレートの仕様を利用できます。詳細については、API リファレンスの `PoolerSpec section <api_reference.md#PoolerSpec>`__ を参照してください。

テンプレートを使用して、ポッドとノードのアフィニティおよび非アフィニティ ルールを細かく制御するなど、好きなようにポッドを構成できます。デフォルトでは、コンテナーは ghcr.io/cloudnative-pg/pgbouncer のイメージを使用します。

PodAntiAffinity を指定する Pooler の例を次に示します。

apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
  name: pooler-example-rw
spec:
  cluster:
    name: cluster-example
  instances: 3
  type: rw

  template:
    metadata:
      labels:
        app: pooler
    spec:
      containers: []
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - pooler
            topologyKey: "kubernetes.io/hostname"

注釈

.spec.template.spec.containers は、PodSpec の必須フィールドであるため、変更されていない場合は明示的に [] に設定する必要があります。 .spec.template.spec.containers が設定されていない場合、マニフェストを適用しようとすると、kubernetes api-server は次のエラーを返します: error validating "pooler.yaml": error validating data: ValidationError(Pooler.spec.template.spec): missing required field "containers"

リソースを設定し、使用する画像を変更する例を次に示します。

apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
  name: pooler-example-rw
spec:
  cluster:
    name: cluster-example
  instances: 3
  type: rw

  template:
    metadata:
      labels:
        app: pooler
    spec:
      containers:
        - name: pgbouncer
          image: my-pgbouncer:latest
          resources:
            requests:
              cpu: "0.1"
              memory: 100Mi
            limits:
              cpu: "0.5"
              memory: 500Mi

高可用性 (HA)

Kubernetes のデプロイメントのおかげで、単一のインスタンスまたは複数のポッドで実行するようにプーラーを構成できます。公開されたサービスは、PgBouncer を実行している利用可能なポッドにクライアントがランダムに分散されるようにします。これにより、基になるサーバー (rw サービスを使用している場合) またはサーバー (複数のレプリカで ro サービスを使用している場合) への接続が自動的に管理および再利用されます。 .

警告

インフラストラクチャが複数のアベイラビリティ ゾーンにまたがり、それらの間でレイテンシが高い場合は、ネットワーク ホップに注意してください。たとえば、ゾーン 2 で実行されているアプリケーションがゾーン 3 で実行されている PgBouncer に接続し、ゾーン 1 の PostgreSQL プライマリを指している場合を考えてみましょう。

PgBouncer 設定オプション

オペレーターは configuration options for PgBouncer のほとんどを管理し、それらのサブセットのみを変更できるようにします。

警告

オペレーターはそれらを検証しないため、各オプションの値を正しく設定する責任があります。

以下に、カスタマイズできる PgBouncer オプションのリストを示します。それぞれに、その特定のパラメーターに関する PgBouncer ドキュメントへのリンクが含まれています。ここで別段の記載がない限り、デフォルト値は PgBouncer によって直接設定されたものです。

で必要

セクションで説明されているように、Prometheus エクスポートによって統計が既に収集されている場合、デフォルトで無効 (0 ) です。

PgBouncer 構成のカスタマイズは、.spec.pgbouncer.parameters マップで宣言的に記述されます。

オペレーターは Pooler 仕様の変更に対応し、サービスを中断することなく、すべての PgBouncer インスタンスが更新された構成をリロードします。

警告

すべての PgBouncer ポッドは、仕様のパラメーターに合わせて同じ構成になります。これらのパラメーターに誤りがあると、プーラー全体**の操作性が損なわれる可能性があります。演算子は、オプションの値を検証しません。

モニタリング

Pooler の PgBouncer 実装には、次を実行することにより、cnpg_pgbouncer_ プレフィックスを持ついくつかのメトリックを自動的に利用可能にするデフォルトの Prometheus エクスポーターが付属しています。

  • SHOW LISTS (プレフィックス: cnpg_pgbouncer_lists )

  • SHOW POOLS (プレフィックス: cnpg_pgbouncer_pools )

  • SHOW STATS (プレフィックス: cnpg_pgbouncer_stats )

CloudNativePG インスタンスと同様に、エクスポーターは PgBouncer を実行している各ポッドのポート 9127 で実行され、Go ランタイムに関連するメトリックも提供します (接頭辞 go_* 付き)。次のコマンドを使用して、PgBouncer を実行しているポッドでエクスポーターをデバッグできます。

kubectl exec -ti <PGBOUNCER_POD> -- curl 127.0.0.1:9127/metrics

cnpg_pgbouncer メトリクスの出力例:

#  HELP cnpg_pgbouncer_collection_duration_seconds Collection time duration in seconds

#  TYPE cnpg_pgbouncer_collection_duration_seconds gauge

cnpg_pgbouncer_collection_duration_seconds{collector="Collect.up"} 0.002443168

#  HELP cnpg_pgbouncer_collections_total Total number of times PostgreSQL was accessed for metrics.

#  TYPE cnpg_pgbouncer_collections_total counter

cnpg_pgbouncer_collections_total 1

#  HELP cnpg_pgbouncer_last_collection_error 1 if the last collection ended with error, 0 otherwise.

#  TYPE cnpg_pgbouncer_last_collection_error gauge

cnpg_pgbouncer_last_collection_error 0

#  HELP cnpg_pgbouncer_lists_databases Count of databases.

#  TYPE cnpg_pgbouncer_lists_databases gauge

cnpg_pgbouncer_lists_databases 1

#  HELP cnpg_pgbouncer_lists_dns_names Count of DNS names in the cache.

#  TYPE cnpg_pgbouncer_lists_dns_names gauge

cnpg_pgbouncer_lists_dns_names 0

#  HELP cnpg_pgbouncer_lists_dns_pending Not used.

#  TYPE cnpg_pgbouncer_lists_dns_pending gauge

cnpg_pgbouncer_lists_dns_pending 0

#  HELP cnpg_pgbouncer_lists_dns_queries Count of in-flight DNS queries.

#  TYPE cnpg_pgbouncer_lists_dns_queries gauge

cnpg_pgbouncer_lists_dns_queries 0

#  HELP cnpg_pgbouncer_lists_dns_zones Count of DNS zones in the cache.

#  TYPE cnpg_pgbouncer_lists_dns_zones gauge

cnpg_pgbouncer_lists_dns_zones 0

#  HELP cnpg_pgbouncer_lists_free_clients Count of free clients.

#  TYPE cnpg_pgbouncer_lists_free_clients gauge

cnpg_pgbouncer_lists_free_clients 49

#  HELP cnpg_pgbouncer_lists_free_servers Count of free servers.

#  TYPE cnpg_pgbouncer_lists_free_servers gauge

cnpg_pgbouncer_lists_free_servers 0

#  HELP cnpg_pgbouncer_lists_login_clients Count of clients in login state.

#  TYPE cnpg_pgbouncer_lists_login_clients gauge

cnpg_pgbouncer_lists_login_clients 0

#  HELP cnpg_pgbouncer_lists_pools Count of pools.

#  TYPE cnpg_pgbouncer_lists_pools gauge

cnpg_pgbouncer_lists_pools 1

#  HELP cnpg_pgbouncer_lists_used_clients Count of used clients.

#  TYPE cnpg_pgbouncer_lists_used_clients gauge

cnpg_pgbouncer_lists_used_clients 1

#  HELP cnpg_pgbouncer_lists_used_servers Count of used servers.

#  TYPE cnpg_pgbouncer_lists_used_servers gauge

cnpg_pgbouncer_lists_used_servers 0

#  HELP cnpg_pgbouncer_lists_users Count of users.

#  TYPE cnpg_pgbouncer_lists_users gauge

cnpg_pgbouncer_lists_users 2

#  HELP cnpg_pgbouncer_pools_cl_active Client connections that are linked to server connection and can process queries.

#  TYPE cnpg_pgbouncer_pools_cl_active gauge

cnpg_pgbouncer_pools_cl_active{database="pgbouncer",user="pgbouncer"} 1

#  HELP cnpg_pgbouncer_pools_cl_cancel_req Client connections that have not forwarded query cancellations to the server yet.

#  TYPE cnpg_pgbouncer_pools_cl_cancel_req gauge

cnpg_pgbouncer_pools_cl_cancel_req{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_cl_waiting Client connections that have sent queries but have not yet got a server connection.

#  TYPE cnpg_pgbouncer_pools_cl_waiting gauge

cnpg_pgbouncer_pools_cl_waiting{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_maxwait How long the first (oldest) client in the queue has waited, in seconds. If this starts increasing, then the current pool of servers does not handle requests quickly enough. The reason may be either an overloaded server or just too small of a pool_size setting.

#  TYPE cnpg_pgbouncer_pools_maxwait gauge

cnpg_pgbouncer_pools_maxwait{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_maxwait_us Microsecond part of the maximum waiting time.

#  TYPE cnpg_pgbouncer_pools_maxwait_us gauge

cnpg_pgbouncer_pools_maxwait_us{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_pool_mode The pooling mode in use. 1 for session, 2 for transaction, 3 for statement, -1 if unknown

#  TYPE cnpg_pgbouncer_pools_pool_mode gauge

cnpg_pgbouncer_pools_pool_mode{database="pgbouncer",user="pgbouncer"} 3

#  HELP cnpg_pgbouncer_pools_sv_active Server connections that are linked to a client.

#  TYPE cnpg_pgbouncer_pools_sv_active gauge

cnpg_pgbouncer_pools_sv_active{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_sv_idle Server connections that are unused and immediately usable for client queries.

#  TYPE cnpg_pgbouncer_pools_sv_idle gauge

cnpg_pgbouncer_pools_sv_idle{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_sv_login Server connections currently in the process of logging in.

#  TYPE cnpg_pgbouncer_pools_sv_login gauge

cnpg_pgbouncer_pools_sv_login{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_sv_tested Server connections that are currently running either server_reset_query or server_check_query.

#  TYPE cnpg_pgbouncer_pools_sv_tested gauge

cnpg_pgbouncer_pools_sv_tested{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_pools_sv_used Server connections that have been idle for more than server_check_delay, so they need server_check_query to run on them before they can be used again.

#  TYPE cnpg_pgbouncer_pools_sv_used gauge

cnpg_pgbouncer_pools_sv_used{database="pgbouncer",user="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_avg_query_count Average queries per second in last stat period.

#  TYPE cnpg_pgbouncer_stats_avg_query_count gauge

cnpg_pgbouncer_stats_avg_query_count{database="pgbouncer"} 1

#  HELP cnpg_pgbouncer_stats_avg_query_time Average query duration, in microseconds.

#  TYPE cnpg_pgbouncer_stats_avg_query_time gauge

cnpg_pgbouncer_stats_avg_query_time{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_avg_recv Average received (from clients) bytes per second.

#  TYPE cnpg_pgbouncer_stats_avg_recv gauge

cnpg_pgbouncer_stats_avg_recv{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_avg_sent Average sent (to clients) bytes per second.

#  TYPE cnpg_pgbouncer_stats_avg_sent gauge

cnpg_pgbouncer_stats_avg_sent{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_avg_wait_time Time spent by clients waiting for a server, in microseconds (average per second).

#  TYPE cnpg_pgbouncer_stats_avg_wait_time gauge

cnpg_pgbouncer_stats_avg_wait_time{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_avg_xact_count Average transactions per second in last stat period.

#  TYPE cnpg_pgbouncer_stats_avg_xact_count gauge

cnpg_pgbouncer_stats_avg_xact_count{database="pgbouncer"} 1

#  HELP cnpg_pgbouncer_stats_avg_xact_time Average transaction duration, in microseconds.

#  TYPE cnpg_pgbouncer_stats_avg_xact_time gauge

cnpg_pgbouncer_stats_avg_xact_time{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_total_query_count Total number of SQL queries pooled by pgbouncer.

#  TYPE cnpg_pgbouncer_stats_total_query_count gauge

cnpg_pgbouncer_stats_total_query_count{database="pgbouncer"} 3

#  HELP cnpg_pgbouncer_stats_total_query_time Total number of microseconds spent by pgbouncer when actively connected to PostgreSQL, executing queries.

#  TYPE cnpg_pgbouncer_stats_total_query_time gauge

cnpg_pgbouncer_stats_total_query_time{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_total_received Total volume in bytes of network traffic received by pgbouncer.

#  TYPE cnpg_pgbouncer_stats_total_received gauge

cnpg_pgbouncer_stats_total_received{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_total_sent Total volume in bytes of network traffic sent by pgbouncer.

#  TYPE cnpg_pgbouncer_stats_total_sent gauge

cnpg_pgbouncer_stats_total_sent{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_total_wait_time Time spent by clients waiting for a server, in microseconds.

#  TYPE cnpg_pgbouncer_stats_total_wait_time gauge

cnpg_pgbouncer_stats_total_wait_time{database="pgbouncer"} 0

#  HELP cnpg_pgbouncer_stats_total_xact_count Total number of SQL transactions pooled by pgbouncer.

#  TYPE cnpg_pgbouncer_stats_total_xact_count gauge

cnpg_pgbouncer_stats_total_xact_count{database="pgbouncer"} 3

#  HELP cnpg_pgbouncer_stats_total_xact_time Total number of microseconds spent by pgbouncer when connected to PostgreSQL in a transaction, either idle in transaction or executing queries.

#  TYPE cnpg_pgbouncer_stats_total_xact_time gauge

cnpg_pgbouncer_stats_total_xact_time{database="pgbouncer"} 0

Clusters と同様に、 プロメテウス オペレーターの例 を使用している場合

次の PodMonitor を定義することで、特定のプーラーをスクレイピングするように構成できます。

apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: <POOLER_NAME>
spec:
  selector:
    matchLabels:
      cnpg.io/poolerName: <POOLER_NAME>
  podMetricsEndpoints:
  - port: metrics

ロギング

ログは、次の例のように、JSON 形式で標準出力に直接送信されます。

{
  "level": "info",
  "ts": SECONDS.MICROSECONDS,
  "msg": "record",
  "pipe": "stderr",
  "record": {
    "timestamp": "YYYY-MM-DD HH:MM:SS.MS UTC",
    "pid": "<PID>",
    "level": "LOG",
    "msg": "kernel file descriptor limit: 1048576 (hard: 1048576); max_client_conn: 100, max expected fd use: 112"
  }
}

接続の一時停止

Pooler 仕様では、デフォルトで false に設定されている paused オプションを介して、宣言的な構成のみを使用して、PgBouncer の PAUSE および RESUME コマンドを利用できます。 true に設定すると、オペレーターは PgBouncer で PAUSE コマンドを内部的に呼び出します。

  1. クエリが完了するのを待った後、PostgreSQL サーバーへのすべてのアクティブな接続を閉じます。

  2. クライアントからの新しい接続を一時停止します

paused オプションが false に戻されると、オペレーターは PgBouncer で RESUME コマンドを呼び出し、Pooler で定義された PostgreSQL サービスへのタップを再度開きます。

重要

将来のバージョンでは、切り替え操作は PgBouncer プーラーと完全に統合され、PAUSE /RESUME 機能を利用して、クライアント アプリケーションによって認識されるダウンタイムを削減します。現時点では、paused 属性を true に設定し、 :ref:``kubectl` に cnpg プラグインを使用する<kubectl に cnpg プラグインを使用する>` を介して切り替えコマンドを発行し、最後に paused 属性を false に復元することで、同じ結果を得ることができます。

制限事項

単一の PostgreSQL クラスター

プーラーの現在の実装は、特定の CloudNativePG クラスター (正確にはサービス) の一部として機能するように設計されています。現時点では、複数のクラスターにまたがるプーラーを作成することはできません。

制御された構成可能性

CloudNativePG は、PgBouncer レイヤーが PostgreSQL と通信するために使用されるいくつかの構成オプションを透過的に管理します。このようなオプションは外部から設定できず、TLS 証明書、認証設定、databases セクション、および users セクションが含まれます。また、単一の PostgreSQL クラスターの特定の使用例を考慮して、採用された基準は、ユーザーが構成できるオプションを明示的にリストすることです。

注釈

より高度でカスタマイズされたシナリオでガンマを完了するために、PgBouncer の別のオペレーターの将来の実装の余地を残しながら、採用されたソリューションがほとんどのユース ケースに対処すると信じる理由があります。