Kubernetes で PostgreSQL が必要な理由
移住することにしましたGolangCIHeroku から Kubernetes へ。それまで、GolangCI はプライマリ データベースとして Heroku PostgreSQL を使用していました。一般的な移行方法は、次のようなマネージド クラウド PostgreSQL サービスを使用することです。AWS オーロラまたGoogleクラウドSQL。たとえば、アルゴリアHeroku から GKE に移行しましたしかし、GolangCI には、AWS や Google Cloud にアクセスせずにローカルの Kubernetes クラスターで動作するオンプレミス バージョンがあるため、完全に制御されたソリューションが必要でした。
ヘルムがあれば 5 分で完了します
私は Kubernetes を初めて使用したので、最初に PostgreSQL をインストールしようとしました。Helm は安定版/postgresql をインストールします
。この兜チャートを使用しますビットナミ
画像。ビットナミには記事Kubernetes 上で本番環境に対応した PostgreSQL を作成するためのこのイメージの使用について。
すぐに、このインストールはそうではないことに気づきました。本番環境に対応した構成次のようにインストールする必要があります:
兜インストール--name pg --namespace postgres -f ./values-production.yamlsteady/postgresqlkubectl -n postgresscale statefulset my-postgresql-slave --replicas=3
自動マスターフェイルオーバーはどこにありますか
データベース マスター (プライマリ) を持つノードがクラッシュした場合、HA PostgreSQL は自動的に存続し、回復する必要があります。前のステップの純粋な Kubernetes ソリューションでは、次のように機能します。
- PostgreSQL マスター ポッドまたはノードがクラッシュする
- Kubernetes が実行するのは、健康診断デフォルトでは 10 秒ごと。調整できます
期間秒
ヘルスチェックを 1 秒ごとに実行します (これは最小間隔です)。クラッシュ後 1 秒以内に Kubernetes がそれを検出します。 - Kubernetes は、PostgreSQL マスター サーバーの新しいポッドをスケジュールします。ノードがクラッシュした場合は、最初に新しいノードを実行する必要がある場合があります。ポッドが失敗しただけの場合、Kubernetes はポッドを再起動するだけで済みます。
- PostgreSQLマスターが起動します
この純粋な Kubernetes HA ソリューションには、セットアップが簡単であるという利点があります。しかし、次のような欠点があります。
- ステップ 1 と 4 の間にダウンタイムがあり、Kubernetes がノードを再起動する必要がある場合、ダウンタイムは数分になる可能性があります。
- 安定した信頼できるクロスゾーン可用性がない
- PostgreSQL マスターはステートフルセットと永続ボリューム。
- デフォルトでは、永続ボリュームは 1 つのゾーンにのみ存在します。フルゾーンの停止が発生した場合、マスターを自動的にフェイルオーバーすることはできません。マスターを持つ Kubernetes ポッドを別の可用性ゾーンにスケジュールすることはできません。
- AWS EBS はディスクのクロスゾーンレプリケーションをサポートしていません。 Google Cloud がサポートしますリージョン永続ディスクしかし、それはベータ版の機能です。
- 私は、クローズドソースのディスクレプリケーションに基づくクロスゾーン高可用性を信頼しません。 HA がオープンソースのコンセンサス アルゴリズムに基づいていればさらに良いでしょう。のこの紙このソリューションはコンセンサスではなく不透明なディスク レプリケーションに基づいているため、作成者はこのソリューションを「最新の HA」とは呼びません。
興味深いものがあります比較純粋な Kubernetes と Stolon HA の。 Stolon は、Kubernetes 上で動作できる PostgreSQL の HA ラッパーです。後で見てみましょう。
Cross DC コンセンサスベースの HA PostgreSQL の選択
私は、コンセンサスに基づいてクロスデータセンター (クロスゾーン) HA PostgreSQL ソリューションが必要であると判断しました。主な選択肢は 2 つあります。
Stolon には次のアーキテクチャがあります。
どちらのプロジェクトも、サポートされている機能という点では似ています。小さな比較:
- パトローニをサポートしているのは、Postgres オペレーター
- どちらのプロジェクトも同期レプリケーションをサポートしています。常連客とストロン。
- どちらもクォーラムベースの同期レプリケーションをサポートしています (ただし、ネイティブにPostgreSQL による):常連客とストロン
私が選んだのは(みたいな)ウルフス) Stolon は、私にとってアーキテクチャがより明確であるためです。
Kubernetes 上で Stolon PostgreSQL を実行する
設定
Helm を使用して k8s に Stolon PostgreSQL をセットアップするために実行した手順は次のとおりです。
スーパーユーザー:
kubectl create Secret 汎用 pg-su\--namespace postgres\--from-リテラル=ユーザー名=「su_ユーザー名」--from-リテラル=パスワード=「su_パスワード」
そしてそれらを設定します:
値.yml
スーパーユーザーの秘密: 名前:ページ-それはユーザー名キー:ユーザー名パスワードキー:パスワード
レプリケーション:
kubectl シークレットの作成汎用 pg-repl\--namespace postgres\--from-リテラル=ユーザー名=「repl_ユーザー名」--from-リテラル=パスワード=「repl_パスワード」
そしてそれらを設定します:
値.yml
レプリケーションシークレット: 名前:ページ-補充ユーザー名キー:ユーザー名パスワードキー:パスワード
- 不要な部分をから削除する
値.yaml
:
値.yml
スーパーユーザーユーザー名: 「ストロン」## スーパーユーザーのパスワード (superuserSecret が設定されていない場合は必須)スーパーユーザーのパスワード:レプリケーションユーザー名: 「リプラスサー」## レプリケーション ユーザーのパスワード (replicationSecret が設定されていない場合は必須)レプリケーションパスワード:
- レプリケーションを構成します。交換
値.yml
クラスタスペック: {} # スリープ間隔: 1s 最大スタンバイ数: 5
とともに
値.yml
クラスタスペック: 同期レプリケーション: 真実 min同期スタンバイ: 1 # クォーラムのようなレプリケーション maxSynchronousStandbys: 1 # クォーラムのようなレプリケーション 初期化モード:新しい
- ポッドをセットする破壊的予算
値.yml
ポッド中断予算: 最小利用可能: 2
- 構成、設定
pgパラメータ
そして、レプリカ数を 3 に設定します (合計 3 つのインスタンス: 1 つのマスター、1 つの同期スタンバイ、および 1 つの非同期スタンバイ):
値.yml
pgパラメータ: 最大接続数: 100# ...キーパー: # ... レプリカ数: 3# ...プロキシー: # ... レプリカ数: 3# ...番兵: # ... レプリカ数: 3
- Prometheus サービス検出アノテーションをセットアップします。
値.yml
注釈: prometheus.io/スクレイプ: "真実" prometheus.io/ポート: 「8080」
- ヘルム チャートをインストールする
$ヘルムインストール--name pg --namespace postgres -f local-values.yml ~/charts/stable/stolon/NAME: pg最終デプロイ日: 木曜日 2 月28 13:16:202019年名前空間: postgresステータス: デプロイされたリソース:==>v1/ServiceAccountNAME SECRETS AGEpg-stolon15秒==>v1beta1/RoleNAME AGEpg-stolon 5s==>v1beta1/RoleBindingNAME AGEpg-stolon 5s==>v1/ServiceNAME クラスター IP 外部 IP ポート(S)AGEpg-stolon-keeper-headless なし<なし> 5432/TCP 5spg-stolon-プロキシ10.39.250.171<なし> 5432/TCP 5s==>v1beta2/DeploymentNAME KINDpg-stolon-proxy Deployment.v1beta2.appspg-stolon-sentinel Deployment.v1beta2.apps==>v1beta2/StatefulSetpg-stolon-keeper StatefulSet.v1beta2.apps==>v1beta1/PodDisruptionBudgetNAME MIN-AVAILABLE MAX-UNAVAILABLE ALLOWED-DISRUPTIONS AGEpg-stolon-keeper2該当なし05spg-stolon-プロキシ2該当なし05spg-stolon-センチネル2該当なし05秒==>v1/ConfigMapNAME データ AGEpg-stolon05s注:Stolon クラスターがインストールされ、初期化されました。スーパーユーザーのパスワードを取得するには、次のコマンドを実行します。PGPパスワード=$(kubectl get Secret --namespace postgres pg-su -ojsonpath=「{.data.パスワード}」 |base64 --decode; エコー)
- インストールされているクラスターを確認します。
$ kubectl -n postgres get allNAME READY STATUS RESTARTS AGEpod/pg-stolon-create-cluster-mhmxm0/1 完了しました04mpod/pg-stolon-keeper-01/1 ランニング04mpod/pg-stolon-keeper-11/1 ランニング04mpod/pg-stolon-proxy-6f648b49d4-cnnpz1/1 ランニング04mpod/pg-stolon-proxy-6f648b49d4-rsv8j1/1 ランニング04mpod/pg-stolon-sentinel-694cf8f76f-8fmr91/1 ランニング04mpod/pg-stolon-sentinel-694cf8f76f-d5f6r1/1 ランニング04mNAME タイプ クラスター IP 外部 IP ポート(S)AGEservice/kubernetes ClusterIP10.39.240.1<なし> 443/TCP 3dservice/pg-stolon-keeper-headless ClusterIP なし<なし> 5432/TCP 4mservice/pg-stolon-proxy ClusterIP10.39.250.171<なし> 5432/TCP 4mNAME 希望の最新の利用可能な年齢deployment.apps/pg-stolon-proxy2 2 2 24mdeployment.apps/pg-stolon-sentinel2 2 2 24mNAME 希望の現在の準備完了年齢replicaset.apps/pg-stolon-proxy-6f648b49d42 2 24mreplicaset.apps/pg-stolon-sentinel-694cf8f76f2 2 24mNAME 希望現在の年齢statefulset.apps/pg-stolon-keeper2 24mNAME 希望成功年齢job.batch/pg-stolon-create-cluster1 14m
ノード間の分布を表示します。
$ kubectl -n postgres get pods -o WideNAME READY STATUS RESTARTS AGE IP NODEpg-stolon-create-cluster-mhmxm0/1 完了しました06m10.36.2.28 gke-primary-services-default-pool-8dd9a533-3tklpg-stolon-keeper-01/1 ランニング06m10.36.2.29 gke-primary-services-default-pool-8dd9a533-3tklpg-stolon-keeper-11/1 ランニング06m10.36.2.30 gke-primary-services-default-pool-8dd9a533-3tklpg-stolon-proxy-6f648b49d4-cnnpz1/1 ランニング06m10.36.2.27 gke-primary-services-default-pool-8dd9a533-3tklpg-stolon-proxy-6f648b49d4-rsv8j1/1 ランニング06m10.36.0.30 gke-primary-services-default-pool-8dd9a533-s2vgpg-stolon-sentinel-694cf8f76f-8fmr91/1 ランニング06m10.36.0.29 gke-primary-services-default-pool-8dd9a533-s2vgpg-stolon-sentinel-694cf8f76f-d5f6r1/1 ランニング06m10.36.1.18 gke-primary-services-default-pool-8dd9a533-h4zs
最初の試行中に発見したエラー
まず、私が得たのは次のとおりです。 2019年-03-01 06:19:51.982 GMT[108194]LOG: 認識されない構成パラメータ「最大接続数」 の ファイル 「/stolon-data/postgres/stolon-temp-postgresql.conf」ライン6 <br/> 2019年-03-01 06:19:51.982 GMT[108194]致命的: 構成ファイル 「/stolon-data/postgres/stolon-temp-postgresql.conf」エラーが含まれています<br/> 2019年-03-01T06:19:52.172Z エラー cmd/keeper.go:1083 インスタンスの起動に失敗しました{"エラー": 「postgres が予期せず終了しました」} <br/> 2019年-03-01T06:19:53.897Z エラー cmd/keeper.go:641 構成された pg パラメーターを取得できません{"エラー":「unix /tmp/.s.PGSQL.5432 にダイヤルします:
それを修正するために変更しました
値.yml
pgパラメータ: 最大接続数: 100
に
値.yml
pgパラメータ: 最大接続数: 「100」
その後不明のためエラーになりましたsha256sum
テンプレート内:
charts/stable/stolon/templates/sentinel-deployment.yaml
注釈: チェックサム/構成: {{include (print .Template.BasePath "/hooks/update)-集まる-スペック-job.yaml") 。|sha256sum}}
それを修正するために、Helm を最新バージョンに更新しました。
クラスターをテストする
簡単なコマンドを確認する
pg クラスター IP を検索します。
$ kubectl -n postgres サービスの取得| fgrepproxypg-stolon-proxy ClusterIP10.39.252.208<なし> 5432/TCP 18m
シークレットからパスワードを見つけます:
エコー $(kubectl get Secret --namespace postgres pg-su -ojsonpath=「{.data.パスワード}」 |base64 --decode)
プロキシ経由で PostgreSQL に接続し、テーブルを作成します。
$ kubectl -n postgres実行する-it pg-stolon-keeper-0 -- psql --host10.39.252.208 --ポート5432--username スーパーユーザー名 -Wテスト=# データベース作成テスト;$ kubectl -n postgres実行する-it pg-stolon-keeper-0 -- psql --host10.39.252.208 --ポート5432--username スーパーユーザー名 -W -dテストテスト=# テーブルテストを作成します (id int 主キーが null ではなく、値テキストが null ではありません);テーブルの作成テスト=# テスト値に挿入 (1, 'value1');入れる0 1テスト=# テストから * を選択; ID |値----+--------1 |値1(1行)
マスターの死亡を確認する
マスター ポッドを見つけます。pg-stolon-keeper-0
マスターだからpg_is_in_recovery
は間違い
それのための。
$ kubectl -n postgres実行する-it pg-stolon-keeper-0 -- psql --host127.0.0.1 --ポート5432--username スーパーユーザー名 -W -dテストテスト=# pg_is_in_recovery() を選択します。pg_is_in_recovery ------------------- f(1行)
マスターの死亡を確認します。現在のマスターに接続します。キーパー-0
プロキシ経由:
$ kubectl -n postgres実行する-it pg-stolon-keeper-0 -- psql --host10.39.252.208 --ポート5432--username スーパーユーザー名 -W -dテストテスト=# テストから * を選択; ID |値----+--------1 |値1(1行)
この SQL シェルを閉じずに、別のウィンドウで次のコマンドを実行します。
$ kubectl -n postgres ポッドの削除 pg-stolon-keeper-0pod「pg-stolon-keeper-0」削除されました
センチネルのログを確認してみましょう。
2019年-03-01T08:25:38.090Z 警告 cmd/sentinel.go:264 利用可能なキーパー情報がありません{「データベース」: 「66ced60c」、"キーパー": 「キーパー0」}2019年-03-01T08:25:43.140Z 警告 cmd/sentinel.go:264 キーパー情報がありません{「データベース」: 「66ced60c」、"キーパー": 「キーパー0」}2019年-03-01T08:25:48.201Z 警告 cmd/sentinel.go:264 利用可能なキーパー情報がありません{「データベース」: 「66ced60c」、"キーパー": 「キーパー0」}2019年-03-01T08:25:53.412Z 警告 cmd/sentinel.go:264 利用可能なキーパー情報がありません{「データベース」: 「66ced60c」、"キーパー": 「キーパー0」}2019年-03-01T08:25:58.462Z 情報 cmd/sentinel.go:976 マスター データベースが失敗しました{「データベース」: 「66ced60c」、"キーパー": 「キーパー0」}2019年-03-01T08:25:58.462Z 情報 cmd/sentinel.go:987 が試行しています探す障害が発生したマスターを置き換える新しいマスター2019年-03-01T08:25:58.463Z 情報 cmd/sentinel.go:1013 db を新しいマスターとして選択しています{「データベース」: 「6784fde9」、"キーパー": 「キーパー1」}2019年-03-01T08:26:08.564Z 情報 cmd/sentinel.go:1132 古いマスター データベースを削除しています{「データベース」: 「66ced60c」、"キーパー": 「キーパー0」}2019年-03-01T08:26:08.564Z 情報 cmd/sentinel.go:1218 新しい synchronousStandby の 1 つが削除されました{「データベース」: 「66ced60c」、「inSyncスタンバイ」: []、「同期スタンバイ」: [「66ced60c」]}2019年-03-01T08:26:08.565Z INFO cmd/sentinel.go:1230 予想される同期スタンバイを現在の既知の同期スタンバイに設定しますの 同期する同期スタンバイ{「inSyncスタンバイ」: []、「同期スタンバイ」: [「66ced60c」]}
私たちはそれを見てますキーパー-1
新しいマスターになった場合は、それを確認してください:
$ kubectl -n postgres実行する-it pg-stolon-keeper-1 -- psql --host127.0.0.1 --ポート5432--username スーパーユーザー名 -W -dテスト-c'pg_is_in_recovery() を選択'pg_is_in_recovery ------------------- f(1行)
kubernetes はポッドを再起動します。
$ kubectl -n postgres ポッドを取得| fgrepkeeperpg-stolon-keeper-01/1 ランニング01mpg-stolon-keeper-11/1 ランニング049mpg-ストロン-キーパー-21/1 ランニング049m
再起動前は次のとおりでした。
- キーパー-0 - マスター
- keeper-1 - 同期レプリカ
- keeper-2 - 非同期レプリカ
再起動後に確認してください:
$ kubectl -n postgres実行する-it pg-stolon-keeper-1猫/stolon-data/postgres/postgresql.conf| fgrep 同期する同期スタンバイ名= 'stolon_46cb4a21'$ kubectl -n postgres実行する-it pg-stolon-keeper-0猫/stolon-data/postgres/recovery.conf| fgrepスロット名プライマリスロット名= 'stolon_3a667e52'$ kubectl -n postgres実行する-it pg-stolon-keeper-2猫/stolon-data/postgres/recovery.conf| fgrepスロット名プライマリスロット名= 'stolon_46cb4a21'
再起動後は次のようになります。
- keeper-0 - 非同期レプリカ
- キーパー-1 - マスター
- keeper-2 - 同期レプリカ
PostgreSQL シェルでプロキシを実行する選択する
また:
テスト=# テストから * を選択;致命的: 接続を終了する予定ですに管理者コマンドサーバーが予期せず接続を閉じました。これはおそらくサーバーを意味します終了しました異常に前にまた その間リクエストを処理しています。つながりにサーバーが失われました。リセットの試行: 成功しました。テスト=# テストから * を選択;ID| 価値----+-------- 1 |値1(1 行)
そして、予想どおりプロキシが古いマスターへの接続を閉じたことがわかります。
ノート
次の 2 つの点に注意する必要があります。
- マスター Stolon を殺害すると、ポッドが再起動されるよりも早くマスターが再選出されました。
- 欠点は、プロキシがマスターにのみ送信されることです。設定する必要があります。
ハプロキシ
奴隷にも行く。
結論
この Stolon セットアップは本番環境で実行されています。GolangCI今。まだ問題は発生していません。