Kubernetesチュートリアルで、コンテナ障害から原因特定と対策までを実施した記録

この記事は、[Kubernetes 1.17 と Ceph を CSI で連携を検証](https://qiita.com/MahoTakara/items/b83dfdd4a2597ba1db23)で作ったKubernetesとCephのダイナミックプロビジョニングの環境を利用して、チュートリアル[Example: Deploying Cassandra with Stateful Sets](https://kubernetes.io/docs/tutorials/stateful-application/cassandra/)でステートフルセット・コントローラーでCassandraのクラスタを実行するものです。

チュートリアルだから簡単にできるだろうと思ったら、コンテナ・イメージが壊れているようで、問題判別とコンテナの作り替えを実施したトラブル対応の記録です。

## 準備

作業前に、次の二つのクラスタをそれぞれ起動します。

* Cephクラスタの起動  https://github.com/takara9/vagrant-ceph
* Kubernetes 1.17 起動 https://github.com/takara9/vagrant-kubernetes

ブロックストレージの初期化とストレージクラスの作成は、[Kubernetes 1.17 と Ceph を CSI で連携を検証](https://qiita.com/MahoTakara/items/b83dfdd4a2597ba1db23)を参考にしてください。このQiitaの記事をトレースして、クラスタが作成されていれば、この記事の内容を実行する準備ができています。


## K8sクラスタ環境のメモリ追加

Javaで開発された Cassandra を起動するために、ワーカーノードに４GB程度のメモリを搭載する必要があります。
前提とした記事の内容が終わった時点では、次のようなリソースの状態にあると思います。つまり、ワーカーノードにメモリが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に増強する。

~~~file:Vagrantfile
!       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を作成するように変更するために、ディレクトリを作成して、マニフェストをダウンロードして編集する。マニフェストは、２種類あり、サービスとステートフルセットの生成用がある。サービスに変更は無いので、ステートフルセットのマニフェストの変更のみとなる。

~~~
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のストレージクラス、ボリュームモードの２箇所である。そして、このファイルの修正箇所以下には、ストレージクラス fast のマニフェストが付いてるが、その部分を削除する。

~~~file:cassandra-statefulset.yaml
  # 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 と返してくる。確かに、そう言われると、完璧である必要は無い。問題を解決することで、明らかに理解が深まる。
チュートリアルは、完璧ではなく、地雷が埋まっていると考えるのがグローバル・スタンダードなのかもしれない。