在Kubernetes教程中,记录了从容器故障的原因识别到解决方案的整个过程
此篇文章利用Kubernetes 1.17和CSI实现了Kubernetes和Ceph的动态供应环境来验证,并使用了教程示例:使用有状态副本集部署Cassandra,从而在状态副本集控制器中运行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左右的内存。
假设我们在前文结尾处的资源状态如下:也就是说,在工作节点上只安装了约1GB的内存,这对于以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,需要创建目录并下载并编辑清单文件。清单文件有两种,其中一种用于生成服务和有状态集。由于服务没有更改,所以只需修改有状态集的清单文件。
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のマニフェストがありますが、その部分を取り除いてください。”
「ステートフルセット的变更部分在volumeClaimTemplates的存储类和卷模式这两个地方。而且,这个文件的修改部分以下有一个附有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的启动和问题识别
通过应用之前编辑的清单,在有状态控制器的管理下生成Pod和持久性存储要求(PVC)。
tkr@luigi:~/sandbox-1/cassandra_sts$ kubectl apply -f cassandra-statefulset.yaml
statefulset.apps/cassandra created
在这里,生成Pod时发生了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
在这份信息中,我们无法确定容器中的文件或目录具体是什么以及它们是否存在。因此,我们将继续进行下一阶段的确认。
獨立啟動問題的容器。
为了识别容器内部的问题,我们可以将容器作为单独的Pod启动并在其中进行故障排查。通过检查清单中的image值,我们可以找到容器的仓库名称,并使用kubectl run命令将容器单独作为Pod启动。在这种情况下,虽然PVC未挂载,但实际上容器应该能够独立运行,因为它是根据正确的构建而创建的。换句话说,只要容器构建正确,它就应该能够独立运行。
以下是一种方式,单独启动成功并能够在交互式shell中执行命令。
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 启动的容器中的文件是否存在。这个 shell 脚本用于将准备状态通知给 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
对于在上述shell中调用的命令,也要观察其操作。仍然存在问题。
# 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镜像的容器,因此我们结束对这个容器的调查。
替代手段的考虑 de
为了实现在Statufulset上运行Cassandora的教程的目标,我们决定探索替代方法。当查看这个教程清单中描述用于启动Cassandra的参数时,发现它似乎与在DockerHub上注册的https://hub.docker.com/_/cassandra容器镜像的启动参数相同。虽然有一些小的添加,但是Cassandra是使用Java运行的,因此像堆大小这样的参数不是Cassandra特定的,而是为JVM而设定的。
因此,我們可以使用kubectl啟動DockerHub上的Cassandra容器,並探索它作為替代容器的可能性。
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启动的shell,结果发现不存在如下所示的情况。
# 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 准备检查中启动的 shell 的容器,以创建预期的内容,以便在教程中进行操作。
按照以下步骤准备 Dockerfile 和 shell。此 shell 是在未启动的容器中显示和确认过的 shell。
tkr@luigi:~/sandbox-1/cassandra_sts$ tree cassandra
cassandra
├── Dockerfile
└── ready-probe.sh
在这个Dockerfile中,只需要给已经在之前进行验证的容器添加一个readyProbe的shell,并提供所需的执行条件。
容器的最大特点之一是可以在现有容器的基础上添加文件,这样可以轻松地添加功能。
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
现在,已成功创建了一个正常运行的容器,因此需要将manifest文件中的readinessProbe恢复原状,并将image的值设置为上述存储库中的容器。然后应用这些更改,就可以实现最初在教程中期望的操作。
进行教程确认操作
在Pod的容器中,执行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的值并确保增加了Pod。
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
总结
在公司中,由外国人开发的教程和学习材料根据我的经验有时候可能存在一些问题。然而,当我确认并指出问题时,他们总是微笑着回答说“这是一次冒险”。确实,这样说起来,完美并不是必要的。通过解决问题,我们能够加深对知识的理解。将教程看作不完美并且潜藏着雷区可能是全球标准。