【学習メモ】コンテナとKubernetes

業務で携わったことは無いですが個人的に興味があったのでコンテナやKubernetesについて学習したので、概要的な内容ですが本記事にまとめてみました。

#コンテナとは
###コンテナの無い従来の世界
例えば、コンテナ技術を使用しない場合、1台のサーバに複数のアプリケーションを動作させる際に、アプリケーションAとアプリケーションBともに「/usr/share/myproject」にインストールすることはできません。インストールディレクトリの重複になります。この場合はアプリケーションBを「/usr/share/myprojectB」のような別のディレクトリを作成して、インストール先を変更したりする調整が必要になります。また、システムAとシステムBでフレームワークを共用していた場合、システムAの都合でフレームワークをアップデートしなければいけなくなった際、アップデート後にシステムBも動作するかどうか事前に検証が必要になります。従来の環境だと複数のアプリケーション・システムが周囲に影響を及ぼし、構築やアップデートに手間や時間がかかるという問題があります。このような問題を解決するのがコンテナという考え方です。

###コンテナとは
コンテナとは「互いに影響しない隔離された実行環境を提供する技術」です。先ほどの例で説明すると、アプリケーションAとアプリケーションBをそれぞれコンテナ化することで、同様のディレクトリパスにアプリケーションをインストールすることが可能になります。また、システムAとシステムBもコンテナ化して、フレームワークもシステムA用とシステムB用で分割することで、システムA都合のフレームワークのアップデートがシステムBに影響を及ぼさなくなります。また、基本的にコンテナの中で動作するアプリケーションはコンテナ内にパッケージングされており、コンテナ外部に設定ファイルを置いたりせず、あくまでコンテナ単位でアプリケーションの動作は完結します。このように、周囲に影響を出さないし、周囲からの影響も受けないでアプリケーションが動作するという環境を実現するのがコンテナ技術です。VM技術との違いは、コンテナはサーバでは無くOSイメージを含んでいないので、非常に軽量という点です。

###コンテナのメリット
コンテナを使用することの代表的なメリットとしては、「基本的にどの環境でも同様に動作する」ことが挙げられると思います。「基本的にどの環境でも同様に動作する」ということは、例えば、検証環境で動作したコンテナはほぼ確実に本番環境でも同様に動作するので、従来の検証環境と本番環境の差異で発生するデプロイ時のトラブルを予防することが出来ますし、コンテナを入れ替えるだけなので作業を簡素化することもできます。また、チーム開発をしている場合は、ベースのコンテナイメージからローカル開発環境を簡単に構築することもできるため、ローカル開発環境の構築等で無駄に時間を使うことも無くなります。要するにコンテナ技術で本番デプロイ時のリスクを軽減して、従来の作業を短縮することができるようになります。

###コンテナの普及
コンテナは年々普及しています。理由は先に述べた、本番デプロイ時のリスクを軽減して、従来の作業を短縮するからですが、要はシステムがユーザのニーズを満たすために、高速でシステムをアップデートする必要性が高まっているからです。IT企業で代表的なGoogle、AWS等の企業も毎年、多数の新サービス提供とアップデートをしていますが、これも顧客・ユーザのニーズを取りこぼさないために行っています。そのためには、高速でシステムをデプロイ・アップデートしていく必要があり、そこで従来のシステムでは対応できず、コンテナ技術が必要というわけです。

###コンテナの課題
2台、3台程度のサーバ上のコンテナであれば、運用・管理することも可能だと思います。サーバ1台に障害が発生したとしても、別のサーバにコンテナを手動で起動して運用することも可能だと思います。しかし、それが10台、20台、100台、1000台と増えた場合、どのサーバにどのコンテナが動作しているのか、日々、正確に把握して管理することが可能でしょうか。Dockerなどのコンテナ技術だけでは運用・管理・可用性・スケーラビリティに対応することが出来ません。そこで人の代わりに、コンテナを運用・管理・可用性・スケーラビリティに対応するものがKubernetesを代表とするコンテナオーケストレーションツールです。

#Kubernetesの概要
###Kubernetesとは
Kubernetesはコンテナのデプロイやスケーリングなどの管理を自動化するためのコンテナオーケストレーションツールです。コンテナオーケストレーションツールはKubernetesの他にOpenShift、DockerSwam、Mesos、Rancher、Amazon ECS等色々とあります。この中でも最も使用されておりデファクトスタンダートになっているのがKubernetesです。([sysdig-2019-container-usage-report](https://sysdig.com/blog/sysdig-2019-container-usage-report/)) Kubernetesを使用することで、コンテナの手動での管理から解放されます。

###Kubernetesの歴史
Linux Foundationのプロジェクトの1つで、コンテナ技術の推進と、その進化を取り巻くテクノロジー業界の足並みを揃えるために創設された財団のCNCF(Cloud Native Computing Foundatioin)がホストして、コミュニティーが主体となって開発が進められています。2015年7月以前はGoogleがホストしていましたがKubernetesバージョン1.0となったタイミングでCNCFに移管されました。Googleの社内で利用されていたコンテナクラスタマネージャの「Borg」をアイデアの元にして作られたのがKubernetesです。

#Kubernetesで出来ること
###スケーリング/オートスケーリング
同じコンテナイメージを元に複数のKubernetesノードにデプロイすることで、負荷分散を実現することや耐障害性を向上することが出来ます。また、負荷に応じて自動的にコンテナ数を増減することが出来ます。

###ロードバランシング
特定のコンテナに負荷が集中しないように、負荷を分散することが出来ます。

###スケジューリング
コンテナをどのKubernetesノードにデプロイするのか管理する機能です。Affinity/Anti Affinityの機能を使用することで、コンテナ内のアプリケーションの特徴やKubernetesノードの性能を意識して、コンテナをKubernetesノードにデプロイすることが出来ます。例えば、ディスクのI/Oが多いコンテナを、ディスクがSSDのKubernetesノードにデプロイする、といった制御が可能になります。

###セルフヒーリング
Kubernetesでは標準でコンテナのプロセス監視を行っており、例えば、Kubernetesノードがダウンした場合、コンテナのプロセスダウンを検知して別のKubernetesノードにコンテナを復旧させる、といった動作が実現できます。このセルフヒーリング機能で耐障害性を高めることが出来ます。

###リソース管理
KubernetesノードのCPUやメモリのリソースに応じて、ユーザが意識する必要無く、コンテナのデプロイ先を自動的に調整します。また、Kubernetesノードのリソースの消費量に従い、Kubernetesクラスタのオートスケール機能でKubernetesノードを自動的に増減することも可能です。

###ローリングアップデート
コンテナ内のアプリケーションをアップデートする際に、新しいコンテナを追加してから古いコンテナを削除する手順を繰り返し行うことで、ダウンタイム無しでのアップデートを実現することが出来ます。

###サービスディスカバリ
前述のオートスケーリングやスケジューリング、セルフヒーリングなどKubernetesはダイナミックにコンテナを稼働させます。そのため、どこにどのコンテナが存在しているのか把握することが難しくなります。Kubernetesはコンテナがどこにいるのか、アドレスは何かなど把握して、ダイナミックに変化するシステムにおいてもコンテナが相互の相互アクセスを実現します。

###設定・機密情報の管理
環境変数やコマンドライン引数、パスワードやTLS証明書などの情報をKubernetesで一元的に管理することが出来ます。環境変数やコマンドライン引数はConfigMapで管理して、パスワードやTLS証明書等の機密情報はSecretsで管理します。

#Kubernetesの基本知識
Kubernetesを知るためには、基本知識を押さえる必要があります。

##基本知識

###マニフェスト
Kubernetesでは、YAML形式やJSON形式で記述したデプロイするコンテナや周辺のリソースを管理するための宣言的なコードをマニフェストと呼びます。コードで環境を管理することが出来るので、KubernetesはIaC(Infrastructure as Code)を実現することが可能です。

###kubectl
kubectlと呼ばれるCLIツールを使用して、後述するKubernetes Masterに命令を送ります。ユーザはkubectlコマンドで様々な操作をすることができるようになります。kubectlコマンドで前述のマニフェストファイルを指定して使用することも出来ます。

###Kubernetes Node
KubernetesにはKubernetes MasterとKubernetes Node(以降、Node)の2つのノードがあります。NodeはDockerホストのような役割で、コンテナを実際に動作させる場所です。

###Kubernetes Master
Kubernetes Master(以降、Master)はマニフェストに記載されている内容を元にkubectlを通じて、指令を受け付けること、そして自分の管理下にあるNode上に受け付けた指令を実現させます。例えば、受け付けたマニフェストファイルが各Node上のコンテナ数を増加させる内容だった場合、実際に各Node上のコンテナ数を増加させる役割をMasterは担います。

##Kubernetesのリソースを知る
###Workloads APIsカテゴリ

####Pod
Kubernetesリソースの最小単位です。Podは中に1つ以上のコンテナを含んでいます。PodにはNICが1つ付いていて、複数のコンテナがPod内に存在している場合は、NICを各コンテナは共用します。特に理由が無い場合、「Podの中のコンテナは１つ」が推奨されています。

####ReplicaSet
ReplicaSetはPodを複数デプロイする場合に使用するリソースです。ReplicaSetに定義した数のコンテナをスケジューリングしてNode上にデプロイします。仮にNodeに障害が発生してそのNode上のコンテナが無くなっても、ReplicaSetに定義したコンテナ数になるように、オートヒーリングで別のNodeに自動復旧します。

####Deployment
複数のReplicaSetを管理するのがDeploymentリソースです。Deploymentはローリングアップデートに利用されます。以下ような手順でDeploymentは古いReplicaSetAのPodを新しいReplicaSetBのPodへローリングアップデートします。
１.(新)ReplicaSetBを作成する
２.(新)ReplicaSetB上のPod数を1つ増やす
３.(古)ReplicaSetA上のPod数を1つ減らす
４.手順2と手順3を繰り返す
５.(古)ReplicasetAのPod数は0で維持する

####DaemonSet
DaemonSetを使用すると各Nodeに１つずつ均等にPodを配置します。ReplicasetのようにPodの数を指定することは出来ませんし、リソースに応じてスケジューリングによってコンテナを配置する動作はしません。用途としてはdatadog agentなどのメトリクスエージェントの実行や、fluentdのようなログ収集デーモンを実行する際などに利用されます。

####StatefulSet
データベースなどのステートフルなアプリケーションをデプロイ・管理するためのリソースです。ワークロードに永続性を持たせるために同じPod名を維持する仕組みがあります。新たにPodを作成する場合は[Pod名]-[Nunber]のように命名します。例えば、``web-0,web-1,web-2``のようになります。

####Job
コンテナを利用して一度限りの処理を実行させるリソースです。JobとRepicaSetとの大きな違いは、「起動するPodが停止することを前提にして作られているかどうか」です。基本的にReplicaSetでは、Podの停止は予期せぬエラーです。一方、Jobの場合は、Podの停止が正常終了ち期待されるようとに向いています。例えば、「特定のサーバとのrsync」や「S3などのObject Storageへのアップロード」などが挙げられます。ReplicaSetなどでは正常終了の数をカウントしているわけではないため、バッチ的な処理の場合はJobを積極的に利用しましょう。

####CronJob
Cronのようにスケジュールされた時間にJobを作成するためのリソースです。
CronJobがJobを管理して、JobがPodを管理するような３層の親子関係になっています。

###ServiceAPIカテゴリ

####【Serviceカテゴリ】
####ClusterIP
最も基本となるServiceです。Kubernetesクラスタ内からのみ疎通性のあるInternal Networkに作り出される仮想IPが割り当てられます。そのため、ClusterIPと呼ばれています。ClusterIPは、Kubernetes Cluster外からトラフィックを受ける必要がない箇所などで、クラスタ内ロードバランサとして利用します。
####Nodeport
全てのKubernetes NodeのIPアドレス:Portで受信したトラフィックをコンテナに転送する形で、外部疎通性を確立します。ClusterIPがKubernetesクラスタ内からのみ通信可能であるのに対して、NodeportはKubernetesクラスタ外からも通信可能となります。
####LoadBalancer
Kubernetesクラスタ外のロードバランサに外部疎通性のある仮想IPを払い出すことが可能で、ノードに障害が発生した場合でも別のノードに通信を切り替えることが可能になり可用性が向上します。NodePortでは結局のところいずれかのKubernetes Noodeに割り当てられたIPアドレス宛に通信を行うため、そのノードが単一障害点(SPoF)となってしまいます。

####【Ingressカテゴリ】
####Ingress
上記のServiceがL4(ネットワーク層・トランスポート層)のロードバランシングを提供するリソースであるのに対して、IngressはL7(アプリケーション層)のロードバランシングを提供するリソースです。Ingressを利用することで、ホスト名を利用したバーチャルホスティングなどHTTPに類する通信を柔軟にPodを振り分けられるようになります。利用するには、別途、Ingress Controllerと外部あるいは内部にロードバランサーが必要です。

###Config & Storage APIsカテゴリ
####Config Map
機密性のないデータをKeyとValueのペアで保存するために使用されるリソースです。環境変数やコマンドライン引数、ボリューム内の設定ファイルとして使用できます。
####Seclet
データベースに接続する際に使用するユーザ名やパスワードなどの機密情報を保管して、コンテナが接続する際にセキュアに情報を渡すリソースです。Secretに機密情報を保存することは、Podの定義やコンテナイメージに直接記載するより安全で柔軟です。
####Persistent Volume
コンテナのデータは一時的なものでPodやコンテナが削除されればデータが消えてしまいます。Volumeはデータを永続化するためのリソースです。また、Pod内のコンテナ間のデータ共有の目的でも使用されます。
####PersistentVolumeClaim
永続化ボリュームの利用請求をするオブジェクトです。PersistentVolumeClaimで指定された条件(容量、ラベル)を元に、永続化領域の要求が発行されると、PersistentVolumeから適したボリュームを割り当てるようになっています。
###Cluster APIsカテゴリ
####Node
KubernetesはコンテナをNode上で実行されるPodに配置することで、ワークロードを実行します。Nodeは1つの物理的なマシンかVMのことを意味しています。これまで説明してきたリソースとは異なり、基本的にはユーザが作成や削除を行うリソースではありません。
####Namespace
物理ノード上に作られる論理的なグループです。規模が大きいシステムでクラスターを分割したい場合や、本番環境やステージング・開発環境を分割する場合に利用されます。　

##まとめ

Kubernetesのオブジェクトですが、ここに記載できていないものがまだ多くあります。学習してみて分かりましたが、Kubernetesは登場人物が多いので机上での学習だとうんざりしてきます。実際にKubernetesクラスタを作って動作確認していく学習方法が理解が進むと思いますので、これからKubernetesの学習を始める方にはそちらの方法をお勧めします。


この記事は以下の情報を参考にして執筆しました。
[Kubernetes公式ドキュメント](https://kubernetes.io/ja/docs/home/)
[Kubernetes完全ガイド 第2版](https://book.impress.co.jp/books/1119101148)
[さわって学ぶクラウドインフラ　docker基礎からのコンテナ構築](https://www.amazon.co.jp/%E3%81%95%E3%82%8F%E3%81%A3%E3%81%A6%E5%AD%A6%E3%81%B6%E3%82%AF%E3%83%A9%E3%82%A6%E3%83%89%E3%82%A4%E3%83%B3%E3%83%95%E3%83%A9-docker%E5%9F%BA%E7%A4%8E%E3%81%8B%E3%82%89%E3%81%AE%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E6%A7%8B%E7%AF%89-%E5%A4%A7%E6%BE%A4-%E6%96%87%E5%AD%9D/dp/4296106422)