---
# System prepended metadata

title: KT#009-kanda Kubernetes
tags: [コンテナ, kubernetes]

---

# KT#009-kanda Kubernetes

###### tags:`kubernetes` `コンテナ`

# イントロダクション
今日は[Kubernetes](https://kubernetes.io/ja/)を紹介します。
多くのサービスの基盤としてデファクトスタンダードになりつつあるコンテナオーケストレーションツールで、聞いたことが多い人もいるでしょうが、自分の考えを整理するためにもまとめてみました。
**実運用は全然したことないので、現場を知らない人の知見です。**
ご了承ください。

#### ※k8sという表記について
以降、本文中でKubernetesと表記する代わりに**k8s**と表記します。
これは、"Kubernetes"が"k + 8文字 + s"であることに由来する略称です。
読み方はKubernetesと同じく"くーばねてぃす"です。

# K8sをざっくり説明する
## 「素」のDockerの弱点
前回、Dockerという技術を紹介しました。
DockerはLinuxカーネルを利用した**コンテナ技術**でしたが、この技術単体でサービスを構築しようとすると、いろいろな障壁が考えられます。
サービスが小規模であれば特に問題はありませんが、規模が大きくになるにつれていろいろと不都合な部分が出てきます。
その最たるものが**可用性**でしょう。自分のマシンでちょっとハンズオンするぐらいなら素のDockerコマンドを叩くぐらいでいいでしょうが、これだと例えば負荷が高まってマシンパワーが足りなくてコンテナが落ちたり、アプリケーションがエラーで止まったり、マシンそのものがダウンしてしまったりなど、サービスが停止してしまう恐れが高いです。
また、**コンテナ同士の連携**も課題として残ります。
あるサービスを提供するのに、それを一つのミドルウェアのみで実現することは少ないと思います。
Webサーバ・アプリケーションサーバ・DBサーバなどを組み合わせる必要があると思いますが、素のDockerだけではそのような設計は不向きです。
## docker-composeの限界
前回のDockerの紹介時には、Dockerと合わせて`docker-compose`というものも紹介しました。
これは、複数のコンテナを同時に立ち上げるときに使えるツールで、適切なyamlファイルを書くことで、うまい具合に設定を流し込んだ状態でコンテナを建てたり、コンテナ間の疎通を名前解決で行えたりします。
「複数のコンテナを連携させるなら`docker-compose`でいいんじゃね？」と思うかもしれませんが、**`docker-compose`はあくまでコンテナの起動などを簡単に行えるツール**であり、サービスの保守・運用の質を向上させるものではありません。要はコンテナが起動したら「はい、お役御免」といった立場なのです。
アプリケーションの可用性を確保してサービスの保守・運用をするという面では、`docker-compose`は機能不足です。
## コンテナオーケストレーションというやつ
というわけで、一口に「複数のコンテナを同時に起動させる」といっても、「基盤目線でのサービスの保守・運用を効率化させる」という点では、複数のコンテナをいい感じにデプロイ・監視し、調和を取る必要があります。それを「コンテナオーケストレーション」といい、k8sはこれを担う「コンテナオーケストレーションプラットフォーム」なのです。
といっても、具体的に何ができるのかわかりにくいので、[この記事](https://thinkit.co.jp/article/13289)からk8sで出来ることリストを引用してみました。
- 複数のDockerホストの管理
- コンテナのスケジューリング
- ローリングアップデート
- スケーリング / オートスケーリング
- コンテナの死活監視
- 障害時のセルフヒーリング
- サービスディスカバリ
- ロードバランシング
- データの管理
- ワークロードの管理
- ログの管理
- Infrastructure as Code
- その他エコシステムとの連携や拡張

少し具体的な表現になりました。これらを少しずつ取り上げていこうと思います。
・・・の前に、k8sを構築するときの構成について少し解説します。

ちなみに、k8sの基本的な使い方としては、**「エンジニアが理想的な状態を指示してやる」** という考え方がわかりやすいと思います。
「こういう感じでサービスを維持したい！」という理想的な状態をyamlファイルに記述し、それをk8sに適用することで、あとは **自動的に** その状態が保たれる、というのがk8sのコンセプトになっています。
# K8sの構成
## 物理
### ノード
物理マシンを指します。
K8sでは複数のノードを同時に稼働させることで可用性を確保します。
また、ノードはマスターノードとワーカーノードの2つがあります。
#### マスターノード
ワーカーノードに対して命令を出すノードです。
コンテナ自体は稼働させず、ユーザからのAPIコールを受け付けます。
#### ワーカーノード
コンテナが稼働するノードです。
マスターノードによってヘルスチェックされます。
### クラスタ
ノードの集合体です。
## コンポーネント
### kube-apiserver
マスターノードで稼働するAPIサーバです。
k8sのユーザは、k8sを管理するときにこのAPIサーバに向かってAPIをコールします。
APIサーバはリクエストに応じてk8s全体を操作します。
つまり、我々エンジニアは、kube-apiserverを通じてk8sを管理します。
このAPIを叩くときに用いるコマンドは`kubectl`です。
エンジニアは設定をyamlファイルにまとめた後に`kubectl apply -f deployment.yaml`のようにAPIを叩くことで、k8sを操作することができるのです。
このようにAPIリクエストを受け取ったマスターノードは、`deployment.yaml`に記述された状態を維持しようとします。
## オブジェクト
### Pod
1つ以上のコンテナを含む集合体です。
1つ以上、というのは1つのPodに複数のコンテナを含むことができるという意味です（そのままの意味）。
とりあえず、デプロイする最小単位、ぐらいの認識でいいです。
同じPod内に存在するコンテナは簡単に疎通できます。
### Service
Podに紐付けられるオブジェクトで、Podの通信を司ります。
こいつがないとPodは他のPodと通信できません。
また、通信相手が同一ノード・同一クラスタ・クラスタ外の場合に応じて3種類用意されています。

**結構奥が深いので、ここだけでもう一回KT会できます。**
### ReplicaSet
上述のPodの可用性を管理します。
あらかじめ決められた数のPodの稼働を確保します。
AWSでいうところのAutoScallingのイメージが近いでしょう。
### Deployment
上述のReplicaSetを管理します。
ReplicaSetのレプリカ数を指定し、ReplicaSetオブジェクトを生成します。
Deploymentの設定を変更すると、自動的にそれに合わせて適切にReplicaSetを管理します。

## クラウドプロパイダのk8sサービスを使うということ
AWSのEKSやGCPのGKEは、**k8sのマネージドサービス**としてリリースされています。
これらのサービスを利用すると何が嬉しいのでしょうか。

もしk8s環境を1から作ろうとすると、まずノードを準備する必要があります。
ノードにあたるVMインスタンスを複数台立ち上げ、k8sでクラスタを組めるようにインストールや適切な設定を施します。
また、ノードそのものの可用性も必要なので、ノードのスケーリングも設定します。

これらの作業はそこそこの手間がかかるのですが、それを解決してくれるのがk8sマネージドサービスです。例えばAWSのEKSを用いると、画面をポチポチしていくだけで必要な数のノードを自動的にワーカーノードグループとしてグルーピングしてくれ、AutoScallingグループとしてもまとめてくれます。
もちろん、必要なパッケージなどはインストール済みですので、クラスタ構築が完了すれば後はyamlファイルを書いて`kubectl apply -f deployment.yaml`を叩くだけです。
（もちろん、`kubectl`コマンドをクライアント側＝エンジニアの端末にインストールして、接続先情報をEKSのクラスタに設定しておく必要はあります）

また、上述のServiceオブジェクトとの親和性も高いです。
ServiceはPodのネットワークを管理しますが、Serviceのタイプに`LoadBalancer`を指定すると、自動的にクラウドプロパイダ上のLBマネージドサービスと連携してくれます。
例えばEKSでService(LB)をデプロイすると、裏で自動的にALBインスタンスが立ち上がり、指定したワーカーノードグループへのトラフィックの振り分けを行ってくれます。

このように、「素」のk8sに更に他のマネージドサービスと連携させることができる、というのがクラウドプロパイダのk8sマネージドサービスを利用するメリットといえます。
# K8sでできること
基本的に公式ドキュメントの[Deployment](https://kubernetes.io/ja/docs/concepts/workloads/controllers/deployment/)を参考にハンズオンします。
## 複数のDockerホストの管理
k8sの機能の基本です。
実際に以下のyamlをapplyしてみます。
```
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80
```
ここでは、レプリカ数を3に設定しています。つまり、**nginxのPodが常に３つ立ち上がっているようにする** とk8sのマスターノードが認識します。
`spec.template.spec`以下では起動させたいコンテナを記述します。今回はバージョン1.14.2ですね。
また、80番ポートを開放させています。
## ローリングアップデート
nginxのバージョンを上げたいときを考えます。
現状、すでに1.14.2が立ち上がっていますが、これを1.16.1にバージョンアップします。
**ただし、サービスを停止させたくありません。** そのためには、今稼働しているPodの一部を落としてバージョンアップした後稼働させ、それを順次繰り返すことによって全体を順番にアップデートさせます。これを「**ローリングアップデート**」と言います。
k8sでは、コンテナのバージョン（厳密にはイメージ？）を変更するようAPIをコールすると、自動的にこのローリングアップデートを走らせることができます。
以下のコマンドを叩いてみます。
```
kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1 --record
```
すると、既存のPodがすべて終了し、新しいPodが3つ稼働します。これは、Deploymentに変更を加えることによってReplicaSetが作成されなおしたからです。
## スケーリング / オートスケーリング
レプリカ数を3から10に増やしたくなったとします。
このようなときは、以下のコマンドを叩いてみます。
```
kubectl scale deployment.v1.apps/nginx-deployment --replicas=10
```
すると、レプリカ数が10に増えました。
これは、レプリカ数を変更しただけなので、ReplicaSetは変わりません（パラメータが変わっただけです）。

もし、レプリカ数を動的に変更したいのであれば、以下のようなコマンドを叩きます。
```
kubectl autoscale deployment.v1.apps/nginx-deployment --min=10 --max=15 --cpu-percent=80
```
このコマンドにより、最小レプリカ数は10で、CPUの使用率が80%を超えるようであればレプリカ数を順次増やしていく設定が可能です。最大レプリカ数は15です。
## コンテナの死活監視
k8sは可用性を高めることができると言いましたが、具体的にどうやってヘルスチェックを行っているのでしょうか。
k8sでは、大きく２つのヘルスチェックのカテゴリがあります。
- liveness
- readiness
### liveness
**コンテナが正常に起動できているか** をチェックします。あくまで起動しているか、だけチェックするので、その上でアプリケーションが正常に動作しているかまではチェックしません。いわば低レベルの起動確認です。
もしこれが`false`であるようなら、k8sはコンテナを再起動させます。少なくともコンテナの起動までは面倒を見るということです。
### readiness
**コンテナ上のアプリケーションが正常に動作しているか** をチェックします。コンテナの起動は正常で、アプリケーションレイヤーでの動作確認です。そのため、k8sはたとえ`false`であってもコンテナの再起動は行いません。

また、確認方法は３通りあります。
- 任意のコマンドを実行してその戻り値で判断
- HTTPリクエストを投げてそのステータスコードで判断
- TCPパケットを投げてポートが開いているかで判断

Webサーバを例にすると、livenessはコマンドで確認し、readinessはHTTPリクエストで確認するような使い方が考えられます。

# まとめ
**Kubernetes** はコンテナオーケストレーションツールとしてデファクトスタンダードになっています。
コンテナを用いたサービスの保守・運用の根幹をなしており、GitやCI/CDとの相性も良いです。
インフラエンジニアはyaml職人としてコーディングし、クラウドプロパイダのマネージドサービスを利用して可用性を確保することができます。

また、ここで紹介しきれなかった機能も山のようにあります。
更に、k8sにアドオンする形で使うことができるサードパーティツールも山のようにあります。
興味のある方は`CNCF`とかでググるとk8sを含むクラウドネイティブアプリケーションツールに詳しくなれると思います。