我在Kubernetes上安装了Rook,并尝试使用块存储

首先

这篇文章已经过时了。我们建议您不要升级使用旧版的rook/ceph,而是引入最新版本,并启用bluestore,并加入HDD。

我正在尝试在Kubernetes环境中构建Ceph环境,并试用Rook。

基本引导步骤在下面的文件中有详细说明,但由于版本更新,稍微做了一些改动,所以请记下备忘录。

在Rook的v1.x及其后续版本中,假定每个Kubernetes节点上都配置了专用的HDD/SSD。您仍然可以在测试中继续指定directories: [path: /var/lib/rook],但在cluster.yaml中,默认设置为useAllDevices: true,并期望存在未使用的块设备(例如/dev/sdb)。使用Rook的新版本时需要注意。如果可能的话,建议避免使用Flex Volume。

论文中的参考文献来源

    • Cloud NativeなストレージRookの検証@ysakashita

 

    Official Ceph Storage Quickstart for v0.8

环境

    • Kubernetes v1.12.1 (Xeon e3-1220v2, 24GB Memory, Ubuntu 16.04.5) x4台

 

    Rook tag:v0.8.3 (git clone https://github.com/rook/rook)

节点在Baremetal(Ubuntu16.04.5)上使用kubespray进行构建。

在执行之前,命名空间的状态如下。

$ kubectl get ns
NAME             STATUS    AGE
default          Active    181d
ingress-nginx    Active    11d
istio-system     Active    13h
kube-public      Active    181d
kube-system      Active    181d
metallb-system   Active    179d

【提醒】有关传送速度的内容

我计划在不久的将来将整个系统更新到最新的v1.6.x版本。

请务必使用最新版本的Rook/Ceph(v1.7或v1.6.8及以上),因为旧版本(如v1.6)存在致命的缺陷。

在此之前,我已经在两个Ceph文件系统之间进行了数据传输,因此现在的环境如下。

    • Kubernetes: v1.16.9 (TX1310m3 x4, Xeon E3-1225v6, 4TB HDD x2 RAID1)

 

    • Kubernetes: v1.19.9 (TX1320m4 x5, Xeon E-2234, 4TB HDD + 500GB SSD)

 

    • Local Network (K8sクラスター内部、両方とも): 10Gbps X520-DA1 + DAC + CRS309-1G-8S+

 

    • Backend Network (K8sクラスター間接続): 1Gbps RJ45ケーブル + Switch

 

    Rook/Ceph v1.0.6 → v1.5.5

网络是在一个单一的192.168.1.0/24内部配置的。

以下是通过rsync从一个集群传输到另一个集群(pod到pod)的约3GB数据(实际传输数据约2.5GB),其中文件数量为2255364,这些文件主要由具有小于4KB的文件组成的Filesystem构成。

sent 2,491,171,331 bytes  received 39,713,035 bytes  136,763.90 bytes/sec
total size is 2,945,556,696  speedup is 1.16

准备完成

在能够运行kubectl命令的节点上,使用git clone将rook的存储库获取下来。

$ git clone https://github.com/rook/rook
$ cd rook
$ git checkout refs/tags/v0.8.3 -b v0.8.3

部署

我基本上会遵循官方文件的指示。
当普通访问官方文档时,会显示主分支(master),然后我会在左下角的菜单中选择v0.8版本。

在之前的步骤后,从rook目录继续执行以下命令即可部署所需的服务。

如果您正在使用minikube,请修改cluster.yaml文件中的dataDirHostPath: /var/lib/rook。

$ cd cluster/examples/kubernetes/ceph
$ kubectl create -f operator.yaml
$ kubectl create -f cluster.yaml

【2018/11/20追記】如果在不更改FLEXVOLUME_DIR_PATH的情况下执行operator.yaml,可能无法正常运行。
虽然可以进行修正,但如果想避免重复劳动,请参考后半部分关于FLEXVOLUME_DIR_PATH的章节。

当我们进行到这一步时,命名空间发生了以下变化。

$ kubectl get ns
NAME               STATUS    AGE
...
rook-ceph          Active    1m
rook-ceph-system   Active    1m

在StorageClass中的使用

在公式文档中,尝试操作 “Block Storage” 部分中的 “StorageClass”。

假设当前目录为 cluster/examples/kubernetes/ceph,将继续执行前面的操作。

根据官方文档,应用 storageclass.yaml 文件,并确认 StorageClass 的状态。

$ kubectl apply -f storageclass.yaml
$ kubectl -n rook-ceph get storageclass
NAME              PROVISIONER          AGE
rook-ceph-block   ceph.rook.io/block   1m

为了确认相关定义,我执行了`kubectl -n rook-ceph get storageclass -o yaml`命令并在末尾添加了`-o yaml`参数。

- apiVersion: storage.k8s.io/v1
  kind: StorageClass
  metadata:
    ...
    name: rook-ceph-block
    namespace: ""
    ...

由于命名空间为空,实际上,无论指定哪个命名空间,都可以引用存储类的定义。

$ kubectl -n default get storageclass
NAME              PROVISIONER          AGE
rook-ceph-block   ceph.rook.io/block   1h

$ kubectl -n kube-system get storageclass
NAME              PROVISIONER          AGE
rook-ceph-block   ceph.rook.io/block   1h

因此,可以从整个系统中使用。

关于使用StatefulSet的案例信息。

我认为在ROOK的官方文档中包含的WordPress示例是从Kubernetes的官方文档中提取的(例如:使用持久卷部署WordPress和MySQL)。

因为这个本身并不是很有趣,所以我决定将之前写过的 Kubernetes 官方实例:使用 PersistentVolume 配置 Cassandra,恢复到原始形式。

在这个Cassandra的例子中,使用了volumeClaimTemplates,并且包含了StorageClass的定义。

在创建命名空间时,Service的定义与官方文件完全一致,没有问题。我选择将其命名为cassandra。

$ kubectl create ns cassandra
$ alias kubectl='kubectl -n cassandra'
$ kubectl create -f https://k8s.io/examples/application/cassandra/cassandra-service.yaml

与官方文档中的cassandra-statefulset.yaml相比,StatefulSet的表现如下。

--- cassandra-statefulset.yaml.orig  2018-11-05 17:17:44.057436173 +0900
+++ cassandra-statefulset.yaml       2018-11-05 17:22:58.843780317 +0900
@@ -53,7 +53,7 @@
           - name: HEAP_NEWSIZE
             value: 100M
           - name: CASSANDRA_SEEDS
-            value: "cassandra-0.cassandra.default.svc.cluster.local"
+            value: "cassandra-0.cassandra.cassandra.svc.cluster.local"
           - name: CASSANDRA_CLUSTER_NAME
             value: "K8Demo"
           - name: CASSANDRA_DC
@@ -86,15 +86,7 @@
       name: cassandra-data
     spec:
       accessModes: [ "ReadWriteOnce" ]
-      storageClassName: fast
+      storageClassName: rook-ceph-block
       resources:
         requests:
           storage: 1Gi
----
-kind: StorageClass
-apiVersion: storage.k8s.io/v1
-metadata:
-  name: fast
-provisioner: k8s.io/minikube-hostpath
-parameters:
-  type: pd-ssd

由于指定了namespace,所以将其从”default”更改为”cassandra”。
在之前的位置,通过在kubectl中添加”-n cassandra”别名,因此在命令行中无需关注namespace。

另外,我们将StorageClass部分完全删除,并将storageClassName的指定更改为之前确认的StorageClass的metadata.name,即rook-ceph-block。

$ kubectl apply -f cassandra-statefulset.yaml 

即使执行到这一步,Pod仍无法启动,让我感到困扰。

$ kubectl describe pod cassandra-0
...
Events:
  Type     Reason       Age                From               Message
  ----     ------       ----               ----               -------
  Normal   Scheduled    24m                default-scheduler  Successfully assigned cassandra/cassandra-0 to node6
  Warning  FailedMount  2m (x10 over 22m)  kubelet, node6     Unable to mount volumes for pod "cassandra-0_cassandra(0ad97640-e0d4-11e8-8510-000db93312a4)": timeout expired waiting for volumes to attach or mount for pod "cassandra"/"cassandra-0". list of unattached volumes=[cassandra-data default-token-hlb95]

我在这里尝试执行了样本的wordpress.yaml等内容,但看起来PV和PVC都被正常创建,但似乎无法从Pod中挂载。

让我们检查正在运行的节点6的/var/log/syslog记录。

Nov  5 18:57:28 node6 kubelet[1264]: E1105 18:57:28.898651    1264 desired_state_of_world_populator.go:311] Failed to add volume "cassandra-data" (specName: "pvc-15227c15-e0df-11e8-8510-000db93312a4") for pod "1527d22f-e0df-11e8-8510-000db93312a4" to desiredStateOfWorld. err=failed to get Plugin from volumeSpec for volume "pvc-15227c15-e0df-11e8-8510-000db93312a4" err=no volume plugin matched

与FLEXVOLUME_DIR_PATH相关的问题。

以下是可能適用的類似案例:

    https://github.com/rook/rook/issues/1888

根据链接的官方文档,可能存在以下两个理由。

    1. FlexVolume的配置

 

    指定的FlexVolume已被指定给kubelet。

我确认了最初的FlexVolume配置,默认情况下会创建一个位于 /usr/libexec/kubernetes/kubelet-plugins/volume/exec/ 的目录。

$ ls -l /usr/libexec/kubernetes/kubelet-plugins/volume/exec/
total 16
drwxr-xr-x 2 root root 4096 Nov  6 00:10 ceph.rook.io~rook
drwxr-xr-x 2 root root 4096 Nov  6 00:10 ceph.rook.io~rook-ceph-system
drwxr-xr-x 2 root root 4096 Nov  6 00:10 rook.io~rook
drwxr-xr-x 2 root root 4096 Nov  6 00:10 rook.io~rook-ceph-system

我以为在这个状态下,使用默认设置应该可以正常运行。但是当我查看下一个 kubelet 的设置时,发现指向了 /var/lib/kubelet/volume-plugins 的不同位置。

$ ps aux|grep volume-plugin-dir
root      1179 11.9  1.7 1848808 142048 ?      Ssl  Nov05  41:51 /usr/local/bin/kubelet    ...  --volume-plugin-dir=/var/lib/kubelet/volume-plugins

为了实现这一点,我们编辑了 cluster/examples/kubernetes/ceph 下的 operator.yaml 文件,设置了 FLEXVOLUME_DIR_PATH。

$ cd cluster/examples/kubernetes/ceph

在这里,编辑了 operator.yaml 文件之后,运行了 $ git diff operator.yaml 命令的结果如下。

--- a/cluster/examples/kubernetes/ceph/operator.yaml
+++ b/cluster/examples/kubernetes/ceph/operator.yaml
@@ -308,8 +308,8 @@ spec:
         # - name: AGENT_TOLERATION_KEY
         #  value: "<KeyOfTheTaintToTolerate>"
         # Set the path where the Rook agent can find the flex volumes
-        # - name: FLEXVOLUME_DIR_PATH
-        #  value: "<PathToFlexVolumes>"
+        - name: FLEXVOLUME_DIR_PATH
+          value: "/var/lib/kubelet/volume-plugins/"
         # Rook Discover toleration. Will tolerate all taints with all keys.
         # Choose between NoSchedule, PreferNoSchedule and NoExecute:
         # - name: DISCOVER_TOLERATION

重新应用此 operator.yaml 文件后,过一段时间,Pod 将逐个重新启动。

$ kubectl apply -f operator.yaml

确认操作

通过至今的工作,rook-ceph 成功启动。

$ kubectl get all
NAME              READY     STATUS    RESTARTS   AGE
pod/cassandra-0   1/1       Running   0          6m
pod/cassandra-1   1/1       Running   0          4m
pod/cassandra-2   1/1       Running   0          1m

NAME                TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/cassandra   ClusterIP   None         <none>        9042/TCP   7h

NAME                         DESIRED   CURRENT   AGE
statefulset.apps/cassandra   3         3         6m

将服务向外部公开

我在这一步重新添加了类型为LoadBalancer的Service,以便从外部访问cassandra。

$ cat 03.cassandra-service.yaml 
apiVersion: v1
kind: Service
metadata:
  labels:
    app: cassandra
  name: cassandracl
spec:
  ports:
  - port: 9042
  selector:
    app: cassandra
  type: LoadBalancer

$ kubectl apply -f 03.cassandra-service.yaml 
service/cassandracl created

到目前为止,可从外部访问的Cassandra集群已经成功运行。

$ kubectl get svc
NAME          TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
cassandra     ClusterIP      None           <none>        9042/TCP         24m
cassandracl   LoadBalancer   10.233.32.79   192.168.100.156   9042:30653/TCP   23m

突然停止运动

在考虑到后续断电情况下,重新启动整个节点后,出现了无法正常运行的问题。查看状态后发现如下情况。

$ kubectl describe pod/cassandra-0
...
Events:
  Type     Reason       Age              From               Message
  ----     ------       ----             ----               -------
  Normal   Scheduled    5m               default-scheduler  Successfully assigned cassandra/cassandra-0 to node3
  Warning  FailedMount  1m (x2 over 3m)  kubelet, node3     Unable to mount volumes for pod "cassandra-0_cassandra(243ea4ec-e188-11e8-8a8f-000db93312a4)": timeout expired waiting for volumes to attach or mount for pod "cassandra"/"cassandra-0". list of unmounted volumes=[cassandra-data]. list of unattached volumes=[cassandra-data default-token-kl6r4]

从Pod的日志中可以看到它已经启动了,但是无法解析cassandra-0的IP地址。

起初我对kube-dns表示怀疑,但当使用StatefulSet时,我发现如果没有定义ClusterIP: None的Service(svc),将无法获取cassandra-0的IP。

所以 svc 定义了两个“cassandra”和“cassandracl”。

当发出对kube-dns(10.233.0.3)的集群名查询时,会以轮询方式返回其下的POD的A记录(IP)。

$ nslookup cassandra.cassandra.svc.cluster.local 10.233.0.3
Server:         10.233.0.3
Address:        10.233.0.3#53

Name:   cassandra.cassandra.svc.cluster.local
Address: 10.233.100.160
Name:   cassandra.cassandra.svc.cluster.local
Address: 10.233.71.56
Name:   cassandra.cassandra.svc.cluster.local
Address: 10.233.74.92

我认为,在删除了ClusterIP: None的svc定义之后,重新定义了type: LoadBalancer的svc,所以在重新启动时才发现了问题。

无法创建下一个PVC的问题

然后,无法使用StorageClass从rook-ceph创建。

$ kubectl -n cassandra describe pvc myclaim
Name:          myclaim
Namespace:     cassandra
StorageClass:  rook-ceph-block
Status:        Pending
Volume:        
Labels:        <none>
Annotations:   control-plane.alpha.kubernetes.io/leader={"holderIdentity":"dd36afbf-e457-11e8-8335-1a7fdbbc44fe","leaseDurationSeconds":15,"acquireTime":"2018-11-20T05:03:14Z","renewTime":"2018-11-20T05:08:29Z","lea...
               kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"PersistentVolumeClaim","metadata":{"annotations":{},"name":"myclaim","namespace":"cassandra"},"spec":{"accessModes":["ReadWr...
               volume.beta.kubernetes.io/storage-provisioner=ceph.rook.io/block
Finalizers:    [kubernetes.io/pvc-protection]
Capacity:      
Access Modes:  
Events:
  Type     Reason              Age              From                                                                                         Message
  ----     ------              ----             ----                                                                                         -------
  Normal   Provisioning        1m (x9 over 5m)  ceph.rook.io/block rook-ceph-operator-6cc45dfb48-kpkj6 dd36afbf-e457-11e8-8335-1a7fdbbc44fe  External provisioner is provisioning volume for claim "cassandra/myclaim"
  Warning  ProvisioningFailed  1m (x9 over 5m)  ceph.rook.io/block rook-ceph-operator-6cc45dfb48-kpkj6 dd36afbf-e457-11e8-8335-1a7fdbbc44fe  Failed to provision volume with StorageClass "rook-ceph-block": Failed to create rook block image replicapool/pvc-958a9ee5-ec81-11e8-849d-000db9331290: failed to create image pvc-958a9ee5-ec81-11e8-849d-000db9331290 in pool replicapool of size 5368709120: Failed to complete '': exit status 2. rbd: error opening pool 'replicapool': (2) No such file or directory
. output:
  Normal  ExternalProvisioning  16s (x78 over 5m)  persistentvolume-controller  waiting for a volume to be created, either by external provisioner "ceph.rook.io/block" or manually created by system administrator

应用toolbox.yaml后,通过ceph命令来检查存储池的状态,发现无法确认replicapool的存在。

$ kubectl -n rook-ceph exec -it rook-ceph-tools -- ceph osd lspools
$

查看池的定义时,发现同时指定了复制和纠删码选项,但在配置示例中却没有提及,因此我简单地选择了只使用复制。

$ kubectl -n rook-ceph get pool replicapool -o yaml
apiVersion: ceph.rook.io/v1beta1
kind: Pool
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"ceph.rook.io/v1beta1","kind":"Pool","metadata":{"annotations":{},"name":"replicapool","namespace":"rook-ceph"},"spec":{"erasureCoded":{"codingChunks":1,"dataChunks":2},"replicated":{"size":3}}}
  creationTimestamp: 2018-11-20T05:02:46Z
  generation: 1
  name: replicapool
  namespace: rook-ceph
  resourceVersion: "32423166"
  selfLink: /apis/ceph.rook.io/v1beta1/namespaces/rook-ceph/pools/replicapool
  uid: 8539e6e1-ec81-11e8-849d-000db9331290
spec:
  erasureCoded:
    codingChunks: 1
    dataChunks: 2
  replicated:
    size: 3

删除了pool后,删除了spec.erasureCoded,只保留了spec.replicated,并再次使用apply -f命令将YAML文件应用,成功运行。

由于仅执行 “apply -f” 命令无法立即反映更改,因此可以推断出这是在稍晚于之前修改 storageclass.yaml 文件的时候重新创建 StorageClass 时才发现的。

【离题不谈】故障排除

仪表板

为了故障排除,将Dashboard Service的类型从ClusterIP更改为LoadBalancer。

$ kubectl -n rook-ceph edit svc rook-ceph-mgr-dashboard
$ kubectl -n rook-ceph get svc rook-ceph-mgr-dashboard
NAME                      TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)          AGE
rook-ceph-mgr-dashboard   LoadBalancer   10.233.49.37   192.168.100.110   7000:32606/TCP   6h

我从浏览器中查看了仪表板,但并没有得到太有趣的结果。
这个仪表板在外观上还可以,但不太值得推荐。

工具箱

当执行ceph命令或rdb命令时,请按照以下步骤进行。

假定您在使用 kubectl apply -f 前先预先进行了 toolbox.yaml 的部署。

$ kubectl -n rook-ceph exec -it rook-ceph-tools bash
..# ceph osd status
# ceph osd status
+----+-------------------------------------+-------+-------+--------+---------+--------+---------+-----------+
| id |                 host                |  used | avail | wr ops | wr data | rd ops | rd data |   state   |
+----+-------------------------------------+-------+-------+--------+---------+--------+---------+-----------+
| 0  | rook-ceph-osd-id-0-6b577745cd-2dlcf | 35.4G |  421G |    0   |     0   |    0   |     0   | exists,up |
| 1  | rook-ceph-osd-id-1-56c8cb7449-wpmk9 | 21.0G |  206G |    0   |     0   |    0   |     0   | exists,up |
| 2  |  rook-ceph-osd-id-2-b8c5d98d5-cmkwh | 20.8G |  206G |    0   |     0   |    1   |    16   | exists,up |
| 3  |  rook-ceph-osd-id-3-894fc4d55-7ttr6 | 18.0G | 89.9G |    0   |     0   |    0   |     0   | exists,up |
| 4  | rook-ceph-osd-id-4-86d4557d64-85vvs | 17.5G | 90.4G |    0   |     0   |    1   |    90   | exists,up |
| 5  |  rook-ceph-osd-id-5-cbfdb6c8b-ws966 | 16.7G | 37.9G |    0   |     0   |    0   |     0   | exists,up |
| 6  |  rook-ceph-osd-id-6-ff948fbbd-dzvfd | 21.0G | 92.2G |    0   |     0   |    0   |     0   | exists,up |
+----+-------------------------------------+-------+-------+--------+---------+--------+---------+-----------+
[root@rook-ceph-tools /]# ceph df
GLOBAL:
    SIZE      AVAIL     RAW USED     %RAW USED
    1295G     1145G         150G         11.63
POOLS:
    NAME              ID     USED      %USED     MAX AVAIL     OBJECTS
    replicapool       1       134M      0.02          834G          48
    myfs-metadata     2      15962         0          834G          21
    myfs-data0        3         98         0          834G           2

使用toolbox.yaml来设置“ceph osd status”的别名可能会很方便。

别名 show_ceph_osd_status=”kubectl -n rook-ceph exec -it rook-ceph-tools — ceph osd status” -> show_ceph_osd_status 别名=”kubectl -n rook-ceph exec -it rook-ceph-tools — ceph osd status”

以上

(Chinese paraphrase: 以上)

广告
将在 10 秒后关闭
bannerAds