ローカル kubernetes 開発環境での永続 Volume 共有

# TL;DR

- 手元の docker for desktop の k8s 内で shell を使って何かしたい。
- k8s 内のPod間でファイルを共有したい、さらに作成したファイルを永続化したい。
- サンプルは全てこちらにあります https://github.com/Bugfire/k8s-workspace

## はじめに

自分は k8s を使って開発を行ったことがなく、ドキュメントを読みながら手探りで試しています。一般的用法から外れている、記法がよろしくない、簡潔にかける等、コメントがあるととても助かります！

kubectl は以下の版です

```bash
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:16:51Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.5", GitCommit:"20c265fef0741dd71a66480e35bd69f18351daea", GitTreeState:"clean", BuildDate:"2019-10-15T19:07:57Z", GoVersion:"go1.12.10", Compiler:"gc", Platform:"linux/amd64"}
```

## 目的

Docker を使った開発で、複数コンテナ間で永続的な Volume で開発・テストを行う。これを k8s で実現したい。

自分は Docker 内のみに存在する Volume で開発を行うのが好きです。これと、一般的なホスト側のファイルを用いるケースと両方で作ってみます。

## 実装

### Docker内Volume - NFS

[ドキュメント](https://kubernetes.io/ja/docs/tasks/configure-pod-container/configure-volume-storage/)をみた感じ local, hostPath Volume は、永続的ですがホスト側FSにマッピングされ、emptyDir はホストFSに依存しませんが  Pod の寿命に依存するので、永続性がないので目的を果たせません。

k8s からすでに外れますが、k8s 外の NFS を用いることにしました。k8s 外であれば Volume は Pod の寿命に依存せず、NFS Persistent Volume を用いることで k8s からマウントして利用します。

便利な NFS 用の Docker image があります、それをそのまま使います。

```bash
$ docker run -d --rm --name nfs -p 2049:2049 -v nfs:/nfs --privileged -e SHARED_DIRECTORY=/nfs itsthenetwork/nfs-server-alpine:latest
```

**注意、NFSへのアクセス制限はかかっていません。ファイアウォールの設定を確認してください。**
**ファイアウォールで外部からの2049ポートへのアクセスを全て落とすか、**
**NFS 側で `-e PERMITTED="192.168.*.*"` 等の制限をかけてください。**

上のワンライナーで nfs と言う名前の Volume を提供する NFS を起動し、

```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-volume
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteMany
  storageClassName: nfs
  persistentVolumeReclaimPolicy: Retain
  nfs:
    server: 192.168.x.x
    path: /
```

このような NFS の Persistent Volume を生成することにしました。

PVC は以下の通り

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-volume-claim
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: nfs
  resources:
    requests:
      storage: 5Gi
```

### ホスト側ファイルを用いたVolume

これは、ホスト側をそのまま用います。特にひねりはありません。

```yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-volume
spec:
  capacity:
    storage: 5Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /PATH-TO-SHARE
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - docker-for-desktop
          - docker-desktop
```

PVC はこちら

```yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-volume-claim
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: local-storage
  resources:
    requests:
      storage: 5Gi
```

## ConfigMap

複数の ConfigMap を合成したり、Immutable な ConfigMap にしたいと、いろいろな記事を参考に、ファイルを合成した上で hash の suffix を追加などを試していたりしました。

しかし kustomize が kubectl に入っていることに気がつき、これを利用することにしました。

k8s は変化が速いので、ググって出た記事が最近の情勢にあってるともかぎらないですね。

### ConfigMapGenerator

base 側に

```yaml
configMapGenerator:
- name: common-env
  env: common.env
```

を記述しておき、overlay 側で

```yaml
configMapGenerator:
- name: common-env
  behavior: merge
  env: common.env
```

としておくとマージされます、便利ですね。

最終的に base 側で

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: workspace
  labels:
    app: k8s-workspace
spec:
  containers:
  - name: workspace
    image: node:lts-stretch
    command: [ "/bin/bash", "-c", "while true; do sleep 1; done" ]
    resources:
      requests:
        cpu: 1000m
        memory: 1000Mi
    envFrom:
    - configMapRef:
        name: common-env
```

とすることで、環境変数にマージ後のものを読み込んでいます。

## Kustomize

Kustomize をせっかく使うので、上の Volume 等も合わせて、階層構造化してみました

- base_common
    - [kustomization.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_common/kustomization.yaml)
        - 共通設定
    - [common.env](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_common/common.env)
        - 共通環境変数
    - [workspace.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_common/workspace.yaml)
        - Pod の共通設定
- base_volume_localpath
    - [kustomization.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_volume_localpath/kustomization.yaml)
        - Volume として[ホスト側ファイルを用いたVolume](#ホスト側ファイルを用いたvolume)を使用
    - [volume-localpath.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_volume_localpath/volume-localpath.yaml)
    - [volume-localpath-claim.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_volume_localpath/volume-localpath-claim.yaml)
- base_volume_nfs
    - [kustomization.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_volume_nfs/kustomization.yaml)
        - Volume として[Docker内Volume - NFS](#docker内volume---nfs)を使用
    - [volume-nfs.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_volume_nfs/volume-nfs.yaml)
    - [volume-nfs-claim.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/base_volume_nfs/volume-nfs-claim.yaml)
- config
    - [kustomization.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/config/kustomization.yaml)
        - base_common 及び base_volume_localpath もしくは base_volume_nfs を派生した上でディレクトリ・IP・環境変数を独自に設定します。
    - [volume-nfs.yaml.sample](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/config/volume-nfs.yaml.sample)
        - オーバレイ設定 volume-nfs.yaml のサンプル。
    - [volume-localpath.yaml.sample](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/config/volume-localpath.yaml.sample)
        - オーバレイ設定 volume-localpath.yaml のサンプル。
    - [common.env](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/config/common.env)
        - ユーザ設定環境変数
    - [workspace.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/kustomize/config/workspace.yaml)
        - workspace Pod のオーバレイ設定。今回は base_common 側に記述しても問題はないが、Volume を使用していることを明示したいので、こちらに記述しました。

## Helm

せっかくなので、helm

```bash
$ helm version
version.BuildInfo{Version:"v3.2.1", GitCommit:"fe51cd1e31e6a202cba7dead9552a6d418ded79a", GitTreeState:"clean", GoVersion:"go1.13.10"}
```

を用いた版を、https://github.com/Bugfire/k8s-workspace/blob/master/helm に作りました。

ここでは、ほぼテンプレートエンジンとしてだけ使い、[config.yaml](https://github.com/Bugfire/k8s-workspace/blob/master/helm/config.yaml.default) を定義して使う形になります。