体系化！Kubernetesまとめ

  
マイクロサービスアーキテクチャーを実現するKubernetesについて参考にさせていただいた「Kubernetes完全ガイド」という本と公式ドキュメントをざっと追いながらまとめてみました。みなさんの参考になればぜひ。

> [Kubernetes完全ガイド](https://www.amazon.co.jp/Kubernetes%E5%AE%8C%E5%85%A8%E3%82%AC%E3%82%A4%E3%83%89-impress-top-gear-%E9%9D%92%E5%B1%B1/dp/4295004804/ref=asc_df_4295004804/?tag=jpgo-22&linkCode=df0&hvadid=295704876452&hvpos=1o2&hvnetw=g&hvrand=399645167014199847&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009317&hvtargid=pla-525497287608&psc=1&th=1&psc=1)

## Kubernetesの基本
Kuberentesは、コンテナ化アプリケーションの展開、スケーリング、および管理を自動化するオーケストレーションツールです。端的に理解するならコンテナに割り当てるリソース管理(ボリューム, IP, アクセスポリシーなど)を行うものということになります。大別すると以下のような役割があり、Kubernetesで定義される各種リソースがその役割を担います。
 
* 複数サーバーでのコンテナ管理(Workloadsリソース)
* コンテナ間のネットワーク管理、負荷分散(Services,LB&Networkリソース)
* コンテナに割り当てるストレージの管理(Storageリソース)
* コンテナリソースへのアクセス設定(Configurationリソース)
* コンテナ、ノードの監視(Securityリソース)

### **Kubernetesにおけるアプリケーション展開の流れ**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122992/b69eac95-d616-2a50-66d5-c5405c21c258.png)

絵だといまいちピンとこないので、以下流れの説明です。

##### １、Kubernetesクラスタの作成

コンテナといえど、実際に動いているのはVMインスタンスまたは物理マシン上です。クラスタ作成とは、インスタンス１台(冗長も加味するなら2台以上)をKubernetesを管理するマスターノードに割り当て、それ以外の複数台インスタンスをワーカーノードとして***コンテナデプロイし放題のプールを作成するイメージ***です。

##### ２、アプリのデプロイ

ここではコンテナを利用して設計したアプリケーションにあたります。

##### ３、Podの生成

Kubernetesにおいての最小単位(=原始単位)は***Pod***です。これは「***コンテナ+コンテナに割り当てる共有ストレージ(ボリューム)やIPアドレスを定義したもの***」になります。Kubernetesにおいてノードリソース状況を判断して自動でPodを配置を行います。そして、Podは配置されたノードに紐付き、ノードで障害が起きた場合は、使用可能なノードに配置されます。(セルフヒーリング機能=?コンテナのVMotion)  
コンテナはPodでリソースを身にまとい、Kubernetes環境に身を置くことで耐障害性・拡張性が増します。

##### ４、アプリを公開する
基本的にKubeMasterではマニュフェストファイル(yaml)と言われるものをコマンドで適用します。  

```sh:マニュフェストファイル適用
$ kubectl apply -f sample.yml
```
  
**リソースのアップデートや削除はマニュフェストファイルを変更して適用することが推奨されています。**
「現在のリソース状態」- 「適用するマニフェスト」の差分を計算してリソース変更を行います。  
  
```sh:リソース操作コマンドまとめ
$ kubectl get pods

$ kubectl delete -f sample.yml
```
yamlファイルの定義など詳しくは、後半のリソースまとめを参照にしてください。例として、たとえばコンテナに何らかの障害が発生して停止した場合、Replica Setというリソースを定義しておけば、自動的にPodを複製して起動することが可能です。また、PodごとにラベルをつけてPodをグループ化して管理するなども可能です。

##### ５、アプリのスケーリング
Podを空きリソースのあるノードに展開することができます。(縮小することも可能である)これにより、アプリの負荷分散が実現可能です。

##### ６、アプリのアップデート
アプリのアップデートではスケーリング(複数のノードを動かしていること)は最低条件になります。
そうすることでPodsを段階的にアップデートして、ダウンタイムをゼロにすることも可能です。( = **ローリングアップデート**)

### **Kubernetesを支える要素とその定義**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122992/56e28986-02b4-b650-7561-ece1be760261.png)

内部的には絵をじっくり見てみると概要がつかめるかと思います。それぞれの構成要素については以下解説です。

##### kubectl
開発者がkubernetes MasterのAPIを直接叩かずに、操作するためのコマンドラインツール。
##### scheduler
どのノードで起動するか決まっていないPodを検知して、Podが起動するノードを割り当てる。
##### controller-manager
各種コントローラー。たとえば、DeploymentやReplicasetの状態を監視しながら、必要に応じてReplicaSetやPodを作成する。
##### etcd
Kubernetesクラスタ情報を永続化して格納するためのKVS。
##### kubelet
WokerNode上で動作し、コンテナ(Docker)の起動、停止の管理を行う。
##### kube-proxy
WorkerNode上で動作し、Pod宛のトラフィックを正しく転送することでPod間の疎通を確保する。


##### 参考:ノード間におけるコンテナ通信について
Kubernetesを使ってコンテナをデプロイする際、**基本的にホストを指定してデプロイすることができません**。(コンテナがその時のリソース状況に応じてホストを決定するため) つまり、デプロイされるまでコンテナのIPアドレスがわかりません。Kubernetesでは**どのホストにコンテナがデプロイされてもコンテナへ自由にアクセスできる仕組み**として`Service`があります。


## 環境構築ツール
Kubernetesを実際に運用する際のポイントについては、引用させていただいた本の著者のブログにもあるように、アプリケーション視点が重要で基本的に自前で***オンプレ構築というよりはマネージドサービスを使って構築・管理するほうが効率的***かと思います。

> [Kubernetes は辛いのか？](https://amsy810.hateblo.jp/entry/2019/04/03/071858)

### minikube(←ローカル環境向け)
Kubernetesをローカルで容易に実行してくれるツールです。
単純なクラスタをデプロイする軽量のKubernetesが実装されています。動作について確認されたい場合は勉強になります。

### パブリッククラウドサービス
大規模環境構築をしたわけでもありませんが、インフラ周りはダッシュボードありきでリソース状況が可視化できるものがおすすめです。Kubernetesを使ったコンテナ運用サービスでリードしているのはGCPかと思いますが、AWS, Azureも対応は進めるでしょう。

* — [Google Kubernetes Engine](https://cloud.google.com/kubernetes-engine/?hl=ja)
* — [Amazon Elastic Kubernetes Service](https://aws.amazon.com/jp/eks/)
* — [Azure Kubernetes Service](https://azure.microsoft.com/ja-jp/services/kubernetes-service/)


補足：クラウドコントローラーマネージャ(CCM)により、クラウドプロバイダがKubernetesと統合できるようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122992/bf4e253d-4d1b-81ee-cba6-453c9ef47b9c.png)

### [Rancher](https://rancher.com/)
Rancher Serverが中央集権的にKubernetesクラスターを管理できます。メリットとしてダッシュボードによって管理できたり、異なるクラウドプロバイダーのプラットフォームを管理できたりすることが挙げられます。

> [様々な環境で実験してみる](https://labs.play-with-k8s.com/)


## Kubernetesリソースまとめ
リソースについては公式を見るのが早いでしょう。開発スピードもあるので、大幅に変わってるかもしれませんが、一応自分用のメモなので、雰囲気をつかみたい方はざっと目を通してみてください。

#### リソースの種類
* 👉Workloadsリソース
* 👉Services, LB&Networkリソース
* 👉Storageリソース
* 👉Configuration, Securityリソース

### 👉Work Loadsリソース

以下で説明しているリソース上位関係は以下の通り。   
  
* Pod << ReplicaSet << Deployment
* Pod << DaemonSet
* Pod << StatefulSet 
* Pod << Job << CronJob
  

#### Pod
1Pod内に1コンテナが基本だが、異なるPortを指定することで２コンテナ以上含めることも可能。1Podに1つのIPアドレスが割り当てられる。


#### ReplicaSet
複数のPodを並行して作成する。 指定した数のPod数を常に維持する。また、Pod数が指定数を超過した場合、Podを削除する。そのときにPodのラベリングをもとにPod数を調整する。
>セルフヒーリング機能...ノードやPodに障害が発生した場合も別ノードでPodを自動的に起動してくれる。   
  
 

  
```yaml:sample-replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
	name: sample
spec:
	replicas: 3
selector: 
		matchLabels:
			tier: web-proxy
	template:
		metadata:
		labels:
            tier: web-proxy
	spec:
		containers:
			-name: nginx-container
			 image: nginx:1.79
			 ports:
				- containerPort: 80
```

#### Deployment
複数のReplicaSetを管理し、ローリングアップデート(Podの稼動状態を維持しながら、順番にPodをアップデートする仕組み)やロールバックを実行するためのリソース。  

  
```yaml:sample-deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sample
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
```

#### DaemonSet
ReplicaSetはPod配置を自動調整するのに対して、DaemonSetは各ノードに1Podを原則として配置(ただし、配置したくないノードを設定することも可能) 

#### StatefulSet
ReplicaSetと異なり、sample-statefulset-1, sample-statefulset-2... sample-statefulset-Nというようにインデックスが付与され、データを永続化させる ( PersistentVolumeClaimで永続化領域を確保する。)
データベースなどに対応するためのリソース。

#### Job
1つ以上のPodを利用して一度限りの処理(バッチ処理)を実行させるリソース。  
specで以下のパラメーターを設定可能
* Completions...目標成功回数(回数分成功したらジョブを終了) 
* Parallelism...並列度 
* backoffLimit...許容失敗数 

#### CroneJob
Jobのスケジュールを管理するリソース 。  
CroneJobのポリシーについては以下を参照
* Allow...Jobの同時実行に対して制限を行わない
* Forbid...前のJobが終了してない場合は次のJobは実行しない 
* Replace...前のJobが終了してない場合はキャンセルして次のJobを実行する 


### 👉Services,LB&Networkリソース

Kubernetes環境では内部ネットワークが設定されるため、Pod間通信は基本的にクラスタ構築時点で可能。  
=> ただServiceを利用すれば、Podのトラフィックのロードバランシングや公開するPod群(サービス)に名前を定義して、IPアドレスやPortの情報を提供することができる。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/122992/8bbab703-0fcf-a612-2592-97e0310c5a5e.png)

  
#### Cluster IP Service
内部ネットワークに仮想IPを割り当て、Podへの通信をロードバランシングしながら転送する役割を持つ。
  
```yaml:sample-clusterip.yml
apiVersion: v1
kind: Service
metadata:
	name: sample-clusterip
spec:
	type: ClusterIP
  ports:
    -name: "http-port"
     protocol: "TCP"
     port: 8080
     targetPort: 80
	selector:
		app: sample-app
```

#### External IP Service
**指定したノード**のIPアドレスとPortで受信→ClusterIPに転送→各Podに転送するように設定する。
(マニュフェストファイルのタイプはClusterIPと同じ)


#### NodePort Service
**全てのKubernetesノードのIPアドレスとPort**で受信(利用できるポート範囲はデフォルトだと30000~32767)→ClusterIPに転送→各Podに転送するように設定する。


#### LoadBalancer Service
Cluster IP Serviceが**Podへの通信をロードバランシングするのに対して**LoadBalancer Serviceは**ノードへの通信をロードバランシングする**。(ノード障害に強い)

```yaml:sample-lb.yml
apiVersion: v1
kind: Service
metadata:
	name: sample-lb
spec:
	type: LoadBalancer
  ports:
		-name: "http-port"
		 protocol: "TCP"
       #LBが払い出す仮想IPとClusterIPで受信するport
		 port: 8080
		 #転送先のコンテナのport
		 targetPort: 80
       #kubernetes上の全ノードのIPアドレスで受信するport
		 nodePort: 30082
 selector:
		app: sample-app
```


#### Headless Service
通常クラスタ内DNSでPod名の名前解決は行われない。
DNS Round Robinを使ってService名からPodのIPアドレスを割り当てるリソース。(Cluster単位のIPアドレスではなく、Pod単位のIPアドレスで考える)  
・DNS Round Robin…1つのドメイン名に複数のIPアドレスを割り当てる負荷分散技術

【前提条件】
- Serviceのspec.typeがClusterIPであること
- Serviceのspec.clusterIPがNoneであること
- Serviceのmetada.nameがStatefulSetのspec.serviceNameと同じであること	

#### ExternalName Service
Service名の名前解決に対してCNAME(外部のドメイン宛)を返すリソース。


#### None-Selector Service
Service名で名前解決すると自分で指定したホストに対してロードバランシングを行うリソース。

#### Ingress
ServiceがL4ロードバランシングを提供するのに対して、L7ロードバランシング(HTTPS終端)を提供する特殊なリソース。  
HTTPS負荷分散を実現できるため、トラフィックの多い通信処理を捌くのに向いている予感。

- クラスタ外のロードバランサーを利用したIngress  
クライアント => L7ロードバランサー(NodePort経由) => 転送先のPod

- クラスタ内にIngress用のPodをデプロイするIngress  
クライアント => L4ロードバランサー => Nginx Pod => 転送先のPod

### 👉Storageリソース
まずVolume, PersistentVolume, PersistentVolumeClaimの違いについては以下の通り。 
* Volume...ホスト領域に用意されている利用可能なボリューム(基本的にはファイルシステムと考えてよい)
* PersistentVolume...外部の永続ボリュームを提供するシステムと連携して、新規のボリューム作成や既存のボリューム削除を行うことができる。 
* PersistentVolumeClaim...PersistentVolumeはクラスタにボリュームを登録するためのリソース。このリソースによって、PersistentVolumeを操作することが可能。 

#### Volume
ディレクトリAWS EBS, Azure Disk, emptyDir, hostPathなど様々なボリュームタイプをサポート。サポートされているボリュームによってその特性も変化する。詳しくは公式を参考。ちなみにemptyDirとhostPathの違いは以下の通り。  
* emptyDirはホストノード上のコンテナ用の一時領域をコンテナ上の領域にマッピングする(すぐ消える)
* hostPathはホストノード上の領域にマッピングできるが、セキュリティ上注意は必要
  
```yaml:sample-emptydir.yml
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /cache
      name: cache-volume
  volumes:
  - name: cache-volume
    emptyDir: {}

```
  
```yaml:sample-hostpath.yml
apiVersion: v1
kind: Pod
metadata:
  name: test-pd
spec:
  containers:
  - image: k8s.gcr.io/test-webserver
    name: test-container
    volumeMounts:
    - mountPath: /test-pd
      name: test-volume
  volumes:
  - name: test-volume
    hostPath:
      # directory location on host
      path: /data
      # this field is optional
      type: Directory

```

#### PersistentVolume(PV)
基本的にネットワーク経由でアタッチされるディスク領域と考えてよい。(もちろんKubernetesクラスタ内のリソース領域)  
`PersistentVolumeClaim(PVC)`によって要求され、プロビジョニングされる。流れとしては、PVCにバインドされたPVが最終的にPodにマウントされます。PVCのyamlファイルのselectorに一致するPVだけがバインドされます。   
  
```yaml:sample-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2
```
  
```yaml:sample-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 8Gi
  storageClassName: slow
  selector:
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}
```


#### VolumeSnapshotContent
クラスタ内のボリュームからスナップショットを作成するリソース。`VolumeSnapshot`というユーザー要求から作成される。

### 👉Configuration, Securityリソース

#### Secret 

ユーザー名やパスワードなどの機密情報を保持するリソース。環境変数として参照させることが可能。Dockerイメージやマニュフェストファイルで記述せず、一括管理可能。(※Configmapとは異なり、機密情報を扱うためにメモリ上の一時的なファイルシステムにデータが保持される仕組みになっている。)以下Secretの種類。    

###### GenericタイプのSecret 
kubectlコマンドやマニュフェストファイルからの作成が可能。 

```yaml
apiVersion: v1 
 kind: Secret  
metadata: 
 name: sample-db-auth 
 type: Opaque  
data:  
	username: cm 9 vdA == # root  
	password: cm 9 vdHBhc 3 N 3 b 3 Jk # rootpassword 
```


###### TLSタイプのSecret 
秘密鍵と証明書のファイルから作成する。
```sh:
$ kubectl create secret tls --save-config tls-sample --key ~/tls.key --cert ~/tls.crt 
```


###### DockerレジストリタイプのSecret 
プライベートレポジトリ用のDockerイメージの取得時に必要な認証用。 
 
```sh
$ kubectl create secret --save-config docker-registry sample-registry-auth 
\ --docker-server = REGISTRY_ SERVER 
\ --docker-username = REGISTRY_ USER  
\ --docker-password = REGISTRY_ USER_ PASSWORD  
\ --docker-email = REGISTRY_ USER_ EMAIL 
```

 

#### Secretの利用方法 

##### 1. 環境変数を渡す 
spec.containers[].envのvalueFrom.secretKeyRef使って指定する 

##### 2. Volumeとしてkeyのみ、Secret全体をマウントする 
keyのみの場合は、Spec.volumes[]のsecret.items[]を指定。 
Secret全体の場合は、Spec.volumes[]のsecret.secretName[]を指定。 
→Volumeにマウントしておくと、定期的にkube-apiserverに変更を確認し、更新してくれる 

 

##### [kubesec](https://github.com/shyiko/kubesec) について

Secretリソースを暗号化して安全に管理するためのオープンソースソフトウェア 。Google Cloud KMS, AWS KMSの暗号鍵と並行して利用することが可能。 


#### Configmap

単純なKey-Value値や設定ファイルを管理するリソース。 
リソース作成方法や利用法はGenericタイプのSecretとほぼ同じ。よって省略。 


    
</br>    
</br>  
  

リソース内容についてあまり綺麗にまとまってなくてすいません。もっと他に綺麗にまとまっている記事があるはず...
  

</br>    
</br>    
</br>    
</br>  
  


マイクロサービス指向の流れでKubernetesにも注目が集まっているのに乗っかって基本的な部分やリソースを含めまとめてみました。最近知ったこととして、コンテナを利用してマイクロサービス化を進めても、最終的には実装するアプリケーションはそのコンテナサービスを組み合わせ、統合することで処理を実装するので、そういったサービス分割後の統合する技術も重要なのだということです。特にコンテナ間通信の高速化・安定性・スリム化については、悩みのタネで様々な解決手法が提示されているようです。[gRPC](https://grpc.io/)などもその一翼を担う技術かと思います。開発でも`docker-compose up`に慣れてきたので、コンテナ本番運用に関する知見を増やしていこうと思います。