2021/06/30

GKEでZalando Postgres Operator On Rook Cephを試す

gkepostgresqlrookceph

概要

弊社では、弊社で運用しているwebアプリのほとんどをk8s上で運上しています。(GKEかEKSを使っています。)

今回は、k8s上で動く高可用性のPostgresqlクラスターが必要になってきたので調査の一環でデプロイまで試してみました。

今回試したのは、Zalando Postgres Operatorです。

ざっと調べた感じ、評判が良さそうだったのと、生のkubectlで操作ができるのが魅力的で、こちらを採用しました。

また、データの永続化のための仕組みとして、RookCeph Block Storateを利用しました。

以下手順を備忘録も兼ねて記載します。

導入手順

以下の手順で作成しました。

  1. GKEクラスターの作成
  2. Rook Cephのクラスター(PVCベース)をデプロイ
  3. Zalando Postgres OperatorとPostgresql Cluster(最小構成main+replica+config)をデプロイ

GKEクラスターの作成

注意事項

  • ノードの数は3としました。(rook cephのcluster sizeにも依存しそう?な気がします。一旦k8sのデフォルトでよくある3を選択しています。)
  • k8sのバージョンは1.20.7-gke.1800を選択しました。(rook cephをインストールするのに、一定のversionより上のものを選択する必要がありそうでした。)
  • 各ノードのimageタイプをUbuntuベースのものを利用する必要がありました。(COSを使ったら、rdbモジュールがなくて、エラーになり、node poolの再作成が必要になってしまいました。)

Rook requires a Linux Kernal built with the RBD module. Many distributions of Linux have this module but some don’t,
e.g. the GKE Container-Optimised OS (COS) does not have RBD. You can test your Kuberetes nodes by running
modprobe rbd. If it says ‘not found’, you may have to
rebuild your kernel
or choose a different Linux distribution.

Rook Cephのクラスターをデプロイ

  • operatorをデプロイします。
# まずは、operatorをデプロイします。
% git clone --single-branch --branch v1.6.6 https://github.com/rook/rook.git
% cd rook/cluster/examples/kubernetes/ceph
% kubectl create -f crds.yaml -f common.yaml -f operator.yaml
# operatorがデプロイされるまで待ちます。(RunningになればOK)
% kubectl get pods -n rook-ceph -w
NAME                                  READY   STATUS              RESTARTS   AGE
rook-ceph-operator-6b986cf46d-bzxh9   1/1     Running             0          34s
  • rook cephクラスターで使うpvc用のstorage classを定義します

storageclass-pd-ssd.yamlという名前で保存して、kubectl apply -f storageclass-pd-ssd.yamlします。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: pd-ssd
provisioner: kubernetes.io/gce-pd
parameters:
  type: pd-ssd
  fstype: ext4
  replication-type: none
  • rook cephクラウスターをデプロイします

cluster-on-pvc.yamlstorageClassName: gp2の箇所をstorageClassName: pd-ssdに編集後、kubectl apply -f cluster-on-pvc.yamlします。

    volumeClaimTemplate:
      spec:
        storageClassName: pd-ssd  # gp2となっている箇所を先程定義したpd-ssdに変更します。(変更箇所は2箇所)
        resources:
          requests:
            storage: 10Gi

以上でRook CephクラスターがGKEにデプロイされました。(以下のような感じで確認できました。)

# check pods of cephcluster
% kubectl get pods -n rook-ceph -w
NAME                                                              READY   STATUS      RESTARTS   AGE
csi-cephfsplugin-8w5cd                                            3/3     Running     0          4m41s
csi-cephfsplugin-fdmmf                                            3/3     Running     0          4m41s
csi-cephfsplugin-provisioner-78d66674d8-nb4st                     6/6     Running     0          4m40s
csi-cephfsplugin-provisioner-78d66674d8-xvvvm                     6/6     Running     0          4m40s
csi-cephfsplugin-zg8tc                                            3/3     Running     0          4m41s
csi-rbdplugin-5c7nw                                               3/3     Running     0          4m42s
csi-rbdplugin-bfrkh                                               3/3     Running     0          4m42s
csi-rbdplugin-provisioner-687cf777ff-4p9zm                        6/6     Running     0          4m41s
csi-rbdplugin-provisioner-687cf777ff-4zg2c                        6/6     Running     0          4m41s
csi-rbdplugin-tsdvf                                               3/3     Running     0          4m42s
rook-ceph-crashcollector-0e6a05b0707cc85c81519b8465f005a0-sj2qb   1/1     Running     0          2m6s
rook-ceph-crashcollector-3ef81d5c1764fc7afc02e7c3cdd9a96a-87x2d   1/1     Running     0          111s
rook-ceph-crashcollector-405f94fd9e0c8f4cf6b22739d5fe11f9-9f95m   1/1     Running     0          2m1s
rook-ceph-mgr-a-7d9b46fdb4-5mdrc                                  1/1     Running     0          2m7s
rook-ceph-mon-a-6bbdfff466-28747                                  1/1     Running     0          4m56s
rook-ceph-mon-b-57c4d78d5f-q68j6                                  1/1     Running     0          3m18s
rook-ceph-mon-c-cc896555d-5m4h6                                   1/1     Running     0          2m29s
rook-ceph-operator-6b986cf46d-xkf5b                               1/1     Running     0          10m
rook-ceph-osd-0-59d68c8894-bq296                                  1/1     Running     0          90s
rook-ceph-osd-1-69d4756cc-kvt4f                                   1/1     Running     0          90s
rook-ceph-osd-2-67f5fd8bfb-r8gdp                                  1/1     Running     0          62s
rook-ceph-osd-prepare-set1-data-0q872c-ccbxl                      0/1     Completed   0          2m3s
rook-ceph-osd-prepare-set1-data-1h4hrj-xj2rs                      0/1     Completed   0          2m3s
rook-ceph-osd-prepare-set1-data-2w7kbn-b9d68                      0/1     Completed   0          2m3s

# check if cephcluster is healthy
% kubectl -n rook-ceph get cephcluster
NAME        DATADIRHOSTPATH   MONCOUNT   AGE     PHASE   MESSAGE                        HEALTH      EXTERNAL
rook-ceph   /var/lib/rook     3          5m52s   Ready   Cluster created successfully   HEALTH_OK

Zalando Postgres OperatorとPostgresql Cluster(最小構成main+replica+config)をデプロイ

  • postgres operatorのデプロイ
% git clone https://github.com/zalando/postgres-operator.git
% cd postgres-operator
kubectl create -f manifests/configmap.yaml
kubectl create -f manifests/operator-service-account-rbac.yaml
kubectl create -f manifests/postgres-operator.yaml
kubectl create -f manifests/api-service.yaml

# check postgres-operator pod's status
% kubectl get pod -l name=postgres-operator -w
NAME                                 READY   STATUS              RESTARTS   AGE
postgres-operator-55b8549cff-hvqfm   1/1     Running             0          12s
  • postgres clusterのデプロイ

先程定義したrook ceph block storageを利用するように、minimal-postgres-manifest.yamlspec.volumeのプロパティに、storageClassを追加して、kubectl apply -f minimal-postgres-manifest.yamlします。

apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
  name: acid-minimal-cluster
  namespace: default
spec:
  teamId: "acid"
  volume:
    size: 1Gi
    storageClass: rook-ceph-block  # この行を追加
  numberOfInstances: 2
  users:
    zalando:
    - superuser
    - createdb
    foo_user: []
  databases:
    foo: zalando
  preparedDatabases:
    bar: {}
  postgresql:
    version: "13"
# postgresql serverのpodが立ち上がっていることが確認できます。(デフォルトでは0がmainで1がreplica)
% kubectl get pods
NAME                                 READY   STATUS      RESTARTS   AGE
acid-minimal-cluster-0               1/1     Running     0          139m
acid-minimal-cluster-1               1/1     Running     0          138m

# postgresql clusterのserviceも立ち上がっていることが確認できました。
% kubectl get svc
NAME                          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)    AGE
acid-minimal-cluster          ClusterIP   172.17.166.182   <none>        5432/TCP   141m
acid-minimal-cluster-config   ClusterIP   None             <none>        <none>     140m
acid-minimal-cluster-repl     ClusterIP   172.17.189.111   <none>        5432/TCP   141m
postgres-operator             ClusterIP   172.17.191.19    <none>        8080/TCP   141m

接続確認

  • mainにログインして、データベースとテーブルを作成して、データをインサートしました。
  • replicaにログインしてデータを参照した後、インサートではエラーになることを確認しました。
# login to main pod
% kubectl exec -it acid-minimal-cluster-0 -- bash
# psql -U postgres
postgres=#  create database zalando_test;
CREATE DATABASE
postgres=# \c zalando_test;
You are now connected to database "zalando_test" as user "postgres".
# create table users (id SERIAL NOT NULL, name VARCHAR(255) NOT NULL, PRIMARY KEY(id));
CREATE TABLE
# insert into users (name) values ('user_a');
INSERT 0 1
# select * from users;
 id |  name  
----+--------
  1 | user_a
(1 row)

# login to replica pod
% kubectl exec -it acid-minimal-cluster-1 -- psql -U postgres -d zalando_test
psql (13.3 (Ubuntu 13.3-1.pgdg18.04+1))
Type "help" for help.

zalando_test=# \dt
         List of relations
 Schema | Name  | Type  |  Owner   
--------+-------+-------+----------
 public | users | table | postgres
(1 row)

zalando_test=# select * from users;
 id |  name  
----+--------
  1 | user_a
(1 row)

# insert into users (name) values ('user_a');
ERROR:  cannot execute INSERT in a read-only transaction

感想

今回はまだpodを無理矢理落としたり(kubectl delete pods acid-minimal-cluster-0)、
GCSへのバックアップなどはためせてないですが、近いうちに最小構成ではない構成を組んでみて試してみようと思います。

基本的にはgithubのドキュメントをみながらなんとか作業できるなという感じだったんですが、ちょっと変わったことをやろうとすると
かなり深くまでみないと制御できなそうだなという感じを受けました。

とはいえ、今後大量データを扱うアプリケーションの実装が控えてたりするので、早めにRookベースのアプリになれておかないとなという感じです。