当投稿はKubernetes Advent Calenderの18日目の記事となります。
17日目はnnao45さんの「【Kubernetes】KubernetesのCronJobのcronスケジュールはどう管理されているのか?」でした。
今日はKubernetesのDatabase Operatorということで、私が何度かカンファレンスで触れているKubeDBについて解説したいと思います。今回は主に耐障害性についての設計とその動作確認をしていきます。
TL:DR
- KubeDBはマルチデータストアなKubernetes Operator。
- しかし、Kubernetes v1.16ではうまく動かない。
- PostgreSQLのレプリケーションは簡単に構築可能、障害時のFOも問題なし
- (開発体制に不安はあるものの)今後のリリースに期待したい。
KubeDBとは
AppsCodeという会社が提供しているプロダクトの一つで、「プロダクショングレードのデータベースをKubernetes上で簡単に使おう!」というキャッチフレーズで開発されているKubernetesのDatabase Operatorです。
何より、KubeDBというネーミングがいいですね。
私もDatabase Operatorを開発するとしたらこの名前にしたかったという思いがあり、ここまでブチ上げたら是非成功して欲しいなと見守っています。
KubeDBの特徴
これまでカンファレンスで説明している、PostgreSQLでKubeDBを利用した際のアーキテクチャは下図のようになります。
特徴としては以下があげられます。
- PostgreSQLだけでなく、高可用構成の様々なデータベースをKubernetesに構築できる。
- PostgreSQLでは、Streaming ReplicationをYAMLベースで簡単に構成できる。
- SnapshotのCRDが準備されており、バックアップ/リカバリもKubernetesから可能。
なお、現在サポートされているデータストアは下記の6つです。
- PostgreSQL
- Elasticsearch
- MySQL
- MongoDB
- Redis
- Memcached
KubeDBを試してみる
今回はPostgreSQLのストリーミング・レプリケーション環境ををKubeDBで構築してみます。
そのためにはまず、KubeDB Operatorのインストールが必要です。
こちらのドキュメントにはシェルスクリプトとHelmでのインストール方法が紹介されています。
しかし!
現時点で最新のv0.13.0-rc.0はKubernetesのv1.16に対応が出来ていません。そのため、Kubernetesのクラスタはv1.15以下をご用意下さい(今回はv1.15で試しています)。
KubeDB Operatorのインストール
まず、事前にdemoというNamespaceを作成し、OperatorとPostgreSQLのインストールはここに行います。
その上で、v1.15のK8sクラスタに対して以下のシェルスクリプトを実行します。こちらが正常に完了したら、PodやPostgreSQL向けのCRDを確認していきます。
# kubedb.shを利用したOperatorのインストール
$curl -fsSL https://github.com/kubedb/installer/raw/v0.13.0-rc.0/deploy/kubedb.sh | bash -s -- --namespace=demo
# Operatorの動作確認
$ kubectl get pod -n demo
NAME READY STATUS RESTARTS AGE
kubedb-operator-5565fbdb8-v9ppf 1/1 Running 0 25m
# PostgresのCRDを確認
$ kubectl get postgresversions
NAME VERSION DB_IMAGE DEPRECATED AGE
10.2 10.2 kubedb/postgres:10.2 true 9m29s
10.2-v1 10.2 kubedb/postgres:10.2-v2 true 9m29s
10.2-v2 10.2 kubedb/postgres:10.2-v3 9m29s
10.2-v3 10.2 kubedb/postgres:10.2-v4 9m29s
10.2-v4 10.2 kubedb/postgres:10.2-v5 9m29s
10.6 10.6 kubedb/postgres:10.6 9m29s
10.6-v1 10.6 kubedb/postgres:10.6-v1 9m29s
10.6-v2 10.6 kubedb/postgres:10.6-v2 9m29s
11.1 11.1 kubedb/postgres:11.1 9m29s
11.1-v1 11.1 kubedb/postgres:11.1-v1 9m29s
11.1-v2 11.1 kubedb/postgres:11.1-v2 9m29s
11.2 11.2 kubedb/postgres:11.2 9m29s
#(以下略)
PostgreSQLクラスタをインストール
次にストリーミング・レプリケーション構成のPostgreSQLクラスタをインストールします。
KubeDBではKind:PostgresのCRDにstandbyModeを指定することで、Warm(リードレプリカなし)とHot(リードレプリカあり)を選ぶことが出来ます。今回はHot Standbyで構築してみます。
また、Postgresのバージョンは11.1にしてみます。利用するYAMLはこちらになります。
apiVersion: kubedb.com/v1alpha1
kind: Postgres
metadata:
name: hot-postgres
namespace: demo
spec:
version: "11.1"
replicas: 3
standbyMode: Hot
storageType: Durable
storage:
storageClassName: "standard"
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
ではこれをapplyし、数分待ってPostgreSQLポッドの起動を確認します。接続用にServiceの作成状況も確認しておきます。
# Postgresのインストール
$ kubectl apply -f v11.hot-postgres.yaml
# Pod起動を確認
$ kubectl get pod -n demo -o wide
NAME READY STATUS RESTARTS AGE IP
hot-postgres-0 1/1 Running 0 3h3m 10.42.2.2
hot-postgres-1 1/1 Running 0 179m 10.42.1.3
hot-postgres-2 1/1 Running 0 178m 10.42.2.3
# Serviceの作成状況を確認
$ kubectl get svc -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
hot-postgres ClusterIP 10.43.228.176 <none> 5432/TCP 33m
hot-postgres-replicas ClusterIP 10.43.220.153 <none> 5432/TCP 33m
PodのIPは後ほど接続確認に使いますので覚えておくと良いことがあります。
Postgresへの接続確認
接続確認にはpsqlとpgbenchを利用するためのPod(pgtools)を使います。
そのために以下のYAMLをKubernetesにapplyしましょう。
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: pgtools
labels:
app: pgtools
namespace: demo
spec:
serviceName: pgtools
replicas: 1
selector:
matchLabels:
app: pgtools
template:
metadata:
labels:
app: pgtools
spec:
containers:
- image: tzkoba/pgbench:0.1
imagePullPolicy: Always
name: pgbench
stdin: true
tty: true
primaryへの接続
先ほどのpgtoolsポッドを使用して、KubeDBで作成したストリーミング・レプリケーション構成のPostgreSQLに接続してみます。
ここで一点注意ですが、KubeDBでは接続用にpostgresユーザとそれに対応したKubernetesのSecretが作成され、パスワードはランダムで生成されます。そのため、こちらのドキュメントを参考にパスワードをデコードして、取得する必要があります。
パスワードが分かったら、まずプライマリのPostgreSQLインスタンスに接続してみましょう。pgtoolsのPodからpsqlを起動し、ホスト名にはhot-postgres(プライマリインスタンスに接続するサービス名)を指定します。
$ kubectl exec -it -n demo pgtools-0 -c pgbench -- psql -h hot-postgres -U postgres
Password for user postgres:
psql (10.5, server 11.1)
Type "help" for help.
postgres=# select inet_server_addr();
inet_server_addr
------------------
10.42.2.2
(1 row)
インスタンスのローカルIPをSelectしてみると、先ほど確認したhot-postgres-0のIPと同じであることが分かります。
replicaへの接続
次にスタンバイのPostgreSQLインスタンス2つへの接続を試してみます。
この際にはホスト名にhot-postgres-replicas(レプリカインスタンスへ接続するサービス名)を指定します。
2つのインスタンスに繋がる様子が見えるように何度か試行すると、以下のような結果が得られます。
$ kubectl exec -it -n demo pgtools-0 -c pgbench -- psql -h hot-postgres-replicas -U postgres -c "select inet_server_addr();"
Password for user postgres:
inet_server_addr
------------------
10.42.1.3
(1 row)
$ kubectl exec -it -n demo pgtools-0 -c pgbench -- psql -h hot-postgres-replicas -U postgres -c "select inet_server_addr();"
Password for user postgres:
inet_server_addr
------------------
10.42.2.3
(1 row)
先ほど確認したhot-postgres-1とhot-postgres-2のPodに接続していることが分かります。
どのようにprimaryとreplicaを識別しているか
さて、primaryとstandbyへの接続はどのように実装されているかを確認しておきましょう。
まず、Podにkubedb.com/roleとしてprimary/replicaのいずれかのラベルが付与されています。このラベルを元にServiceが振分けを行っています。
# ラベルの確認
$ kubectl get pod -n demo --show-labels
NAME READY STATUS RESTARTS AGE LABELS
hot-postgres-0 1/1 Running 0 3h42m controller-revision-hash=hot-postgres-c996c7956,kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=primary,statefulset.kubernetes.io/pod-name=hot-postgres-0
hot-postgres-1 1/1 Running 0 3h38m controller-revision-hash=hot-postgres-c996c7956,kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=replica,statefulset.kubernetes.io/pod-name=hot-postgres-1
hot-postgres-2 1/1 Running 0 3h37m controller-revision-hash=hot-postgres-c996c7956,kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=replica,statefulset.kubernetes.io/pod-name=hot-postgres-2
# ServiceのSelectorも確認しておく
$ kubectl get svc -n demo -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
hot-postgres ClusterIP 10.43.228.176 <none> 5432/TCP 3h49m kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=primary
hot-postgres-replicas ClusterIP 10.43.220.153 <none> 5432/TCP 3h49m kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=replica
この設計ですと、プライマリインスタンスの障害時にラベルの貼り換えが必要になりますね。では実際にどう動くのか見てみましょう。
primary障害時の動作
プライマリインスタンスのPod(hot-postgres-0)をおもむろにDeleteしてみます。その結果、Podは同じまたは別のNodeで再起動されますが、その結果は以下のようになります。
# ラベルが変更されたことを確認する
$ kubectl get pod -n demo --show-labels
NAME READY STATUS RESTARTS AGE LABELS
hot-postgres-0 1/1 Running 0 31s controller-revision-hash=hot-postgres-c996c7956,kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=replica,statefulset.kubernetes.io/pod-name=hot-postgres-0
hot-postgres-1 1/1 Running 0 3h49m controller-revision-hash=hot-postgres-c996c7956,kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=primary,statefulset.kubernetes.io/pod-name=hot-postgres-1
hot-postgres-2 1/1 Running 0 3h48m controller-revision-hash=hot-postgres-c996c7956,kubedb.com/kind=Postgres,kubedb.com/name=hot-postgres,kubedb.com/role=replica,statefulset.kubernetes.io/pod-name=hot-postgres-2
すごく細かいのですが、hot-postgres-0のkubedb.com/roleがreplicaに、代わりにhot-postgres-1のkubedb.com/roleがprimaryになっています。
念のため、接続確認もしておきましょう。
-- primaryに接続、hot-postgres-1のIPになっていることがわかる
$ kubectl exec -it -n demo pgtools-0 -c pgbench -- psql -h hot-postgres -U postgres -c "select inet_server_addr();"
Password for user postgres:
inet_server_addr
------------------
10.42.1.3
(1 row)
-- replicaに接続、再起動後のhot-postgres-0のIPになっている
$ kubectl exec -it -n demo pgtools-0 -c pgbench -- psql -h hot-postgres-replicas -U postgres -c "select inet_server_addr();"
Password for user postgres:
inet_server_addr
------------------
10.42.0.8
(1 row)
-- replicaに接続、hot-postgres-2のIPになっている
$ kubectl exec -it -n demo pgtools-0 -c pgbench -- psql -h hot-postgres-replicas -U postgres -c "select inet_server_addr();"
Password for user postgres:
inet_server_addr
------------------
10.42.2.3
(1 row)
なお、この場合でもStatefulSetとAWSのEBSをPVとして使っているのでデータは永続化されています。
まとめ
さて、ここまでどうだったでしょうか。
KubeDB自体はPostgreSQLのストリーミング・レプリケーション構成を使うには非常に簡単で、設計も良く出来たOSSだと思います。今日は書けなかったSnapshotやDormantDatabaseの考え方など良く寝られたDatabase Operatorです。(DormantDatabaseの概念はこちらなどを参照)
しかし、残念なことに開発リソースが潤沢とは言えないようで、Kubernetesのバージョンアップに追随できていないように見えます。この辺りで強力なコントリビュートが出来る企業、コミュニティが今後は必要になってくるかもしれません。
何より名前が良い(2回目)ので、ぜひ盛り上げて頂きたいですね。
よろしくお願いします。