この記事は、Kubernetes 1.17 と Ceph を CSI で連携を検証で作ったKubernetesとCephのダイナミックプロビジョニングの環境を利用して、チュートリアルExample: Deploying Cassandra with Stateful Setsでステートフルセット・コントローラーでCassandraのクラスタを実行するものです。
チュートリアルだから簡単にできるだろうと思ったら、コンテナ・イメージが壊れているようで、問題判別とコンテナの作り替えを実施したトラブル対応の記録です。
準備
作業前に、次の二つのクラスタをそれぞれ起動します。
- Cephクラスタの起動 https://github.com/takara9/vagrant-ceph
- Kubernetes 1.17 起動 https://github.com/takara9/vagrant-kubernetes
ブロックストレージの初期化とストレージクラスの作成は、Kubernetes 1.17 と Ceph を CSI で連携を検証を参考にしてください。このQiitaの記事をトレースして、クラスタが作成されていれば、この記事の内容を実行する準備ができています。
K8sクラスタ環境のメモリ追加
Javaで開発された Cassandra を起動するために、ワーカーノードに4GB程度のメモリを搭載する必要があります。
前提とした記事の内容が終わった時点では、次のようなリソースの状態にあると思います。つまり、ワーカーノードにメモリが1G程度しか実装されていない状態で、Cassandraをクラウスた構成で動かすには、メモリが足りない状態です。
tkr@luigi:~/sandbox-1/k8s$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 115m 5% 678Mi 76%
node1 19m 1% 461Mi 51%
node2 178m 17% 474Mi 53%
仮想サーバーをシャットダウンして、メモリ搭載量を増やして、再び起動します。
tkr@luigi:~/sandbox-1/k8s$ vagrant halt
tkr@luigi:~/sandbox-1/k8s$ vi Vagrantfile
Vagrantfileを編集してワーカーノードのメモリは4G、コア数2に増強する。
! vbox.cpus = 2
! vbox.memory = 4096
! vbox.cpus = 1
! vbox.memory = 1024
再度、起動する。
tkr@luigi:~/sandbox-1/k8s$ vagrant up
これでメモリが増えているのが確認できる。
tkr@luigi:~/sandbox-1/k8s$ kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
master 88m 4% 702Mi 78%
node1 29m 1% 268Mi 6%
node2 31m 1% 324Mi 8%
Cassandraのマニフェストの修正
チュートリアルでは、minikubeのhostpathをストレージクラスとして使用するようになっているので、これをCephでファイルストレージのPVCを作成するように変更するために、ディレクトリを作成して、マニフェストをダウンロードして編集する。マニフェストは、2種類あり、サービスとステートフルセットの生成用がある。サービスに変更は無いので、ステートフルセットのマニフェストの変更のみとなる。
tkr@luigi:~/sandbox-1$ mkdir cassandra_sts
tkr@luigi:~/sandbox-1$ cd cassandra_sts/
tkr@luigi:~/sandbox-1/cassandra_sts$ curl -O https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/application/cassandra/cassandra-service.yaml
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl apply -f cassandra-service.yaml
service/cassandra created
tkr@luigi:~/sandbox-1/cassandra_sts$ curl -O https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/application/cassandra/cassandra-statefulset.yaml
ステートフルセットの変更箇所は、volumeClaimTemplatesのストレージクラス、ボリュームモードの2箇所である。そして、このファイルの修正箇所以下には、ストレージクラス fast のマニフェストが付いてるが、その部分を削除する。
# These are converted to volume claims by the controller
# and mounted at the paths mentioned above.
# do not use these in production until ssd GCEPersistentDisk or other ssd pd
volumeClaimTemplates:
- metadata:
name: cassandra-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: csi-rbd-sc
volumeMode: Filesystem
resources:
requests:
storage: 1Gi
<以下は削除>
Cassandraの起動と問題判別
前述で編集したマニフェストを適用してステートフルセットコントローラー管理下で、ポッドと永続ストレージ要求(PVC)を生成する。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl apply -f cassandra-statefulset.yaml
statefulset.apps/cassandra created
ここで、ポッドが生成中にエラー CreateContainerError
となってしまった。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl get po
NAME READY STATUS RESTARTS AGE
cassandra-0 0/1 CreateContainerError 0 10s
エラーの原因を探るために、kubectl delete sts cassandra
を実行して、再度、kubectl apply -f ...
を実行するとともに、以下のように、イベント状況を表示して、エラー発生に至までの、経緯を確認する。この結果では Containerd がコンテナを生成した際に、ファイルかディレクトリが存在しないため、フェールしたことがわかる。
この時、/var/lib/containerd/io.containerd.grpc.v1.cri/containers/...
は、コンテナ内のファイルシステムが置かれている場所なので、コンテナのホスト(ワーカーノード)の問題ではなくて、コンテナ内部の問題と見なせる。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl get events -w
LAST SEEN TYPE REASON OBJECT MESSAGE
14m Warning FailedScheduling pod/cassandra-0 error while running "VolumeBinding" filter plugin for pod "cassandra-0": pod has unbound immediate PersistentVolumeClaims
14m Normal Scheduled pod/cassandra-0 Successfully assigned default/cassandra-0 to node1
14m Normal SuccessfulAttachVolume pod/cassandra-0 AttachVolume.Attach succeeded for volume "pvc-ceecfc8e-4a00-46eb-8c9e-b120324962c1"
12m Normal Pulling pod/cassandra-0 Pulling image "gcr.io/google-samples/cassandra:v13"
12m Normal Pulled pod/cassandra-0 Successfully pulled image "gcr.io/google-samples/cassandra:v13"
14m Warning Failed pod/cassandra-0 Error: failed to create containerd container: taking runtime copy of volume: open /var/lib/containerd/io.containerd.grpc.v1.cri/containers/3abd359ac0fc24fc1482f1823902521f20d9aff2a6270190098f9b84e50e1667/volumes/7ce93de4ec0e455a268f7cd2c17f8b9d2b2a56f23ca751daa21361368ad0a629: no such file or directory
この情報では、コンテナ内で具体的に何のファイルかディレクトリが、存在しないのか、判別はできない。そこで、次の段階の確認を進める。
問題のコンテナを単体で起動
コンテナ内部の問題を判別するため、コンテナを単体でポッドとして起動して、原因を探ってみる。マニフェスト内のimage
の値から、コンテナのリポジトリ名を求め、kubectl run ...
でコンテナをポッドとして単体で起動する。この時に、PVCがマウントされないが、本来コンテナは単体でも動作するようにビルドされているものなので、動作するはずである。というか、正しくコンテナがビルドされていれば、単体でも動作する。
次のように、単体で起動に成功して、対話型シェルでコマンドも実行できる。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl run -it cas --image=gcr.io/google-samples/cassandra:v13 --restart=Never sh
If you don't see a command prompt, try pressing enter.
# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 9.7G 5.1G 4.6G 53% /
tmpfs 64M 0 64M 0% /dev
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/sda1 9.7G 5.1G 4.6G 53% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 2.0G 12K 2.0G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 2.0G 0 2.0G 0% /proc/acpi
tmpfs 2.0G 0 2.0G 0% /proc/scsi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
そこで、単体でCassandraのツールを起動して、動作を確認してみる。そうしたところ、Cassandraのコマンドで、エラーが発生する。
コマンドの実行で、ファイルが存在しない、文法エラーが発生すると状況だ。どうやら、コンテナのビルドに失敗しているようだ。
# nodetool version
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 1: /etc/cassandra/cassandra-env.sh: free: not found
expr: syntax error
expr: syntax error
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 59: [: Illegal number:
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 63: [: Illegal number:
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 67: [: Illegal number:
expr: syntax error
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 81: [: Illegal number:
nodetool: Failed to connect to '127.0.0.1:7199' - ConnectException: 'Connection refused (Connection refused)'.
問題の原因とは思えないが、一応、readinessProbeから起動されるコンテナの中のファイルの存在もチェックしておく。準備状態をKubernetesへ知らせるシェルで、exit 0で完了すれば、ポッド内のコンテナの起動が完了した事を示す。
# cat /ready-probe.sh
# !/bin/bash
# Copyright 2016 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
if [[ $(nodetool status | grep $POD_IP) == *"UN"* ]]; then
if [[ $DEBUG ]]; then
echo "UN";
fi
exit 0;
else
if [[ $DEBUG ]]; then
echo "Not Up";
fi
exit 1;
fi
上記のシェルで呼び出されるコマンドについても、動作をみておく。やはり問題がある。
# nodetool status
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 1: /etc/cassandra/cassandra-env.sh: free: not found
expr: syntax error
expr: syntax error
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 59: [: Illegal number:
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 63: [: Illegal number:
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 67: [: Illegal number:
expr: syntax error
/usr/local/apache-cassandra-3.11.2/bin/nodetool: 81: [: Illegal number:
nodetool: Failed to connect to '127.0.0.1:7199' - ConnectException: 'Connection refused (Connection refused)'.
ここまでの調査で、gcr.io/google-samples/cassandra:v13 のイメージから、コンテナが生成できないことが判ったので、このコンテナに関する調査は終了する。
代替手段の検討
CassandoraをStatufulsetで動かすというチュートリアルの目的を達成するために、代替手段を探ることにした。このチュートリアルのマニフェストのCassandraを起動するためのパラメータを記述した項目をみると、どうやら、DockerHubに登録された https://hub.docker.com/_/cassandra のコンテナ・イメージの起動パラメータと同じであることが判った。多少の追加がみられるが、Cassandraは、Javaで動作することから、ヒープサイズなどは、Cassandra固有のものではなく、JVMの為のものだ。
そこで、DockerHubの Cassandra コンテナを、kubectlで起動して、代替コンテナとしての可能性を探る。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl run -it cas2 --image=cassandra:3.11 --restart=Never sh
If you don't see a command prompt, try pressing enter.
# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 9.7G 5.1G 4.6G 53% /
tmpfs 64M 0 64M 0% /dev
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/sda1 9.7G 5.1G 4.6G 53% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 2.0G 12K 2.0G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 2.0G 0 2.0G 0% /proc/acpi
tmpfs 2.0G 0 2.0G 0% /proc/scsi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
# nodetool status
nodetool: Failed to connect to '127.0.0.1:7199' - ConnectException: 'Connection refused (Connection refused)'.
この結果、nodetoolが正常に動作するので、チュートリアルのイメージ gcr.io/google-samples/cassandra:v13
が環境依存か壊れているものと断定できる。
そこで、代替として利用できるのか確認するために、readinessProbeから起動されるシェルを確認したところ、以下の様に存在していない。
# ls /
bin boot dev docker-entrypoint.sh etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
そこで、暫定対策として、マニフェストの中のreadinessProbeの部分をコメントにして起動したところ、正常に動作した。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl get po -w
NAME READY STATUS RESTARTS AGE
cas2 0/1 Completed 0 4m30s
cassandra-0 0/1 ContainerCreating 0 6s
mysql-0 1/1 Running 0 45m
cassandra-0 1/1 Running 0 12s
cassandra-1 0/1 Pending 0 0s
cassandra-1 0/1 Pending 0 0s
cassandra-1 0/1 Pending 0 2s
cassandra-1 0/1 ContainerCreating 0 2s
cassandra-1 1/1 Running 0 21s
cassandra-2 0/1 Pending 0 0s
cassandra-2 0/1 Pending 0 0s
cassandra-2 0/1 Pending 0 4s
cassandra-2 0/1 ContainerCreating 0 4s
cassandra-2 1/1 Running 0 24s
cassandra-2 0/1 Error 0 36s
cassandra-2 1/1 Running 1 38s
^C
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl get po
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Running 0 2m26s
cassandra-1 1/1 Running 0 2m14s
cassandra-2 1/1 Running 1 113s
永続ストレージのマウントを確認
Cassandraのコンテナに入って、確認したところ、PVCからプロビジョニングされたストレージ /dev/rdb3 が /cassandra_data にマウントされて、動作していることが確認できる。また、nodetoolコマンドも動作することが確認できた。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl exec -it cassandra-2 sh
# df -h
Filesystem Size Used Avail Use% Mounted on
overlay 9.7G 5.4G 4.3G 56% /
tmpfs 64M 0 64M 0% /dev
tmpfs 2.0G 0 2.0G 0% /sys/fs/cgroup
/dev/rbd3 976M 2.6M 958M 1% /cassandra_data
/dev/sda1 9.7G 5.4G 4.3G 56% /etc/hosts
shm 64M 0 64M 0% /dev/shm
tmpfs 2.0G 12K 2.0G 1% /run/secrets/kubernetes.io/serviceaccount
tmpfs 2.0G 0 2.0G 0% /proc/acpi
tmpfs 2.0G 0 2.0G 0% /proc/scsi
tmpfs 2.0G 0 2.0G 0% /sys/firmware
# ls -la /cassandra_data
total 24
drwxrwxrwx 3 root root 4096 Jan 21 14:10 .
drwxr-xr-x 1 root root 4096 Jan 21 14:10 ..
drwx------ 2 root root 16384 Jan 21 14:10 lost+found
# nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.244.2.32 88.91 KiB 256 65.4% 20c0973f-a120-4518-9af3-e97b386ab2b8 rack1
UN 10.244.1.29 88.9 KiB 256 68.3% 81e20762-29d2-44bc-9932-b8932d6ac3c3 rack1
UN 10.244.2.31 108.6 KiB 256 66.3% 2507ee0c-a5c9-4771-9272-3d9749106b07 rack1
コンテナの作り直し
readinessProbeから起動されるシェルを内包したコンテナをビルドして、チュートリアルで意図していたコンテンを作ってみる。
以下の様に、Dockerfileとシェルを準備する。シェルは起動しなかったコンテナで表示して確認したシェルである。
tkr@luigi:~/sandbox-1/cassandra_sts$ tree cassandra
cassandra
├── Dockerfile
└── ready-probe.sh
このDockerfileでは、前述で動作が確認できたコンテナに、readinessProbeのシェルを追加して、実行件を与えただけである。
コンテナの最大の特徴の一つは、既存のコンテナの上にファイルを追加できる点である。これで簡単に機能を追加することができる。
tkr@luigi:~/sandbox-1/cassandra_sts$ cat cassandra/Dockerfile
FROM cassandra:3.11
COPY ready-probe.sh /
RUN chmod +x /ready-probe.sh
コンテナリポジトリのタグを追加して、コンテナをビルドする。
tkr@luigi:~/sandbox-1/cassandra_sts/cassandra$ docker build -t maho/cassandra:3.11x .
Sending build context to Docker daemon 4.608kB
Step 1/2 : FROM cassandra:3.11
3.11: Pulling from library/cassandra
804555ee0376: Pull complete
f62a86f474e7: Pull complete
b08b2c5fa0f7: Pull complete
462a01774618: Pull complete
4a4246974523: Pull complete
bb514a91a0e3: Pull complete
f8a11c726003: Pull complete
98440d0d3ef0: Pull complete
7419891d3b45: Pull complete
c2bdf673f50c: Pull complete
Digest: sha256:0169fcc29fc49c0373bfb887d5e4c772ccf54da6ec36b1037b98ddd28b4cf6ce
Status: Downloaded newer image for cassandra:3.11
---> 68144c842c79
Step 2/2 : COPY ready-probe.sh /
---> 432cf0978c37
Successfully built 432cf0978c37
Successfully tagged maho/cassandra:3.11x
出来たコンテナ・イメージを、DockerHubのリポジトリに登録する。そのためには、アカウントを保有して、Docker Hubにログインする必要がある。
tkr@luigi:~/sandbox-1/cassandra_sts/cassandra$ docker login
Login Succeeded
tkr@luigi:~/sandbox-1/cassandra_sts/cassandra$ docker push maho/cassandra:3.11x
The push refers to repository [docker.io/maho/cassandra]
1177245d7951: Pushed
22513ea110cd: Mounted from library/cassandra
38ba3e8effd4: Mounted from library/cassandra
353dc2db654a: Mounted from library/cassandra
b6cce465f513: Mounted from library/cassandra
ecc83a7863bf: Mounted from library/cassandra
ad04a9a46d6e: Mounted from library/cassandra
61e1fabc3e51: Mounted from library/cassandra
c23c503e7b3c: Mounted from library/cassandra
e5bef8a45040: Pushed
814c70fdae62: Mounted from library/cassandra
3.11x: digest: sha256:4cd52e14ff74da54f8465a1cf549b587f5429440ae588e884682fd370941c36a size: 2619
これで、正常に動作するコンテナが出来たので、マニフェストのreadinessProbeの元に戻して、imageの値に、上記のリポジトリ名のコンテナを設定する。そして、適用すると、元来チュートリアルで期待していた動作が実現できる。
チュートリアルの確認操作を実施する
ポッドのコンテナで、Casandraのコマンドを実行して、Cassandraクラスタのステータスを確認する。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl exec -it cassandra-0 -- nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 10.244.2.32 88.91 KiB 256 65.4% 20c0973f-a120-4518-9af3-e97b386ab2b8 rack1
UN 10.244.1.29 88.9 KiB 256 68.3% 81e20762-29d2-44bc-9932-b8932d6ac3c3 rack1
UN 10.244.2.31 108.6 KiB 256 66.3% 2507ee0c-a5c9-4771-9272-3d9749106b07 rack1
スケールさせた時に、PVCを伴って増加するかの確認を行う。それには、マニフェストを編集して、replocas
の値を増やしてポッドの増加を確認する。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl edit statefulset cassandra
statefulset.apps/cassandra edited
spec:
podManagementPolicy: OrderedReady
replicas: 4
revisionHistoryLimit: 10
selector:
matchLabels:
ポッドが増え、PVCの数も増えていることが確認できる。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl get pods -l="app=cassandra"
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Running 0 8m42s
cassandra-1 1/1 Running 0 8m30s
cassandra-2 1/1 Running 1 8m9s
cassandra-3 1/1 Running 0 17s
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
cassandra-data-cassandra-0 Bound pvc-320c4269-bf6d-4d28-a868-907ed531dce3 1Gi RWO csi-rbd-sc 8m49s
cassandra-data-cassandra-1 Bound pvc-552897c0-dd20-4dbc-94b5-bcf54daafb22 1Gi RWO csi-rbd-sc 8m36s
cassandra-data-cassandra-2 Bound pvc-dcb3f287-d1a6-4614-87c0-ed6eed08e648 1Gi RWO csi-rbd-sc 8m15s
cassandra-data-cassandra-3 Bound pvc-9275585d-62fa-4b49-a88b-89125e2f5c7a 1Gi RWO csi-rbd-sc 23s
次に、ステートフルセットを削除しても、永続ボリューム要求、永続ボリュームが維持される事を確認する。
tkr@luigi:~/sandbox-1$ kubectl delete sts cassandra
statefulset.apps "cassandra" deleted
tkr@luigi:~/sandbox-1$ kubectl get po
NAME READY STATUS RESTARTS AGE
cassandra-0 1/1 Terminating 0 153m
cassandra-1 0/1 Terminating 0 152m
cassandra-2 0/1 Terminating 0 151m
tkr@luigi:~/sandbox-1$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
cassandra-data-cassandra-0 Bound pvc-1fa0622a-d8d9-486f-ba21-8ef8e72513f9 1Gi RWO csi-rbd-sc 153m
cassandra-data-cassandra-1 Bound pvc-2a098deb-949d-4573-9ba6-8132f8583934 1Gi RWO csi-rbd-sc 152m
cassandra-data-cassandra-2 Bound pvc-8e682bdc-639b-40a8-b2b7-3a528888013d 1Gi RWO csi-rbd-sc 151m
cassandra-data-cassandra-3 Bound pvc-fbb5ff06-aa35-488e-9bcd-5b02c6efcb64 1Gi RWO csi-rbd-sc 4m13s
まとめ
会社でも外人が開発したチュートリアルや学習教材は、経験上、バギーなことがある。でも、検証して問題を指摘すると、にこやかに、it's adventure と返してくる。確かに、そう言われると、完璧である必要は無い。問題を解決することで、明らかに理解が深まる。
チュートリアルは、完璧ではなく、地雷が埋まっていると考えるのがグローバル・スタンダードなのかもしれない。