この記事は、
softdeviceのみんなでゆるく記事を書く。 Advent Calendar 2020
の12/2分の記事として公開しています。
はじめに
kubernetes
を使用してHA クラスタを組み始めると、その他の HA ではない部分 が気になってきます。
僕のケースでは、メインデータベースの PostgresQL
がクラスタの外部にあり、いくら kubernetes
が HA 構成になっていても、データベースが単一障害点に成り得る状況でした。
この状況を回避するため、Rook + Ceph
でクラスタストレージを構成し、その上に Postgres Operator
で PostgresQL
データベースを構築する事を目指します。
kubernetes
の HA クラスタを作る方法は、以前書いた記事がありますので、構築方法をお探しの方は見て見てみてください。
1. Rook + Ceph
まずは、土台である Rook + Ceph
を設定していきます。
そもそも Rook + Ceph
とは、ざっくりと(雑に)言うと、S3とかGCSみたいな分散型ストレージをオンプレミス環境で使う事が出来るOSS といったところでしょうか。
もちろん使い方は S3
や GCS
とは全く違うのですが、データをクラスタの中に分散させて保管しておく事で冗長性の高いストレージを実現できます。
前準備
インストールの前に、以下に記載されている要件を満たす必要があります。
https://rook.io/docs/rook/v1.5/ceph-prerequisites.html
https://rook.io/docs/rook/v1.5/k8s-pre-reqs.html
色々な環境があると思いますが、僕は以下の点だけ対応しました。
ディスクを追加
Cephディスクの要件として、
- 生のディバイス (パーティション無し、未フォーマット)
- 生のパーティション (未フォーマット)
- ブロックモードのストレージクラスから利用可能な
PersistentVolume
を要求されます。
今回使用するクラスタは ESXi
で動いているので、さくっと新規ドライブを追加しました。
インストール
公式の手順通りに進めていきますが、とても簡単です。
https://rook.io/docs/rook/v1.4/ceph-quickstart.html
gitからrookをcloneします。
https://github.com/rook/rook/releases
git clone --single-branch --branch release-1.4 https://github.com/rook/rook.git
examples
に入っている以下の2つの yaml を適用します。
cd rook/cluster/examples/kubernetes/ceph
kubectl create -f common.yaml
kubectl create -f operator.yaml
ここまで実行したら、rook-ceph-operator
が READY
になるまで待ちます。
kubectl -n rook-ceph get pod
NAME READY STATUS RESTARTS AGE
rook-ceph-operator-5668d7cd54-8q788 1/1 Running 0 36s
rook-discover-m8jzz 1/1 Running 0 34s
rook-discover-sxkm8 1/1 Running 0 34s
rook-discover-ttdwk 1/1 Running 0 34s
READY
になったら、最後の以下のコマンドを実行します。
kubectl create -f cluster.yaml
実際の使用は次の Postgres Operator
の項目で行うとして、基本的な設定はこれで完了です!
ダッシュボード
cephにはダッシュボードがついているのですが、クラスタの中からしか見れません。
これを外向けにするには、以下のようなyamlを使用します。
apiVersion: v1
kind: Service
metadata:
name: rook-ceph-mgr-dashboard-external-https
namespace: rook-ceph
labels:
app: rook-ceph-mgr
rook_cluster: rook-ceph
spec:
ports:
- name: dashboard
port: 8443
protocol: TCP
targetPort: 8443
nodePort: 31443
selector:
app: rook-ceph-mgr
rook_cluster: rook-ceph
sessionAffinity: None
type: NodePort
kubectl apply -f dashboard-external-https.yaml
ページにアクセスすると認証を求められますが、パスワードは以下で取得できます。
kubectl -n rook-ceph get secret rook-ceph-dashboard-password -o jsonpath="{['data']['password']}" | base64 --decode && echo
ユーザー名は admin
です。
(削除)
もし何らかの理由でクラスタを削除したい場合、ただ単に namespace
を削除したりするだけではだめで、以下の手順に従う必要があります。
一番重要なのは、クラスタで使用したディスクを初期化する必要がある、というところですね。
VMを使用している場合は、仮想ディスクを削除してもう一度追加したら良いのですが、そうでない場合は以下のコマンドでディスクをクリアします。
#!/usr/bin/env bash
DISK="/dev/sdb"
# Zap the disk to a fresh, usable state (zap-all is important, b/c MBR has to be clean)
# You will have to run this step for all disks.
sgdisk --zap-all $DISK
# Clean hdds with dd
dd if=/dev/zero of="$DISK" bs=1M count=100 oflag=direct,dsync
# Clean disks such as ssd with blkdiscard instead of dd
blkdiscard $DISK
# These steps only have to be run once on each node
# If rook sets up osds using ceph-volume, teardown leaves some devices mapped that lock the disks.
ls /dev/mapper/ceph-* | xargs -I% -- dmsetup remove %
# ceph-volume setup can leave ceph-<UUID> directories in /dev (unnecessary clutter)
rm -rf /dev/ceph-*
2. Postgres Operator
Rook + Ceph
の設定が終わったら、その上に Postgres Operator
を設定していきます。
インストール
こちらも公式の手順に従ってやっていきます。
https://access.crunchydata.com/documentation/postgres-operator/4.4.1/quickstart/
インストーラーマニフェストをDLしてきます。
curl https://raw.githubusercontent.com/CrunchyData/postgres-operator/v4.4.1/installers/kubectl/postgres-operator.yml > postgres-operator.yml
今回は Rook
を使用して構成するので、DLしてきた設定ファイル内の以下の項目を rook
に設定します。
backrest_storage: "rook"
backup_storage: "rook"
primary_storage: "rook"
replica_storage: "rook"
設定ファイルを適用します。
名前空間 pgo
が必要になるので、併せて作成します。
kubectl create namespace pgo
kubectl apply -f postgres-operator.yml
これで kubernetes
内の準備は出来ましたが、この中に PostgresQL
のクラスタを作るには専用のコマンドが必要になります。
このインストーラも提供されているので、DLしてきて権限を付与し、実行します。
curl https://raw.githubusercontent.com/CrunchyData/postgres-operator/v4.4.1/installers/kubectl/client-setup.sh > client-setup.sh
chmod +x client-setup.sh
./client-setup.sh
このコマンドは、~/.pgo
ディレクトリを作ってその中に必要なデータをインストールするので、パスを通しておきます。また、その他必要な環境変数を登録しておきます。
cat <<EOF >> ~/.bashrc
export PATH=${HOME?}:$PATH
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:9443'
export PGO_NAMESPACE=pgo
EOF
source ~/.bashrc
クイックスタートでは PGO_APISERVER_URL
の箇所でポートが 8443
となっていますが、僕の環境ではそのポートは kubernetes
で使用していたので、変更しました。
基本的にインストール作業はここで完了ですが、クイックスタートの通りインストールの確認をしてみます。
kubectl -n pgo get deployments
NAME READY UP-TO-DATE AVAILABLE AGE
postgres-operator 1/1 1 1 61d
kubectl -n pgo get pods
NAME READY STATUS RESTARTS AGE
postgres-operator-5fbcccf9b5-svrpz 4/4 Running 0 3h51m
Postgres クラスタ作成
確認が終わったら、実際にクラスタを作っていきますが、その前に Rook
を使ったストレージクラスを作ります。
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: postgres-replicapool
namespace: rook-ceph
spec:
failureDomain: host
replicated:
size: 3
requireSafeReplicaSize: true
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: postgres-rook-ceph-block
provisioner: rook-ceph.rbd.csi.ceph.com
parameters:
clusterID: rook-ceph
pool: postgres-replicapool
imageFormat: "2"
imageFeatures: layering
csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/provisioner-secret-namespace: rook-ceph
csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner
csi.storage.k8s.io/controller-expand-secret-namespace: rook-ceph
csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node
csi.storage.k8s.io/node-stage-secret-namespace: rook-ceph
csi.storage.k8s.io/fstype: ext4
allowVolumeExpansion: true
reclaimPolicy: Retain
kubectl apply -f storageclass-pgo.yaml
ストレージクラスを作成した後 pgo
コマンドを使用するのですが、そのためにはポートフォワーディングが必要になります。以下のコマンドでフォワーディングしますが、このコマンドはフォアグラウンドで実行されるので別ウィンドウで実行する必要があります。
kubectl -n pgo port-forward svc/postgres-operator 9443:8443
ポートフォワーディングされている間 pgo
コマンドが使えます。早速 test
という名前のクラスタを作ってみます。
~/.bashrc
で PGO_NAMESPACE
を定義しているので、名前空間を省略するとそこで設定した値が使用されます。
pgo create cluster test
created cluster: test
workflow id: ca0bd585-c37e-4595-8f4f-8614e993905b
database name: test
users:
username: testuser password: r+[pN>@+AU=}xOD{h4Ee)G/f
若干時間がかかりますが、以下のコマンドで状況を確認できます。Services も Instances も UP になったら完了です。
pgo test test
cluster : test
Services
primary (10.102.60.6:5432): UP
Instances
primary (test-76989b6696-vrb59): UP
このままだとクラスタ外から接続できないので、サービスの設定を変更します。
kubectl edit svc test -n pgo
(省略)
spec:
clusterIP: 10.102.60.6
ports:
- name: sshd
port: 2022
protocol: TCP
targetPort: 2022
- name: postgres
nodePort: 30001 # <- 追加
port: 5432
protocol: TCP
targetPort: 5432
selector:
pg-cluster: test
role: master
sessionAffinity: None
type: NodePort # <- ClusterIP から変更
NodePortは、30000~32767である必要があります。
これで保存すると、外部から接続できるようになります。以下のコマンドで接続できます。パスワードは、Postgresクラスタを作成した時に表示されるものを使用してください。
docker run --rm -it postgres psql postgres -h <ノードのip> -U testuser --password -p 30001
psql (13.1 (Debian 13.1-1.pgdg100+1), server 12.4)
Type "help" for help.
postgres=>
これで、クラスタ内に PostgresQL
を構築する事が出来ました。
おわりに
分散ストレージやHA構成のDBは kubernetes
よりももっと前からあった技術ですが、学習コストが高く、それぞれをHA化しようと思うと相当な学習が必要になります。
今回の方法であれば、 kubernetes
をある程度学習するとその知識の範囲で構成する事が出来るため、それぞれを別々の技術として学ぶよりも学習コストが低いと思われます。また、 kubernetes
が動く環境であればどこでも動くというのもメリットです。
僕もただ動かしているだけでそこまで深い理解には至っていないですが、この記事が他の同じような課題を抱えている人の一助になれば幸いです。