KubernetesでMySQLのクラスタツールVitessを動かす

More than 1 year has passed since last update.


目的

DockerやKubernetes(k8s)のおかげで様々なソフトウェアを気軽に試す、あるいは開発で利用できるようになりましたが、本番利用を考えると多くの考慮点を解決しなければいけません。その1つがNFRs(Non-Functional Requirements: 非機能要件)です。例えば可用性の観点では、スケールアウトしにくいRDBはk8sとは相性が悪いです。

有名なMySQLはレプリケーションに対応していますが、マスタが死んだときのスレーブの自動昇格をどうやって実現するかの考慮が必要ですし、昇格までの時間はサービス停止になります。また、Oracle公式のMySQL Clusterはk8sへの対応が明確に表明されてないと思っています。

そこで、今回はそこをよしなにしてくれるVitessをk8sで動かしてみます。

VitessはYouTubeが開発したMySQLのクラスタリングツールです。最近CNCFにプロジェクトがホストされたので、それなりに由緒正しいものになりました。


https://vitess.io/


検証は以下の環境で行いました。


  • IBM Cloud Private 2.1.0.2 (Kubernetes 1.9.1)


手順


Helm版について

Chartは以下サイトで公開されていますが、残念ながらまだalphaクオリティとのことです。なので今回は公式サイトの手動の手順に従って検証します。


https://github.com/vitessio/vitess/tree/master/helm/vitess



etcd-operatorのインストール

先にetcd-operatorをデプロイしておきます。今回はnamespaceをdefaultからteruに変えます。ServiceAccountはdefaultではなくetcd-operatorという名称で新規作成し、ClusterRoleを割り当てます。RoleではなくClusterRoleにする理由は、最初のetcd-operatorはCRD(Custom Resource Definition)というものを自動作成しますが、それにはRoleではなくClusterRoleが必要になるため、のようです。このように必要最低限の権限を付与するのがk8sの本番運用では考慮が必要になります(これが大変です)。

ServiceAccountを作成します。

$ kubectl create sa etcd-operator -n teru

ClusterRoleとClusterRoleBindingを作成します。

$ git clone https://github.com/coreos/etcd-operator.git

$ cd etcd-operator
$ export SA_NAME=etcd-operator
$ export ROLE_NAME=etcd-operator
$ export ROLE_BINDING_NAME=etcd-operator
$ export NAMESPACE=teru

$ sed -e "s/<ROLE_NAME>/${ROLE_NAME}/g" example/rbac/cluster-role-template.yaml | kubectl create -f - -n ${NAMESPACE}
$ sed -e "s/<ROLE_NAME>/${ROLE_NAME}/g" -e "s/<ROLE_BINDING_NAME>/${ROLE_BINDING_NAME}/g" -e "s/<NAMESPACE>/${NAMESPACE}/g" example/rbac/cluster-role-binding-template.yaml -e "s/ name: default/ name: ${SA_NAME}/" | kubectl create -f - -n ${NAMESPACE}

Deploymentを作成します。

$ sed -e "s/^    spec:/    spec:\n      serviceAccountName: ${SA_NAME}/" example/deployment.yaml | kubectl create -f - -n ${NAMESPACE}

ログにエラーが出てないか確認します。

$ kubectl get pod -n teru | grep etcd

etcd-operator-65f6cd5964-l98p6 1/1 Running 0 6m

$ kubectl logs etcd-operator-65f6cd5964-l98p6 -n teru
time="2018-03-28T17:40:25Z" level=info msg="etcd-operator Version: 0.9.1"
time="2018-03-28T17:40:25Z" level=info msg="Git SHA: 16f0e1b3"
time="2018-03-28T17:40:25Z" level=info msg="Go Version: go1.9.4"
time="2018-03-28T17:40:25Z" level=info msg="Go OS/Arch: linux/amd64"
time="2018-03-28T17:40:25Z" level=info msg="Event(v1.ObjectReference{Kind:\"Endpoints\", Namespace:\"teru\", Name:\"etcd-operator\", UID:\"1938312d-32af-11e8-a731-000c293da360\", APIVersion:\"v1\", ResourceVersion:\"603114\", FieldPath:\"\"}): type: 'Normal' reason: 'LeaderElection' etcd-operator-65f6cd5964-l98p6 became leader"


Goのインストール

Vitessの各種ツール類を作成するためにGoをインストールします。

$ cd

$ wget https://dl.google.com/go/go1.10.linux-amd64.tar.gz
$ tar zxvf go1.10.linux-amd64.tar.gz
$ sudo cp -r go /usr/local/

環境変数に次のものを追加します。


~/.bash_profile

export GOPATH=${HOME}/go

export PATH=${PATH}:/usr/local/go/bin:${GOPATH}/bin


vtctlclientのビルドとインストール

以下のコマンドでvtctlclientのビルドとインストールをします。成功すれば${GOPATH}/bin/vtctlclientが出来ます。

$ go get vitess.io/vitess/go/cmd/vtctlclient

$ ls ${GOPATH}/bin/vtctlclient
/home/teru/go/bin/vtctlclient


バックアップの設定

configure.shを実行します。ここでは主にバックアップをどこに保管するかを指定します。GCS(Google Cloud Storage)かNFSが選べます。今回はNFSにしますが、NFSはPod内にマウントされている必要があります。ここでは/mntにしておきます。

$ cd ${GOPATH}/src/vitess.io/vitess/examples/kubernetes

$ ./configure.sh
Vitess Docker image (leave empty for default) []:
Backup Storage (file, gcs) [gcs]: file
Root directory for backups (usually an NFS mount): /mnt

NOTE: You must add your NFS mount to the vtctld-controller-template
and vttablet-pod-template as described in the Kubernetes docs:
http://kubernetes.io/v1.0/docs/user-guide/volumes.html#nfs

NOTEの通り、2つのファイルを修正し、NFS領域をマウントする設定をテンプレートファイルに追記します。


vtctld-controller-template.yaml

(略)

volumeMounts
- name: backup # 追加
mountPath: /mnt # 追加
(略)
volumes:
- name: backup # 追加
persistentVolumeClaim: # 追加
claimName: vitess-backup # 追加


vttablet-pod-template.yaml

(略)

volumeMounts
- name: backup # 追加
mountPath: /mnt # 追加
(略)
volumeMounts
- name: backup # 追加
mountPath: /mnt # 追加
(略)
persistentVolumeClaim: # 追加
claimName: vitess-backup # 追加

PersistentVolumeClaimを作っておきます。私は以前の記事でNFSの動的プロビジョニングのためのStorageClassを作っておいたので、それを使います。


vitess-pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim
metadata:
name: vitess-backup
annotations:
volume.kubernetes.io/storage-class: "nfs"
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 8Gi

$ kubectl create -f vitess-pvc.yaml


etcdの起動

環境変数VITESS_NAMEにnamespaceを、CELLSにセル名をセットし、etcdを実行します。

$ export VITESS_NAME=teru

$ export CELLS=vitess

$ ./etcd-up.sh
Creating etcd service for 'global' cell...
etcdcluster "etcd-global" created
Creating etcd service for 'vittes' cell...
etcdcluster "etcd-vittes" created

Podの状態を確認します。しばらく待つとRunningになります。

$ kubectl get pods -n teru | grep etcd

etcd-global-2z5mpjjqmt 1/1 Running 0 11s
etcd-global-4kzbnlkqwf 1/1 Running 0 27s
etcd-global-r66b9nsqrq 1/1 Running 0 36s
etcd-operator-65f6cd5964-ff24g 1/1 Running 0 1h
etcd-vitess-5wxv9g7tv8 1/1 Running 0 35s
etcd-vitess-ltfflh2gxq 1/1 Running 0 27s
etcd-vitess-nbxfjqsjxd 1/1 Running 0 11s


vtlctldの起動

$ ./vtctld-up.sh 

Creating vtctld ClusterIP service...
service "vtctld" created
Creating vtctld replicationcontroller...
replicationcontroller "vtctld" created
persistentvolumeclaim "vitess-backup" created

To access vtctld web UI, start kubectl proxy in another terminal:
kubectl proxy --port=8001
Then visit http://localhost:8001/api/v1/proxy/namespaces/teru/services/vtctld:web/

言われたとおりにkubectl proxy --port=8001してURLにアクセスしてみましょう。まだ何も定義がありませんが、Vitessのコントロールパネルにアクセスできます。

image.png


トポロジーの設定

トポロジーを設定します。

./kvtctl.sh AddCellInfo -server_address http://etcd-${CELLS}-client:2379 ${CELLS}


vttabletの起動

vttabletを起動します。vttabletとは、mysqldと同じPodで動くものだそうです。環境変数KEYSPACEに任意の値をセットしておきます。また、起動するPod数を調整します。以下の例では、全体のPod数を3、そのうち1つをReadOnlyにしています。デフォルトでは5個のPodが起動しそのうち2つがReadOnlyなのですが、私の検証機ではスペック不足で全て起動できませんでしたので、数を減らしました。

$ export KEYSPACE=teru_keyspace

$ export TABLETS_PER_SHARD=3
$ export RDONLY_COUNT=1
$ ./vttablet-up.sh
Creating teru_keyspace.shard-0 pods in cell vitess...
Creating pod for tablet vitess-0000000100...
pod "vttablet-100" created
Creating pod for tablet vitess-0000000101...
pod "vttablet-101" created
Creating pod for tablet vitess-0000000102...
pod "vttablet-102" created

先ほどのコントロールパネルを見ると、teru_keyspaceで1つのシャードができています。

image.png

Podの状態を表示します。全てRunningになっています。

$ kubectl get pods -n teru | grep vtt

vttablet-100 2/2 Running 1 59s
vttablet-101 2/2 Running 1 59s
vttablet-102 2/2 Running 1 59s

Tabletの状態を表示します。

$ ./kvtctl.sh ListAllTablets ${CELLS}

Starting port forwarding to vtctld...
vitess-0000000100 teru_keyspace 0 replica 10.1.45.220:15002 10.1.45.220:3306 []
vitess-0000000101 teru_keyspace 0 replica 10.1.225.43:15002 10.1.225.43:3306 []
vitess-0000000102 teru_keyspace 0 rdonly 10.1.45.221:15002 10.1.45.221:3306 []

Replicaが2つ、ReadOnlyが1つできています。Masterはまだなくてよいです。


データベースの初期化

データベースを初期化します。

$ ./kvtctl.sh InitShardMaster -force ${KEYSPACE}/0 ${CELLS}-0000000100

Starting port forwarding to vtctld...
W0404 03:09:31.694664 23743 main.go:58] W0403 18:09:31.688983 reparent.go:181] master-elect tablet vitess-0000000100 is not the shard master, proceeding anyway as -force was used
W0404 03:09:31.696059 23743 main.go:58] W0403 18:09:31.689397 reparent.go:187] master-elect tablet vitess-0000000100 is not a master in the shard, proceeding anyway as -force was used

今回は初回なのでまだマスターが存在しない状態です。そのため、InitShardMaster -forceで一番目のTabletであるvitess-0000000100を強制的にマスターにしています。改めてTabletの状態を見ると、masterが出現しました。

$ ./kvtctl.sh ListAllTablets ${CELLS}

Starting port forwarding to vtctld...
vitess-0000000100 teru_keyspace 0 master 10.1.45.220:15002 10.1.45.220:3306 []
vitess-0000000101 teru_keyspace 0 replica 10.1.225.43:15002 10.1.225.43:3306 []
vitess-0000000102 teru_keyspace 0 rdonly 10.1.45.221:15002 10.1.45.221:3306 []


テーブルの作成

それではテーブルを作成します。ソースツリー付属のテストテーブルを使います。

$ cat create_test_table.sql

CREATE TABLE messages (
page BIGINT(20) UNSIGNED,
time_created_ns BIGINT(20) UNSIGNED,
message VARCHAR(10000),
PRIMARY KEY (page, time_created_ns)
) ENGINE=InnoDB

$ ./kvtctl.sh ApplySchema -sql "$(cat create_test_table.sql)" ${KEYSPACE}
Starting port forwarding to vtctld...

次のコマンドで各tabletにテーブル定義がレプリケーションされていることがわかります。

$ ./kvtctl.sh GetSchema ${CELLS}-0000000100

$ ./kvtctl.sh GetSchema ${CELLS}-0000000101
$ ./kvtctl.sh GetSchema ${CELLS}-0000000102

(例)
Starting port forwarding to vtctld...
{
"database_schema": "CREATE DATABASE /*!32312 IF NOT EXISTS*/ {{.DatabaseName}} /*!40100 DEFAULT CHARACTER SET utf8 */",
"table_definitions": [
{
"name": "messages",
"schema": "CREATE TABLE `messages` (\n `page` bigint(20) unsigned NOT NULL,\n `time_created_ns` bigint(20) unsigned NOT NULL,\n `message` varchar(10000) DEFAULT NULL,\n PRIMARY KEY (`page`,`time_created_ns`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8",
"columns": [
"page",
"time_created_ns",
"message"
],
"primary_key_columns": [
"page",
"time_created_ns"
],
"type": "BASE TABLE",
"data_length": "16384",
"row_count": "0"
}
],
"version": "5b2e5dbcb5766b6c69fe55c81b6ea805"
}


データのバックアップ

ここで最初のバックアップをとります。バックアップはReadOnlyノードの1つを指定して取得します。なぜReadOnlyノードを指定するかというと、バックアップの間はレプリケーションが一時的に停止するので、静止点が取れるのだそうです。

$ ./kvtctl.sh Backup ${CELLS}-0000000102

Starting port forwarding to vtctld...

正しくとれたか確認してみます。

$ ./kvtctl.sh ListBackups ${KEYSPACE}/0

Starting port forwarding to vtctld...
2018-04-03.181103.vitess-0000000102

このバックアップがあると、今後ノードを足したときに自動的にこれを使ってリストアし、マスタからの差分をレプリケーションしてくれるのだそうです。便利ですね。


Routing Schemaの初期化

説明を読んでもよくわかりませんでしたが、何かの初期化のようです。

$ ./kvtctl.sh RebuildVSchemaGraph

Starting port forwarding to vtctld...


vtgateの起動

構成作業の最後ですが、クライアントから生きてるMySQLにルーティングするためのvtgateを起動します。デフォルトではレプリカが3つ起動しますが私の検証環境はリソース不足なので1個にします。

$ export VTGATE_REPLICAS=1

$ ./vtgate-up.sh
Creating vtgate service in cell vitess...
service "vtgate-vitess" created
Creating vtgate replicationcontroller in cell vitess...
replicationcontroller "vtgate-vitess" created

$ kubectl get pods -n teru | grep vt
vtctld-rcdtg 1/1 Running 0 2h
vtgate-vitess-4cb6w 1/1 Running 0 1m
vttablet-100 2/2 Running 1 39m
vttablet-101 2/2 Running 1 39m
vttablet-102 2/2 Running 1 39m

以上でセットアップは完了です。今後、実際に動かしていろいろ検証したいと思います。