独学Kubernetes　コンテナ開発の基本を最速で理解する

# はじめに
本記事は、これからKubernetesをはじめる方を対象とし、コンテナ開発の基本を最速で理解するための入門記事になります。

入門記事といっても、コンテナ型仮想化技術に対する単なる抽象概念の理解は前提知識とし、少し掘り下げながら、Kubernetesの導入や、基本的な操作及び周辺技術の理解を目的にまとめています。

## 前提知識
Kubernetesをはじめるにあたり、コンテナ型仮想化技術に関する知識が求められます。
本記事では、コンテナ型仮想化技術としてDockerを使用しますが、Dockerの基本的なことについてはTL;DRになるので割愛します。なお、おさえておきたい知識としては、以下になります。

- コンテナ型仮想化技術（概要レベル）
- コンポーネント（Docker Engine/Docker Compose/Docker Swarm/Docker Machine/Docker Kitematic/Docker Registry等）
- Dockerの操作（イメージ・コンテナ）
- オーケストレーションツールの知識
- Docker Hub

## コンテナ型仮想化技術について少し深堀
コンテナ型仮想化技術は、OSのカーネルの機能により実現さています。
OSのカーネルの機能を使用することで、ホストのリソースを各コンテナごとに隔離及び分配することで、独立した空間を作り出しています。また、この独立した空間を作り出す仕組みを提供しているソフトウェアとして、LXCがあります。

[LXC](https://ja.wikipedia.org/wiki/LXC)は、Linux カーネルが持つコンテナ機能のためのユーザスペースのインターフェースです。 DockerとLXCは混合しやすいですが、それぞれ別のソフトウェアになります。Dockerは、アプリケーションのデプロイを目的として設計されているため、「1コンテナ1プロセス」をうたっていますが、LXCは「OSレベル仮想化のソフトウェア」と言われています。 Dockerも0.8まではLXCを使用していました。

現在、Dockerアーキテクチャは4つのコンポーネントに分割されています。Docker Engine、containerd、containerd-shm、及びrunC。バイナリはそれぞれdocker、docker-containerd、docker-containerd-shim、及びdocker-runcと呼ばれます。

![ss_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/1e715077-1d5e-9708-7539-53b1d0588472.png)

コンテナを実行するために、Docker Engineはイメージを作成し、それをcontainerdに渡します。containerdは、runCを使用してコンテナを実行するcontainerd-shimを呼び出します。その後、containerd-shimはランタイム（この場合はrunC）がコンテナの起動後に終了できるようにします。このようにすると、コンテナ用の長時間のランタイムプロセスを持つ必要がなくなるため、デーモンレスコンテナを実行できます。

### ランタイム
ランタイムは、プログラム実行時のことや、実行するときに必要な物を意味します。
コンテナランタイムについて少し捕捉します。

#### containerd
[containerd](https://containerd.io/)は、ランタイムが実行したコンテナを管理します。

containerdはもともとDocker社によって開発されてきましたが、標準的なランタイム実装を実現するために、Docker社から中立的な団体である**[Cloud Native Computing Foundationに（CNCF）](https://www.cncf.io/)**に、2017年3月に寄贈されました。

#### containerd-shim
containerd-shimは、デーモンレスコンテナを可能にします。
起動されたすべてのコンテナの親プロセスであり、デーモンなしのコンテナも許可されます。

#### runC
runCは、コンテナを作成や起動したりするバイナリのプログラムです。また、runCはDocker Engineと同じ技術であるlibcontainer上に実装されています。

Docker社によって開発され、オープンソース化されていますが、現在はコンテナ型仮想化技術の標準化に取り組む**[Open Container Initiative（OCI）](https://www.opencontainers.org/)**（※1）が定義する仕様で、事実上コンテナ型仮想化技術のデファクトスタンダードとなっています。実装としては、Namespace、cgroups、pivot_root、AppArmor、SELinux、seccomp等により、セキュア（※2）なコンテナを作り出しています。

（※1）Open Container Initiative（OCI）は、コンテナエコシステム内での潜在的な断片化や分割を回避するために、ソフトウェアコンテナの共通標準を確立することを目的とし、二つの指定が含まれています。

- runtime-spec：ランタイム仕様  
- image-spec：イメージ仕様

（※2）2019年2月12日に、runcの脆弱性（CVE-2019-5736）が発見されています。この脆弱性により、悪意のあるコンテナが（最小限のユーザーで）許可されます。ホストのruncバイナリを上書きして、rootレベルになるホスト上で任意のコマンドを実行することができるようになります。

- Dockerで使用しているランタイムの確認  
`docker info | grep 'Runtime'`

#### libcontainer
libcontainerは、Go言語で実装されたライブラリです。

Dockerのコンテナ機能は、0.8までLXCに依存してきましたが、0.9以降はカーネルのコンテナAPIを直接コールするlibcontainerドライバに変更されました。

### カーネル
カーネルは、オペレーティングシステム (OS) のコアとなる部分です。
CPU、メモリ、デバイスの入出力といったハードウェアリソースを抽象化します。

**コンテナ型仮想化技術は、ホスト型やハイパーバイザー型等他の仮想化技術に比べて、オーバーヘッドが少ないため、軽量で高速に動作するのが特徴です。**

#### ファイルシステム
Dockerでは、複数のファイルシステム上のディレクトリやファイルをレイヤとして重ね合わせ、それらを仮想的に一つのファイルシステムとして扱うことにより、イメージを実現しています。また、イメージレイヤに対するコンテナ上でのファイルの更新は、コピーオンライトによって実現しています。コピーオンライト処理は、ストレージドライバによって実装が異なります。

例として、[Aufs](https://ja.wikipedia.org/wiki/Aufs)は、複数の異なるファイルシステム (ブランチと呼ばれる) のファイルやディレクトリ同士を透過的に重ねる (マージする) ことができる技術です。

- Dockerで使用しているドライバの確認
`docker info | grep 'Storage Driver'`

- Docker イメージレイヤの確認
`docker history イメージ`

#### Namespace
Namespaceは、コンテナ毎に異なる論理的な空間を提供し、隔離するための技術です。

| 名前空間       | 定数       | 概要       |
|:-----------------|:-----------------:|:-------------------|
| IPC        | CLONE_NEWIPC      | System V プロセス間通信オブジェクトと呼ばれる、POSIXメッセージキュー等内部のプロセス間通信を分離する。|
| MOUNT    | CLONE_NEWNS       | Namespace内に隔離されたファイルシステムツリーを作り、ファイルシステムツリーを分離する。|
| Network  |CLONE_NEWNET     | ネットワークデバイス、IPアドレス、ポート番号、ルーティングテーブル、フィルタリングテーブル等ネットワークリソースを分離する|
| PID　       | CLONE_NEWPID      | PID(プロセスID)を分離する。|
| UID    | CLONE_NEWUSER     | UIDやGIDを分離する。|
| UTS        | CLONE_NEWUTS      | uname() システムコールから返される2つのシステム識別子(ホスト名やNISドメイン名)を分離する。|

#### cgroups
[cgroups](https://ja.wikipedia.org/wiki/Cgroups)は、プロセスグループのリソース(CPU、メモリ、ディスクI/O等)の利用を制限・隔離するLinuxカーネルの機能です。グループ内のプロセスから他のグループのリソースが見えないようにできているのは、名前空間による隔離機能が働いているためです。

興味があれば、カーネルソースのtask_struct()を見てみるといいかもしれません。

Linux カーネル内部では、プロセスは task_struct と呼ばれるかなり大きな構造体で表現されます。task_struct は ./linux/include/linux/sched.h に含まれています。

#### chroot
chrootは古い技術ですが、仮想化技術をより理解するためには、知ってるといいでしょう。

[chroot](https://ja.wikipedia.org/wiki/Chroot)は、ファイルシステムの名前空間を隔離します。もう少し分かりやすく言うと、これ以上のディレクトリに行かせないように、ルートディレクトリを変更します。但し、隔離するのは、あくまでファイルシステムのみです。他のリソースは隔離しないため、ユーザーIDやネットワークインターフェイスは共通のものが見えます。

身近な例としては、bindの構築で使用されています。chrootすることで、/var/named/chrootがルートディレクトリになり、bindプロセスがアクセス可能な範囲を制限することを目的としています。これにより、万が一攻撃を受けて侵入されても、/var/namedディレクトリだけが影響を受けます。

chrootを使用する場合は、どこを起点にしているかで相対的位置を理解しておくと、トラブルの防止になります。

## Kubernetes概要
Kubernetesは、宣言型構成及びコンテナ自動運用を可能とするコンテナオーケストレーションツールの一つです。分散システムとしてAPIを提供するために、**信頼性・可用性・拡張性**を実現します。

Googleが開発（※1）して2014年にオープンソース化されたKubernetesは、2017年10月のDockerCon EUでの、DockerのKubernetes統合発表を機に、取り巻く状況は大きく変わっていきました。現在は主要なクラウドサービスでもサポートされ、依然コンテナオーケストレーションツールとして、その重要性は変わっていません。

- GKE（Google Kubernetes Engine）
- Amazon EKS（Amazon Elastic Container Service for Kubernetes）
- AKS（Azure Kubernetes Service）（※2）
- CEK（Container Engine for Kubernetes）（※3）

（※1）Googleが社内のアプリケーション基盤として利用していた「Borg」と呼ばれるシステムが元
（※2）ACS（Azure Container Service）のサポートは2020年1月31日をもって終了
（※3）Oracleが提供するクラウドサービス

### オーケストレーションツール
オンプレミスにおけるシステムの拡張方式は、スケールアップやスケールアウトの運用が一般的でしたが、クラウドの登場により、必要なときに、必要なリソースを迅速に構築する仕組みとしてオーケストレーションツールが生まれました。コンテナに係るオーケストレーションツールとして、主要なクラウドサービス以外でも以下のような種類があります。

| 開発元         　 | サービス           |
|:-----------------|:------------------|
| Red Hat          | OpenShift         | 
| Pivotal          | PKS               | 
| Docker           | Swarm             | 
| Apache           | Apache Mesos      | 
| Rancher Labs     | Rancher           | 
| Mesosphere       | Mesosphere DC/OS  | 
| Cloud Foundry    | CFCR（Cloud Foundry Container Runtime）| 

オーケストレーションツールは、Kubernetesのシェアが高いですが、Googleがオープンソース化したgVisor、OpenStack Foundationが開発したKata Containers、Kubernetesにネイティブ対応したコアランタイムのcontainerd等多様なランタイムが登場すると見込まれており、コンテナ型仮想化技術の動向踏まえて、今後も目が離せません。

### アーキテクチャ
![k8s_arc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/089b3294-50e6-0750-ddb3-fa694384ba8f.png)

- Kubernetesを構成する要素はリソースとして区別され、Kubernetes Cluster（以降、クラスタ）はKubernetesの管理するリソースの集合体である
- NodeはKubernetes クラスタで管理されるDockerホストであり、MasterとNodeに区別される
- クラウドサービスで提供されるGKEの様なマネージドサービスでは、開発者がMasterを意識することはほとんどない
- Kubernetes クラスタのリソースに対する操作は標準化されたAPIによる行う
- Deploymentを1つの単位として、アプリケーションをデプロイする

### Kubernetesのコンポーネント
![k8s_com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/cb1d6510-8568-7267-5a22-a104ffe2c14a.png)


MasterでAPIの受け付けを行い、Nodeの管理やKubernetes クラスタのコントロールを行なっています。Node内のkubeleteにより、Pod内のコンテナを管理します。また、ダッシュボードやDNS等のアドオンにより、Kubernetes クラスタとして機能します。

Kubernetesのコンポーネントの詳細は、[Kubernetes Components](https://kubernetes.io/docs/concepts/overview/components/)を参照。

### CRI（Container Runtime Interface）
KubernetesとDockerのあいだでは、Kubernetesで標準化されたAPIであるCRI（Container Runtime Interface）によってやりとりが行われます。以下は、KubernetesでのContainer Runtime Interface（CRI）の紹介についてのブログ記事です。

[Introducing Container Runtime Interface (CRI) in Kubernetes](https://kubernetes.io/blog/2016/12/container-runtime-interface-cri-in-kubernetes/)

なお、KubernetesとCRIの対応についていは、[Git Hubのcontainerd/criのリポジトリのREADME](https://github.com/containerd/cri/blob/master/README.md)を参照。

### Kubernetesの意義
Kubernetesの意義を知りましょう。
Borgの歴史は、[Borg, Omega, and Kubernetes](https://storage.googleapis.com/pub-tools-public-publication-data/pdf/44843.pdf)から、知ることができます。

### その他
Kubernetesに関連するツールや、フレームワーク等について以下に記載します。

- kubeadm   
kubeadmは、ベストプラクティスに準拠した最低限実行可能なKubernetesクラスタを構築することができます。ラップトップ、サーバー、Raspberry Pi等色々なプラットフォームに対応しています。
- minikube  
minikubeは、macOS、Linux、およびWindows上にローカルKubernetesクラスタを構築することができます。
- k3s  
k3sは、Rancher Labs社がオープンソースで公開したKubernetesのディストリビューションです。わずか40MBでありながら、標準のKubernetesとしての機能を備えつつ、Kubernetes認証プログラムである「Certified Kubernetes」を取得。[Rancher Labs](https://k3s.io/)よりダウンロードできます。
- Knative  
[Knative](https://cloud.google.com/knative/?hl=ja)は、最新のサーバーレス ワークロードをビルド、デプロイ、管理できる Kubernetes ベースのプラットフォーム。ワークロードをオンプレミスで実行している環境においてもコンテナをビルドできます。
- OpenFaaS  
[OpenFaaS](https://www.openfaas.com/)は、Docker SwarmとKubernetesをサポートしているFaaSフレームワークで、リクエストに応じてコードを実行、状況に応じて自動的にインスタンスをスケールさせるということができます。オンプレミスや各社のクラウドサービス上でも導入することができます。
- kubeless  
[Kubeless](https://kubeless.io/)は、Kubernetes固有のサーバーレスフレームワークで、基盤となるインフラストラクチャを気にせずに小さなコード（関数）をデプロイできます。
- Osiris  
Osirisは、アイドル状態のワークロードを自動的にゼロまでスケールアップし、ゼロまでのスケールのワークロードをインバウンド要求によってオンデマンドで自動的に再アクティブ化することを可能にすることで、Kubernetesクラスター内のリソース効率を向上させます。まだ、開発中のコンポーネントのようです。
- Virtual Kubelet  
Virtual Kubeletは、Microsoftが始めたOSSプロジェクトです。Kubernetesを拡張して、特定のワークロードをノードレスのサービスとして実行することができます。イメージとしては、VMwareのvMotionのように、Virtual Kubeletが動作するノードとは別のコンピューティングリソースに、コンテナを展開できます。
- Anthos   
[Anthos](https://cloud.google.com/anthos/)は、Kubernetesをベースにアプリケーションのマルチクラウド対応を実現する新サービスです。Google Cloud上ではGoogle Kubernetes Engine（GKE）上で稼働し、オンプレミスではGKE on Prem上で稼働。さらにAWS上でも稼働予定のため、Google以外のクラウドサポートが見込まれています。また、オンプレミスでのAnthosの実行環境は、現時点ではGKE on premの稼働条件であるVMware 6.5以上、F5 BIG-IPとされています。

## Kubernetesのインストール
ローカル環境でKubernetesをインストールして使ってみましょう。

本記事の環境は以下になります。

環境：iMac
前提条件：Docker Desktop for Macがインストール済み

1. 画面上部のDockerアイコンから、「Preferences」をクリックして、「Kubernetes」を選択します。
<img width="502" alt="スクリーンショット 2019-03-02 09.17.34.png" src="https://qiita-image-store.s3.amazonaws.com/0/221759/381e19b2-62a2-fef2-0409-a396e0f2103f.png">

2. 「Install」をクリックします。
<img width="503" alt="スクリーンショット 2019-03-02 09.18.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/221759/692731ae-df5d-b4fe-4eea-67cb1b23c27e.png">

3. インストールが始まるので少し待ちます。
<img width="501" alt="スクリーンショット 2019-03-02 09.18.42.png" src="https://qiita-image-store.s3.amazonaws.com/0/221759/cfa5f8fd-8883-c1eb-d598-62546e93a3d8.png">

4. インストールが終わったら「Close」をクリックして、「Kubernetes is running」と緑色で表示されたら正常に起動しています。
<img width="502" alt="スクリーンショット 2019-03-02 09.21.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/221759/f1a0c767-beea-aa77-e0de-ff7dd407cb07.png">

## Kubernetesの操作
Kubernetesは、コマンドラインとGUIで操作ができます。

### kubectl   
kubectlは、Kubernetesをコマンドラインで操作するためのツールです。kubectlの概要（コマンドの構文等）は、 [Overview of kubectl](https://kubernetes.io/docs/reference/kubectl/overview/)を参照。

#### Kubernetes クラスタの状態確認  
Kubernetes クラスタの状態確認行うコマンドを以下に記載します。

- クラスタのバージョン確認   
`kubectl version`

```console
Client Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.11", GitCommit:"637c7e288581ee40ab4ca210618a89a555b6e7e9", GitTreeState:"clean", BuildDate:"2018-11-26T14:38:32Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"darwin/amd64"}
Server Version: version.Info{Major:"1", Minor:"10", GitVersion:"v1.10.11", GitCommit:"637c7e288581ee40ab4ca210618a89a555b6e7e9", GitTreeState:"clean", BuildDate:"2018-11-26T14:25:46Z", GoVersion:"go1.9.3", Compiler:"gc", Platform:"linux/amd64"}
```

- クラスタの正常性確認   
`kubectl get componentstatus`

```console
NAME                 STATUS    MESSAGE              ERROR
controller-manager   Healthy   ok                   
scheduler            Healthy   ok                   
etcd-0               Healthy   {"health": "true"}
```

- クラスタのノード一覧表示   
`kubectl get nodes`

```console
NAME                 STATUS    ROLES     AGE       VERSION
docker-for-desktop   Ready     master    59d       v1.10.11
```

- クラスタのノード詳細情報表示   
`kubectl describe nodes ノード名`

### ダッシュボード
ダッシュボードを使用することで、ブラウザからKubernetes クラスタを管理することができます。

以下のコマンドを実行し、ダッシュボードを追加します。

`kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v1.8.3/src/deploy/recommended/kubernetes-dashboard.yaml`

```console
secret "kubernetes-dashboard-certs" created
serviceaccount "kubernetes-dashboard" created
role.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created
rolebinding.rbac.authorization.k8s.io "kubernetes-dashboard-minimal" created
deployment.apps "kubernetes-dashboard" created
service "kubernetes-dashboard" created
```

以下のコマンドを実行し、デプロイされたことを確認します。

`kubectl get pod --namespace=kube-system -l k8s-app=kubernetes-dashboard`

```console
NAME                                    READY     STATUS    RESTARTS   AGE
kubernetes-dashboard-7d5dcdb6d9-6tpxv   1/1       Running   0          1m
```

以下のコマンドを実行し、ダッシュボードへのプロキシサーバを起動します。

`kubectl proxy`

```console
Starting to serve on 127.0.0.1:8001
```

ブラウザから、[ダッシュボード](http://localhost:8001/api/v1/namespaces/kube-system/services/https:kubernetes-dashboard:/proxy)にアクセスすると、以下の様な画面が表示されるので「SKIP」をクリックします。

**（※）実際に運用で使用する際は、認証機能を使用した方がセキュリティ的に望ましいです。**

<img width="2048" alt="スクリーンショット 2019-03-02 09.37.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/221759/8092d597-f8e9-fd73-cb43-f2bbda2fb3d0.png">

ダッシュボードが表示されます。

<img width="2048" alt="スクリーンショット 2019-04-17 19.12.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/d4d8bdf1-9fc5-9e2a-06eb-d03084d938ee.png">

#### トークンの取得方法
ダッシュボードログイン時に、Tokenを指定してログインする方法を以下に記載します。

以下のコマンドを実行し、デフォルトトークンを特定します。

`kubectl -n kube-system get secret | grep default-token`

```console
default-token-fx2v4                              kubernetes.io/service-account-token   3         52d
```

以下のコマンドを実行し、tokenの内容を出力します。

`kubectl -n kube-system describe secret  default-token-fx2v4`

```console
Name:         default-token-fx2v4
Namespace:    kube-system
Labels:       <none>
Annotations:  kubernetes.io/service-account.name=default
              kubernetes.io/service-account.uid=d9b76415-3c80-11e9-9164-025000000001

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1025 bytes
namespace:  11 bytes
token:      （※）トークンの内容が出力される
```

ダッシュボードログイン時に、Tokenを指定し、上記コマンド出力結果のtokenを入力することでログインができます。

### Kubernetes API オブジェクト
Kubernetes上にある全てのリソースは、RESTfulリソース（Kubernetesオブジェクト）で表現されます。

例えば、Kubernetes Deploymentは、Kubernetes クラスタ上で実行されているアプリケーションを表すことができるオブジェクトです。Kubernetesオブジェクトを操作するために、Kubernetes APIを使用します。

Kubernetes APIのオブジェクトは、YAMLあるいはJSONで表現できます。オブジェクトの作成、更新、削除等の操作はYAMLやJSONのファイルを使用してAPIによる操作が行えます。

### Pod
Podは、Kubernetes クラスタ上での最小のデプロイ単位です。Pod内のアプリケーションは、同じIPアドレスとポートを使用します。Podの設定は、Podマニフェストに記述します。

Podマニフェストは、YAMLあるいはJSONで記述します。一般的にはYAMLで記述する方法が汎用的です。YAMLではインデントを使ってデータの階層構造を表すため、非常に読みやすく書きやすいですが、デプロイ時のエラーの原因でもあります。

KubernetesのYAMLフォーマットの書き方は[Understanding Kubernetes Objects](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/)を参照。

#### Pod操作
[kuard](https://github.com/kubernetes-up-and-running/kuard)を例に、基本的なPod操作を行うコマンドを以下に記載します。

**kubectlコマンドの引数で指定するリソース種別は、「pod」または「pods」のどちらでも実行できます。また、略して「po」で指定することもできます。**

- kuard-pod.yaml 

```yaml
apiVersion: v1
kind: Pod
metadata:
  name: kuard
spec:
  containers:
    - name: kuard
      image: gcr.io/kuar-demo/kuard-amd64:1
      ports:
        - containerPort: 8080
          name: echo
          protocol: TCP
```

-  インスタンス起動   
`kubectl apply -f kuard-pod.yaml`

```console
pod "kuard" created
```

-  Podの一覧表示   
`kubectl get pod`

```console
NAME                READY     STATUS    RESTARTS   AGE
kuard               1/1       Running   0          5s
```

-  Podの詳細情報表示   
`kubectl describe pod kuard`

```console
Name:         kuard
Namespace:    default
Node:         docker-for-desktop/192.168.65.3
Start Time:   Sun, 21 Apr 2019 20:41:37 +0900
Labels:       <none>
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"v1","kind":"Pod","metadata":{"annotations":{},"name":"kuard","namespace":"default"},"spec":{"containers":[{"image":"gcr.io/kuar-demo/kua...
Status:       Running
IP:           10.1.0.65
Containers:
  kuard:
    Container ID:   docker://c5440df7d9eec2b10aa77baf29fe690597a98d68681d4ea49d1ee075745d5f78
    Image:          gcr.io/kuar-demo/kuard-amd64:1
    Image ID:       docker-pullable://gcr.io/kuar-demo/kuard-amd64@sha256:bd17153e9a3319f401acc7a27759243f37d422c06cbbf01cb3e1f54bbbfe14f4
    Port:           8080/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sun, 21 Apr 2019 20:41:39 +0900
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-vgzn7 (ro)
Conditions:
  Type           Status
  Initialized    True 
  Ready          True 
  PodScheduled   True 
Volumes:
  default-token-vgzn7:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-vgzn7
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason                 Age   From                         Message
  ----    ------                 ----  ----                         -------
  Normal  Scheduled              41s   default-scheduler            Successfully assigned kuard to docker-for-desktop
  Normal  SuccessfulMountVolume  41s   kubelet, docker-for-desktop  MountVolume.SetUp succeeded for volume "default-token-vgzn7"
  Normal  Pulled                 40s   kubelet, docker-for-desktop  Container image "gcr.io/kuar-demo/kuard-amd64:1" already present on machine
  Normal  Created                39s   kubelet, docker-for-desktop  Created container
  Normal  Started                39s   kubelet, docker-for-desktop  Started container
```

-  Podの削除   
`kubectl delete pod/kuard`    
`kubectl delete -f kuard-pod.yaml`

```console
pod "kuard" deleted
```

-  インスタンスのログ取得   
`kubectl logs kuard`    

```console
2019/04/23 09:10:58 Starting kuard version: v0.8.1-1
2019/04/23 09:10:58 **********************************************************************
2019/04/23 09:10:58 * WARNING: This server may expose sensitive
2019/04/23 09:10:58 * and secret information. Be careful.
2019/04/23 09:10:58 **********************************************************************
2019/04/23 09:10:58 Config: 
{
  "address": ":8080",
  "debug": false,
  "debug-sitedata-dir": "./sitedata",
  "keygen": {
    "enable": false,
    "exit-code": 0,
    "exit-on-complete": false,
    "memq-queue": "",
    "memq-server": "",
    "num-to-gen": 0,
    "time-to-run": 0
  },
  "liveness": {
    "fail-next": 0
  },
  "readiness": {
    "fail-next": 0
  },
  "tls-address": ":8443",
  "tls-dir": "/tls"
}
2019/04/23 09:10:58 Could not find certificates to serve TLS
2019/04/23 09:10:58 Serving on HTTP on :8080
```

-  コンテナ接続（対話的）
`kubectl exec -it kuard ash`    

-  ポートフォワード 
`kubectl port-forward kuard 8080:8080`

### ReplicaSet
レプリカという言葉は、複製と言う意味です。
Kubernetes クラスタ上でのReplicaSetは、Podの複製を行います。
ReplicaSetマニフェストで定義したreplicasの値の通り(※)にPodの複製を行います。

(※)既にPodが起動していて、不足していればその分Podを複製します。多い場合にReplicaSetマニフェストを適用すると、その分削除されます。

#### ReplicaSet操作
kuardを例に、基本的なReplicaSet操作を行うコマンドを以下に記載します。

**kubectlコマンドの引数で指定するリソース種別は、「replicaset」または「replicasets」のどちらでも実行できます。また、略して「rs」で指定することもできます。**

- kuard-rs.yaml

```yaml
apiVersion: extensions/v1beta1
kind: ReplicaSet
metadata:
  name: kuard
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: kuard
        version: "2"
    spec:
      containers:
        - name: kuard
          image: gcr.io/kuar-demo/kuard-amd64:2
```

- ReplicaSetの作成   
`kubectl apply -f kuard-rs.yaml`

```console
replicaset.extensions "kuard" created
```

- ReplicaSetの一覧表示  
`kubectl get replicaset`

```console
NAME      DESIRED   CURRENT   READY     AGE
kuard     1         1         1         6s```
```

- ReplicaSetの詳細情報表示   
`kubectl describe rs kuard`

```console
Name:         kuard
Namespace:    default
Selector:     app=kuard,version=2
Labels:       app=kuard
              version=2
Annotations:  kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"extensions/v1beta1","kind":"ReplicaSet","metadata":{"annotations":{},"name":"kuard","namespace":"default"},"spec":{"replicas":1,"templat...
Replicas:     1 current / 1 desired
Pods Status:  1 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=kuard
           version=2
  Containers:
   kuard:
    Image:        gcr.io/kuar-demo/kuard-amd64:2
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                   Message
  ----    ------            ----  ----                   -------
  Normal  SuccessfulCreate  1m    replicaset-controller  Created pod: kuard-d65gb
```

- ReplicaSetのスケール   
`kubectl scale replicasets kuard --replicas=4`

```console
replicaset.extensions "kuard" scaled
```

- ReplicaSetの削除   
`kubectl delete rs kuard`

```console
replicaset.extensions "kuard" deleted
```

### Service
Serviceは、サービスディスカバリを定義します。
サービスディスカバリとは、システム上のインフラにおけるサービスの問題を検出すると言うことを意味します。

例えば、マイクロサービスとしてサービスを提供しているコンテナにシステム障害が起きて停止した場合、障害が発生したコンテナ（Pod）に対するトラフィックを制御することができます。他にも、何らかのシステム障害発生時に、システム監視による検知でDNSレコードを更新したりして、DNSでサービスのディスカバリが行うことができます。

仕組みとして、各Podには固有のIPアドレスがありますが、それらのIPはServiceなしではクラスタの外部に公開されません。Serviceにより、アプリケーションはトラフィックを受信できます。

#### ClusterIP
デフォルトは、ClusterIPになります。ClusterIPはクラスタ内の内部にサービスを公開できます。ClusterIPにより、クラスタ内のPod間通信はServiceを介して行うことはできますが、外部からのアクセスはできません。

#### NodePort
NATを使用して、外部からクラスタに対するアクセスが可能になります。

#### LoadBalancer
主にクラウドサービスで提供されているロードバランサと連携して使用します。ロードバランサがサポートされている場合、サービスに固定の外部IPを割り当てることができます。

#### ExternalName
クラスタ内から外部のホストの名前解決を行うためのエイリアス（CNAME record）を設定することができます。なお、本機能はkube-dns v1.7以上になります。

### Ingress
Kubernetes v1.1で追加されたIngressは、クラスタ外からのHTTP及びHTTPSの通信をクラスタ内のServiceに公開します。

### Job
Jobは、指定された数のポッドの処理が成功するまで実行されるため、バッチ処理などに適しています。処理が成功するまで、JobコントローラがPodを作り直します。

### ConfigMap
コンテナ運用で重要なのは、コンテナの特性を失わずに運用を行うことです。
例えば、Webサーバ機能を持つコンテナの設定を変えるために、Nginxの設定ファイルを変更するのはポータビリティを低下させます。環境変数を使用することで、チューニングがしやすくなります。環境変数の組み込みは色々やり方があると思いますが、KubernetesにはConfigMapという便利な機能があります。

#### ConfigMap操作
kuardを例に、基本的なConfigMap操作を行うコマンドを以下に記載します。

- my-config.txt

```text
parameter1 = value1
parameter2 = value2
```

- ConfigMapの作成   
`kubectl create configmap my-config --from-file=my-config.txt --from-literal=extra-param=extra-value --from-literal=another-param=another-value`

```console
configmap "my-config" created
```

- ConfigMapの一覧表示   
`kubectl get configmap`

```console
NAME        DATA      AGE
my-config   3         2d
```

- ConfigMapの詳細情報表示   
`kubectl get configmap my-config -o yaml`

```console
apiVersion: v1
data:
  another-param: another-value
  extra-param: extra-value
  my-config.txt: |
    parameter1 = value1
    parameter2 = value2
kind: ConfigMap
metadata:
  creationTimestamp: 2019-04-25T06:23:43Z
  name: my-config
  namespace: default
  resourceVersion: "1030540"
  selfLink: /api/v1/namespaces/default/configmaps/my-config
  uid: ac8694d8-6722-11e9-97d1-025000000001
```

- ConfigMapの削除   
`kubectl delete configmap my-config`

```console
configmap "my-config" deleted
```

### Deployment
Deploymentは、Kubernetesクラスタ上で、デプロイされたアプリケーションのバージョン管理ができるオブジェクトです。Deploymentにより、ダウンタイムを発生させることなく、アプリケーションのバージョンのロールアウトが行えます。また、DeploymentはReplicaSetを管理します。

#### Deployment操作
基本的なDeployment操作を行うコマンドを以下に記載します。

**kubectlコマンドの引数で指定するリソース種別は、「deployment」または「deployments」のどちらでも実行できます。また、略して「deploy」で指定することもできます。**

- runコマンドでデプロイ  
`kubectl run nginx --image=nginx:1.7.12`

```console
kubectl run nginx --image=nginx:1.7.12
```

- Deploymentの一覧表示  
`kubectl get deployment`

```console
kAME      DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
nginx     1         1         1            1           52s
```

- Deploymentの詳細情報表示  
`kubectl describe deployment nginx`

```console
dName:                   nginx
Namespace:              default
CreationTimestamp:      Sat, 27 Apr 2019 16:13:52 +0900
Labels:                 run=nginx
Annotations:            deployment.kubernetes.io/revision=1
Selector:               run=nginx
Replicas:               1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  1 max unavailable, 1 max surge
Pod Template:
  Labels:  run=nginx
  Containers:
   nginx:
    Image:        nginx:1.7.12
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   nginx-7d9bf79f9b (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  29s   deployment-controller  Scaled up replica set nginx-7d9bf79f9b to 1
```

- Deploymentのスケール  
`kubectl scale deployment nginx --replicas=2`

```console
deployment.extensions "nginx" scaled
```

- Deploymentの削除  
`kubectl delete deployment nginx`

```console
deployment.extensions "nginx" deleted
```

- Deploymentの作成 
`kubectl get deployment nginx --export -o yaml > nginx-deployments.yaml`

- nginx-deployments.yaml   

```yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  annotations:
    deployment.kubernetes.io/revision: "1"
  creationTimestamp: null
  generation: 1
  labels:
    run: nginx
  name: nginx
  selfLink: /apis/extensions/v1beta1/namespaces/default/deployments/nginx
spec:
  progressDeadlineSeconds: 600
  replicas: 1
  revisionHistoryLimit: 10
  selector:
    matchLabels:
      run: nginx
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      creationTimestamp: null
      labels:
        run: nginx
    spec:
      containers:
      - image: nginx:1.7.12
        imagePullPolicy: IfNotPresent
        name: nginx
        resources: {}
        terminationMessagePath: /dev/termination-log
        terminationMessagePolicy: File
      dnsPolicy: ClusterFirst
      restartPolicy: Always
      schedulerName: default-scheduler
      securityContext: {}
      terminationGracePeriodSeconds: 30
status: {}
```

**`kubectl scale`コマンドではなく、Deploymentを宣言的に管理するする場合は、YAMLファイルのレプリカ数を変更して、`kuberctl apply`コマンドでDeploymentを更新します。また、合わせてコンテナイメージの部分を変更することで、新しいバージョンのロールアウトを可能とします。**

- ロールアウトの状態確認   
`kubectl rollout status deployment nginx`

```console
deployment "nginx" successfully rolled out
```

-  Deploymentが管理しているReplicaSetsのイメージ確認   
`kubectl get replicasets -o wide`

```console
NAME               DESIRED   CURRENT   READY     AGE       CONTAINERS   IMAGES                                             SELECTOR
nginx-784d96dbff   1         1         1         1m        nginx        nginx:1.9.10                                       pod-template-hash=3408528699,run=nginx
nginx-7d9bf79f9b   0         0         0         35m       nginx        nginx:1.7.12                                       pod-template-hash=3856935956,run=nginx
```

-  ロールアウト履歴   
`kubectl rollout history deployment nginx`

```console
deployments "nginx"
REVISION  CHANGE-CAUSE
1         <none>
2         Update nginx to 1.9.10
```

-  デプロイしたリビジョンの詳細情報表示   
`kubectl rollout history deployment nginx --revision=2`

```console
deployments "nginx" with revision #2
Pod Template:
  Labels:	pod-template-hash=3408528699
	run=nginx
  Annotations:	kubernetes.io/change-cause=Update nginx to 1.9.10
  Containers:
   nginx:
    Image:	nginx:1.9.10
    Port:	<none>
    Host Port:	<none>
    Environment:	<none>
    Mounts:	<none>
  Volumes:	<none>
```

-  ロールバック   
`kubectl rollout undo deployment nginx`

```console
deployment.apps "nginx"
```

### Persistent Volumes
Kubernetesは、ストレージをプロビジョニングする方法として、静的または動的による二つの方法があります。

Persistent Volumesは、コンテナとは別に独立した永続化データを管理するための仕組みを静的に提供します。また、Kubernetesでは、NFS、iSCSI、クラウドプロバイダー固有のストレージシステムをサポートしています。

PePersistent Volumesの使い方は、[Configure a Pod to Use a PersistentVolume for Storage](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/)を参照。

#### PersistentVolumeClaim
Persistent Volumesを作成したら、PersistentVolumeClaimも作成します。
Persistent Volumesを使用することで、論理的なストレージ割り当てを行い、Podがストレージに対するアクセスを要求できます。

その他、動的ボリューム割り当ては、StorageClassesオブジェクトを使用することで可能になります。

永続的ボリュームの詳細については、[Persistent Volumes](https://kubernetes.io/docs/concepts/storage/persistent-volumes/)を参照。

## エラー時の勘所
以下は、本記事執筆時に遭遇したエラーです。

- Podにアクセスできない場合（ローカル環境）
ホストのFWやウイルス対策ソフトの設定を確認します。例として、ウイルス対策ソフトによりポート通信が遮断されることはよく考えられます。

- ConfigMapを使用して、Podが作成できない
`kubectl describe pod ポッド名`の出力結果より、「Events」を見て、「Error: Couldn't find key 環境変数 in ConfigMap 」のログが出力されていたら、ConfigMapの環境変数に謝りがあります。ConfigMapの環境変数が正しいか見直しましょう。

```console
Warning  Failed                 4m (x7 over 5m)    kubelet, docker-for-desktop  Error: Couldn't find key extra-param in ConfigMap default/my-config
```

- StatefulSetオブジェクトを使用して、MongoDBのPodを作成できない   
原因は、YAMLフォーマットの「spec」-「serviceName」で大文字を使用していたため。

```console
Events:
  Type     Reason        Age               From                    Message
  ----     ------        ----              ----                    -------
  Warning  FailedCreate  3s (x10 over 5s)  statefulset-controller  create Pod mongo-0 in StatefulSet mongo failed error: Pod "mongo-0" is invalid: spec.subdomain: Invalid value: "Mongo": a DNS-1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name',  or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')
```

# おわりに
これからDevOpsエンジニア（※）を目指すにあたり、Kubernetesは必要不可欠なスキルです。

また、Kubernetesを更に理解するためには、コンテナ型仮想化技術やLinux等の知識も必須になってきます。

Kubernetesは深淵の海のように範囲が広く深いため、学習コストは高いですが、オンプレミスのインフラ経験を活かしてステップアップすることは十分に可能です。インフラエンジニアからDevOpsエンジニアへの階段を登りましょう。

（※）日本で言うインフラエンジニアは、オンプレミスのサーバ（OS/MW）・ネットワーク等幅広いですが、海外にならい開発と運用を組み合わせたDevOpsというアプローチとともに、SRE（Site Reliability Engineering）をイメージしています。

## 参考
本記事で記載している一部のサンプルコードは、以下の書籍を参考にしています。

>出典：日本語版「入門 Kubernetes」Kelsey Hightower、Brendan Burns、Joe Beda　著、オライリー・ジャパン、ISBN978-4-87311-840-6

### 公式
- [Installing kubeadm](https://kubernetes.io/docs/setup/independent/install-kubeadm/)
- [kubectlのリファレンスドキュメント](https://kubernetes.io/docs/reference/generated/kubectl/kubectl-commands)

### GitHub
- [containerd](https://github.com/containerd/containerd)
- [minikube](https://github.com/kubernetes/minikube)
- [OpenFaaS](https://github.com/openfaas/faas)
- [kubeless](https://github.com/kubeless/kubeless)
- [Osiris](https://github.com/deislabs/osiris)
- [Virtual Kubelet](https://github.com/virtual-kubelet/virtual-kubelet)