初めてのkubernetes（Minikube） Pod連携とデータの永続化編

# 概要

前回の「[初めてのkubernetes（Minikube） Windows環境でチュートリアル＋α](https://qiita.com/comefigo/items/35a84c68b8cd333442f1)」に続き、アプリケーションサーバ用PodとDBサーバ用Podの連携をやってみたので理解したことをメモとして残したいと思います

**※minikubeコマンドを実行する際は管理者権限で実行してください**

# 実行環境

- Windows 10 pro
- Minikube v0.28.0
- Docker Version 18.06.1-ce-win73 (19507)

# さっそく手順

## 1. サンプルアプリケーションの取得

今回はNode.jsのアプリケーションからRedisのデータを取得して表示する**[簡単なサンプルアプリケーション](https://github.com/comefigo/k8s-webapp-sample)**を使っていきますので、ソースをローカルにクローンしてください

ローカルで事前に動作確認できるように`docker-compose`も用意していますので、適宜に活用してください

```javascript:server.js
const http = require('http');
const redis = require('redis');
let redis_host = 'redis';
let redis_port = 6379;

// k8s環境の場合は、redisの接続先とポートは環境変数から取得
if (process.env.MODE && process.env.MODE == 'k8s') {
    redis_host = process.env.SAMPLE_REDIS_SERVICE_HOST;
    redis_port = process.env.SAMPLE_REDIS_SERVICE_PORT;
}
const client = redis.createClient({
    host: redis_host,
    port: redis_port
});

// 表示用の変数をredisに保存する
client.set('sample', 'hello world');
client.set('sample2', 'hello Qiita');

// 表示用の変数
let redis_val = '';
client.get('sample', function (err, data) {
    redis_val += '<li>' + data + '</li>';
});
client.get('sample2', function (err, data) {
    redis_val += '<li>' + data + '</li>';
});

const handleRequest = function (request, response) {
    console.log('Received request for URL: ' + request.url);
    response.writeHead(200);
    response.end('<h1>redis value:</h1><ul>' + redis_val + '</ul>');
};

// echo env
const envs = process.env;
console.log(envs);

const www = http.createServer(handleRequest);
www.listen(3000);

```

## 2. Dockerクライアントの接続先の変更

前回同様Dockerクライアントの向き先を予めMinikubeに変更してください

```powershell:powershell
> minikube docker-env --shell powershell | Invoke-Expression

> docker images

# kubernetes関連のイメージが表示されればOK
```

## 3. イメージのビルド

サンプルアプリケーションのDockerイメージをMinikubeにビルドします

```powershell:powershell
> docker build -t sample-app:v1 --no-cache ./app
```

## 4. Deployment

Minikubeにデプロイします

```powershell:powershell
> kubectl apply -f ./deployment_k8s_sample_app.yml
```

```yaml:deployment_k8s_sample_app.yml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-redis
  labels:
    app: sample-redis
    type: db
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-redis
      type: db
  template:
    metadata:
      labels:
        app: sample-redis
        type: db
    spec:
      containers:
      - name: sample-redis
        image: redis:4
        ports:
        - containerPort: 6379
        # redis-volumeボリュームとredisのデータ領域の/data
        volumeMounts:
        - mountPath: /data
          name: redis-volume
      # データ永続化するためのボリューム
      volumes:
      - name: redis-volume
        # ボリュームの種別はhostPath
        hostPath:
          path: /home/docker/redis-data
          # ディレクトリがなければ作る
          type: DirectoryOrCreate
---
apiVersion: v1
kind: Service
metadata:
  name: sample-redis
  labels:
    app: sample-redis
    type: db
spec:
  type: LoadBalancer
  selector:
    app: sample-redis
    type: db
  ports:
  - port: 6379
    targetPort: 6379
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample-app
  labels:
    app: sample-app
    type: app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-app
      type: app
  template:
    metadata:
      labels:
        app: sample-app
        type: app
    spec:
      containers:
      - name: sample-app
        image: sample-app:v1
        ports:
        - containerPort: 3000
        # k8s環境用で稼働しているかどうかの環境変数
        env:
        - name: MODE
          value: k8s
---
apiVersion: v1
kind: Service
metadata:
  name: sample-app
  labels:
    app: sample-app
    type: app
spec:
  type: LoadBalancer
  selector:
    app: sample-app
    type: app
  ports:
  - port: 3000
```

## 5. 確認

Podの確認

```powershell:powershell
> kubectl get po

NAME                            READY     STATUS    RESTARTS   AGE
sample-app-694d9bf49f-66b7b     1/1       Running   0          37m
sample-redis-55c5b46859-n7pnt   1/1       Running   0          42m
```

Serviceの確認

```powershell:powershell
> kubectl get svc

NAME           TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes     ClusterIP      10.96.0.1        <none>        443/TCP          20d
sample-app     LoadBalancer   10.101.237.112   <pending>     3000:32482/TCP   43m
sample-redis   LoadBalancer   10.111.4.116     <pending>     6379:32400/TCP   43m
```

アプリケーションの確認

```powershell:powershell
> minikube service sample-app
```

ブラウザで以下のように表示されればOKです

```
redis value:

hello world
hello Qiita
```

# 説明

Dockerと同じようにDB Pod(redis)接続情報を記す環境変数を取得することができる
その代わりにServiceとして公開しないといけないようです
Serviceを利用することにより、トラフィックの負荷分散、サービスディスカバリー、クラスタ内のDNSを利用できるようになります
なので、アプリケーションのredisの接続先を環境変数から取得しています

## server.js

```javascript:server.js

// k8s環境の場合は、redisの接続先とポートは環境変数から取得
// MODEはDeploymentで環境変数として設定しています
if (process.env.MODE && process.env.MODE == 'k8s') {
    redis_host = process.env.SAMPLE_REDIS_SERVICE_HOST;
    redis_port = process.env.SAMPLE_REDIS_SERVICE_PORT;
}
```

環境変数を確認してみましょう

```powershell:powershell
kubectl logs sample-app-694d9bf49f-66b7b

{ KUBERNETES_SERVICE_PORT: '443',
  KUBERNETES_PORT: 'tcp://10.96.0.1:443',
  SAMPLE_APP_PORT_3000_TCP_ADDR: '10.97.74.141',
  NODE_VERSION: '6.9.2',
  HOSTNAME: 'sample-app-565db6587d-xtblr',
  MODE: 'k8s',
  SAMPLE_APP_PORT_3000_TCP_PORT: '3000',
  SAMPLE_APP_PORT_3000_TCP_PROTO: 'tcp',
  HOME: '/root',
  SAMPLE_REDIS_SERVICE_HOST: '10.103.205.192',
  SAMPLE_APP_PORT_3000_TCP: 'tcp://10.97.74.141:3000',
  SAMPLE_APP_SERVICE_HOST: '10.97.74.141',
  SAMPLE_REDIS_PORT: 'tcp://10.103.205.192:6379',
  SAMPLE_REDIS_SERVICE_PORT: '6379',
  SAMPLE_REDIS_PORT_6379_TCP_ADDR: '10.103.205.192',
  KUBERNETES_PORT_443_TCP_ADDR: '10.96.0.1',
  PATH: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
  SAMPLE_REDIS_PORT_6379_TCP_PORT: '6379',
  KUBERNETES_PORT_443_TCP_PORT: '443',
  SAMPLE_REDIS_PORT_6379_TCP_PROTO: 'tcp',
  SAMPLE_APP_PORT: 'tcp://10.97.74.141:3000',
  SAMPLE_APP_SERVICE_PORT: '3000',
  NPM_CONFIG_LOGLEVEL: 'info',
  KUBERNETES_PORT_443_TCP_PROTO: 'tcp',
  SAMPLE_REDIS_PORT_6379_TCP: 'tcp://10.103.205.192:6379',
  KUBERNETES_PORT_443_TCP: 'tcp://10.96.0.1:443',
  KUBERNETES_SERVICE_PORT_HTTPS: '443',
  KUBERNETES_SERVICE_HOST: '10.96.0.1',
  PWD: '/' }
```

# データの永続化

データの永続化するために、ボリュームを[hostPath](https://kubernetes.io/docs/setup/minikube/#mounted-host-folders)として定義しています
hostPathはNode上の任意のパスをボリュームとして作ることができます
MinikubeのようにシングルNodeの場合は問題ないが、複数Node構成の場合は、データの整合性を考慮しなければならないため、他のボリューム種別を利用すること

以下ではデータの永続化の動作検証手順になります

## 1. Deployment、サービスの削除

データが永続化しているかどうかを確認するために、一度削除します

```powershell:powershell
> kubectl delete -f ./deployment_k8s_sample_app.yml
```

## 2. server.jsを修正

redisに値を保存するコードをコメントアウトします

```javascript:server.js

// 表示用の変数をredisに保存する
// client.set('sample', 'hello world');
// client.set('sample2', 'hello Qiita');

```

## 3. イメージをリビルド

タグは`v2`でビルド

```powershell:powershell
> docker build -t sample-app:v2 --no-cache ./app
```

## 4. deployment_k8s_sample_app.ymlを編集

デプロイするイメージをv2に変更

```yaml:deployment_k8s_sample_app.yml
~中略~
      containers:
      - name: sample-app
        image: sample-app:v2
~中略~
```

## 5. 再Deployment

```powershell:powershell
> kubectl apply -f ./deployment_k8s_sample_app.yml

deployment.apps "sample-redis" created
service "sample-redis" created
deployment.apps "sample-app" created
service "sample-app" created
```

## 5. 動作確認

前述と同様に値を確認できる

```powershell:powershell

> minikube service sample-app

```