Docker for Mac上に構築したk8sのVolumeをcachedして速度を改善する

## 背景
Docker for Macをお使いであれば一度は
ホストとコンテナ間のマウントが遅いと感じたことがあると思います。

Docker やDocker Composeであれば、マウント先のコンテナディレクトリ末尾に`:cached`とフラグを立ててあげることでホストディレクトリに対する読み書きのパフォーマンスが改善されます。
[参考URL](https://blog.docker.com/2017/05/user-guided-caching-in-docker-for-mac/)

例えばdockerでは

```sh
$ docker run -v /your/project/dir:/mount/dir:cached your_image
```

Docker Composeでは

```yml:docker-compose.yml
version: '3.7'
services:
  your-app:
    restart: always
    image: your_image
    volumes:
      - ./project/dir:/mount/dir:cached
```
マウント先のディレクトリ末尾に`:cached`をつけるだけなので手軽ですね！
シンプル！

本記事ではこの`:cached`を恩恵をk8s環境が得るまでを紹介していきます。

##はじめに
[Docker for Macのissue](https://github.com/docker/for-mac)でも議論されていますが、
現状（2019/01/22時点）Docker for Macの仕様としての機能提供ではなく、
現状の仕組みを利用したちょっとした小技程度と考えてもらえたらと思います。
今後のアップデートで、よりスマートに実現可能な方法も出てくる可能性があります。

本記事の方法は上記Issueの[こちら](https://github.com/docker/for-mac/issues/2549#issuecomment-442265169)の方法を参考にしています。

## 動作環境
- macOS Mojave 10.14.2
- Docker for Mac 2.0.0.2
    - Engine : 18.09.1
    - Kubernetes: v1.10.11

## それではやってみる。

Issueの[こちら](https://github.com/docker/for-mac/issues/2549#issuecomment-442265169)をざっくり要約すると、
**「一度cachedされたホストディレクトリは、他のコンテナもその恩恵にあずかるよ」**ってことみたいです。

まず、あらかじめcachedしたいホストディレクトリをマウントしたコンテナを
予めバックグラウンドで実行しておきます。

```bash
$ docker run --rm -d \
-v /your/host/directory:/tmp/project:cached \
busybox:latest \
watch -n 1 date
```

```bash
$ docker ps
CONTAINER ID   IMAGE           COMMAND            CREATED        STATUS        PORTS  NAMES
90a9a40edaa6   busybox:latest  "watch -n 1 date"  2 seconds ago  Up 2 seconds         objective_visvesvaraya
```

`docker ps`で起動中のコンテナを確認すると先程立ち上げたものが確認できます。

続いてdeploymentのマニフェストファイルをk8sにapplyしましょう。

```yaml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cached-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cached-app
  template:
    metadata:
      labels:
        app: cached-app
    spec:
      containers:
        - name: your-container
          image: your_image
          volumeMounts:
            - name: cached-data
              mountPath: /your/project/directory
      volumes:
        - name: cached-data
          hostPath:
            path: /your/host/directory
            type: Directory
```

```bash
$ kubectl apply -f /path/to/manifest/deployment.yml
deployment.apps "cached-deployment" created
```

以上です！お疲れ様でした！

## 結果の確認

検証として、バックエンドをDjango, DBをSQLite3で構築したPodをk8s上にデプロイしてみました。

適当なクエリを投げ、ヒットしたレコードを列挙するページで応答速度を計測しています。
SQLiteのファイルサイズは約130MBです。
####cached前
![スクリーンショット 2019-01-24 15.34.49.png](https://qiita-image-store.s3.amazonaws.com/0/94505/721a0d1c-5dfc-a916-7436-66cf42f31e24.png)

####cached後
![スクリーンショット 2019-01-24 15.33.10.png](https://qiita-image-store.s3.amazonaws.com/0/94505/1269b6c6-b0ed-77b8-b120-552dd67270fc.png)

cached前に比べ、約10倍ほど応答速度が上がっているのが確認できました。

## ちなみに
先述の方法はLocalタイプのPersistant Volumeについても同様の結果が得られました。

```yaml:StorageClass
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner
volumeBindingMode: WaitForFirstConsumer
```

```yaml:PersistentVolume
apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-cached-pv
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /your/host/directory
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - docker-for-desktop
```

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

上記のマニフェストで作成したPersistant Volumeを使用するために
`deployment.yml`のvolume定義部分を書き換えます。

```yaml:deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: cached-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cached-app
  template:
    metadata:
      labels:
        app: cached-app
    spec:
      containers:
        - name: your-container
          image: your_image
          volumeMounts:
            - name: cached-data
              mountPath: /your/project/directory
      volumes:
        - name: cached-data
          persistentVolumeClaim:
            claimName: local-cached-claim
```



localタイプのボリュームの詳細は以下のブログが詳しいです。
https://kubernetes.io/blog/2018/04/13/local-persistent-volumes-beta/

## 終わりに
多少強引な方法ではありましたが、
なんとかk8s上のコンテナに対してcachedオプションの恩恵をあずかることが出来ました。
もし他にもこんな方法あるよー！って方がいらっしゃいましたら教えていただきたいです。。
ありがとうございました！