kubeadmって何やってるの？公式ページをちゃんと読む

# はじめに
つい先日[kubeadmを使ってKubernetes環境を構築](https://qiita.com/FY0323/items/c84b01e7976d45801d48)しましたが、実際kubeadmは何をやっているのかよく分かっていませんでしたので、何か説明してくれているものはないか、と探しました。すると公式ページで説明してくれていたので、読んでみました。ただ幾つかのページにまたがって説明していることもあり、ある程度ここでまとめました。**個人用のまとめの側面が強いため、間違いも含まれていると思われますので、そのような箇所がありましたらご指摘いただければ幸いです**。

# kubeadmとは
[公式ページ](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm/)

kubeadmはKubernetesクラスター環境を高速に作成する方法として```kubeadm init```と```kubeadm join```というコマンドを提供するツールです。Kubernetesのクラスターを立ち上げ、稼働させる上で最低限必要なものを提供します。

# kubeadm詳細の説明

[公式ページ](https://kubernetes.io/docs/reference/setup-tools/kubeadm/implementation-details/#core-design-principles)

## コアとなる設計指針
```kubeadm init```と```kubeadm join```で構築されるクラスターは、以下のような条件を満たしているべきと考えられています。

* セキュアである
  * RBACを使用する
  * [Node Authorization](https://kubernetes.io/docs/reference/access-authn-authz/node/)を利用する
  * control planeコンポーネント間でのセキュアなやりとり
  * API serverとkubelet間でのセキュアなやりとり
  * kubelet APIを厳重にする
  * システムコンポーネントが安全にAPIにアクセスできる
  * Bootstrap Tokenがアクセスできるものを厳重にする
  * etc...
* 簡単に使える
  * 数コマンドで構築が完了する
* 拡張可能である
  * 特定のネットワークプロバイダーに依存しない
  * 幅広いパラメータをカスタマイズしたconfigを利用できる

Kubernetesをシンプルに構築できるようにするため、kubeadmは限られた定数・変数、そしてファイルしか使用しません。
ディレクトリ構造、使用するファイルは以下のようになります。

* ```/etc/kubernetes```: アプリケーションの中心となるディレクトリ。また各control planeコンポーネントのコンフィグファイル(kubeconfig)の格納先ともなります。
  * ```kubelet.conf```
  * ```controller-manager.conf```
  * ```scheduler.conf```
  * ```admin.conf```: クラスター認証、kubeadmのために利用する
* ```/etc/kubernetes/manifests```: kubeletが静的なPodマニフェストを探す際の場所となります。
  * ```etcd.yaml```
  * ```kube-apiserver.yaml```
  * ```kube-controller-manager.yaml```
  * ```kube-scheduler.yaml```
* 各種証明書とキーファイル
  * ```ca.crt```,```ca.key```: Kubernetes Certificate Authority(認証局)
  * ```apiserver.crt```,```apiserver.key```: APIサーバ認証書とキーファイル
  * ```apiserver-kubelet-client.crt```,```apiserver-kubelet-client.key```:APIサーバで使われるクライアント認証書とキーファイル。これを使ってkubeletと安全に接続します。
  * ```sa.pub, sa.key```: ServiceAccountに署名するときにcontroller managerで使われるキーファイル
  * ```front-proxy-ca.crt, front-proxy-ca.key```:front proxy認証局
  * ```front-proxy-client.crt, front-proxy-client.key```: front proxyクライアントの証明書とキーファイル

# kubeadm initで何を行っているか
[公式ページ](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/)

全体の流れは以下のようになります。

* init前のチェック(preflight checks)
* kubelet起動
* 各種証明書の作成
* control plane用のファイル作成(kubeconfig, manifest)
* etcd用のファイル作成(manifest)
* control planeの起動待ち
* kubeadm,kubeletのコンフィグアップロード
* control planeラベリング
* TLS Bootstrap
* addonsインストール(kube-proxy, DNS)

## Preflight checks
クラスターを立ち上げ前のチェックになります。```--ignore-preflight-errors```オプションをつけることでこの項目をスキップすることができます。
チェックする項目は以下になります。太字の項目はエラー発生項目で、条件を満たさないとエラーとなります。

* Kubernetesのバージョンがkubeadmより上かどうか（マイナーバージョンも含む）
* Kubernetesシステム要件
  * OS: linux上か
  * **Kernel: バージョンが3.10+、4+か**
  * **cgroupがセットアップ状態か**
  * (Dockerを使っている場合)**Dockerサービスが起動しているか、Docker endpointが存在するか**、バージョンは17.03より上か
  * (他のCRIを使う場合)**crictl socketが反応しないか**
* **ユーザがrootか**
* **ホスト名がDNSサブドメインか**
* ホスト名がネットワーク的にreachableか
* **kubeletバージョンがkubeadmでサポートするものより上か**
* kubeletサービスが存在してenableか
* firewalldが停止しているか
* **APIサーバのbindPort、あるいは10250~10252のポート番号が空いているか**
* **```/etc/kubernetes/manifest```フォルダが既に存在していないか、存在してもファイルが存在しないか**
* **```/proc/sys/net/bridge/bridge-nf-call-iptables```の値が1か**
* **advertiseアドレスがIPv6の場合```/proc/sys/net/bridge/bridge-nf-call-ip6tables```の値が1か**
* **swapがオフか**
* 以下のコマンドがコマンドパスに存在するか
  * **ip, iptables, mount, nsenter**
  * ebtables, ethtool, socat, tc, touch, crictl
* API server, controller manager, scheduler用の外部のarg flagが有効でないオプションを使っていないか
* ```https://API.AdvertiseAddress:API.BindPort```がproxyを介して接続していないか
* Podサブネットとの接続がproxyを介していないか
* **(外部etcdを利用する場合)etcdバージョンは3.0.14より上か、証明書とキーが提供されているか**
* **(外部etcdを利用しない場合)ポート番号2379は使われていないか、etcdデータフォルダが既に存在しないか、またはファイルが存在しないか**
* **(ABACを利用する場合)abac_policy.jsonが存在するか**
* **(Webhookを利用する場合)webhook_authz.confが存在するか**

## 各種証明書の作成
kubeadmで作成する証明書は上に書いていますが、その利用目的はそれぞれ異なります。これらの証明書はデフォルトで```/etc/kubernetes/pki```に保存されますが、```--cert-dir```を用いることで指定ができます。

* Kubernetesクラスターの自己証明書: ca.crt, ca.key
* API server用のサーバ証明書: apiserver.crt, apiserver.key。ca.crtを用いて作成します。
* API server用のクライアント証明書: apiserver-kubelet-client.crt, apiserver-kubelet-client.key。kubeletと安全に接続するために使用します。
* ServiceAccountトークン署名用の個人鍵と公開鍵: sa.key, sa.pub
* front proxy用の認証局: front-proxy-ca.crt, front-proxy-ca.key

## control planeコンポーネント用kubeconfigファイルの作成
* kubelet: ```/etc/kubernetes/kubelet.conf```ファイル中ではkubeletごとのクライアント証明書が含まれています。
* controller manager: ```/etc/kubernetes/controller-manager.conf```ファイル中にはcontroller managerごとのクライアント証明書が含まれます。
* scheduler: ```/etc/kubernetes/scheduler.conf```schedulerごとのクライアント証明書が含まれます。
* kubeadm: ```/etc/kubernetes/admin.conf```クラスターをまたがって全権を持つユーザ(root)を定義します。

## control planeコンポーネント用manifestファイルの作成
各control planeの設定はオプションで指定できます。

* Podマニフェスト共通の設定
  * namespace: kube-system
  * label: ```tier:control-plane```,```component:{component-name}```
  * annotation: ```scheduler.alpha.kubernetes.io/critical-pod```
  * hostNetwork: true
  * controller managerとschedulerがAPI server参照先とするアドレス: 127.0.0.1
  * local etcdを使用する場合のアドレス: 127.0.0.1:2379
  * デフォルトではコンテナイメージは```k8s.gcr.io```からpullされる
  * ```--dry-run```: 一時フォルダにmanifestファイルが作成される

### API server
* ユーザが設定可能な値(Pod manifest)
  * apiserver-advertise-address, apiserver-bind-port
  * service-cluster-ip-range
  * (外部etcdを利用する場合)etcd-servers, etcd-cafile, etcd-certfile, etcd-keyfile
  * (cloud providerを利用している場合)--cloud-provider
* ユーザが設定可能な値(その他)
  * --insecure-port
  * --enable-bootstrap-token-auth: TLS bootstrapで```BootstrapTokenAuthenticator```を有効にすることができます
  * --allow-privileged: true。kube-proxyなどで要求されます。
  * --requestheader-client-ca-file: front-proxy-ca.crt
  * --enable-admission-plugins: Dynamic Admission Controlなどのpluginを有効にすることができます。
  * --kubelet-preferred-address-types: InternalIP, ExternalIP, Hostname。Nodeの名前解決ができない場合に```kubectl log```と他のAPIサーバのkubeletとの通信を可能にします。
  * 前段で作成した各証明書の利用

### Controller manager
* ユーザが設定可能な値(Pod manifest)
  * --allocate-node-cidrs
  * --cluster-cidr
  * --cloud-provider
* ユーザが設定可能な値(その他)
  * --controllers: TLS bootstrapでデフォルトのcontrollerに加えて```BootstrapSigner```,```TokenCleaner```を有効にできます
  * --use-service-account-credentials
  * 前段で作成した各証明書の利用

## Local Etcd用manifestファイルの作成
* localhost:2379を使用
* HostNetwork=True
* hostPathマウント: dataDirからホストのファイルシステムにマウントします

## Control planeの起動を待つ
```localhost:6443/healthz```がOKを返すまで待機しますが、```localhost:10255/healthz```(kubelet liveness)や```localhost:10255/healthz/syncloop```(kubelet readiness)がOKを返さない(それぞれ40秒と60秒)と、deadlockを探知してfailします。

## kubeadm ClusterConfigurationのConfigMapの保存
```kube-system```namespace配下に```kubeadm-config```というConfigMapを作成します。これにより、将来的に行われるであろうkubeadm upgradeなどの実行コマンドが実行時のクラスター環境を定義し、そのデータを基に新たな定義をすることが可能になります。kubeadmのバージョンが1.7.xより低い場合は、kubeadm upgrade実行より前に、手動でConfigMapを作成する必要があります。

## masterノードのラベリング
Control planeが立ち上がるとすぐに2つのアクションを行います。
* ```node-role.kubernetes.io/master=""```のラベリング
* ```node-role.kubernetes.io/master:NoSchedule```のTaint: masterノードをスケジュール対象外にし、Podなどが作られないようにします。

## TLS Bootstrapのコンフィグ
kubeadmでは新たなノードをジョインさせるためにBootstrap Tokenを使用します。Bootstrap TokenはシンプルにTokenを作成することのできるもので、基本的にはkubeadmをサポートするものとなります。このステップでは以下のようなアクションが行われます。[公式ページはこちら](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/)。

* Bootstrap Tokenの作成: 自動的に作成することも、```--token```でユーザが指定したTokenを作成することもできます。一度作成したTokenは24時間有効で、新たなTokenを作成するには```kubeadm token```コマンドを使用します。
* 新規ノードのCSR(Certificate Signing Request) API呼び出しの許可: デフォルトで作成されるユーザグループ```system:bootstrappers:kubeadm:default-node-token```に所属するユーザは許可されます。
* 新規作成Tokenの自動承認の設定
* ノードの証明書ローテーションの設定(自動承認付き): TLS証明書の有効期限が1年のため、証明書のローテーションを行う場合があります。[参考リンク](https://kubernetes.io/docs/tasks/tls/certificate-rotation/)
* cluster-info ConfigMapの作成: ```kube-public```namespaceにcluster-infoというConfigMapを作成します。

## Addonインストール
kube-proxyとDNSをインストールします。

* kube-proxy: ```kube-system```namespaceにkube-proxy用のServiceAccountが作成され、DaemonSetを使ってkube-proxyがデプロイされます。
* DNS: こちらも```kube-system```namespaceにServiceAccountを作成し、```kube-dns```という名前のDNSをDployment, Serviceでデプロイします。
Kubernetes 1.11以降はデフォルトDNSはCoreDNSとなり、それ以前のバージョンのKubernetesを使用する場合はCoreDNSを利用可能にする必要があります(```--feature-gates=CoreDNS=true```)。kube-dnsという名前でデプロイされますが、実態はCoreDNSです。

## kubeadm initオプションの利用
kubeadm initコマンド時にオプションをつけることで、ユーザが一部設定をすることができます。ここではよく使うのでは?と思ったオプションを紹介します。

* --apiserver-advertise-address: API serverがadvertiseする予定のIPアドレスを指定できます。
* --apiserver-bind-port: API serverと接続する際のポート番号を指定できます。
* --dry-run
* --ignore-preflight-errors: preflight checkのステップをスキップします。
* --image-repository: コンテナイメージのリポジトリ先を指定できます。デフォルトは```k8s.gcr.io```です。
* --pod-network-cidr: PodネットワークのIPアドレスレンジを指定できます。
* --service-cidr: Service VIPで利用するIPアドレスレンジを指定できます。
* --skip-token-print: kubeadm init実行時のデフォルトで発行されるtokenの表示をスキップします。
* --token: 利用するtokenをユーザが指定できます。
* --token-ttl: 発行するtokenの有効期限を指定できます。デフォルトは```24h0m0s```です。```0```を指定すると有効期限がなくなります。

## kubeadm init phaseの利用
[公式ページ](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init-phase/)

```kubeadm init phase```コマンドを利用することで、上記ステップごとでの詳細な設定を可能にすることができます。kubeadm v1.8では```kubeadm alpha phase```だったものが、v1.13では```kubeadm init phase```として利用できるようになりました。ここでは各ステップでよく使うのでは?と思ったオプションを紹介します。

* 共通コマンド
  * --config: kubeadmコンフィグファイルを指定します(まだ実験的なもののようです)
  * -h,--help: ヘルプコマンド
* kubeadm init phase preflight
  * --ignore-preflight-errors
* kubeadm init phase certs: 各種証明書に対して個別に指定ができます。
  * --cert-dir: 発行した証明書の保存先を指定できます。デフォルトは```/etc/kubernetes/pki```です。
  * --csr-dir: CSRとprivate keyの出力先を指定できます。
  * --csr-only: 証明書を発行する代わりにCSRを作成します。
* kubeadm init phase kubeconfig: control plane用のkubeconfigファイル作成です。各種control planeに対して個別に設定できます。
  * --apiserver-advertise-address
  * --apiserver-bind-port
* kubeadm init phase kubelet-start
  * --cri-socket: CRI socketを指定できます。デフォルトは```/var/run/dockershim.sock```です。
* kubeadm init phase control-plane
  * --apiserver-advertise-address
  * --apiserver-bind-port
  * --feature-gates: 前章addonsでCoreDNSを指定する際に利用します。
  * --image-repository
  * --pod-network-cidr
  * --service-cidr
* kubeadm init phase etcd
  * --image-repository
* kubeadm init phase mark-control-plane
* kubeadm init phase bootstrap-token
  * --skip-token-print
* kubeadm init phase upload-config: このコマンドの代わりに```kubeadm config```コマンドを利用することもできます。
  * --kubeconfig: 利用するkubeconfigファイルを指定できます。デフォルトは```/etc/kubernetes/admin.conf```です。
* kubeadm init phase addon
  * --apiserver-advertise-address
  * --apiserver-bind-port
  * --image-repository
  * --pod-network-cidr
  * --service-cidr
  * --service-dns-domain: ドメインを指定できます。デフォルトは```cluster.local```です。

# kubeadm joinで何を行っているか
[公式ページ](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/)

kubeadmでクラスターを開始する際、双方向でのtrustが必要になります。ここではdiscoveryとTLS bootstrapの2つのステップに分かれます。
discoveryでは、API serverのIPアドレスとkubeconfigファイルを利用するために探索するステップになります。ユーザが指定することもできます。 
全体の流れは以下のようになります。

* init前のチェック
* cluster-infoのdiscovery
* TLS Bootstrap

## Preflight Checks
基本的には```kubeadm init```コマンドでのpreflight checkのサブセットとなる項目をチェックします。

## Discovery cluster-info
ここでは2つの主目的があります。

* shared token discovery: ```kubeadm join```で```--discovery-token```を実行した場合は、```kube-system```namespaceの```cluster-info```ConfigMapから、クラスターCA証明書を取り出します。ここでは中間者攻撃を避けるため、幾つかのステップを実行します。
* File/https discovery: ```kubeadm join```で```--discovery-file```を実行した場合は、ローカルに置かれたファイルかhttps経由でのダウンロードファイルから該当ファイルを検索します。

## TLS Bootstrap
cluster-infoがわかると、```bootstrap-kubelet.conf```ファイルが書き込まれ、kubeletがTLS Bootstrapを実行することが可能になります。TLS Bootstrapでは事前に作成されたshared tokenを用いてCSRを投稿し、承認されて```ca.crt```,```kubelet.conf```ファイルが作成されることで完了します。これらは```bootstrao-kubelet.conf```ファイルが削除されるまで有効となります。

## kubeadm joinオプションの利用
kubeadm joinコマンド時にもオプションをつけることで、ユーザが一部設定をすることができます。

* --apiserver-advertise-address
* --apiserver-bind-port
* --discovery-file: クラスター情報を持つファイル、またはURLを指定できます。
* --discovery-token: 使用するtokenを指定できます。
* --ignore-preflight-errors

# まとめ・感想
書いているうちに頭が混乱してきてしまったのですが、とにかくkubeadmの概要は以下のようになるかと思います。

* (実行環境を整えれば)数コマンドでKubernetesのクラスターが構築できる。
* オプションをつけることでユーザが比較的カスタマイズできる。
* [コンフィグファイルを利用してkubeletを設定する](https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/)、[コンフィグファイルを指定してkubeadmの設定をする](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/#options)など、まだ実験段階のものも幾つかあり、より幅広い設定をできる可能性もある。

ただ、ここまでまとめて思ったのは、**本番環境ではkubeadmはどこまで利用できるのか?**という点でした。例えば証明書周りはあまりカスタマイズが出来ないようですが(ファイルの出力先などは指定できますが、証明書自体の設定はあまり出来ない)、それ以外は比較的色々とできるようにみえます。新たなクラスターの追加も```kubeadm join```で簡便にできますし、十分使い物になるのでは、と思うのですが。。。引き続き本番環境へのkubeadm適用について調べていきたいと思います。

# 参考リンク
[Kubernetes: v1.7 で導入された Node Authorization とは](https://qiita.com/tkusumi/items/f6a4f9150aa77d8f9822)
[Kubernetes の TLS 証明書について調べてみた](https://blog.manabusakai.com/2018/10/kubernetes-tls-certificate/)
[kubeadmインストール構成とthe-hard-wayインストール構成の違い](https://qiita.com/MahoTakara/items/258de5ad1cbbdb29d101)