TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

CloudNativePGを読む:PID 1のマネージャー、ソケットの向こうのプラグイン、WALの断片の旅

ステータス:下書き(2026-10-17)、一部実測。 基準バージョン:cloudnative-pg v1.30.0(コミット 4b5e244a7d03)、plugin-barman-cloud v0.14.0(1e13020fe5f0)、 cnpg-i v0.6.0(844d20b2b783)、cnpg-i-machinery v0.4.2(e3ead2b31db4)。小規模なクラスターがまさにこのバージョン(オペレーター 1.30.0、プラグイン v0.14.0)を 稼働させているため、実測は2026-10-17にそのクラスターで 読み取りのみ を行って取得しました(CRの読み取り、ポッド構成の読み取り、実行中のポッド内の /proc・設定 ファイルの読み取り、ログの読み取り)。書き込み・再起動・復旧のテストは行っていません。 作成ルール:実測データを基本とし、測定できなかったものは「未測定」、確認できなかったものは「未確認」と表記します。

目次

1. 一行まとめ: PostgreSQLに寄り添う「常駐管理人」と「外注の配達員」

PostgreSQLをKubernetesで運用する際の難しさは、Podを起動することではなく、primaryを決定し、障害時に切り替え、WALをもれなく外部へ搬送することです。CloudNativePG(CNPG)はStatefulSetを使わずにPodとPVCを直接作成し、各Pod内に 常駐管理人(インスタンスマネージャー)をPID 1として配置し、PostgreSQLを子プロセスとして管理します。

最近のバージョンでは、バックアップとWALアーカイブという「荷物の搬送」をオペレーター本体から切り離し、 外注の配達員(プラグイン)に任せる方向へ移行しています。両者の契約書にあたるのがCNPG-IというgRPCインターフェースであり、CNPGプロジェクトが直接開発している配達員がbarman-cloudプラグインです。本体がS3・GCS・Azureの仕様を意識する必要がなくなり、プラグインを本体のリリースとは独立して修正できるようにするための設計です。

コンポーネント 役割 実行場所
CNPGオペレーター Cluster・Backup・Pooler等のreconcile、Pod・PVC・サービスの作成、failoverの判断 cnpg-system のDeployment
インスタンス マネージャー(manager instance run) PostgreSQL を子として実行、設定ファイル作成、ステータス HTTP エンドポイントを提供 インスタンス Pod の PID 1
manager wal-archive / wal-restore PostgreSQL の archive_command·restore_command が呼び出す使い捨てのコマンド インスタンス Pod、WAL ごとに1回
barman-cloud プラグイン、operator の半分 Cluster を監視して RBAC を準備、Pod にサイドカーをインジェクションするパッチを生成 cnpg-system の Deployment
barman-cloud プラグイン、instance の半分 WAL のアーカイブ・復元、ベースバックアップ、メトリクス インスタンス Pod のサイドカー
ObjectStore CR どのバケットに、どのような資格情報で、どのような圧縮で アプリのネームスペース

2. 構造図

오퍼레이터, 플러그인 두 반쪽, 인스턴스 파드 cnpg-system 네임스페이스 CNPG 오퍼레이터 Cluster 컨트롤러 Backup 컨트롤러 Plugin 컨트롤러 플러그인 저장소 (gRPC 연결 풀) barman-cloud operator 반쪽 Identity ReconcilerHooks OperatorLifecycle Deployment 1개 gRPC mTLS 인스턴스 파드 (DB 한 대) postgres 컨테이너 PID 1: manager (instance run) └ postgres (자식) └ wal-archive (매번) plugin-barman-cloud instance 반쪽 네이티브 사이드카 WAL · Backup Metrics 소켓 init: bootstrap-controller → manager 복사 상태 조회 /pg/status 사이드카 주입 패치 barman-cloud-* S3 호환 객체 저장소 ObjectStore CR 어디에·어떤 자격으로 소켓: 두 컨테이너가 같이 붙인 /plugins 볼륨의 유닉스 소켓. 오퍼레이터 ↔ 플러그인 Deployment 는 Service 로 찾는다.
図: オペレーター、プラグインの2つの半分、インスタンス Pod

図の読み方:

3. コードマップ

4つのリポジトリを一緒に見ます。最上位の重要な部分だけをピックアップしました。

パス 内容 最初に読むべきファイル
cloudnative-pg api/v1/ ClusterやBackupなどのCRDのGo型定義とヘルパー関数 cluster_types.go, backup_funcs.go
cloudnative-pg internal/controller/ オペレーターのreconcileループ(Cluster、Backup、Pluginなど) cluster_controller.go, backup_controller.go
cloudnative-pg internal/cnpi/plugin/ CNPG-Iクライアント: プラグイン検出、コネクションプール、RPCラッパー repository/setup.go, client/wal.go
cloudnative-pg internal/cmd/manager/ 単一バイナリのサブコマンド(instance run, wal-archive, backup …) instance/run/cmd.go, walarchive/cmd.go
cloudnative-pg pkg/management/postgres/ インスタンスマネージャー本体: PostgreSQLの実行、ウェブサーバー、アーカイバ archiver/archiver.go, webserver/local.go
cloudnative-pg pkg/postgres/ PostgreSQL設定値のルール(必須値を含む) configuration.go
cnpg-i proto/ インターフェース定義(サービス9個) identity.proto, wal.proto
cnpg-i-machinery pkg/pluginhelper/ プラグイン側gRPCサーバーの骨組み、オブジェクトデコーダー http/server.go
plugin-barman-cloud internal/cnpgi/ プラグインの実装: operator/(Deployment側)、 instance/(サイドカー側)、 common/(WAL) operator/lifecycle.go, common/wal.go
plugin-barman-cloud api/v1/ ObjectStore CRD objectstore_types.go

4. コアパスをたどる

4.1 Cluster reconcileループ: プラグインを読み込んでから開始する

Reconcile (internal/controller/cluster_controller.go:169)は、Clusterを読み込んだ後、一番最初に このClusterが使用するプラグイン名を集めます。 (getPluginsNeededForReconcile, :298). .spec.plugins と外部クラスターに記載されているプラグインを合わせたリストです。それらの名前で5秒以内に接続を取得し、 (WithPlugins, internal/cnpi/plugin/client/client.go:79) contextに格納してから内部ループに入ります。プラグインが見つからない場合は、phaseを「Unknown plugin」に変更し、10秒後に再度確認します。記載されているにもかかわらず見つからないプラグインを静かに見過ごさないようにするためです。

内部の reconcile, :310 は 長めですが、順序が明確です。

  1. デフォルト値の補完、イメージの決定、プラグイン状態の更新、補助オブジェクト(Secret、Service、PDB)の作成 (createPostgresClusterObjects, cluster_create.go:57).
  2. プラグインの pre-reconcile フック (preReconcilePluginHooks, plugins.go:36)。 barman-cloud ではここで、サイドカーが ObjectStore と Secret を読み込むための Role および RoleBinding を作成します(セクション 4.5)。
  3. HTTPS で各インスタンスの状態を照会する (GetStatusFromInstances, pkg/management/postgres/webserver/client/remote/instance.go:183)。 primary が 2 つあるように見える場合は、 何もせずに 5 秒後に再度確認します。復帰した古い primary が自分で降格するのを待ち、 データの分岐を防ぎます。
  4. 切り替え(switchover/failover)の処理、Pod と PVC の調整 (reconcileResources, :825).
  5. 最後にプラグインの post フックと状態の同期、そして「healthy」フェーズの記録 (finalizeReconciliation, plugins.go:105).

オペレーターが Kubernetes 内でオブジェクトを作成する際にもプラグインが介在します。オペレーターのクライアントは Create·Patch·Delete を ラップし、最初にプラグインの LifecycleHook を呼び出します (extendedClient.Create, internal/cnpi/plugin/operatorclient/client.go:71)。 インスタンス Pod の仕様を作成する NewInstance, pkg/specs/pods.go:485 の最後でも、同じフックが「evaluate」アクションとして呼び出されます。プラグインが返した JSON パッチが適用された Pod が実際に作成されるため、ウェブフックなしでサイドカーのインジェクションが行われます。

4.2 オペレーターがプラグインを見つける方法:1 つの Service と 1 つのソケットフォルダ

プラグインは 2 つの方法で検出されます。

どの経路であれ、接続直後に行うことは同じです。 LoadPlugin, internal/cnpi/plugin/connection/connection.go:442 この Identity サービスに名前・バージョンと、 どのようなサービスを実装したのかを問い合せ、実装されたサービスごとにさらに詳細な能力(capability)を問い合せます。 以降のすべての呼び出しはこの能力リストによってフィルタリングされます。例えば WAL アーカイブは、 ARCHIVE_WAL の能力を持つプラグインにのみ送信されます。

CNPG-I のサービスは cnpg-i/proto/ に9つあります。 Identity(identity.proto:23), Operator, OperatorLifecycle, ReconcilerHooks, WAL(wal.proto:23), Backup, RestoreJobHooks, Postgres, Metrics です。すべてのリクエストに Cluster 全体が JSON で載せて送信されます。プラグインがKubernetesから Cluster を再読み込みする必要をなくし、オペレータが確認した その瞬間の Cluster として判断できるようにするためです。

基準となるバージョンのオペレータのコードで実際に呼び出される RPC を数えてみると(*Client().<RPC> の呼び出し箇所を grep)、WAL サービスの Status· SetFirstRequired と Operator サービスの Deregister は呼び出し元が見つかりませんでした。proto には存在しますが、1.30.0 のオペレータではまだ 使用していない箇所のようです(意図は未確認)。

プラグイン側のサーバーの骨組みは cnpg-i-machinery が提供します。 Server.Start, pkg/pluginhelper/http/server.go:152 は Identity を最初に登録し、「enricher」関数に残りのサービスをアタッチさせます。サーバーアドレスが与えられた場合は TCP、そうでない場合はプラグイン名で Unix ソケットを開きます(createListener, :304).

4.3 インスタンスマネージャー: PID 1 は PostgreSQL ではない

インスタンス Pod のコマンドは /controller/manager instance run です(第 6 節の実測)。このバイナリは bootstrap-controller init コンテナが 自分自身を共有ボリュームにコピーしたものです(bootstrap NewCmd, internal/cmd/manager/bootstrap/cmd.go:34). runSubCommand, internal/cmd/manager/instance/run/cmd.go:157 を読むと、この PID 1 は実質的に 小さなコントローラーマネージャーです。controller-runtime の上にインスタンス・データベース・ロールの reconciler、ログパイプ、 PostgreSQL ライフサイクルマネージャー、リモートおよびローカルのウェブサーバー、メトリクスサーバーを載せます。

PostgreSQL はライフサイクルマネージャーが起動します (runPostgresAndWait, internal/cmd/manager/instance/run/lifecycle/run.go:40)。 初期化(どのロールで起動するかシグナルファイルを合わせる作業)が終わるのを待ってから postmaster を子として実行し、postmaster が終了すると マネージャーもその事実を把握して次の行動を決めます。PID 1 が PostgreSQL ではなくマネージャーである理由はここにあります。Kubernetes がコンテナを 終了・起動する単位と、PostgreSQL を停止・再起動する単位を分離することで、設定の再読み込みやプライマリーの降格といった作業を コンテナの再起動なしに 行えるようになります。

オペレーターが第 4.1 節のステップ 3 で問い合わせる先は、リモートウェブサーバーの /pg/status です (pgStatus, pkg/management/postgres/webserver/remote.go:320)。 逆に、Pod 内のワンタイムコマンド(wal-archive, backup)はローカルウェブサーバーを経由して PID 1 に処理を委譲するか、Cluster キャッシュを取得します。

4.4 WAL の一幕の旅: archive_command から S3 まで

WAL 한 조각과 백업 한 번이 가는 길 ① WAL 아카이브 ② 백업(Backup CR) postgres archiver archive_command 실행 manager wal-archive PID 1 과 같은 바이너리 archiveWALViaPlugins /plugins 소켓을 훑는다 WAL.Archive (gRPC) 사이드카가 받는다 barman-cloud-wal-archive --gzip, 한 조각 약 0.25초 조건 갱신 ContinuousArchiving=True ScheduledBackup 매일 한 번 Backup 을 만든다 Backup 컨트롤러 checkPrerequisites method 가 plugin 인가? 예 → 플러그인 이름만 확인 아니오 → .spec.backup 필요 파드 안에서 exec manager backup 「이름」 Backup.Backup (gRPC) 로컬 웹서버 → 사이드카 barman-cloud-backup 끝나면 phase=completed destinationPath / serverName / wals·base serverName 은 Cluster 의 플러그인 파라미터에서 #1166 이 다룬 오류 method 생략 → 기본값 → no backup section #1164 이 다룬 자리 serverName 은 ObjectStore 가 아니라 플러그인 파라미터
図: WAL の一部と 1 回のバックアップが通る道
  1. archive_command はユーザーが変更できません。 CNPG はこの値を「必須設定」として固定します (pkg/postgres/configuration.go:589, CnpgConfigurationSettings.MandatorySettings)。値は /controller/manager wal-archive --log-destination … %p です。 どのようなバックアップ手段を使う場合でも PostgreSQL 側の設定は 1 行で共通であり、変更されるのはその背後にある Go コードだけです。
  2. wal-archive コマンド。 NewCmd, internal/cmd/manager/walarchive/cmd.go:40 はローカルウェブサーバーのキャッシュから Cluster を取得し、 archiver.Run, pkg/management/postgres/archiver/archiver.go:124 を呼び出します。自分が現在のプライマリーではない場合(切り替え中)は拒否します。同じ WAL を 2 つのノードが同時にアップロードするのを防ぐためです。 終了後、成功・失敗を ContinuousArchiving 条件として Cluster に記録します。
  3. プラグインを優先。 internalRun, :156 の最初の動作はプラグインへの処理の委譲です。
	// We allow plugins to archive WALs even if there is no plugin
	// directly enabled by the user, to retain compatibility with
	// the old API.
	if err := archiveWALViaPlugins(ctx, cluster, pgData, walName); err != nil {
		return err
	}

	// If the used chosen a plugin to do WAL archiving, we don't
	// trigger the legacy archiving process.
	if cluster.GetEnabledWALArchivePluginName() != "" {
		return nil
	}

出典: cloudnative-pg v1.30.0 pkg/management/postgres/archiver/archiver.go, Apache-2.0

.spec.plugins[] のうち isWALArchiver: true であるプラグインが存在する場合、従来の(in-tree) barman のパスは一切経由しません。存在しない場合はその下で .spec.backup.barmanObjectStore を確認し、それもない場合は何もせずに成功を返します。バックアップが設定されていない Cluster でも archive_mode は有効になっており同じコマンドが実行されますが、WAL はどこにもアップロードされず成功として処理されます(第 6 節の実測)。 4. ソケットをスキャンして gRPC。 archiveWALViaPlugins, :277 は /plugins のソケットのうち Cluster で有効になっているプラグインのみを選択して接続し、 ArchiveWAL, internal/cnpi/plugin/client/wal.go:34 が Cluster JSON と WAL のパスを載せて WAL.Archive を呼び出します。WAL ごとに新しいプロセスが起動し、接続も新しく確立される構造です。 5. サイドカーが受信する。 プラグインの WALServiceImplementation.Archive, internal/cnpgi/common/wal.go:107 は Cluster JSON からプラグインのパラメータ(barmanObjectName, serverName)を取り出し、ObjectStore を読み込んで資格情報を環境変数に 変換し、初回であれば対象パスが空であることを確認した後、 maxParallel 分の準備ができた WAL をさらに収集して barman-cloud-wal-archive を 並列実行します。パスを指定する引数に configuration.ServerName が入ります (:196)。この値が第 5 節の serverName の話につながります。

archive_command が失敗すると、PostgreSQL は同じ WAL で再試行を行い、その間に pg_wal が蓄積されるため、このチェーンのどのリンクが途切れても 最終的に ディスクが埋まることとして現れます。barman CLI の終了コードは gRPC エラー文字列でラップされて返されます(第 6 節)。

4.5 barman-cloud プラグイン:1 つのバイナリ、2 つの半分

プラグインバイナリのサブコマンドは instance·operator·restore·healthcheck 4 つあります (cmd/manager/main.go:52).

4.6 バックアップ 1 回:method が道を選ぶ

BackupReconciler.Reconcile, internal/controller/backup_controller.go:127 は Cluster を見つけた後、形式的な前提条件から確認します (checkPrerequisites, :390).

	if backup.Spec.Method == apiv1.BackupMethodPlugin {
		if len(cluster.Spec.Plugins) == 0 {
			const message = "cannot proceed with the backup as the cluster has no plugin configured"
			return flagMissingPrerequisite(message, "ClusterHasNoBackupExecutorPlugin")
		}

		return nil, nil
	}

	if cluster.Spec.Backup == nil {
		const message = "cannot proceed with the backup as the cluster has no backup section"
		return flagMissingPrerequisite(message, "ClusterHasBackupConfigured")
	}

出所:cloudnative-pg v1.30.0 internal/controller/backup_controller.go、Apache-2.0

method: plugin の場合はプラグインが 1 つでもあるかどうかだけを確認して通過します。その他の method はすべて .spec.backup を要求します。失敗は TerminalError で終了し、再試行しません。設定の誤ったバックアップをエンドレスに再試行しても意味がないためです。

通過すると、method によって道が分かれます。 plugin と barmanObjectStore は インスタンスマネージャーが行うバックアップであり (IsManagedByInstance, api/v1/backup_funcs.go:319), volumeSnapshot はオペレーターが直接スナップショットを取得します。インスタンス側のバックアップは対象の Pod(target の設定に応じて primary または最も進んだ standby)を 選択した後、その Pod の PostgreSQL コンテナで /controller/manager backup <이름> を exec します (startInstanceManagerBackup, :968)。 このコマンドはローカルのウェブサーバーにバックアップを要求し (requestBackup, pkg/management/postgres/webserver/local.go:131)、 plugin の場合は PluginBackupCommand.invokeStart, webserver/plugin_backup.go:72 がそのソケットで Backup.Backup を呼び出します。サイドカの BackupServiceImplementation.Backup, instance/backup.go:71 が barman-cloud-backup を実行し(サーバー名の引数もやはり configuration.ServerName)、結果を Backup の status として返します。

5. 執筆者の貢献と続く場所

執筆者は in-tree の barmanObjectStore でバックアップを行っていた CNPG 1.30 クラスターをプラグイン(v0.14.0)に移行する際に 2 度つまずき、その経験をドキュメントの PR 2 つとして送信しました。どちらも 2026-10-17 時点でレビュー待ちです。

① serverName はどこに行くのか — plugin-barman-cloud#1164(issue #1163)。 以前のガイドは in-tree の barmanObjectStore 節を ObjectStore の spec.configuration にそのまま移行するよう指示されます。しかし、そこに serverName が存在する場合、ObjectStore CRD はそれを拒否します。ルールは api/v1/objectstore_types.go:61 の CEL の 1 行(!has(self.serverName))であり、メッセージは「Cluster の serverName プラグインパラメータを使用してください」です。プラグインがこの値を 読み取る場所は NewFromCluster, internal/cnpgi/operator/config/config.go:142 です。

	serverName := cluster.Name
	for _, plugin := range cluster.Spec.Plugins {
		if plugin.IsEnabled() && plugin.Name == metadata.PluginName {
			if pluginServerName, ok := plugin.Parameters["serverName"]; ok {
				serverName = pluginServerName
			}
		}
	}

出所: plugin-barman-cloud v0.14.0 internal/cnpgi/operator/config/config.go, Apache-2.0

この値がセクション 4.4 の WAL パスとセクション 4.6 のバックアップパスにそのまま組み込まれます。つまり、 serverName は「バケット内のフォルダー名」であり、移行に伴ってこの値が 変更されると、プラグインは既存のバックアップの隣に 新しいフォルダーを作成して書き込みを開始します。外部クラスター(復元・レプリカのソース)側ではデフォルト値も異なります。 in-tree は外部クラスター名になりますが (ExternalCluster.GetServerName, cloudnative-pg/api/v1/cluster_funcs.go:403)、 プラグインは 現在作成中の Cluster の名前になります(config.go:163, :173)。PR では 以前のガイドにこれら 2 つの違いを記載しました。外部クラスター側はコードの照合でのみ確認し、復元による再検証は行っておりません(未実測)。

② 「no backup section」 — plugin-barman-cloud#1166(Issue #1165)。 プラグインへ移行する際に Cluster から .spec.backup を削除すると、賃借 method を指定していない Backup および ScheduledBackup は上記セクション 4.6 の引用の 2 番目の 分岐に落ちます。Backup CRD の method のデフォルト値が barmanObjectStore であるためです(セクション 6 でインストールされた CRD で確認)。エラーメッセージがプラグイン ドキュメントのトラブルシューティングセクションに存在しなかったため、そのセクションに原因と修正方法(method: plugin + pluginConfiguration.name, kubectl cnpg backup -m plugin)を 追加しました。

③ #679 のコメント。 SeaweedFS で WAL アーカイブが終了コード 4 で失敗するという報告に対し、同じサイドカーイメージ(v0.14.0)では再現しないという表と、 「終了コード 4 は barman の一般的なエラーであるため、サイドカーログの直前の例外行を確認してください」という案内を残しました。セクション 4.4 のステップ 5 がまさにその コマンドを実行する箇所であり、終了コードが gRPC エラーにラップされて manager wal-archive まで伝播します。

6. 実測: 小規模クラスターでの調査結果

2026-10-17、すべてのコマンドは読み取り(kubectl get·logs、実行中の Pod に対して exec による /proc・設定ファイル cat)です。アプリの名前空間と クラスター名はマスクし、元データは別途保管しました。

インストールされているバージョンとクラスター。 cnpg-system にオペレーター(cloudnative-pg:1.30.0)とプラグイン(plugin-barman-cloud:v0.14.0)の Deployment がそれぞれ 1 つずつ存在します。Cluster は 4 つあり、すべて Cluster in healthy state、PostgreSQL 18.4 イメージです。

Cluster インスタンス プラグイン .spec.backup ObjectStore
アプリ DB クラスター A 2 barman-cloud(isWALArchiver: true) なし あり
認証サービス DB クラスター B 2 barman-cloud なし あり
クラスター C、D 1, 2 なし なし なし

プラグインの検出(セクション 4.2)。 プラグイン Service のラベルは cnpg.io/pluginName: barman-cloud.cloudnative-pg.io、アノテーションは cnpg.io/pluginPort: 9090 と、サーバーおよびクライアント証明書の Secret 名の 2 つでした。オペレーターのログには過去 24 時間に Registered plugin が 7 回ありました。クラスター A の .status.pluginStatus はバージョン 0.14.0、機能 TYPE_RECONCILER_HOOKS·TYPE_LIFECYCLE_SERVICE· TYPE_INSTANCE_SIDECAR_INJECTION であり、セクション 4.5 の operator 側の Identity 応答と完全に一致しています。

インスタンス Pod(セクション 4.3・4.5)。 クラスター A の両方の Pod は同じ構成です。

項目 値
initContainers bootstrap-controller(オペレーターイメージ 1.30.0)、 plugin-barman-cloud(サイドカーイメージ v0.14.0、 restartPolicy: Always)
containers postgres が 1 つ
postgres コンテナ PID 1 /controller/manager instance run --status-port-tls --log-level=info
postmaster PID 37、親 PID 1。その下位に checkpointer、walwriter、archiver(「last was …66」)、walsender など
/plugins ソケットファイル barman-cloud.cloudnative-pg.io が 1 つ、 /proc/net/unix では待機 1・接続 1

サイドカーは containers ではなく initContainers に restartPolicy: Always として組み込まれていました。セクション 4.5 で読み取った内容そのものです。 プラグイン Deployment のデバッグログにはインジェクションパッチが残っており、そのログの caller は internal/cnpgi/operator/lifecycle.go:364 でした。 v0.14.0 タグにおける該当ファイルの 364 行目がまさに "generated patch" のログ行であるため、実行中のビルドと読み取ったソースが同一のバージョンであることを追加で 確認できました。パッチの内容は、 /spec/volumes に plugins emptyDir、 /spec/initContainers/1 にサイドカーを追加する JSON パッチでした。

archive_command(セクション 4.4)。 PGDATA の設定ファイルを読み取りました。

ファイル 値
custom.conf archive_command = '/controller/manager wal-archive --log-destination /controller/log/postgres.json %p'
custom.conf archive_mode = 'on'
override.conf restore_command = '/controller/manager wal-restore --log-destination /controller/log/postgres.json %f %p'

Cluster 設定の archive_timeout は 5 分です。バックアップもプラグインもないクラスター C の custom.conf にも同じ archive_command と archive_mode = 'on' が存在していました。セクション 4.4 のステップ 3 によれば、それらの WAL はどこにも送信されず「成功」として終了します。

WAL アーカイブログ。 サイドカーログにおける過去 24 時間の Archived WAL file が 322件でした。5分ごとに強制的にWALを切り替えると1日288個になるため、残りは実際に書き込まれて満杯になった断片のようです。直近の4つの断片(UTC 16:06~16:21、5分間隔)にかかった時間(elapsedWalTime)は0.237~0.256秒でした。各行の前には Executing barman-cloud-wal-archive と引数リスト(--gzip, --endpoint-url <가림>, s3://<버킷>/、サーバー名)が記録されています。一方、postgresコンテナのログには同じ24時間の間に wal-archive が含まれる行が0件でした。成功した wal-archive はinfoレベルで何も残さないため、 WALアーカイブを確認するにはサイドカーのログを見る必要があります。

バックアップ(4.6節)。 ScheduledBackupは2つあり、どちらも method: plugin と pluginConfiguration.name を記載しています。インストールされているCRDを読み取ると、Backupの spec.method のデフォルト値は実際に barmanObjectStore であり、ObjectStore CRDの spec.configuration には !has(self.serverName) のルールがそのまま残っていました(5節の2つのPRの根拠)。

Cluster バックアップ数 結果 備考
A 31(09-16~10-16、毎日) すべて completed 直近のもの:03:30:00開始、03:33:40終了(約3分40秒)
B 28(初回の手動1 + 毎日27) completed 26、failed 2 10-06・10-07失敗: rpc error: code = Unknown desc = exit status 1

Bの失敗メッセージは、4.4節の最後で述べた「barman CLIの終了コードがgRPCエラー文字列でラップされた形」そのものです。原因自体はこの記事では追っていません(未確認)。

ステータスが2か所にある。 クラスターAの .status.firstRecoverabilityPoint と .status.lastSuccessfulBackup は 2026-08-23 で止まっていました。in-treeでバックアップを行っていた最後の日です。APIタイプの注釈に「Deprecated: バックアッププラグインには設定されない」と記載されているフィールドのため、プラグインに移行してからは更新されません。現在の値はObjectStore側にあります。

どこ 最初の復旧可能時点 最後の成功バックアップ
Cluster .status(in-tree時代の値) 2026-08-23 07:25 2026-08-23 07:32
ObjectStore .status.serverRecoveryWindow 2026-09-16 03:33 2026-10-16 03:33

保持ポリシーが30日のため、ObjectStore側の最初の復旧時点は1か月前で切り捨てられます。アラートがCluster側のフィールドを参照している場合、移行後に静かに停止します。

接続リークはなかった。 4.4節の archiveWALViaPlugins をはじめとする4つの場所で、使用しないプラグインリポジトリをもう一つ作成して閉じるコードを持っているため(8節)、実際のクライアント側コネクションプールが閉じられずに蓄積されていないかを確認しました。26日間毎日バックアップを行ったプライマリポッドにおいて、 /proc/net/unix のプラグインソケットは待機1・接続1のみでした。リークの形跡はありません。

7. どこから読むべきか

  1. cnpg-i/proto/identity.proto と wal.proto。契約書から見ると、双方のコードが簡単に読み解けます。
  2. cnpg-i-machinery pkg/pluginhelper/http/server.go:152。プラグインがサーバーを開く方式(TCP vs ソケット)を確認します。
  3. plugin-barman-cloud internal/cnpgi/operator/lifecycle.go:88 と common/wal.go:107。2つの半分がそれぞれ行うことです。
  4. cloudnative-pg internal/controller/plugin_controller.go:135 → internal/cnpi/plugin/connection/connection.go:442。オペレーターがプラグインを見つけて機能を読み取る場所です。
  5. pkg/management/postgres/archiver/archiver.go:156。in-treeとプラグインのパスが1つの関数で分岐します。
  6. internal/controller/cluster_controller.go:310。最後に大きなループを読むと、これまで見てきた断片がどこに組み込まれるかがわかります。

8. 初めての貢献におすすめの場所

ソースコードを読んでいて実際に目についたものです。まだどこにも投稿しておらず、投稿する前に既存のIssueを検索する必要があります。

両方のリポジトリとも、CONTRIBUTINGがCloudNativePGの共通貢献ガイドライン(行動規範、AIの使用、ワークフロー)を指しています。AIの支援を受けた場合は、コミットの Assisted-by トレーラーで明記するのがこのプロジェクトのやり方です。2026-10-17時点で good first issue のラベルで開かれていたIssueは、本体リポジトリに2件、プラグインリポジトリに0件でした。

9. 制限事項

ティティの一言。 PostgreSQLは家の中で手紙を書くだけで、郵便受け(ソケット)に入れると隣の配達員(サイドカー)が持って出かけます。封筒に書く宛先がまさにserverNameなので、引っ越すときに宛先を書き換えると、古い郵便受けの隣に新しい郵便受けができてしまうのです!

参考

すべての資料は2026-10-17に閲覧しました。

引用したコードはcloudnative-pgおよびplugin-barman-cloudのものであり、両リポジトリともApache License 2.0です。

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます