Postgres Operator on Azure Kubernetes Service

AKS (Azure Kubernetes Service) のお勉強がてら Kubernetes 関連の記事を眺めていたところ、「[データ永続化と構築運用の自動化を実現する「PostgreSQL on Kubernetes」の仕組み：クラウドネイティブ時代のデータベース（2） - ＠IT](https://www.atmarkit.co.jp/ait/articles/2105/20/news006.html)」という記事があり、その中で紹介されている [Postgres Operator](https://access.crunchydata.com/documentation/postgres-operator/latest/) が気になったので触ってみました。

![How it Works - PGO](https://access.crunchydata.com/documentation/postgres-operator/4.7.0/Operator-Architecture.png)

# 環境

* AKS (v1.21.1)
* Postgres Operator (v4.7.0)
* WSL 2 (Ubuntu 20.04)

## 準備

Azure CLI などは Windows 環境でも利用できますが、Azure ドキュメント類において bash 表記になっていたりして環境変数の設定がコピペで実施できなくて面倒だったりするので、WSL 2 の Ubuntu を利用します。

* [Azure CLI のインストール](https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-linux?pivots=apt)

また、以降の手順はベースとして [Quickstart](https://access.crunchydata.com/documentation/postgres-operator/latest/quickstart/) を参考にしています。一部抜粋ではありますので、詳細な内容については上記のマニュアルも併せて読んでみてください。

# インストール

## Postgres Operator のインストール (AKS へのデプロイ)

```
kubectl create namespace pgo
kubectl apply -f https://raw.githubusercontent.com/CrunchyData/postgres-operator/v4.7.0/installers/kubectl/postgres-operator.yml
```

デフォルトの環境では特にエラーもなくデプロイできました。

## Postgres Operator Client のインストール

```sh
curl https://raw.githubusercontent.com/CrunchyData/postgres-operator/v4.7.0/installers/kubectl/client-setup.sh > client-setup.sh
chmod +x client-setup.sh

./client-setup.sh
```

注意点としては、このスクリプトの中で Kubernetes と接続する必要があるため、`kubectl` コマンドで Kubernetes クラスターに接続できていることを確認しておきましょう。

下記のようなメッセージが出ていれば OK のはずです。

```
pgo client files have been generated, please add the following to your bashrc
export PATH=/home/<user>/.pgo/pgo:$PATH
export PGOUSER=/home/<user>/.pgo/pgo/pgouser
export PGO_CA_CERT=/home/<user>/.pgo/pgo/client.crt
export PGO_CLIENT_CERT=/home/<user>/.pgo/pgo/client.crt
export PGO_CLIENT_KEY=/home/<user>/.pgo/pgo/client.key
```

公式の手順に則り、環境変数を追加しておきます。

```sh
cat <<EOF >> ~/.bashrc
export PGOUSER="${HOME?}/.pgo/pgo/pgouser"
export PGO_CA_CERT="${HOME?}/.pgo/pgo/client.crt"
export PGO_CLIENT_CERT="${HOME?}/.pgo/pgo/client.crt"
export PGO_CLIENT_KEY="${HOME?}/.pgo/pgo/client.key"
export PGO_APISERVER_URL='https://127.0.0.1:8443'
export PGO_NAMESPACE=pgo
EOF

source ~/.bashrc
```

## インストール確認

別のターミナルを開き、下記のコマンドを実行してアクセスできるようにしておきます。この接続 (ポートフォワーディング) は以降も使うので、実行しっぱなしにしておきましょう。

```
kubectl -n pgo port-forward svc/postgres-operator 8443:8443
```

そのうえで、`pgo version` コマンドがエラーなく実行できれば、インストール完了です。

```
$ pgo version
pgo client version 4.7.0
pgo-apiserver version 4.7.0
```

# 試してみる

## クラスターの作成 (1)

`hippo` というクラスターを作成してみます。コマンドの実行後、PostgreSQL のユーザー名とパスワードが表示されます。

```
$ pgo create cluster -n pgo hippo
created cluster: hippo
workflow id: 934184a4-9e9f-4283-bfd5-f1ead389eb58
database name: hippo
users:
        username: testuser password: :-7Pq?*y/,TmzzQgPbqFMDoX
```

作成状況は、`pgo test` コマンドで確認できます。最初はまだ `DOWN` 状態ですが…

```
$ pgo test -n pgo hippo

cluster : hippo
        Services
        Instances
                primary (): DOWN
```

しばらく待つと、`UP` 状態になります。

```
$ pgo test -n pgo hippo

cluster : hippo
        Services
                primary (10.0.51.3:5432): UP
        Instances
                primary (hippo-6458f6b475-7mstm): UP
```

Pod を確認してみます。新しい Pod が作成されています！

```
$ kubectl -n pgo get po
NAME                                          READY   STATUS      RESTARTS   AGE
backrest-backup-hippo-7hhp5                   0/1     Completed   0          103s
hippo-6458f6b475-7mstm                        1/1     Running     0          3m4s
hippo-backrest-shared-repo-7fb55dfcd4-92v65   1/1     Running     0          3m49s
pgo-deploy-6kvkd                              0/1     Completed   0          27m
postgres-operator-668987fdb4-7hv9q            4/4     Running     1          26m
```

Service を確認しますが、EXTERNAL-IP が振られていないので外部アクセスはできません😢

```
$ kubectl -n pgo get svc
NAME                         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
hippo                        ClusterIP   10.0.51.3      <none>        2022/TCP,5432/TCP            3m51s
hippo-backrest-shared-repo   ClusterIP   10.0.167.120   <none>        2022/TCP                     3m51s
postgres-operator            ClusterIP   10.0.42.206    <none>        8443/TCP,4171/TCP,4150/TCP   26m
```

## クラスターの作成 (2)

サクッと試せたので、もう少しオプションを付けて作成してみます。`--service-type` で LoadBalancer を指定し、`--replica-count` でレプリカ数を 2 にしてみます。

```
pgo create cluster lb-rep-db \
  --service-type=LoadBalancer \
  --replica-count 2 \
```

10分くらい時間がかかる場合がありますので、気長に待ちます☕

完了したら、Pod と Service を確認してみます。

```
$ kubectl -n pgo get po
NAME                                              READY   STATUS      RESTARTS   AGE
backrest-backup-lb-rep-db-57mjg                   0/1     Completed   0          8m15s
lb-rep-db-9cc696cd7-g74dk                         2/2     Running     0          9m
lb-rep-db-backrest-shared-repo-68899b9bfb-x82v7   1/1     Running     0          9m20s
lb-rep-db-crul-777f4cc998-qdc8p                   2/2     Running     0          7m42s
lb-rep-db-wsjq-546bc99984-kchvs                   2/2     Running     0          7m42s
pgo-deploy-6kvkd                                  0/1     Completed   0          45m
postgres-operator-668987fdb4-7hv9q                4/4     Running     1          44m

$ kubectl -n pgo get svc
NAME                             TYPE           CLUSTER-IP     EXTERNAL-IP      PORT(S)                                        AGE
lb-rep-db                        LoadBalancer   10.0.6.195     20.43.88.99      9187:31938/TCP,2022:32201/TCP,5432:30720/TCP   9m25s
lb-rep-db-backrest-shared-repo   ClusterIP      10.0.89.222    <none>           2022/TCP                                       9m25s
lb-rep-db-replica                LoadBalancer   10.0.23.7      52.155.112.137   9187:30002/TCP,2022:32284/TCP,5432:30629/TCP   7m47s
postgres-operator                ClusterIP      10.0.42.206    <none>           8443/TCP,4171/TCP,4150/TCP                     44m
```

先ほどの `hippo` の結果と比べると、Pod では `lb-rep-db-crul-777f4cc998-qdc8p` と `lb-rep-db-wsjq-546bc99984-kchvs` が、Service では `lb-rep-db-replica` が増えています。これが、レプリカサーバーの Pod と Service になっているようです。

また、EXTERNAL-IP も振られているので、外部アクセスができるようになりました。

## スケールアップしてみる

このクラスタをスケールアップしてみます。スケールアップのコマンドは `pgo scale` です。

```
$ pgo scale lb-rep-db
WARNING: Are you sure? (yes/no): yes
created Pgreplica lb-rep-db-ymwn
```

新しいレプリカ (`lb-rep-db-ymwn-9c8975556-b72h7`) が作成されました。今度はそれほど時間はかからず、手元の環境だと 1 分くらいでした。

```
$ kubectl -n pgo get po
NAME                                              READY   STATUS              RESTARTS   AGE
backrest-backup-lb-rep-db-57mjg                   0/1     Completed           0          9m33s
lb-rep-db-9cc696cd7-g74dk                         2/2     Running             0          10m
lb-rep-db-backrest-shared-repo-68899b9bfb-x82v7   1/1     Running             0          10m
lb-rep-db-crul-777f4cc998-qdc8p                   2/2     Running             0          9m
lb-rep-db-wsjq-546bc99984-kchvs                   2/2     Running             0          9m
lb-rep-db-ymwn-9c8975556-b72h7                    2/2     Running             0          1m37s
pgo-deploy-6kvkd                                  0/1     Completed           0          47m
postgres-operator-668987fdb4-7hv9q                4/4     Running             1          46m
```

## スケールダウンしてみる

スケールダウンは `pgo scaledown` コマンドです。落としたいサーバを指定することができます。

`--query` オプションを追加してレプリカ名を確認し…

```
$ pgo scaledown lb-rep-db --query

Cluster: lb-rep-db
REPLICA                 STATUS          NODE            REPLICATION LAG         PENDING RESTART
lb-rep-db-crul          running         aks-agentpool-92398728-vmss00000p                  0 MB                   false
lb-rep-db-wsjq          running         aks-agentpool-92398728-vmss00000r                  0 MB                   false
lb-rep-db-ymwn          running         aks-agentpool-92398728-vmss00000q                  0 MB                   false
```

`--target` オプションで指定してスケールダウンします。

```
$ pgo scaledown lb-rep-db --target=lb-rep-db-wsjq
```

今回は、スケールダウン中の Pod の動きを追ってみました。

```
$ kubectl -n pgo get po -w
NAME                                              READY   STATUS      RESTARTS   AGE
backrest-backup-lb-rep-db-57mjg                   0/1     Completed   0          12m
lb-rep-db-9cc696cd7-g74dk                         2/2     Running     0          13m
lb-rep-db-backrest-shared-repo-68899b9bfb-x82v7   1/1     Running     0          13m
lb-rep-db-crul-777f4cc998-qdc8p                   2/2     Running     0          11m
lb-rep-db-wsjq-546bc99984-kchvs                   2/2     Running     0          11m
lb-rep-db-ymwn-9c8975556-b72h7                    2/2     Running     0          3m9s
pgo-deploy-6kvkd                                  0/1     Completed   0          49m
postgres-operator-668987fdb4-7hv9q                4/4     Running     1          49m

lb-rep-db-rmdata-wzbw-c8tfc                       0/1     Pending     0          0s
lb-rep-db-rmdata-wzbw-c8tfc                       0/1     Pending     0          0s
lb-rep-db-rmdata-wzbw-c8tfc                       0/1     ContainerCreating   0          0s
lb-rep-db-wsjq-546bc99984-kchvs                   2/2     Terminating         0          12m
lb-rep-db-wsjq-546bc99984-kchvs                   2/2     Terminating         0          12m
lb-rep-db-rmdata-wzbw-c8tfc                       1/1     Running             0          2s
lb-rep-db-wsjq-546bc99984-kchvs                   0/2     Terminating         0          12m
lb-rep-db-wsjq-546bc99984-kchvs                   0/2     Terminating         0          12m
lb-rep-db-wsjq-546bc99984-kchvs                   0/2     Terminating         0          12m
lb-rep-db-rmdata-wzbw-c8tfc                       0/1     Completed           0          10s
lb-rep-db-rmdata-wzbw-c8tfc                       0/1     Terminating         0          10s
lb-rep-db-rmdata-wzbw-c8tfc                       0/1     Terminating         0          10s
```

`lb-rep-db-rmdata-wzbw-c8tfc` という Pod が作成された後、`lb-rep-db-wsjq-546bc99984-kchvs` Pod が削除されているのが分かります。`*-rmdata-*` な Pod は、名前からすると削除処理のジョブ的な動きをする Pod ですかね。なかなか面白いです。

-----

今回は、このあたりまでにしておこうと思います。

[公式ドキュメント](https://access.crunchydata.com/documentation/postgres-operator/latest/) にあるように、他にも様々な機能があります。「定期バックアップ機能」も試してみたいですし、「永続領域は Azure Files 等に保存する」とかもやってみたいですねー。