Rook CephをKubernetes on EC2で極力お金を使わずに作る

# はじめに

Rook Ceph（またはCeph）を勉強用やお試し用に作ろうとすると、まあまあお金と手間が掛かりがちです。

例えばオンプレミスでRook Cephを作る場合は、フォーマットされていないストレージデバイス(HDDやSSD等)を用意する必要があります。またマネージドなKubernetesの例だと、Amazon EKSではControl Planeの料金だけでひと月に73ドル(2024年6月22日時点で)掛かります。手持ちのパソコンでVagrantやVirtualBoxを使うと追加のストレージとコストは掛からないので全然ありですが（筆者もいつか試してはみたいです）、既存の物理マシンのリソースはそれなりに使ってしまうでしょう。

そこで今回は、「極力お金を使わず」かつ「既存マシンやストレージも使わず」にRook Cephを作る方法を考えてみました。具体的には、Amazon EC2上に構築したKubernetesクラスターでRook Cephを作ってみます。

書いていったらRook Cephとは関係のない内容も盛り沢山に含まれてだいぶゴツい内容になってしまいました。しかしそれらの情報のほとんどが環境を作るのに必要となるため、是非スキップせずに順番に読み進めていただければと思います。

# 事前準備と知識

本記事の内容を読み進めて実施していただくにあたり、以下のツールとそれらの基本的な知識が必要となります。

* 必要なもの
    * AWSアカウント
    * TerraformとAnsibleを実行可能なマシン
* 必要な知識とスキル
    * 公開鍵/秘密鍵を使ってSSHできる
    * AWS
        * EC2, EBS, セキュリティグループあたりが理解できている
    * Terraform と Ansible
        * 変数の設定と基本的なコマンドが実行できる
    * Kubernetes
        * kubectlの基本的なコマンドが使える
        * HelmとHelmfileが使える
    * Ceph
        * 知識がなくても本記事の手順で構築はできますが、おそらく「何をやっているのかよーわからん...」状況になります。そのためCephと、そのコンポーネントに関する基本的な理解はあった方がいいです。

# 構成概要

KubernetesクラスターでRook Cephを構築するために使う主要なAWSリソースは以下になります。

* Control Plane用（1台）
    * EC2インスタンス
        * OS : Ubuntu 22.04
        * インスタンスタイプ : t3a.small（2vCPU, 2GiBメモリ）
* Worker Node用（3台）
    * EC2インスタンス
        * OS : Ubuntu 22.04
        * インスタンスタイプ : t3a.small（2vCPU, 2GiBメモリ）
    * EBSボリューム（OSD用）
        * タイプ : gp3
        * サイズ : 5GiB

図で非常にざっくりとした構成を書いてみます。AWSリソースを黒色、k8sのコンポーネントを青色で書いています。
ポイントは、Worker NodeにCeph OSD(Object Storage Daemon)用のEBSボリュームが追加されているところです。Cephは「フォーマットされていないストレージデバイス」を必要とするので、この条件に相当する追加のEBSボリュームを用意しています。

![k8s-ec2-rook-ceph.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370519/9b7e1dfe-fee8-3081-2748-c1c1b20a19cd.png)

より詳細は、手順で解説していきます。

# 費用はどれくらい？

タイトルが「極力お金を使わずに」なので、先にひと月の概算費用を計算しておきましょう。
「リージョンはアジアパシフィック(東京)で、1ヶ月は730時間」と想定して計算します。

#### EC2インスタンス

t3a.small x 4台 に掛かる計算をします。

```
1台1時間あたり 0.0245 USD
→ 1台1ヶ月で 17.89 USD (= 0.0245 x 730)
→ 4台1ヶ月で 71.54 USD (= 17.89 x 4)

常時稼働はせず、1日2時間くらい動かすとすると
71.54 x 2/24 = 5.96 USD / month
```

####  EBSボリューム

手順で出てきますが、「gp3を75GiB, gp2を8GiB」使う想定です。
AWS Pricing Caluclatorで計算すると次のような結果になりました。

```
gp3が75GiB → 7.20 USD / month
gp2が8GiB → 0.96 USD / month

EBS合計 : 8.16 USD / month
```

####  合計

5.96 + 8.16 = **14.12 USD / month**

円安が進んだとして「1ドル = 160円」とすると、
日本円では 14.12 x 160 = **2259円 / 月** になりました。

「月500円くらいで行けんじゃね」と思ってたんで、正直ショックな結果となりました...
（ストレージがだいぶ高い...）

気を取り直して、、手順を書いていきます。

# 手順

以下に手順を記載します。Terraform、Ansible、Helm等の多くのツールを使いやることも少し多いのですが、よろしければ試していただけると幸いです。

## (1) AWSリソースの作成

まずは、Kubernetesクラスターを構築するためのAWSリソースを作成します。
手前味噌ですが、以下の k8s-ec2-terraform を使うと割と手軽に(?)AWSリソースを作成できます。

https://github.com/showchan33/k8s-ec2-terraform/tree/55965a311b848dfa11e18f14094c28646ba703d1

### ``terraform.tfvars``ファイルの用意

上記ソースの ``terraform-rook-ceph.tfvars.sample`` を ``terraform.tfvars`` という名前でコピーします。
以下に、今回の構成に関係しそうな箇所を抜き出してみます。

```tf:terraform.tfvars
var_provider = {
  region     = "ap-northeast-1"
  # (1-1) AWSにアクセス可能なIAMユーザの認証情報を設定
  access_key = "AKxxxxx....."
  secret_key = "yyyyyzzzzz....."
}

...

ec2_control_plane = {
  ...
  # (1-2) インスタンスタイプは t3a.small 以上のスペックにする
  instance_type   = "t3a.small"
  ...
}

ec2_workers = [
  # 1つ目のWorker Nodeの設定
  {
    ...
    instance_type   = "t3a.small" # (1-2)
    ...
    # (1-3) EC2インスタンスのルートボリューム
    root_block_device_config = {
      volume_size = 20
      volume_type = "gp3"
    }
    # (1-4) Worker NodeにアタッチするEBSボリューム
    ebs_configs = [
      {
        size = "5"
        type = "gp3"
        tags = { Name = "worker1_volume1" }
      },
    ]
  }
  # 2つ目のWorker Nodeの設定
  {
    ...
    instance_type   = "t3a.small" # (1-2)
    ...
    root_block_device_config = { # (1-3)
      volume_size = 20
      volume_type = "gp3"
    }
    ebs_configs = [ # (1-4)
      {
        size = "5"
        type = "gp3"
        tags = { Name = "worker2_volume1" }
      },
    ]
  }
  # 3つ目のWorker Nodeの設定
  {
    ami_owners      = ["099720109477"]
    ami_name_values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
    instance_type   = "t3a.small" # (1-2)
    instance_name     = "k8s_worker3"
    private_ip        = "10.0.0.7"
    ssh_pubkey        = "./files/sshkey.pub"
    aws_key_pair_name = "ssh_key_worker3"
    root_block_device_config = { # (1-3)
      volume_size = 20
      volume_type = "gp3"
    }
    ebs_configs = [ # (1-4)
      {
        size = "5"
        type = "gp3"
        tags = { Name = "worker3_volume1" }
      },
    ]
  }
]

...

ingress_rules_k8s_additional = {
  # (1-5) CNIとして使うCiliumが必要なポート/プロトコルを開ける設定
  vxlan = {
    from_port = 8472
    to_port   = 8472
    protocol  = "udp"
  }
  cilium_health = {
    from_port = 4240
    to_port   = 4240
    protocol  = "tcp"
  }
}

...
```

以下、各設定内容に関する解説です。

#### (1-1) AWSにアクセス可能なIAMユーザの認証情報を設定

```tf
  access_key = "AKxxxxx....."
  secret_key = "yyyyyzzzzz....."
```

AWSの各種リソースを作る権限を持ったユーザーを認証するための情報を入力します。環境によっては認証不要なケースもあるかもしれないので、その場合はコメントアウトしてください。

#### (1-2) インスタンスタイプは t3a.small （2vCPU, 2GiBメモリ） 以上のスペックにする

```tf
    instance_type   = "t3a.small"
```

Rook Cephを作るのに、（本当はもっと下げたいところですが）どうしてもこれぐらいのスペックが最低限必要そうです。
Control Planeに関しては2GiB以上のメモリがないと``kubeadm init``でk8sクラスターを作るのに失敗してしまいます。
またWorker Nodeはこれよりもスペックの低い ``t3a.nano``（2vCPU, 1GiBメモリ）でも試してみましたが、Rook Ceph関連のリソースをインストールしている途中でスペックが足りないためか ``NotReady`` になってしまい上手く作れませんでした。

#### (1-3) Worker Nodeのルートボリュームの設定

```tf
    root_block_device_config = {
      volume_size = 20
      volume_type = "gp3"
    }
```

EC2のルートボリュームのデフォルトのサイズは8GBのようで、Rook Cephを作るにはこの容量では足りませんでした。そこで、サイズを20GBに変更しています。実際にRook Cephを作ってみると、ルートボリュームを10GB近く使うのを確認できます。

#### (1-4) Worker NodeにアタッチするEBSボリュームを追加

```tf
    ebs_configs = [
      {
        size = "5"
        type = "gp3"
        tags = { Name = "worker1_volume1" }
      },
      ...
```

Rook Cephのストレージとして使うボリュームです。EC2のOSが入っているルートボリュームはフォーマット済で、Cephのストレージとしては使えません。そこで別のEBSボリュームを用意してRook Cephのオブジェクトをそこに格納できるようにします。
Rook Cephのストレージとして使うボリュームのサイズは5GB以上が必要のようで、最小の5GBに設定しています。

#### (1-5) CNIとして使うCiliumが必要なポート/プロトコルを開ける設定

```tf
ingress_rules_k8s_additional = {
  vxlan = {
    from_port = 8472
    to_port   = 8472
    protocol  = "udp"
  }
  cilium_health = {
    from_port = 4240
    to_port   = 4240
    protocol  = "tcp"
  }
}
```

本記事ではCNI(Container Network Interface)としてCiliumを使うため、8472/udp と 4240/tcp のポート/プロトコルを設定しています。他のCNIを使う場合はそれに適した設定に変更してください。

### AWSリソースの作成

準備ができたら、``terraform apply``でAWSリソースを作成します。
Control PlaneおよびWorkerノードにはパブリックIPアドレスが付与され、``terraform apply``実行後の標準出力で最後にoutputとして出力されてます。

問題なくapplyできた場合は、以下のようなAWSリソースが出来ていることを確認できます。

#### EC2インスタンス
実行コマンド
```
aws ec2 describe-instances \
--query "Reservations[*].Instances[*].[Tags[?Key=='Name'].Value | [0],InstanceId,InstanceType,PublicIpAddress,State.Name]" \
--output table
```
出力結果
```
---------------------------------------------------------------------------------------
|                                   DescribeInstances                                 |
+-------------------+-----------------------+------------+-----------------+----------+
|  k8s_control_plane|  i-xxxxxxxxxxxxxxxxx  |  t3a.small |  aa.bbb.ccc.ddd | running  |
|  k8s_worker1      |  i-yyyyyyyyyyyyyyyyy  |  t3a.small |  ee.fff.ggg.hhh | running  |
|  k8s_worker2      |  i-zzzzzzzzzzzzzzzzz  |  t3a.small |  ii.jjj.kkk.lll | running  |
|  k8s_worker3      |  i-aaaaaaaaaaaaaaaaa  |  t3a.small |  mm.nnn.ooo.ppp | running  |
+-------------------+-----------------------+------------+-----------------+----------+
```

#### EBSボリューム
実行コマンド
```
aws ec2 describe-volumes \
--query "Volumes[*].{Name:Tags[?Key=='Name'].Value | [0], VolumeId:VolumeId, VolumeType:VolumeType, Size:Size}" \
--output table
```
出力結果
```
--------------------------------------------------------------------
|                          DescribeVolumes                         |
+------------------+-------+-------------------------+-------------+
|       Name       | Size  |        VolumeId         | VolumeType  |
+------------------+-------+-------------------------+-------------+
|  None            |  8    |  vol-aaaaaaaaaaaaaaaaa  |  gp2        |
|  None            |  20   |  vol-bbbbbbbbbbbbbbbbb  |  gp3        |
|  None            |  20   |  vol-ccccccccccccccccc  |  gp3        |
|  None            |  20   |  vol-ddddddddddddddddd  |  gp3        |
|  worker1_volume1 |  5    |  vol-eeeeeeeeeeeeeeeee  |  gp3        |
|  worker2_volume1 |  5    |  vol-fffffffffffffffff  |  gp3        |
|  worker3_volume1 |  5    |  vol-ggggggggggggggggg  |  gp3        |
+------------------+-------+-------------------------+-------------+
```
名前がNoneになっているリソースは、各EC2インスタンスのルートボリュームに相当します。

## (2) Kubernetesクラスターの構築

作成したEC2インスタンスにKubernetesクラスターを構築します。こちらも自作になりますが、以下のAnsible Playbookで作成することができます。

https://github.com/showchan33/k8s-setup

[README](https://github.com/showchan33/k8s-setup/blob/main/README.ja.md)に従い、SSH接続用の鍵の設定、Control PlaneおよびWorker NodeのEC2インスタンスのホスト名（パブリックIPアドレス）を指定したら、以下のコマンドを順番に実行します。

**Control Planeの構築**
```
ansible-playbook site_control-plane.yml
```
**Worker Nodeの構築**
```
ansible-playbook site_worker.yml
```

各Ansible Playbookが正常終了した後、Control PlaneのEC2インスタンスにSSH接続してkubectlコマンドを実行すると、次のようにKubernetesクラスターが作られているのを確認できるはずです。

```
$ kubectl get nodes
NAME          STATUS     ROLES           AGE     VERSION
ip-10-0-0-4   NotReady   control-plane   8m40s   v1.29.6
ip-10-0-0-5   NotReady   <none>          90s     v1.29.6
ip-10-0-0-6   NotReady   <none>          90s     v1.29.6
ip-10-0-0-7   NotReady   <none>          90s     v1.29.6
```

```
$ kubectl get pod -A
NAMESPACE     NAME                                  READY   STATUS    RESTARTS   AGE
kube-system   coredns-76f75df574-s5222              0/1     Pending   0          10m
kube-system   coredns-76f75df574-xbskj              0/1     Pending   0          10m
kube-system   etcd-ip-10-0-0-4                      1/1     Running   0          10m
kube-system   kube-apiserver-ip-10-0-0-4            1/1     Running   0          10m
kube-system   kube-controller-manager-ip-10-0-0-4   1/1     Running   0          10m
kube-system   kube-proxy-hgzqt                      1/1     Running   0          3m27s
kube-system   kube-proxy-w44qp                      1/1     Running   0          10m
kube-system   kube-proxy-qmdn4                      1/1     Running   0          3m27s
kube-system   kube-proxy-xl7sb                      1/1     Running   0          3m27s
kube-system   kube-scheduler-ip-10-0-0-4            1/1     Running   0          10m
```

### 補足 : kubectlを実行するクライアントの変更方法について

Control PlaneのEC2インスタンスに毎回SSH接続してkubectlコマンドを実行したり、Helmを使える状態にしたりするのは結構面倒です。そこで以下記事の手順を実行すれば、kubectlを実行するクライアントを変更できます。

https://qiita.com/showchan33/items/02e4a5f02b08c08d7813#kubectl%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%81%AE%E5%A4%89%E6%9B%B4%E6%96%B9%E6%B3%95%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%AA%AD%E3%81%BF%E9%A3%9B%E3%81%B0%E3%81%97%E3%81%A6%E3%82%82ok

## (3) CNIの構築

今回はCNIにCiliumを使います。Ciliumの構築に関しては以下の公式サイト等をご参照ください。

https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/#install-the-cilium-cli

ご参考ですが、私自身もHelmを使ってCiliumを構築する記事を以下に書いています。

https://qiita.com/showchan33/items/f336d46af383d4c746d4

一つ注意点ですが、EC2インスタンスに使っていたサブネット``10.0.0.0/27``はCiliumのデフォルトのPod CIDR ``10.0.0.0/8`` と重なってしまっています。そこでHelmチャートの以下のvalueを変更して、PodのCIDRがEC2インスタンスのIPと重複しないようにする必要があります。

```yaml
ipam:
  operator:
    clusterPoolIPv4PodCIDRList: ["192.168.0.0/16"] # CIRDはあくまで一例です
```

### デプロイ状況の確認

ここまでで順調に設定できていれば、ノードやPodが以下のような状態になっているはずです。

```
$ kubectl get nodes
NAME          STATUS   ROLES           AGE   VERSION
ip-10-0-0-4   Ready    control-plane   20m   v1.29.6
ip-10-0-0-5   Ready    <none>          12m   v1.29.6
ip-10-0-0-6   Ready    <none>          12m   v1.29.6
ip-10-0-0-7   Ready    <none>          12m   v1.29.6
```

```
$ kubectl get pod -A -o custom-columns='NAMESPACE:metadata.namespace,NAME:metadata.name,READY:status.containerStatuses[*].ready,STATUS:status.phase,NODE:spec.nodeName'
NAMESPACE     NAME                                  READY       STATUS    NODE
kube-system   cilium-dgm9k                          true        Running   ip-10-0-0-5
kube-system   cilium-lwd8f                          true        Running   ip-10-0-0-4
kube-system   cilium-operator-f45f4975f-ldf7g       true        Running   ip-10-0-0-5
kube-system   cilium-operator-f45f4975f-wklmd       true        Running   ip-10-0-0-6
kube-system   cilium-qtxvc                          true        Running   ip-10-0-0-7
kube-system   cilium-w9f82                          true        Running   ip-10-0-0-6
kube-system   coredns-76f75df574-s5222              true        Running   ip-10-0-0-4
kube-system   coredns-76f75df574-xbskj              true        Running   ip-10-0-0-4
kube-system   etcd-ip-10-0-0-4                      true        Running   ip-10-0-0-4
kube-system   hubble-relay-84849f9dd5-mntdv         true        Running   ip-10-0-0-6
kube-system   hubble-ui-79b7f9f4b-gh5zg             true,true   Running   ip-10-0-0-6
kube-system   kube-apiserver-ip-10-0-0-4            true        Running   ip-10-0-0-4
kube-system   kube-controller-manager-ip-10-0-0-4   true        Running   ip-10-0-0-4
kube-system   kube-proxy-hgzqt                      true        Running   ip-10-0-0-5
kube-system   kube-proxy-qmdn4                      true        Running   ip-10-0-0-7
kube-system   kube-proxy-w44qp                      true        Running   ip-10-0-0-4
kube-system   kube-proxy-xl7sb                      true        Running   ip-10-0-0-6
kube-system   kube-scheduler-ip-10-0-0-4            true        Running   ip-10-0-0-4
```

## (4) Rook Cephの構築

ようやくRook Cephを作れる状態になりました。

### Rook CephのCRD および rook-ceph-operator のインストール

まずはRook Cephのクラスターの作るための基盤となるCRD(CustomResourceDefinition)リソースをインストールし、``rook-ceph-operator``をデプロイします。

以下のようなHelmfileを用意します。 

```yaml:helmfile-rook-ceph.yaml
repositories:
- name: rook-release
  url: https://charts.rook.io/release

releases:
- name: rook-ceph
  namespace: rook-ceph
  chart: rook-release/rook-ceph
  version: v1.14.6
  values:
  - csi:
      csiRBDProvisionerResource: |
        - name : csi-provisioner
          resource:
            requests:
              memory: 10Mi
              cpu: 10m
        - name : csi-resizer
          resource:
            requests:
              memory: 10Mi
              cpu: 10m
    （以後続く）
```

values以降は、低スペックなEC2インスタンス2台にRook Cephの全てのPodがデプロイするための調整に関する設定です。今回は、全てのコンテナに関して「メモリ10Mi, CPU10m」のrequestを設定し（実運用ではあり得ないと思いますがw）、無理やりにでもWorker NodeでPodが起動するようにしています。 全てのパラメータに関しては[rook-ceph/values.yaml(v1.14.6)](https://github.com/rook/rook/blob/v1.14.6/deploy/charts/rook-ceph/values.yaml#L222)をご参照ください。リンクの222行目から409行目までの全てのリソースに対して「メモリ10Mi, CPU10m」のrequestsの設定が要るので、多少根気の要る作業になります。

（25/01/04追記）上記と同じ事をやっているHelmfileをGitHubの以下に格納しました。よろしければご活用ください。

https://github.com/showchan33/rook-ceph-playground/blob/main/helmfiles/rook-ceph.yaml

Helmfileの準備ができたら、Rook CephのHelmチャートを以下のコマンドでデプロイします。

```
helmfile apply -f helmfile-rook-ceph.yaml
```

しばらくすると、次のようにrook-ceph-operatorのPodが作られているのを確認できるはずです。

```
$ kubectl get po -n rook-ceph
NAME                                  READY   STATUS    RESTARTS   AGE
rook-ceph-operator-689fcb6b9d-q2r75   1/1     Running   0          33s
```

### CephClusterのマニフェストを作成

以下のCephClusterリソースのマニフェスト ``cluster.yaml`` の内容を、Podのスペックの調整等を行うために一部変更します。

https://github.com/rook/rook/blob/v1.14.6/deploy/examples/cluster.yaml

変更箇所は以下の2つです。

#### 各リソースのrequestsを変更

219行目の ``resources:`` 以降に以下の設定を追加して、低スペックのEC2インスタンスでもmon,mgr,osd等を起動できるようにします。

```diff:cluster.yamlの一部
@@ -217,6 +218,22 @@
   #   monitoring:
   #   crashcollector:
   resources:
+    mon:
+      requests:
+        cpu: 50m
+        memory: 32Mi
+    mgr:
+      requests:
+        cpu: 50m
+        memory: 32Mi
+    osd:
+      requests:
+        cpu: 50m
+        memory: 32Mi
+    crashcollector:
+      requests:
+        cpu: 50m
+        memory: 32Mi
   #The requests and limits set here, allow the mgr pod to use half of one CPU core and 1 gigabyte of memory
   #   mgr:
   #     limits:
```

#### ストレージのconfigを変更

Cephのストレージとして使うディスクが5GBのため、255行目のコメントを外して ``databaseSizeMB``の値を1024に指定します。
256行目の``osdsPerDevice``は変更しなくてもよかったかもしれませんが、念のため「1デバイスあたり1OSD」を明示的に指定したくてコメントを外しています。

```diff:cluster.yamlの一部
@@ -252,12 +269,20 @@
     config:
       # crushRoot: "custom-root" # specify a non-default root label for the CRUSH map
       # metadataDevice: "md0" # specify a non-rotational storage so ceph-volume will use it as block db device of bluestore.
-      # databaseSizeMB: "1024" # uncomment if the disks are smaller than 100 GB
-      # osdsPerDevice: "1" # this value can be overridden at the node or device level
+      databaseSizeMB: "1024" # uncomment if the disks are smaller than 100 GB
+      osdsPerDevice: "1" # this value can be overridden at the node or device level
       # encryptedDevice: "true" # the default value for this option is "false"
     # Individual nodes and their config can be specified as well, but 'useAllNodes' above must be set to false. Then, only the named
     # nodes below will be used as storage resources.  Each node's 'name' field should match their 'kubernetes.io/hostname' label.
```

### CephClusterのデプロイ

``cluster.yaml``を変更したら、CephClusterリソースをデプロイします。

```
kubectl apply -f cluster.yaml
```

3,4分程度待つと、以下のようにRook Cephのリソースがデプロイされます。

```
$ kubectl get deploy -n rook-ceph
NAME                                   READY   UP-TO-DATE   AVAILABLE   AGE
csi-cephfsplugin-provisioner           2/2     2            2           3m18s
csi-rbdplugin-provisioner              2/2     2            2           3m18s
rook-ceph-crashcollector-ip-10-0-0-5   1/1     1            1           3m1s
rook-ceph-crashcollector-ip-10-0-0-6   1/1     1            1           2m46s
rook-ceph-crashcollector-ip-10-0-0-7   1/1     1            1           3m1s
rook-ceph-exporter-ip-10-0-0-5         1/1     1            1           3m1s
rook-ceph-exporter-ip-10-0-0-6         1/1     1            1           2m46s
rook-ceph-exporter-ip-10-0-0-7         1/1     1            1           3m1s
rook-ceph-mgr-a                        1/1     1            1           3m1s
rook-ceph-mgr-b                        1/1     1            1           3m1s
rook-ceph-mon-a                        1/1     1            1           4m45s
rook-ceph-mon-b                        1/1     1            1           3m30s
rook-ceph-mon-c                        1/1     1            1           3m16s
rook-ceph-operator                     1/1     1            1           24h
rook-ceph-osd-0                        1/1     1            1           2m25s
rook-ceph-osd-1                        1/1     1            1           2m20s
rook-ceph-osd-2                        1/1     1            1           2m18s
rook-ceph-tools                        1/1     1            1           61s

$ kubectl get ds -n rook-ceph
NAME               DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
csi-cephfsplugin   3         3         3       3            3           <none>          3m23s
csi-rbdplugin      3         3         3       3            3           <none>          3m23s
```

### 参考：CephClusterを作り直したい場合の対応方法

何かしらの設定が間違っている、EC2インスタンスのスペックが足りない等の原因で全てのリソースがデプロイできない場合もあるかもしれません。その場合、次の(1)~(3)のコマンドを実行することでCephクラスターのリセットが可能です。

※場合によっては(1)のみでよかったり、(1)(2)のみでよかったりすることもあります。

#### (1) Cephクラスターの削除

```
kubectl delete -f cluster.yaml
```

#### (2) Cephのconfigディレクトリの削除 （各Worker Nodeで実行が必要）

```
sudo rm -rf /var/lib/rook/*
```

#### (3) Cephが使っているデバイスの中身を削除 （各Worker Nodeで実行が必要）

```
sudo sgdisk --zap-all /dev/[device-name]
```

上記コマンドは、OSDが一回作成された後に再度作り直す場合に必要となります。``[device-name]``については今回のケースだと、Worker Node上で以下のようなコマンドを実行することで確認可能です。

```
$ lsblk | grep " 5G "
nvme1n1      259:1    0    5G  0 disk
```

#### リセットする際の参考情報

Rook Cephの公式サイトも併せて参照いただければより確実です。

https://rook.io/docs/rook/latest/Getting-Started/ceph-teardown/

### rook-ceph-toolsのデプロイ

rook-ceph-toolsのDeploymentをデプロイすることにより、Cephの状態を見たり設定変更等の操作ができるようになります。[こちら](https://github.com/rook/rook/blob/v1.14.6/deploy/examples/toolbox.yaml)の ``toolbox.yaml`` のソースをダウンロードし、次のコマンドでデプロイします。

```
kubectl apply -f toolbox.yaml
```

デプロイ後、``rook-ceph-tools``のコンテナから以下のように``ceph``コマンド等を使ってCephクラスターの状態を確認できるようになります。

```
$ kubectl exec -it -n rook-ceph deploy/rook-ceph-tools -- sh
```

以下はコンテナ内で実行可能なコマンド例です。

```
sh-4.4$ ceph status
  cluster:
    id:     f2dfe9e4-50e7-4cdb-84db-efe5be488336
    health: HEALTH_OK

  services:
    mon: 3 daemons, quorum a,b,c (age 4m)
    mgr: a(active, since 57s), standbys: b
    osd: 3 osds: 3 up (since 2m), 3 in (since 3m)

  data:
    pools:   1 pools, 1 pgs
    objects: 2 objects, 577 KiB
    usage:   81 MiB used, 15 GiB / 15 GiB avail
    pgs:     1 active+clean
```

```
sh-4.4$ ceph pg ls | awk '{ print $1 " " $11 " " $15 " " $16 }' | column -t
PG   STATE         UP          ACTING
1.0  active+clean  [1,2,0]p1   [1,2,0]p1
...
```

```
sh-4.4$ ceph osd df
ID  CLASS  WEIGHT   REWEIGHT  SIZE    RAW USE  DATA     OMAP  META    AVAIL    %USE  VAR   PGS  STATUS
 2    ssd  0.00490   1.00000   5 GiB   27 MiB  780 KiB   0 B  26 MiB  5.0 GiB  0.53  1.00    1      up
 0    ssd  0.00490   1.00000   5 GiB   27 MiB  780 KiB   0 B  26 MiB  5.0 GiB  0.53  1.00    1      up
 1    ssd  0.00490   1.00000   5 GiB   27 MiB  780 KiB   0 B  26 MiB  5.0 GiB  0.53  1.00    1      up
                       TOTAL  15 GiB   81 MiB  2.3 MiB   0 B  79 MiB   15 GiB  0.53
MIN/MAX VAR: 1.00/1.00  STDDEV: 0
```

```
sh-4.4$ ceph osd tree
ID  CLASS  WEIGHT   TYPE NAME             STATUS  REWEIGHT  PRI-AFF
-1         0.01469  root default
-7         0.00490      host ip-10-0-0-5
 2    ssd  0.00490          osd.2             up   1.00000  1.00000
-3         0.00490      host ip-10-0-0-6
 0    ssd  0.00490          osd.0             up   1.00000  1.00000
-5         0.00490      host ip-10-0-0-7
 1    ssd  0.00490          osd.1             up   1.00000  1.00000
```

# おわりに

長かったですが、ようやくRook Cephのクラスターを構築できました。
これで「極力お金を使わずに」「既存マシンやストレージにも手を加えずに」「EC2で閉じた環境で」Rook Cephを自由に弄れるはずです！

# おまけ

CNIをCiliumで構築したので、Hubble UIでRook Cephがどんな通信をしているのかを可視化してみました。

![screenshot 485.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/370519/151c8a60-0a1d-9468-8684-fb04a643ecb4.png)

右側のポート3300で空いているの3つのリソースがMONで、同じく右側にあるポート6800はMGRのようです。そして左側でポート6804,6806等で待ち受けている3つの箱はOSDということがわかりました。