おうちkubernetesでデータを永続化する

# 目的
おうちでKubernetes運用していますか？
私はVPSを借りてKubernetesを運用しています。

しかし、kubernetesを運用していくなかで、なんどもOSを入れ直したり、インスタンスを作り直したりしたいが、アプリケーションのデータを失いたくない。そんなことはありませんか？

# 解決策
Google Driveに定期的に保存することを検討します。

# Rclone
https://rclone.org/

ローカルファイルをありとあらゆるSaaSに同期できるツール
これをサイドカーに入れて、定期的にデータをバックアップすることを考えます。

# Secret作成

Rcloneは、同期先の情報をconfigファイルとして与えることができます。
この中には認証情報なども含まれるためKubernetesのSecretリソースとして用意するのが良さそうです。

`rclone config` コマンドで対話形式でコンフィグファイルを作ることができます。
手元のマシンにrcloneをインストールしても良いのですが、今回はdockerでやってみました。

```
$ mkdir config
$ docker run --rm -v `pwd`/config:/config -t -i tynor88/rclone:latest rclone config --config=/config/.rclone.conf

[s6-init] making user provided files available at /var/run/s6/etc...exited 0.
[s6-init] ensuring user provided files have correct perms...exited 0.
[fix-attrs.d] applying ownership & permissions fixes...
[fix-attrs.d] done.
[cont-init.d] executing container initialization scripts...
[cont-init.d] 10-adduser: executing...

GID/UID
-------------------------------------
User uid:    911
User gid:    911
-------------------------------------

[cont-init.d] 10-adduser: exited 0.
[cont-init.d] 40-config: executing...
crontab => 0 * * * * /app/rclone.sh
chmod: /config/Rclone.sh: No such file or directory
[cont-init.d] 40-config: exited 0.
[cont-init.d] done.
[services.d] starting services
crond[199]: crond (busybox 1.24.2) started, log level 0
crond[199]: user:root entry:*/15	*	*	*	*	run-parts /etc/periodic/15min
crond[199]: user:root entry:0	*	*	*	*	run-parts /etc/periodic/hourly
crond[199]: user:root entry:0	2	*	*	*	run-parts /etc/periodic/daily
crond[199]: user:root entry:0	3	*	*	6	run-parts /etc/periodic/weekly
crond[199]: user:root entry:0	5	1	*	*	run-parts /etc/periodic/monthly
crond[199]: user:abc entry:0 * * * * /app/rclone.sh
[services.d] done.
No remotes found - make a new one
n) New remote
s) Set configuration password
q) Quit config
n/s/q> n
name> hoge
Type of storage to configure.
Choose a number from below, or type in your own value
 1 / Amazon Drive
   \ "amazon cloud drive"
 2 / Amazon S3 (also Dreamhost, Ceph, Minio)
   \ "s3"
 3 / Backblaze B2
   \ "b2"
 4 / Dropbox
   \ "dropbox"
 5 / Encrypt/Decrypt a remote
   \ "crypt"
 6 / Google Cloud Storage (this is not Google Drive)
   \ "google cloud storage"
 7 / Google Drive
   \ "drive"
 8 / Hubic
   \ "hubic"
 9 / Local Disk
   \ "local"
10 / Microsoft OneDrive
   \ "onedrive"
11 / Openstack Swift (Rackspace Cloud Files, Memset Memstore, OVH)
   \ "swift"
12 / Yandex Disk
   \ "yandex"
Storage> 7
Google Application Client Id - leave blank normally.
client_id>
Google Application Client Secret - leave blank normally.
client_secret>
Remote config
Use auto config?
 * Say Y if not sure
 * Say N if you are working on a remote or headless machine or Y didn't work
y) Yes
n) No
y/n> N
```

config/.rclone.conf が出来上がるので、kubernetesにsecretとしてデプロイします。

```
$ kubectl create secret generic rclone-config --from-file=./config/.rclone.conf
```

# Rclone定期実行
rcloneを定期実行するDockerイメージがあります。今回はこれを使います。
https://hub.docker.com/r/tynor88/rclone/

# デプロイ
今回は例として、gollum（gitをバックエンドとするwiki） をデプロイし、そのデータをGoogleDriveに同期してみます。

初回起動時は以前保存していたものを復元するために GoogleDrive -> Pod で同期を行います。
これはpod.spec.initContainersで実現しています。 ( https://kubernetes.io/docs/concepts/workloads/pods/init-containers/ )

起動中はgollumとは別にrcloneのコンテナを動作させて、定期的に Pod -> GoogleDrive で同期を行います。

図にするとこんな感じ。

<img style="border:solid;" src="https://qiita-image-store.s3.amazonaws.com/0/100375/9fe442ea-d31a-0a6e-5b33-40852e8f710e.png" width=50%>

以下がmanifest上記を実現するmanifestです。

```gollum-with-rclone.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    run: gollum
  name: gollum
  namespace: default
spec:
  selector:
    matchLabels:
      run: gollum
  template:
    metadata:
      labels:
        run: gollum
    spec:
      initContainers:  # gollumのために、git initを初めにしておく
      - name: init
        image: golang:latest # gitがあればなんでも良い
        command:
        - sh
        - -c
        - cd /root/wikidata && git init
        volumeMounts:
        - mountPath: /root/wikidata
          name: data
      - name: first-rclone
        image: tynor88/rclone:latest
        command:  # rclone初回実行 (GoogleDrive -> Pod)
        - sh
        - -c
        - /usr/bin/with-contenv sh /app/rclone.sh
        env:
        - name: SYNC_COMMAND
          value: rclone sync GoogleDrive:/gollum /data
        volumeMounts:
        - mountPath: /data
          name: data
        - mountPath: /root
          name: config
      containers:
      - name: gollum
        image: suttang/gollum:latest # 本命のgollumイメージ
        volumeMounts:
        - mountPath: /root/wikidata
          name: data
      - name: rclone
        image: tynor88/rclone:latest # 同期用のrclone (Pod -> GoogleDrive)
        imagePullPolicy: Always
        env:
        - name: SYNC_DESTINATION
          value: GoogleDrive
        - name: SYNC_DESTINATION_SUBPATH
          value: gollum
        - name: CRON_SCHEDULE
          value: 0 * * * *
        volumeMounts:
        - mountPath: /data
          name: data
        - mountPath: /config
          name: config
      volumes:
      - name: data  # wikiのデータを入れる
        emptyDir: {}
      - name: config # secretからConfigを読み出す
        secret:
          defaultMode: 511 # 雑に0777
          secretName: rclone-config
```

クラスタにデプロイするためには下記を実行します。

```
$ kubectl apply -f gollum-with-rclone.yaml
```

できたー！

<img src="https://qiita-image-store.s3.amazonaws.com/0/100375/bc05bbb9-ec0e-1bac-d9d3-defb01a3ebc5.png" width=50%>


# 注意
- 定期実行によるバックアップなので、その隙間の変更はバックアップされない。
    - preStopなどでもバックアップするとより良いと考えられる
        - それでも突然死などはカバーしきれないので注意が必要
- replicaを1以上にすることは想定していません（おうちなので）
    - その場合はアプリケーションとデータを分離して、データのバックアップをGoogleDriveにSyncするなどさらなる工夫が必要そうです。

# まとめ
おうちでKubernetesクラスタを構築する際に問題となるデータの永続化について、Rcloneを用いた、GoogleDriveへの定期バックアップスクリプトを紹介しました。
ファイルであればなんでも同期できるため、どんなデータでも永続化できることが特徴です。

Kubernetesは変化が早いため、常に最新のKubernetesの環境が手元にあるのは非常に良い勉強になるため、おうちKubernetesクラスタ、おすすめです。

(Raspberry piを使ったKubernetesの構築方法も紹介していますので、こちらも参考にしてみてください https://qiita.com/hatotaka/items/48a88ecb190e1f5e03c3 )

このエントリは、Z Lab　のメンバーによる [Z Lab Advent Calendar 2017](https://qiita.com/advent-calendar/2017/zlab) として業務時間中に書きました。