Zalando Postgres Operator を試してみた

ふとしたきっかけで、自宅ラボに PostgreSQL as a Service が欲しくなったので Zalando Postgres Operator を試してみました。

Zalando Postgres Operator とは

Zalando Postgres Operator(以下、Postgres Operator)とは、Kubernetes クラスタに HA 構成の PostgreSQL クラスタを簡単に構築してくれる Operator です。

Postgres Operator を Kubernetes クラスタにデプロイすると、postgresql というカスタムリソースを宣言したマニフェストを使うだけで簡単に PostgreSQL クラスタを構築できます。

また、専用の Web UI を使って PostgreSQL クラスタを管理することもできます。

この Postgres Operator を自宅ラボの Kubernetes クラスタにデプロイして PostgreSQL as a Service として利用します。

Zalando SE

Zalando SE とは、ヨーロッパを中心に展開するオンラインファッションプラットフォームを運営する企業です(日本で言うところの ZOZO でしょうか?)。

Zalando SE は GitHub で複数のオープンソースのプロジェクトを公開しています。

https://github.com/zalando

Zalando Postgres Operator も Zalando が開発しているプロジェクトの一つです。

構成

公式ドキュメント の画像を拝借させてもらうのですが、 Postgres Operator デプロイ後に postgresql カスタムリソースをデプロイすると、下図のリソースが Postgres Operator によって作成されます。

f:id:nnstt1:20210122034306p:plain

HA 構成の PostgreSQL クラスタを構築するため、Pod には Spilo というコンテナイメージが使用されます。

Spilo コンテナの中では PostgreSQLPatroni というサービスが動いています。

Patroni は PostgreSQL クラスタのノードを監視して、マスタが故障した際に自動フェイルオーバーを実現するためのサービスです。

f:id:nnstt1:20210122034401p:plain

Spilo と Patroni は共に Zalando のプロジェクトとなっています。

今回初めて Zalando を知りましたが、ほとんど自分たちで開発していてすごいですね…。

インストール

Postgres Operator のインストール方法には以下の3通りあります。

  • マニュアル
  • Kustomize
  • Helm

今回はマニュアル、つまりマニフェストをデプロイする方法で Postgres Operator をインストールします。

公式リポジトリ内のマニフェストkubectl apply するだけで Operator と Web UI をインストールできます。

$ git clone https://github.com/zalando/postgres-operator.git
$ cd postgres-operator/

$ kubectl create namespace zalando-postgres

# Postgres Operator
$ kubectl apply -f manifests/configmap.yaml
$ kubectl apply -f manifests/operator-service-account-rbac.yaml  # namespace: defualt のものがあるので注意
$ kubectl apply -f manifests/postgres-operator.yaml
$ kubectl apply -f manifests/api-service.yaml

# UI
$ kubectl apply -f ui/manifests/

# 確認
$ kubectl get all -n zalando-postgres
NAME                                        READY   STATUS    RESTARTS   AGE
pod/postgres-operator-f6bfcd5b4-zsh2n       1/1     Running   0          3d7h
pod/postgres-operator-ui-6dc47bbbc6-phdvw   1/1     Running   0          4d9h

NAME                           TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
service/postgres-operator      ClusterIP      10.96.100.47     <none>          8080/TCP       6d23h
service/postgres-operator-ui   LoadBalancer   10.104.183.177   192.168.2.252   80:31363/TCP   14d

NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/postgres-operator      1/1     1            1           6d23h
deployment.apps/postgres-operator-ui   1/1     1            1           14d

NAME                                              DESIRED   CURRENT   READY   AGE
replicaset.apps/postgres-operator-f6bfcd5b4       1         1         1       6d23h
replicaset.apps/postgres-operator-ui-6dc47bbbc6   1         1         1       13d

postgres-operatorpostgres-operator-ui の Pod がデプロイされました。

注意点 1

manifests/operator-service-account-rbac.yaml には namespace: default と指定されているため、Postgres Operator 用の namespace を default 以外にする場合には変更が必要です。

# manifests/operator-service-account-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: postgres-operator
  namespace: zalando-postgres

注意点 2

Web UI のマニフェストはそのままでは namespace: default しか管理対象となりません。 全 namespace を対象とする場合は、ui/manifests/deployment.yamlTARGET_NAMESPACE"*" を指定します。

# ui/manifests/deployment.yaml
env:
  - name: "TARGET_NAMESPACE"
    value: "default"    # "*" に変更

以上で Postgres Operator のインストールは完了です。

PostgreSQL クラスタ構築

Postgres Operator の準備ができたので、PostgreSQL クラスタを構築していきます。 クラスタ構築には2通りあります。

  • Web UI
  • マニュアル

Web UI

さきほど作成した postgres-operator-ui に Service または Ingress 経由でアクセスすると、クラスタ作成画面が表示されます。

必要な項目を入力して「Create Cluster」を押すと PostgreSQL クラスタが作成されます。

f:id:nnstt1:20210122024955p:plain

マニュアル

マニュアルの場合は、postgresql リソースを宣言したマニフェストを準備します。

公式リポジトリのサンプルや、Web UI のクラスタ作成画面に表示されるマニフェストが参考になります。

以下は自宅ラボの k8s クラスタにデプロイしたお試しマニフェストです。 Rook で用意した storageClass を追加しています。

# postgres.yaml
apiVersion: acid.zalan.do/v1
kind: postgresql
metadata:
  name: acid-example
  namespace: default
  labels:
    team: acid
spec:
  teamId: acid
  postgresql:
    version: "13"    # note: String
  numberOfInstances: 1
  volume:
    size: 1Gi
    storageClass: rook-ceph-block
  users:
    example: []
  databases:
    example: example
  allowedSourceRanges:
  resources:
    requests:
      cpu: 100m
      memory: 100Mi
    limits:
      cpu: 500m
      memory: 500Mi

postgresql リソースのデプロイが完了すると、Postgres Operator により StatefulSet や Service の各種リソースが作成されます。

$ kubectl get all
NAME                      READY   STATUS    RESTARTS   AGE
pod/acid-example-0            1/1     Running   0          164m

NAME                      TYPE           CLUSTER-IP       EXTERNAL-IP     PORT(S)        AGE
service/acid-example          ClusterIP      10.98.124.54     <none>          5432/TCP       7d
service/acid-example-config   ClusterIP      None             <none>          <none>         7d
service/acid-example-repl     ClusterIP      10.106.121.165   <none>          5432/TCP       7d

NAME                        READY   AGE
statefulset.apps/acid-example   1/1     7d

NAME                                TEAM   VERSION   PODS   VOLUME   CPU-REQUEST   MEMORY-REQUEST   AGE   STATUS
postgresql.acid.zalan.do/acid-example   acid   13        1      1Gi      100m          100Mi            7d    Running

注意点

Web UI の注意点として、volume.storageClass の指定ができません。

そのため、Rook Ceph で作成した storageClass などを指定する場合はマニフェストを使う必要があります。

Web UI では基本的な情報を埋め込んだマニフェストを作成し、細かい設定はマニフェストに直接記述するのが良さそうです。

接続確認

kubectl port-forward を使い、ローカル環境から PostgreSQL クラスタへ接続できるか確認します。

本当はフェイルオーバーなど HA 構成の特性を検証してみたかったのですが、PostgreSQL について勉強不足のため実施していません。

Postgres Operator によりログイン用のパスワードが Secret として作成されています。

$ export PGMASTER=$(kubectl get pods -o jsonpath={.items..metadata.name} -l application=spilo)
$ kubectl port-forward $PGMASTER 6432:5432 &

$ kubectl get secret postgres.acid-example.credentials -o 'jsonpath={.data.password}' | base64 -d
$ export PGSSLMODE=require
$ psql -U postgres -h localhost -p 6432
Handling connection for 6432
ユーザ postgres のパスワード: 
Handling connection for 6432
psql (13.1)
SSL 接続 (プロトコル: TLSv1.2、暗号化方式: ECDHE-RSA-AES256-GCM-SHA384、ビット長: 256、圧縮: オフ)
"help"でヘルプを表示します。

postgres=# 

無事に Postgres Operator で作成した PostgreSQL クラスタに接続することができました。

参考にさせていただいたサイト

speakerdeck.com

今回 PostgreSQL as a Service を構築するにあたって情報を提供くださった @tzkb さんのスライドです。

そもそもの「Kubernetes 上でデータベースを動かす際の課題」から、より深い話まで説明されています。

仕事では SQL Server メインなのですが、Database on Kubernetes という観点では課題は同じはずですので、とても勉強になりました。

www.sraoss.co.jp

Zalando Postgres Operator の紹介をされている記事です。

今回のネタは、ほぼ上記記事の内容を元にしています。 フェイルオーバーなどのクラスタ構成の検証もされていて興味深かったです。

recruit.gmo.jp

Patroni を試されている記事です。

自分は Patroni が何者か分からない状態だったので参考になりました。

さいごに

PostgreSQL as a Service のための Zalando Postgres Operator の使い方について確認しました。

PostgreSQL については本当に初心者なのですが、そのような状態でも Kubernetes クラスタがあれば簡単に PostgreSQL as a Service が実現できました。

もし手元に Kubernetes クラスタをお持ちなら、クラウドサービスではなく Zalando Postgres Operator も選択肢に入れてみると面白いかもしれません。