ストレージオーケストレーター Rook : 第3話 Rook-Ceph破壊命令

f:id:ututaq:20191125022735p:plain

こんにちは、レッドハットでストレージを中心にクラウドインフラを生業にしている宇都宮です。

いやーG1 Climaxも終わりましたね。今年のG1は矢野が盛り上げてくれたおかげで…ってプロレスの話じゃなくて。Rookですよ。3話目ですね、1クール13話やるまでは打ち切りにならないようがんばります。
前回は、実際にRookを使ってKubernetes(k8s)クラスター上にCephストレージをデプロイして使ってみました。
今回はCephに障害が起きたとき、Persistent Volume(PV)を使っているアプリケーションのI/Oにどのような影響が起きるかを見てみましょう。

ところで前回OpenShift Meetupというイベントがあることを紹介したのですが、来る9/11(水)に開催予定のOpenShift Meetup Tokyo #6はストレージ特集です!東京会場の一般席はありがたいことに満席ですが、ブログ/Tweet発信枠と大阪・福岡のサテライト会場にはまだ空きがありますよー!

ちなみにOpenShift Meetupは#4からcrash.academyさんに録画をお願いしていて、リプレイを観ることができるんです!#5の動画はまだですが#4はもう公開されてます。#4で宇都宮がRookとCephについて話してた動画もあるのでユーザー登録して観てみてね!

あと勝手にRookの日本コミュニティを立ち上げました。コンテンツはまだ何もないけど、ぼちぼちイベントをする段取りができたらここで告知しまーす。

以上番宣でした!

worker nodeの追加

はいそれでは本題に入ります。前回はworker nodeが3台でこいつらをapp用とCeph用で兼用してたのですが、今回はCephのノード障害を起こすためにapp用のworkerを分けておきたいと思います。worker nodeを追加して適当にlabelを振って、こんな感じでいきます。(色んな事情からkubernetes再デプロイしてます)

[utubo@tutsunom ~]$ kubectl get node -L nodetype --sort-by='{.metadata.creationTimestamp}'
NAME                             STATUS   ROLES    AGE     VERSION   NODETYPE
ip-172-20-109-185.ec2.internal   Ready    master   19d     v1.14.1   
ip-172-20-58-254.ec2.internal    Ready    master   19d     v1.14.1   
ip-172-20-64-178.ec2.internal    Ready    master   19d     v1.14.1   
ip-172-20-125-166.ec2.internal   Ready    node     4d12h   v1.14.1   storage
ip-172-20-57-197.ec2.internal    Ready    node     4d12h   v1.14.1   storage
ip-172-20-71-77.ec2.internal     Ready    node     4d12h   v1.14.1   storage
ip-172-20-55-91.ec2.internal     Ready    node     4d12h   v1.14.1   app
ip-172-20-75-119.ec2.internal    Ready    node     4d12h   v1.14.1   app
ip-172-20-117-251.ec2.internal   Ready    node     4d12h   v1.14.1   app

ちなみに、各ノードたちはus-east-1a〜us-east-1cのAZに散らばっています。つまりCephもAZをまたいでクラスターになっています。今の状況を絵で書いてみるとこんな具合です。

f:id:ututaq:20190813190612p:plain

前回と同様にstorage nodeにRook-Ceph OperatorとCephをデプロイしてblock用のpoolとstorageclassを作ります。なおこの時に使ったyamlはgistに上げてますのでよろしければどうぞ。

[utubo@tutsunom ~]$ kubectl -n my-rook-ceph get CephBlockPool
NAME          AGE
replicapool   4d1h
[utubo@tutsunom ~]$ kubectl -n my-rook-ceph get sc
NAME            PROVISIONER             AGE
default         kubernetes.io/aws-ebs   19d
gp2 (default)   kubernetes.io/aws-ebs   19d
rbd             ceph.rook.io/block      4d1h
[utubo@tutsunom ~]$ kubectl -n my-rook-ceph  exec -it rook-ceph-operator-f5dd99499-lp2tn -- sh
sh-4.2# ceph status ; ceph osd tree
sh-4.2# ceph status ; ceph osd tree
  cluster:
    id:     99f4030e-4283-4776-8067-b6c028d31beb
    health: HEALTH_OK
 
  services:
    mon: 3 daemons, quorum a,b,c (age 3d)
    mgr: a(active, since 3d)
    osd: 9 osds: 9 up (since 3d), 9 in (since 3d)
 
  data:
    pools:   1 pools, 100 pgs
    objects: 3 objects, 19 B
    usage:   9.1 GiB used, 72 GiB / 81 GiB avail
    pgs:     100 active+clean
 
ID CLASS WEIGHT  TYPE NAME                               STATUS REWEIGHT PRI-AFF 
-1       0.07910 root default                                                    
-7       0.02637     host ip-172-20-125-166-ec2-internal                         
 2   ssd 0.00879         osd.2                               up  1.00000 1.00000 
 5   ssd 0.00879         osd.5                               up  1.00000 1.00000 
 8   ssd 0.00879         osd.8                               up  1.00000 1.00000 
-3       0.02637     host ip-172-20-57-197-ec2-internal                          
 0   ssd 0.00879         osd.0                               up  1.00000 1.00000 
 4   ssd 0.00879         osd.4                               up  1.00000 1.00000 
 6   ssd 0.00879         osd.6                               up  1.00000 1.00000 
-5       0.02637     host ip-172-20-71-77-ec2-internal                           
 1   ssd 0.00879         osd.1                               up  1.00000 1.00000 
 3   ssd 0.00879         osd.3                               up  1.00000 1.00000 
 7   ssd 0.00879         osd.7                               up  1.00000 1.00000 

はい、Cephクラスターできてますね。後はアプリなんですが今回はdateをビシバシ叩く奴では面白みがないので、portworxのfio-toolsを使ってfioを走らせてみましょう。このyamlで、20GBのPVをfioのPodにattachして、3分間の4K random writeをかけてみます。ベンチ目的じゃないのでそんなに高い負荷はかけません。

[utubo@tutsunom ~]$ kubectl apply -f my-fiotools-aio-portworx-write.yaml 
persistentvolumeclaim/fio-data-write created
deployment.extensions/fio-tester-write created
service/fiotools-write created
[utubo@tutsunom ~]$ kubectl get all,pvc,pv
NAME                                    READY   STATUS    RESTARTS   AGE
pod/fio-tester-write-646f78868f-7fzkj   1/1     Running   0          10s

NAME                     TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
service/fiotools-write   NodePort   100.71.144.167   <none>        8000:31182/TCP   9s

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/fio-tester-write   1/1     1            1           10s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/fio-tester-write-646f78868f   1         1         1       11s

NAME                                   STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/fio-data-write   Bound    pvc-eb9fe2a4-bd8b-11e9-a4d2-0a0909b905da   20Gi       RWO            rbd            12s

NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                      STORAGECLASS   REASON   AGE
persistentvolume/pvc-eb9fe2a4-bd8b-11e9-a4d2-0a0909b905da   20Gi       RWO            Delete           Bound    fio-tools/fio-data-write   rbd                     12s

はい、それではこの間に障害を起こしてみます。

デバイス障害

まずは手始めにデバイス障害を引きおこしてみましょう。ベアメタルならブッコ抜くところですがAWSなんでノードから1つEBSをForce Detachすればいいですかね。
じゃあosd.7に相当するEBSをブッコ抜きます。

[utubo@tutsunom ~]$ kubectl -n my-rook-ceph  exec -it rook-ceph-operator-f5dd99499-lp2tn -- sh
sh-4.2# ceph status ; ceph osd tree
  cluster:
    id:     99f4030e-4283-4776-8067-b6c028d31beb
    health: HEALTH_WARN
            1 osds down
            Degraded data redundancy: 108/846 objects degraded (12.766%), 38 pgs degraded, 43 pgs undersized
            too few PGs per OSD (28 < min 30)

  services:
    mon: 3 daemons, quorum a,b,c (age 3d)
    mgr: a(active, since 3h)
  osd: 9 osds: 8 up (since 2m), 9 in (since 3d)

  data:
    pools:   1 pools, 100 pgs
    objects: 282 objects, 1.0 GiB
    usage:   12 GiB used, 69 GiB / 81 GiB avail
    pgs:     108/846 objects degraded (12.766%)
             57 active+clean
             38 active+undersized+degraded
             5  active+undersized

  io:
    client:   6.0 MiB/s wr, 0 op/s rd, 1.51k op/s wr

ID CLASS WEIGHT  TYPE NAME                               STATUS REWEIGHT PRI-AFF
-1       0.07910 root default
-7       0.02637     host ip-172-20-125-166-ec2-internal
 2   ssd 0.00879         osd.2                               up  1.00000 1.00000
 5   ssd 0.00879         osd.5                               up  1.00000 1.00000
 8   ssd 0.00879         osd.8                               up  1.00000 1.00000
-3       0.02637     host ip-172-20-57-197-ec2-internal
 0   ssd 0.00879         osd.0                               up  1.00000 1.00000
 4   ssd 0.00879         osd.4                               up  1.00000 1.00000
 6   ssd 0.00879         osd.6                               up  1.00000 1.00000
-5     0.02637     host ip-172-20-71-77-ec2-internal
 1   ssd 0.00879         osd.1                               up  1.00000 1.00000
 3   ssd 0.00879         osd.3                               up  1.00000 1.00000
 7   ssd 0.00879         osd.7                             down  1.00000 1.00000

サヨウナラosd.7。(厳密にはまだCephクラスタからoutになってないのですが10分くらいしたら本当にサヨウナラになります)
さあではEBSをForce Detachした時のI/Oを見てみましょう。fioの方で1秒ごとのIOPSとlatencyのログを吐くようにしてるので、それらからグラフを書いてみます。

f:id:ututaq:20190813190617p:plain

へえ。6-7msecって意外と良いlatencyでwriteするのね。AZ跨ぎのCephなら10msec以上は覚悟だと思ってたけど。
それは良しとして、左側の赤い点線がEBSをブッコ抜いた瞬間です。ログを見ると50〜52秒の3秒間はIOPSもlatencyも全く記録がありません。latencyのログで53秒目に4000msecほどの記録があるので、4,5秒ほどwriteが止まってたって事ですね。 4,5秒のI/O停止は大抵のアプリケーションでは問題ないでしょう。一般的に、環境によって程度の差はあれ、ストレージで障害が起きると幾ばくかのI/O停止は発生します。I/O完全無停止を保つことは難しい。そのため、アプリに数秒のI/O停止も許容されないような要件がある場合は、例えばデータパス上にI/Oをキャッシュするレイヤーをはさむなどして、I/O停止を吸収できるようなアーキテクチャーのアプリを開発することが必要になります。

ノード障害

次はノード障害を引きおこしてみましょう。先程と同じ用にfioが動いてる間にstorage nodeをstopしちゃいます。
osd.7をもう一回戻して、ip-172-20-71-77-ec2-internalノードを落とします。

sh-4.2# ceph status ; ceph osd tree 
  cluster:
    id:     99f4030e-4283-4776-8067-b6c028d31beb
    health: HEALTH_WARN
            3 osds down
            1 host (3 osds) down
            Degraded data redundancy: 282/846 objects degraded (33.333%), 91 pgs degraded
            too few PGs per OSD (22 < min 30)
            1/3 mons down, quorum a,b
 
  services:
    mon: 3 daemons, quorum a,b (age 40s), out of quorum: c
    mgr: a(active, since 3d)
    osd: 9 osds: 6 up (since 27s), 9 in (since 3d)
 
  data:
    pools:   1 pools, 100 pgs
    objects: 282 objects, 1.0 GiB
    usage:   12 GiB used, 69 GiB / 81 GiB avail
    pgs:     282/846 objects degraded (33.333%)
             91 active+undersized+degraded
             9  active+undersized
 
  io:
    client:   7.7 MiB/s wr, 0 op/s rd, 1.36k op/s wr
 
ID CLASS WEIGHT  TYPE NAME                               STATUS REWEIGHT PRI-AFF 
-1       0.07910 root default                                                    
-7       0.02637     host ip-172-20-125-166-ec2-internal                         
 2   ssd 0.00879         osd.2                               up  1.00000 1.00000 
 5   ssd 0.00879         osd.5                               up  1.00000 1.00000 
 8   ssd 0.00879         osd.8                               up  1.00000 1.00000 
-3       0.02637     host ip-172-20-57-197-ec2-internal                          
 0   ssd 0.00879         osd.0                               up  1.00000 1.00000 
 4   ssd 0.00879         osd.4                               up  1.00000 1.00000 
 6   ssd 0.00879         osd.6                               up  1.00000 1.00000 
-5       0.02637     host ip-172-20-71-77-ec2-internal                           
 1   ssd 0.00879         osd.1                             down  1.00000 1.00000 
 3   ssd 0.00879         osd.3                             down  1.00000 1.00000 
 7   ssd 0.00879         osd.7                             down  1.00000 1.00000 

アヒャヒャヒャ。Podはどうなってる?

[utubo@tutsunom images]$ kubectl -n my-rook-ceph get pod
NAME                                                         READY   STATUS      RESTARTS   AGE
rook-ceph-agent-8wpx2                                        1/1     Running     0          4d6h
rook-ceph-agent-btcj4                                        1/1     Running     0          4d6h
rook-ceph-agent-fpb9j                                        1/1     Running     0          4d6h
rook-ceph-agent-lcp4q                                        1/1     Running     0          4d6h
rook-ceph-agent-rrknj                                        1/1     Running     0          4d6h
rook-ceph-mgr-a-7f675b44bf-hwf5c                             1/1     Running     0          4d3h
rook-ceph-mon-a-5f78486c7d-sbsvr                             1/1     Running     0          4d3h
rook-ceph-mon-b-6986666976-rlkr2                             1/1     Running     0          4d3h
rook-ceph-mon-d-b9b7968cc-vgvv9                              0/1     Pending     0          67s
rook-ceph-operator-f5dd99499-lp2tn                           1/1     Running     0          3d16h
rook-ceph-osd-0-84fd9fc787-tzzzc                             1/1     Running     0          4d3h
rook-ceph-osd-1-84f67559bf-hcqdq                             0/1     Pending     0          67s
rook-ceph-osd-2-5987f7774c-4zgjc                             1/1     Running     0          4d3h
rook-ceph-osd-3-69554f8dd7-gcbk2                             0/1     Pending     0          67s
rook-ceph-osd-4-5648d65b77-wd78f                             1/1     Running     0          4d3h
rook-ceph-osd-5-76545dd774-44fx2                             1/1     Running     0          4d3h
rook-ceph-osd-6-ddb557bc-qkwdk                               1/1     Running     0          4d3h
rook-ceph-osd-7-796d8d7bff-mfk24                             0/1     Pending     0          67s
rook-ceph-osd-8-86b7b44f85-hjf2j                             1/1     Running     0          4d3h
rook-ceph-osd-prepare-ip-172-20-125-166.ec2.internal-pm4n7   0/2     Completed   0          3d16h
rook-ceph-osd-prepare-ip-172-20-57-197.ec2.internal-hnrrz    0/2     Completed   0          3d16h
rook-discover-4zfs9                                          1/1     Running     0          4d6h
rook-discover-8qgsx                                          1/1     Running     0          4d6h
rook-discover-dll95                                          1/1     Running     0          4d6h
rook-discover-fqcc2                                          1/1     Running     0          4d6h
rook-discover-lxvzp                                          1/1     Running     0          4d6h

ip-172-20-71-77-ec2-internalで動いてたosd.[1,3,7]mon-d(mon-cに代わって上がろうとしてる)がPendingになってます。復活しようとしてもScheduleできるノードがないから復活できない感じですね。魂と精神はあるけど生き返れないとはハガレン的。後で人体錬成(ノード追加)するから待っててね。
それではさっきと同じ様にIOの様子を見てみましょう。

f:id:ututaq:20190813190655p:plain

やっぱりノード障害はデバイス障害とは違って長くなりますね。ログを見ると75〜100秒までIOPSの記録がないので、ざっくり30秒くらいしてから戻ってます。
結構長くなった理由は、Ceph的な話ですが、osdが無くなるとそのosdをprimaryとしていたplacement group(pg)はread/writeすることができなくなります。そのためmonがpgmapを更新してprimary osdを生きているosdになるようにremapするんですが、remapのpeeringが終わるまではそのpgはI/Oできません。よってpeeringのpgへのfioのwriteが待たされて、結果長いこと待つことになったのではないかと想像します。
また、monが落ちてしまったことで改めてelectionが起きてしまい、pgmap含めcluster mapの更新を始めるのに時間がかかったこともあるのではないかと。

ブロックストレージが30秒停まるというのはおっさんの宇都宮的にはそんなもんかなという気分です。もう時代錯誤かもしれんけど。
Cephのパラメータを調整すればもっと早く戻せるのかとか、たくさん考察できそうですが、ちょっと長くなりそうなのでやめときます。

ちなみに、I/Oが復活してからIOPSとlatencyがほんのり良くなってますよね。これはノードが落ちるまでは三重でレプリカしていたけど、ノードが落ちてからは二重レプリカとなったため、その分1回のwrite requestに対するCephクラスター内でのwrite量が減ったためです。

まとめ

そういうわけで今回はRook-Cephのクラスターで障害が起きるとI/Oにどんな影響があるのか調べてみました。
次回はRook-Cephのアーキテクチャを説明していこうと思います。今回RWXできなかったので次回おまけでできたらいいな。

というわけで、今回はここまで。

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。