Longhorn + WordPress on GKE

![o2wj9tkjmbankhwst8dhk5f3xws.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/45c5dd45-1de7-cc7a-bf40-c25069a15f23.png)

2019.10.29にCNCF(Cloud Native Computing Foundation)のSandbox ProjectとなったLonghornについて見ていきたいと思います。

# 1.What's Longhorn ?

![project-longhorn-logo-blue-1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/47796ab3-10be-a273-1ff9-adb64fd61c8e.png)

Longhornは、RancherLabs社が開発したOpenEBSベースのKubernetes用の分散ブロックストレージシステムです。ストレージ種別としては、ブロックストレージです。2017年のDockerCon時に発表されて2019年10月末に、CNCFのSandbox Projectになりました。

2019年12月時点での最新バージョンはv0.7.0となります。

特徴としては、以下となります。

- 単一障害点のないエンタープライズグレード？の分散ストレージ
- ブロックストレージの増分スナップショット
- 効率的な変更ブロック検出に基づいて構築されたセカンダリストレージ（NFSまたはS3互換オブジェクトストレージ）へのバックアップ
- 繰り返しのスナップショットとバックアップ
- 自動化された無停止アップグレード。実行中のボリュームを中断することなく、Longhornソフトウェアスタック全体をアップグレード可能
- 直感的なGUIダッシュボード

現時点では、アルファ品質のソフトウェアのためフィードバック等を求めています。

インストール条件としては、以下となります。

- Docker v1.13以降
- Kubernetes v1.14以降
- open-iscsiは、Kubernetesクラスターのすべてのノードにインストールが必要
- GKEの場合、open-iscsiがすでに含まれているため、ゲストOSイメージとしてUbuntuを推奨
- Debian / Ubuntuの場合、apt-get install open-iscsiを使用してインストール
- RHEL / CentOSの場合、yum install iscsi-initiator-utilsを使用してインストール
- ホストファイルシステムは、データを保存するためにノードでファイルエクステント機能をサポート、現在は以下をサポート
    - ext4
    - XFS

インストール方法は、kubectl、Helm、Rancherカタログで行うことができます。

Longhornの詳細についてはこちらの資料をご参考いただければ幸いです。

- [CNCF Sandbox Project Longhorn](https://speakerdeck.com/cyberblack28/cncf-sandbox-project-longhorn)

こちらの資料の「Longhorn + WordPress on GKE」のハンズオンテキストは以下となります。

# 2.Longhorn + WordPress on GKE

GKEを利用して、実際にLonghornとアプリケーション（WordPress）を連携してみます。

## GKE Prepare

GKE上にLonghorn環境を構築して、WordPressをデプロイしてみます。GKEにLonghornをインストールする場合には、ノードで利用するOSがGKEデフォルトの「コンテナ最適OS」ではなく、「Ubuntu OS」にする必要があります。

GKEのプロビジョニング時に変更します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/35615d74-bc6e-b365-6541-a244a498674e.png)

クラスタ構築完了後に、RBACを有効にするために、Longhornをインストールする前に次のコマンドを実行します。

<name@example.com>は、Googleアカウントのメールアドレスを入力します。

```command:コマンド
$ kubectl create clusterrolebinding cluster-admin-binding --clusterrole=cluster-admin --user=<name@example.com>
clusterrolebinding.rbac.authorization.k8s.io/cluster-admin-binding created
```

## Longhorn Install

KubernetesクラスターにLonghornをインストールします。

### kubectlの場合

```command:コマンド
$ kubectl apply -f https://raw.githubusercontent.com/longhorn/longhorn/master/deploy/longhorn.yaml
namespace/longhorn-system created
serviceaccount/longhorn-service-account created
clusterrole.rbac.authorization.k8s.io/longhorn-role created
clusterrolebinding.rbac.authorization.k8s.io/longhorn-bind created
customresourcedefinition.apiextensions.k8s.io/engines.longhorn.rancher.io created
customresourcedefinition.apiextensions.k8s.io/replicas.longhorn.rancher.io created
customresourcedefinition.apiextensions.k8s.io/settings.longhorn.rancher.io created
customresourcedefinition.apiextensions.k8s.io/volumes.longhorn.rancher.io created
customresourcedefinition.apiextensions.k8s.io/engineimages.longhorn.rancher.io created
customresourcedefinition.apiextensions.k8s.io/nodes.longhorn.rancher.io created
customresourcedefinition.apiextensions.k8s.io/instancemanagers.longhorn.rancher.io created
configmap/longhorn-default-setting created
daemonset.apps/longhorn-manager created
service/longhorn-backend created
deployment.apps/longhorn-ui created
service/longhorn-frontend created
deployment.apps/longhorn-driver-deployer created
```

Longhornが、デプロイされるNamespaceは、longhorn-systemになります。確認してみます。

```command:コマンド
$ kubectl -n longhorn-system get pod
NAME                                        READY   STATUS    RESTARTS   AGE
csi-attacher-9df5cfb99-7qjt2                1/1     Running   0          13m
csi-attacher-9df5cfb99-d246j                1/1     Running   0          13m
csi-attacher-9df5cfb99-wq47z                1/1     Running   0          13m
csi-provisioner-5bd8d7cb74-bs9kz            1/1     Running   0          13m
csi-provisioner-5bd8d7cb74-vwrxs            1/1     Running   0          13m
csi-provisioner-5bd8d7cb74-xspb5            1/1     Running   0          13m
engine-image-ei-3827e67c-fhz8w              1/1     Running   0          15m
engine-image-ei-3827e67c-q7tpj              1/1     Running   0          15m
engine-image-ei-3827e67c-zwq8v              1/1     Running   0          15m
instance-manager-e-52b53b48                 1/1     Running   0          13m
instance-manager-e-bd7d7bb1                 1/1     Running   0          13m
instance-manager-e-fe19c451                 1/1     Running   0          13m
instance-manager-r-346984ea                 1/1     Running   0          13m
instance-manager-r-78d20189                 1/1     Running   0          13m
instance-manager-r-aa6cd210                 1/1     Running   0          13m
longhorn-csi-plugin-4rhb7                   2/2     Running   0          13m
longhorn-csi-plugin-5nb42                   2/2     Running   0          13m
longhorn-csi-plugin-v7pwc                   2/2     Running   0          13m
longhorn-driver-deployer-647688999c-db8rm   1/1     Running   0          15m
longhorn-manager-cqdtn                      1/1     Running   0          15m
longhorn-manager-vt2g2                      1/1     Running   0          15m
longhorn-manager-zwwsm                      1/1     Running   1          15m
longhorn-ui-76dfddf9cd-ktk82                1/1     Running   0          15m
```

Longhornダッシュボードにもアクセスしてみます。

longhorn-frontendというServiceがLoadBalancerとなっているので、EXTERNAL-IPを確認します。

```command:コマンド
$ kubectl -n longhorn-system get svc
NAME                TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)        AGE
csi-attacher        ClusterIP      10.12.1.41    <none>          12345/TCP      18m
csi-provisioner     ClusterIP      10.12.2.61    <none>          12345/TCP      18m
longhorn-backend    ClusterIP      10.12.9.174   <none>          9500/TCP       20m
longhorn-frontend   LoadBalancer   10.12.6.218   34.69.255.105   80:30063/TCP   20m
```

表示されたEXTERNAL-IPをブラウザで開きます。

![screencapture-34-69-255-105-dashboard-2019-11-09-15_11_53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/294750bc-0e2c-70e6-9faa-beb60bd6c9cc.png)

### helmの場合

Helmのインストールは[こちら](https://helm.sh/docs/using_helm/#installing-helm)を参照。
Helmインストール完了後、以下コマンドを実行します。

```command:コマンド
$ git clone https://github.com/longhorn/longhorn.git
Cloning into 'longhorn'...
remote: Enumerating objects: 71, done.
remote: Counting objects: 100% (71/71), done.
remote: Compressing objects: 100% (56/56), done.
remote: Total 1220 (delta 29), reused 24 (delta 11), pack-reused 1149
Receiving objects: 100% (1220/1220), 631.08 KiB | 832.00 KiB/s, done.
Resolving deltas: 100% (677/677), done.
```

HelmからLonghornをインストールします。

```command:コマンド
$ helm install ./longhorn/chart --name longhorn --namespace longhorn-system
NAME:   longhorn
LAST DEPLOYED: Sun Nov 10 00:03:31 2019
NAMESPACE: longhorn-system
STATUS: DEPLOYED

RESOURCES:
==> v1/ClusterRole
NAME           AGE
longhorn-role  1s

==> v1/ClusterRoleBinding
NAME           AGE
longhorn-bind  1s

==> v1/ConfigMap
NAME                      AGE
longhorn-default-setting  1s

==> v1/DaemonSet
NAME              AGE
longhorn-manager  1s

==> v1/Deployment
NAME                      AGE
longhorn-driver-deployer  1s
longhorn-ui               1s

==> v1/Pod(related)
NAME                                       AGE
longhorn-driver-deployer-7488ccf89b-xtcbp  0s
longhorn-manager-4fxh9                     0s
longhorn-manager-4nwf6                     0s
longhorn-manager-hblbw                     0s
longhorn-ui-5b58f6d79d-jvp2w               0s

==> v1/Service
NAME               AGE
longhorn-backend   1s
longhorn-frontend  1s

==> v1/ServiceAccount
NAME                      AGE
longhorn-service-account  1s

==> v1/StorageClass
NAME      AGE
longhorn  1s

==> v1beta1/CustomResourceDefinition
NAME                                  AGE
engineimages.longhorn.rancher.io      1s
engines.longhorn.rancher.io           1s
instancemanagers.longhorn.rancher.io  1s
nodes.longhorn.rancher.io             1s
replicas.longhorn.rancher.io          1s
settings.longhorn.rancher.io          1s
volumes.longhorn.rancher.io           1s


NOTES:
1. Get the application URL by running these commands:
  kubectl get po -n $release_namespace
```

Longhornが、デプロイされるNamespaceは、longhorn-systemになります。確認してみます。

```command:コマンド
$ kubectl -n longhorn-system get pod
NAME                                        READY   STATUS    RESTARTS   AGE
csi-attacher-9df5cfb99-4qbjj                1/1     Running   0          12m
csi-attacher-9df5cfb99-bxgqr                1/1     Running   0          12m
csi-attacher-9df5cfb99-mqzmd                1/1     Running   0          12m
csi-provisioner-5bd8d7cb74-2mdt5            1/1     Running   0          12m
csi-provisioner-5bd8d7cb74-5k7cl            1/1     Running   0          12m
csi-provisioner-5bd8d7cb74-vwzkr            1/1     Running   0          12m
engine-image-ei-3827e67c-mk9bn              1/1     Running   0          14m
engine-image-ei-3827e67c-ncz4v              1/1     Running   0          14m
engine-image-ei-3827e67c-xj4ww              1/1     Running   0          14m
instance-manager-e-14a0c272                 1/1     Running   0          13m
instance-manager-e-6a1c06ef                 1/1     Running   0          12m
instance-manager-e-e2299b26                 1/1     Running   0          13m
instance-manager-r-19f5f4c6                 1/1     Running   0          13m
instance-manager-r-53a1e7d8                 1/1     Running   0          13m
instance-manager-r-90b860f6                 1/1     Running   0          12m
longhorn-csi-plugin-b6rb8                   2/2     Running   0          12m
longhorn-csi-plugin-cqnk5                   2/2     Running   0          12m
longhorn-csi-plugin-wgnsv                   2/2     Running   0          12m
longhorn-driver-deployer-7488ccf89b-xtcbp   1/1     Running   0          14m
longhorn-manager-4fxh9                      1/1     Running   1          14m
longhorn-manager-4nwf6                      1/1     Running   0          14m
longhorn-manager-hblbw                      1/1     Running   0          14m
longhorn-ui-5b58f6d79d-jvp2w                1/1     Running   0          14m
```

Longhornダッシュボードにもアクセスしてみます。

longhorn-frontendというServiceがLoadBalancerとなっているので、EXTERNAL-IPを確認します。

```command:コマンド
$ kubectl -n longhorn-system get svc
NAME                TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
csi-attacher        ClusterIP      10.0.1.11    <none>        12345/TCP      16m
csi-provisioner     ClusterIP      10.0.0.46    <none>        12345/TCP      16m
longhorn-backend    ClusterIP      10.0.11.53   <none>        9500/TCP       17m
longhorn-frontend   LoadBalancer   10.0.1.213   34.84.3.65    80:30016/TCP   17m
```

表示されたEXTERNAL-IPをブラウザで開きます。

![screencapture-34-84-3-65-dashboard-2019-11-10-00_22_51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/9f0dc0ff-f29f-e88a-0d05-ed2d13591414.png)

### Rancherの場合

GKEのクラスタをRancherにインポートした状態からの手順です。

「Catalog」からLONGHORNを検索して、クリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/30739f13-a9a3-fc84-bdb3-60b57922273b.png)

「0.6.2」を選択します。2019年11月時点では、GKEのkubernetesバージョンにLonghorn v0.7.0 が対応していないため v0.6.2 を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/4d5e6116-c62c-edd7-3efc-3880b8b9ba36.png)

「L4 Balancer」を選択して、「Launch」ボタンをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/a6bb8eb3-e0ed-debb-eeba-a9631fd2b43f.png)

「80/tcp」をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/daba9bea-4f3e-2045-17f2-20c33adfab5f.png)

UIが表示されることを確認します。

![screencapture-34-84-156-24-dashboard-2019-11-17-00_27_44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/f4362f83-6eb8-d140-4a71-ca7a68becd6e.png)


## Create Longhorn Volume

Kubernetesボリュームを作成する前に、まずストレージクラスを作成する必要があります。longhornというStorageClassを作成します。

```command:コマンド
$ kubectl create -f https://raw.githubusercontent.com/longhorn/longhorn/master/examples/storageclass.yaml
storageclass.storage.k8s.io/longhorn created
```

storageclass.yamlの内容は以下となります。

```command:storageclass.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: longhorn
provisioner: rancher.io/longhorn
parameters:
  numberOfReplicas: "3"
  staleReplicaTimeout: "2880"
  fromBackup: ""
#  diskSelector: "ssd,fast"
#  nodeSelector: "storage,fast"
#  recurringJobs: '[{"name":"snap", "task":"snapshot", "cron":"*/1 * * * *", "retain":1},
#                   {"name":"backup", "task":"backup", "cron":"*/2 * * * *", "retain":1,
#                    "labels": {"interval":"2m"}}]'
```

Longhorn StorageClassを利用したPVCを作成します。

```command:コマンド
$ vim longhorn-pvc.yaml
-------------------------------------------------------------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: longhorn-volv-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 10Gi
-------------------------------------------------------------------
Esc + :wq
```

```command:コマンド
$ kubectl create -f longhorn-pvc.yaml
persistentvolumeclaim/longhorn-volv-pvc created
```

この後WordPressではWordPressとMySQLそれぞれで利用できるようもう一つ作成します。

```command:コマンド
$ vim longhorn-pvc2.yaml
-------------------------------------------------------------------
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: longhorn-volv-pvc2
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: longhorn
  resources:
    requests:
      storage: 10Gi
-------------------------------------------------------------------
Esc + :wq
```

```command:コマンド
$ kubectl create -f longhorn-pvc2.yaml
persistentvolumeclaim/longhorn-volv-pvc2 created
```

pvとpvcの状況を確認します。

```command:コマンド
$ kubectl get pv,pvc
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                        STORAGECLASS   REASON   AGE
persistentvolume/pvc-d2eaf832-02bc-11ea-b04c-42010a800123   10Gi       RWO            Delete           Bound    default/longhorn-volv-pvc    longhorn                25m
persistentvolume/pvc-e089f32f-02bf-11ea-b04c-42010a800123   10Gi       RWO            Delete           Bound    default/longhorn-volv-pvc2   longhorn                3m25s

NAME                                       STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE
persistentvolumeclaim/longhorn-volv-pvc    Bound    pvc-d2eaf832-02bc-11ea-b04c-42010a800123   10Gi       RWO            longhorn       25m
persistentvolumeclaim/longhorn-volv-pvc2   Bound    pvc-e089f32f-02bf-11ea-b04c-42010a800123   10Gi       RWO            longhorn       3m27s
```

## WordPress Deploy

WordPressに必要となるSecretを作成します。

```command:コマンド
$ kubectl create secret generic mysql --from-literal=password=longhorn
secret/mysql created
```

MySQLのDeploymentを作成します。その際、作成したPVC「longhorn-volv-pvc」を指定します。

```command:コマンド
$ vim mysql.yml
-----------------------------------------------------
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - image: mysql:5.6
          name: mysql
          env:
            - name: MYSQL_ROOT_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mysql
                  key: password
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: mysql-local-storage
              mountPath: /var/lib/mysql
      volumes:
        - name: mysql-local-storage
          persistentVolumeClaim:
            claimName: longhorn-volv-pvc
-----------------------------------------------------
Esc + :wq
```

デプロイします。

```command:コマンド
$ kubectl create -f mysql.yml
deployment.extensions/mysql created
```

デプロイ状況を確認します。

```command:コマンド
$ kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
mysql-656cf9f974-5l4bs   1/1     Running   0          3m37s
```

WordPressのDeploymentを作成します。その際、作成したPVC「longhorn-volv-pvc2」を指定します。

```command:コマンド
$ vim wordpress.yml
-----------------------------------------------------
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  replicas: 1
  selector:
    matchLabels:
      app: wordpress
  template:
    metadata:
      labels:
        app: wordpress
    spec:
      containers:
        - image: wordpress
          name: wordpress
          env:
          - name: WORDPRESS_DB_HOST
            value: mysql:3306
          - name: WORDPRESS_DB_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysql
                key: password
          ports:
            - containerPort: 80
              name: wordpress
          volumeMounts:
            - name: wordpress-local-storage
              mountPath: /var/www/html
      volumes:
        - name: wordpress-local-storage
          persistentVolumeClaim:
            claimName: longhorn-volv-pvc2
-----------------------------------------------------
Esc + :wq
```

デプロイします。

```command:コマンド
$ kubectl create -f wordpress.yml
deployment.extensions/wordpress created
```

デプロイ状況を確認します。

```command:コマンド
$ kubectl get pods
NAME                         READY   STATUS    RESTARTS   AGE
mysql-656cf9f974-5l4bs       1/1     Running   0          15m
wordpress-7f4768b5cc-bxvcr   1/1     Running   0          44s
```

WordPressからMySQLに通信するためのServiceを作成します。

```command:コマンド
$ vim mysql-service.yml
-----------------------------------------------------
apiVersion: v1
kind: Service
metadata:
  name: mysql
  labels:
    app: mysql
spec:
  type: ClusterIP
  ports:
    - port: 3306
  selector:
    app: mysql
-----------------------------------------------------
Esc + :wq
```

```command:コマンド
$ kubectl create -f mysql-service.yml
service/mysql created
```

ブラウザからWordPressへアクセスするためのServiceを作成します。

```command:コマンド
$ vim wordpress-service.yml
-----------------------------------------------------
apiVersion: v1
kind: Service
metadata:
  labels:
    app: wordpress
  name: wordpress
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
  selector:
    app: wordpress
-----------------------------------------------------
Esc + :wq
```

```command:コマンド
$ kubectl create -f wordpress-service.yml
service/wordpress created
```

EXTERNAL-IPを確認して、ブラウザでアクセスしてみます。

```command:コマンド
$ kubectl get svc
NAME         TYPE           CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP      10.12.0.1     <none>        443/TCP        114m
mysql        ClusterIP      10.12.7.159   <none>        3306/TCP       3m37s
wordpress    LoadBalancer   10.12.12.30   34.70.77.28   80:31566/TCP   96s
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61606/1ff80adc-6473-b675-bf32-edf64ab14c7a.png)

# Documents

- [longhorn.io](https://longhorn.io/)
- [GitHub](https://github.com/longhorn/longhorn)
- [Longhorn Accepted into CNCF](https://rancher.com/blog/2019/longhorn-accepted-into-cncf/)
- [an open source project for microservices-based distributed block storage](https://rancher.com/blog/2017/announcing-longhorn-microservices-block-storage/)
- [Rancherのヤツ知ってる？いや、そっちじゃなくてLonghornのほう](https://speakerdeck.com/tsukaman/rancherfalseyatuzhi-tuteru-iya-sotutiziyanakutelonghornfalsehou)
- [CNCF Sandbox Project Longhorn](https://speakerdeck.com/cyberblack28/cncf-sandbox-project-longhorn)

# KubeCon + CloudNativeCon NA 2019

Rancher Labs Software Architect Sheng Yang、Longhornメイン開発者の登壇動画とスライド資料となります。

- [Intro to Longhorn: Open Source Cloud-Native Distributed Block Storage Built On and For K8s - Sheng Yang, Rancher Labs（PDF）](https://static.sched.com/hosted_files/kccncna19/8b/Longhorn-KubeCon-NA-ppt-2019.pdf)
- [Intro to Longhorn: Open Source Cloud-Native Distributed Block Storage Built On and For K8s - Sheng Yang, Rancher Labs（動画）](https://static.sched.com/hosted_files/kccnceu19/c0/Longhorn-KubeCon_EU_2019.pdf)