kubernetes上のelasticsearchでsnapshotを採取したい

#はじめに
おうちkubernetesのelasticsearchでもデータのバックアップをしたいので、snapshotを使えるように勉強しました。

#前提環境
以下の記事で構築した環境です。elasticsearchは都度最新に更新しています。

https://qiita.com/Y-Shikase/items/f12271df70099e14834f

また、NFS永続ストレージ用としてSynologyのNASを利用しています。

#snapshotのリポジトリ作成
自宅のelasticsearchはstatefulsetで3ノードにデプロイしているため、3ノード同時に読み書き可能な永続ストレージにする必要があります。
つまり、ReadWriteManyをサポートしているボリュームプラグインを利用しなければなりません。

https://kubernetes.io/ja/docs/concepts/storage/persistent-volumes/#%E3%82%A2%E3%82%AF%E3%82%BB%E3%82%B9%E3%83%A2%E3%83%BC%E3%83%89

自宅ではCephを利用できるようにしているのでCephでも良さそうなのですが、今回はCephの障害時に備えてバックアップを採取したいので、自宅のNASで使える共有サービスのNFSを利用します。

##NFS永続ストレージ(pv,pvc)の作成

NFS(NAS)上に保存したいので、NFSのpvを作成します。(NFS共有は作成済みです)
全ノードから読み書きできるようにaccessModesを「ReadWriteMany」にします。利用するのは初めてです...

```yaml:es-snapshot-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: es-snapshot-pv
spec:
  capacity:
    storage: 300Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteMany
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: nfs-es-snapshot
  mountOptions:
    - hard
  nfs:
    server: 192.168.1.4
    path: /volume1/elastic_backup
```

pvcについても、accessModesを「ReadWriteMany」にします。
※個人的な運用ポリシーとして、pvと同じ容量にして、pvとpvcを1対1にしてます。

```yaml:es-snapshot-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: es-snapshot-pvc
  namespace: elastic
spec:
  accessModes:
    - ReadWriteMany
  resources:
    requests:
      storage: 300Gi
  storageClassName: nfs-es-snapshot
```

両方applyします。

```shell-session:
# kubectl apply -f es-snapshot-pv.yaml
# kubectl apply -f es-snapshot-pvc.yaml
```

##elasticsearchコンテナへのmount

elasticsearchのマニフェストファイルの前提は、以下の記事になります。ここのstatefulsetのyamlを編集します。

https://qiita.com/Y-Shikase/items/f12271df70099e14834f#statefulset%E3%81%A7%E3%81%AE3%E3%83%8E%E3%83%BC%E3%83%89%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%BF%E3%81%AE%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4basic%E8%AA%8D%E8%A8%BC%E3%81%82%E3%82%8A%E7%89%88

編集内容としては、以下になります。
- elasticsearchコンテナの環境変数「path.repo」にsnapshot用のディレクトリを指定する
- 前段で作成したNFSのpvcをelasticsearchコンテナのsnapshot用ディレクトリにmountさせる
- snapshot含めたファイルの作成をelasticユーザにするため、securityContext.fsGroupにelasticユーザのuidを指定する

```yaml:elasticsearch-sts-sec.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: elasticsearch
  namespace: elastic
spec:
  selector:
    matchLabels:
      app: elasticsearch
  serviceName: "elasticsearch"
  replicas: 3
  template:
    metadata:
      labels:
        app: elasticsearch
    spec:
      initContainers:
      # 略
      containers:
      - name: elasticsearch
        image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
        # 略
        env:
# envにelasticsearchの環境変数path.repoを追記
        - name: path.repo
          value: /es-snapshot
        volumeMounts:
# volumeMountsに/es-snapshotにmountする定義を追記
        - name: es-snapshot
          mountPath: /es-snapshot
      volumes:
# volumesにNFSのpvcを追記
        - name: es-snapshot
          persistentVolumeClaim:
            claimName: es-snapshot-pvc
# securityContextのfsGroupを1000(elasticsearchコンテナ内でのelasticユーザ)に設定
      securityContext:
        fsGroup: 1000
    # 略
```

ビビりなので、一旦deleteで完全に停止させてから、applyします。(applyの意味ない...)

```shell-session:
# kubectl delete -f elasticsearch-sts-sec.yaml
# kubectl apply -f elasticsearch-sts-sec.yaml
```

##snapshotリポジトリの登録
elasticsearchのsnapshot repository APIでリポジトリを登録します。

https://www.elastic.co/guide/en/elasticsearch/reference/current/put-snapshot-repo-api.html

私はNode-REDから登録しましたが、curlコマンドでは以下のようになるかと思います。(未確認です)

```shell-session:
# curl -H 'Content-Type: application/json' -XPUT -u elastic http://elasticsearch:9200/_snapshot/full -d '
{
    "type" : "fs",
    "settings" : {
        "location" : "/es-snapshot/snapshot",
        "compress" : true
    }
}'
```

#動作確認
リポジトリに対してelasticsearchで言うところのドキュメントを作成する事でスナップショット(バックアップ）が採取されます。
indicesに「*」を指定する事で全てのインデックスのスナップショットを採取します。

```shell-session:
# curl -H 'Content-Type: application/json' -XPUT -u elastic http://elasticsearch:9200/_snapshot/full/full_2021-04-27_120000 -d '
{
    "indices": "*",
    "ignore_unavailable": true,
    "include_global_state": false
}'
```

採取したスナップショットは、GETリクエストで確認できます。

```shell-session:
# curl -XGET -u elastic http://elasticsearch:9200/_snapshot/full/\*?pretty
Enter host password for user 'elastic':
{
  "snapshots" : [
    {
      "snapshot" : "full_2021-04-27_120000",
      "uuid" : "hmFb8IzXSzOZpyynJtd29w",
      "version_id" : 7120099,
      "version" : "7.12.0",
      "indices" : [
        "ilm-history-3-000003",
        ".kibana-event-log-7.11.1-000003",
        "ilm-history-2-000003",
        ".kibana-event-log-7.11.0-000003",
        ".kibana-event-log-7.10.1-000004",
        "sensor-temperature",
        ".kibana-event-log-7.10.1-000002",
                 :
```

REST APIなので色々運用に組み込めるかと思います。

#おわりに
elasticsearchのバックアップという事で、最初は「コマンド1つでできるくらいのものかな？」と考えていたのですが、あらかじめストレージを準備して採取するという本格的なバックアップソフト並みのものでした。今回は簡単に利用できるまでの部分しか触れておりません。

今はNode-REDで毎日フルバックアップ(といっても自動で差分取得します）と、古いものは自動で削除しています。
ちゃんと詳細な動作を確認していませんが、最古のスナップショットを削除しても、次点の最古がベースになるようになっているようで、かなり賢いようです。