CentOS7 kubespray で kubernetes cluster を作る - deploy application-

# Overview

kubernetesを使って実際にアプリケーションを動かしてみる。
人によって、コンテナだったり、Functionと呼んでいたりと、なんかいろいろあるみたいだが
ここでは「アプリケーション」と呼んでおく。
DinD等もあるので、アプリケーションという呼び方が正しいかどうかは状況に依りそうだ。

# Precondition 

1. kubernetes 環境ができあがっている
2. dashborad へのアクセスができる
 
[CentOS7 kubespray で kubernetes cluster を作る - その1-](https://qiita.com/ainamori/items/cf24466c6c0f4cec16d5)
から続けているひとは「後片付け」だけしなければそのまま使える。

# Scope of work

1. Application deploy (Nginx)
2. 外部からのアクセス許可
3. データ・設定を保存する Application の deploy
4. 後片付け

# How to Operation

## 1. Application deploy (Nginx)

### Log in kube master

- login to kubernetes master host

```
 $ ssh k8master01
```

### Create deploy

- Deploy nginx image
 - nginx の versionはお好みで。ver指定が無くともimage=nginx ぐらいはしないと起動しない
 - サービスポートも80で起動するようにオプションを追加して起動。

```
 $ kubectl run nginx --image=nginx --port=80
```

ちなみに nginx:1.13 みたいな形でVerを指定してイメージを取得することもできる。
このイメージはデフォルトで dockerhub と google cloud を見ているようだ。

### Check deployment

デプロイ状況のチェック。
イメージサイズが小さいのですぐに起動するとは思うが
get pod で STATUS が Running となっていない場合は、動作していない事になる。
 
```
 $ kubectl get deploy                                                                                                                                                     
NAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     1         1         1            1           12m

 $ kubectl get pods                                                                                                                                                       
NAME                    READY     STATUS    RESTARTS   AGE
nginx-f846cb5bc-4nkb4   1/1       Running   0          12m

 $ kubectl get pods --all-namespaces                                                                                                                                      
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
default       nginx-f846cb5bc-4nkb4                   1/1       Running   0          12m
  --- snip ----
```

### Check deployment detail

```  
 $ kubectl describe pod nginx-f846cb5bc-4nkb4
Name:           nginx-f846cb5bc-4nkb4
Namespace:      default
Node:           k8master02/192.168.240.175
Start Time:     Thu, 01 Feb 2018 08:48:04 +0900
Labels:         pod-template-hash=940276167
                run=nginx
Annotations:    <none>
Status:         Running
IP:             10.233.126.193
Controlled By:  ReplicaSet/nginx-f846cb5bc
Containers:
  nginx:
    Container ID:   docker://1f55bf003409b9204a19c0089f651218ab6e03040174ba512a4a3d6d3a903503
    Image:          nginx:1.13
    Image ID:       docker-pullable://nginx@sha256:285b49d42c703fdf257d1e2422765c4ba9d3e37768d6ea83d7fe2043dad6e63d
    Port:           80/TCP
    State:          Running
      Started:      Thu, 01 Feb 2018 08:48:06 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-2jsqt (ro)
Conditions:
  Type           Status
  Initialized    True 
  Ready          True 
  PodScheduled   True 
Volumes:
  default-token-2jsqt:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-2jsqt
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     <none>
Events:
  Type    Reason                 Age   From                 Message
  ----    ------                 ----  ----                 -------
  Normal  Scheduled              13m   default-scheduler    Successfully assigned nginx-f846cb5bc-4nkb4 to k8master02
  Normal  SuccessfulMountVolume  13m   kubelet, k8master02  MountVolume.SetUp succeeded for volume "default-token-2jsqt"
  Normal  Pulled                 13m   kubelet, k8master02  Container image "nginx:1.13" already present on machine
  Normal  Created                13m   kubelet, k8master02  Created container
```

### Check from dashboard

- Open web browser, then access dashboad URL


![kurbernetes-dashboard.png](https://qiita-image-store.s3.amazonaws.com/0/82979/61929b2a-5c54-3759-07d4-4beff7042c1d.png)

詳細
![nginx-detail.png](https://qiita-image-store.s3.amazonaws.com/0/82979/e7da2a76-6b6c-d6be-bffe-b7e0d6e191d8.png)

## 2. Expose nginx application by node ip

このままでは外部からアクセスができないので外部アクセスを可能にする。
kubernetes では service の追加（作成）という操作になる。

```
 $ kubectl expose deploy nginx --port=80 --target-port=80
```

- 実行結果。

```
 $ kubectl get service nginx                                           
NAME      TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)   AGE
nginx     ClusterIP   10.233.4.37   <none>        80/TCP    6s
```

- nginxへのアクセス

```
 $ curl http://10.233.4.37                                             
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
```

このようにClusterIPが付与され、アクセスできるようになった。
ただし、あくまでこのアドレスは、kubernetesの中での通信用の為、外部からはアクセスできない。

とりあえず外部からアクセスするためには 2つの方法がある。

1. kube proxy を使う
2. NodePort を使う

### kube proxy 利用

master で kube proxy を起動する。
例によって addressやaccept-hostsを書かないと
外部からのアクセスがauth failとなってしまう為、以下のオプションを付与する。

```
 # kubectl proxy --address=0.0.0.0 --accept-hosts=".*" 
Starting to serve on [::]:8001
```

これで kube proxy を起動したホストの 8001 ポートへアクセスすれば良いのだが、
これはAPIアクセスにすぎないのでURLを正しく指定する必要がある。
URLパスは以下のようになるので、 pod name を把握する必要がある。

http://<kube proxy server IP>:8001/api/v1/proxy/namespaces/default/pods/<pod name>/

以下のような場合、pod name は **nginx-7587c6fdb6-4jvp7** となる。

```
 $ kubectl get pod                                                             
NAME                     READY     STATUS    RESTARTS   AGE
nginx-7587c6fdb6-4jvp7   1/1       Running   0          1
```

従って、URLも以下のようになる。

http://192.168.240.175:8001/api/v1/proxy/namespaces/default/pods/nginx-7587c6fdb6-4jvp7/

![deploy-api.png](https://qiita-image-store.s3.amazonaws.com/0/82979/c65ff76c-4712-3a51-6a69-7e406cd11bb8.png)


### NodePort によるアクセス

kube proxyによるアクセスの場合、URLが非常に長く直感的にアクセスがしづらい。
従って、NodePortを使ってコンテナのサービスを名実ともに外に出してやるのに
最も簡単な方法がNordPort。

先ほどのExpose (Create service)に対して編集をかけてやるだけで良い。

```
 $ kubectl edit service nginx 
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2018-02-02T22:34:59Z
  labels:
    run: nginx
  name: nginx
  namespace: default
  resourceVersion: "140161"
  selfLink: /api/v1/namespaces/default/services/nginx
  uid: 4cf93e78-0869-11e8-82c7-000c296cb132
spec:
  clusterIP: 10.233.4.37
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: nginx
  sessionAffinity: None
  type: ClusterIP
status:
  loadBalancer: {}
```

このように edit で開いたファイルで **ClusterIP** を **NodePort** に変更してやる。
Linuxデフォルトであればviで開かれるはず。

```
 $ kubectl get service nginx
NAME      TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
nginx     NodePort   10.233.4.37   <none>        80:31700/TCP   59m
```

このように 31700 番ポートでアクセスできるようになる。

![nginx-nodeport.png](https://qiita-image-store.s3.amazonaws.com/0/82979/cb1c5ec6-1a8f-71a3-63c8-2c7788bfb803.png)

### 欄外

ちなみにpod情報、serviceファイルを理解したり
設定ファイルとして書き出す為にYAMLやJSONで出力することもできる。

```
 $ kubectl get pod nginx -o yaml
 $ kubectl get services nginx -o yaml
```


## 3. データ・設定を保存する Application の deploy

続いて、データや設定を保存するためのApplicationをdeployしてみる。
よりデータや構造をわかりやすくするために今度は jenkins を例にやってみることにする。

### Create deployment file for Jenkins 

以下を参考にjenkinsのdeploymentファイルを作成する。

- https://kubernetes.io/docs/concepts/workloads/controllers/deployment/

なお、jenkinsの deployには
[こちら](https://qiita.com/suzukihi724/items/5e16855c3941e629ff70)で既に解説されているが
どうも1.9では、 deploy で作る方が良いらしい。
従って、Deploy形式で作成することにする。

```
 $ vi jenkins-deployment.yaml
```

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: jenkins-deployment
  labels:
    app: jenkins
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jenkins
  template:
    metadata:
      labels:
        app: jenkins
    spec:
      volumes:
        - name: jenkinsdata
          hostPath:
             path: /var/tmp/jenkins_home
      containers:
      - name: jenkins
        image: jenkins
        ports:
        - containerPort: 8080
        volumeMounts:
        - mountPath: /var/jenkins_home
          name: jenkinsdata
```

ちなみに1. - 2. も同じようにdeploymentファイルで作成する場合は
以下のようなYAMLになっている

```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        ports:
        - containerPort: 80
```

先ほどのjenkins-deploymentがnginxと大きく異なるのは **volumes** の項。
**hostPath:**　以下が追加されている。

ここで /var/tmp/jenkins_home と記載されており作成されたPodは
Nodeの /var/tmp/jenkins_home を **mountPath**の /var/jenkins_home をマウントする。
これにより jenkins のインストールコンテンツがコンテナ外に置かれるため、
Podが死んでもデータが残るようになる。
なお、 /var/tmp/jenkins_home は必ず全てのノードで 777 で作成しておくこと。
「全ての」が鬼門。Replicaの数とノード数によっては運良く、777で作成されたノードで
deployが始まり、うまく起動することもあるが、そうなると今度はそのノードが死んだときに
他のノードへ移った時にdeploy失敗という切ない事態に陥る。

![deploy-error.png](https://qiita-image-store.s3.amazonaws.com/0/82979/3d5b3b22-b800-77e4-f0fd-dcf14b35851b.png)

![error-log.png](https://qiita-image-store.s3.amazonaws.com/0/82979/8f841d04-939b-5b7b-9a71-78def3477893.png)

つまり全てのノードでこれが必要

```
 node$ mkdir /var/tmp/jenkins_home
 node$ sudo chmod 777 /var/tmp/jenkins_home
```


### Deployment jenkins

先ほど作成された deploymentファイルを元にdeployを行う

```
 $ kubectl create -f jenkins-deployment.yaml 
```

### Expose jenkins

2. の項で述べたようなもの。
外部からのアクセスできるよう service を設定する。
こちらもYAMLの流し込み方式でやってみる。

```
apiVersion: v1
kind: Service
metadata:
  name: jenkins-services
spec:
  ports:
  - nodePort: 30000
    port: 80
    protocol: TCP
    targetPort: 8080
  selector:
    app: jenkins
  type: NodePort
```

```
 $ kubectl create -f jenkins-services.yaml 
```

NodePortで作成しており、ポートを30000に指定している。


```
 $ kubectl get service
NAME               TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
jenkins-services   NodePort    10.233.8.39   <none>        80:30000/TCP   5s
```

これでノードのIP 30000 へアクセスするとJenkinsが見えるようになる。

![jenkins_top.png](https://qiita-image-store.s3.amazonaws.com/0/82979/5efa0454-bccc-14da-94e4-037db049c9b8.png)

### データの共有

今回の項で jenkins のデータは Node の /var/tmp/jenkins_home にマウント
（コンテナの外）してある。
ただし、このデータはノード間では共有されない。
従って、ここで設定した情報は、ノードが動かない限りはpodを作り直しても復活するが
ノードが移ると見事に無くなってしまう。そして移動先のノードで設定をしたものは
移動先のノードに保存されるため、再び元のノードに戻ってきた際にはロールバックしたような状態になる。

やってみると面白いが、これでは実用性がまるで無いのでデータを外に出すときは
その外に出したデータをノード間でどうやって同期するか？と考える必要がある。

- せっかくセットアップしたのに・・・

![node01_jenkins.png](https://qiita-image-store.s3.amazonaws.com/0/82979/c48da690-e59f-a7bc-37a2-59117087d619.png)

![setup_jenkins.png](https://qiita-image-store.s3.amazonaws.com/0/82979/c4f85c80-3c5f-06d5-ce88-5a40d891bfe0.png)

- node01を落とす

![node1_down.png](https://qiita-image-store.s3.amazonaws.com/0/82979/1586227a-4193-afd4-7228-8b6ae12b4f26.png)

![node3_jenkins.png](https://qiita-image-store.s3.amazonaws.com/0/82979/ccb11e5a-383c-4f7a-6a4c-3ae8fc7daa06.png)


- node03 で上がった jenkinsにログインすると・・・

![jenkins_node3.png](https://qiita-image-store.s3.amazonaws.com/0/82979/566a3ca0-7d64-87b3-5e21-2bb7278feb63.png)

やり直し・・・ orz


## 4. 後片付け

それぞれ service と deploy(pod)を削除して完了。

```
 $ kubectl delete service nginx
 $ kubectl delete deploy nginx
 $ kubectl delete service jenkins-services
 $ kubectl delete deploy jenkins-deployment
```

全てのノードでデータを削除

```
 node$ rm -rfv /var/tmp/jenkins_home
```

# メモ


# 参考

- [Kubernetes デプロイメント についての自習メモ (その１)](https://qiita.com/MahoTakara/items/0245a9f18c7a97bcdd31)
- [kubectlコマンドの使い方(1.2)](https://qiita.com/hana_shin/items/ef1a20239001ac83a78d)