Kubernetes The Hard Way on vagrant

# はじめに
先日、CKA を受験し辛くも合格しました。
試験勉強として定番となっている「Kubernetes The Hard Way」で Kubernetes を構築してみました。
今回はその備忘もかねて手順を記録してみました。
Kubernetes のツールを使わずにインストールすることで、どういうコンポーネントが必要で、どこに配置するのかを確認できるかと思います。今回は、参考サイトを元に vagrant 上で構築しました。

参考サイト：
[本家 The Hard Way](https://github.com/kelseyhightower/kubernetes-the-hard-way)
[Vagrant版 The Hard Way](https://github.com/kenfdev/kubernetes-the-hard-way-vagrant)

# 環境構成
開発端末：Windows 10 Pro
VirtualBox：6.0.18
Vagrant：2.2.5
※Vagrantにはあらかじめ以下のpluginをインストールしています。

```console:vagrant_plugin
# ホストからゲストへのファイル転送プラグイン
> vagrant install vagrant-scp

# vagrant ssh の代わりに、teraterm で接続できるプラグイン
> vagrant install vagrant-teraterm

> vagrant plugin list
vagrant-scp (0.5.7, global)
vagrant-teraterm (1.0.0, global)
```

**サーバ/ネットワーク構成**

vagrant box：Ubuntu 18.04.4 LTS
kubernetes：1.15.3
containerd：1.2.9
etcd：3.4.0

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/6dccb918-4be7-0651-43ff-0faabb00a097.png)

# The Hard Way

## 全体の流れ
流れは以下の通りです。

1. ノードの作成
2. 証明書作成、設定ファイル作成
3. etcd 構築
4. Controller 構築
5. Worker 構築
6. クライアント設定
7. 動作確認
8. お片付け

では早速構築していきましょう。

## 01. ノードの作成
**The Hard Way**
[01-prerequisites](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md)
[03-compute-resources](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/03-compute-resources.md)

まずはサーバを構築します。
今回は vagrant で 7 台のサーバを一気に立てます。
※後述で説明、注意事項を記載しています

```ruby:Vagrantfile
# -*- mode: ruby -*-
# vi: set ft=ruby :

Vagrant.configure("2") do |config|
  config.vm.box = "ubuntu/bionic64"
  # vagrant deploy timeout(sec)
  config.vm.boot_timeout = 600

  # VirtualMachine common settings
  config.vm.provider "virtualbox" do |vb|
    vb.cpus = "1"
    vb.memory = "512"
    vb.customize [ "modifyvm", :id, "--uartmode1", "disconnected" ]
  end

  # LB/workspace machine
  config.vm.define "lb-0" do |c|
    c.vm.hostname = "lb-0"
    c.vm.network "private_network", ip: "10.240.0.40"

    # install and setting haproxy
    c.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y haproxy

grep -q -F 'net.ipv4.ip_nonlocal_bind=1' /etc/sysctl.conf || echo 'net.ipv4.ip_nonlocal_bind=1' >> /etc/sysctl.conf

cat >/etc/haproxy/haproxy.cfg <<EOF
global
  log /dev/log  local0
  log /dev/log  local1 notice
  chroot /var/lib/haproxy
  stats socket /run/haproxy/admin.sock mode 660 level admin
  stats timeout 30s
  user haproxy
  group haproxy
  daemon
  ca-base /etc/ssl/certs
  crt-base /etc/ssl/private
  ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
  ssl-default-bind-options no-sslv3
defaults
  log  global
  mode  tcp
  option  tcplog
  option  dontlognull
  timeout connect 5000
  timeout client  50000
  timeout server  50000
  errorfile 400 /etc/haproxy/errors/400.http
  errorfile 403 /etc/haproxy/errors/403.http
  errorfile 408 /etc/haproxy/errors/408.http
  errorfile 500 /etc/haproxy/errors/500.http
  errorfile 502 /etc/haproxy/errors/502.http
  errorfile 503 /etc/haproxy/errors/503.http
  errorfile 504 /etc/haproxy/errors/504.http
frontend k8s
  bind 10.240.0.40:6443
  default_backend k8s_backend
backend k8s_backend
  balance roundrobin
  mode tcp
  server controller-0 10.240.0.10:6443 check inter 1000
  server controller-1 10.240.0.11:6443 check inter 1000
  server controller-2 10.240.0.12:6443 check inter 1000
EOF

systemctl restart haproxy
    SHELL
  end

  # k8s controller servers
  (0..2).each do |n|
    config.vm.define "controller-#{n}" do |c|
      c.vm.hostname = "controller-#{n}"
      c.vm.network "private_network", ip: "10.240.0.1#{n}"

      # setting hosts file
      c.vm.provision "shell", inline: <<-SHELL
cat >/etc/hosts <<EOF
127.0.0.1       localhost

# KTHW Vagrant machines
10.240.0.10     controller-0
10.240.0.11     controller-1
10.240.0.12     controller-2
10.240.0.20     worker-0
10.240.0.21     worker-1
10.240.0.22     worker-2
EOF
      SHELL
    end
  end

  # k8s worker servers
  (0..2).each do |n|
    config.vm.define "worker-#{n}" do |c|
      c.vm.hostname = "worker-#{n}"
      c.vm.network "private_network", ip: "10.240.0.2#{n}"

      # setting hosts file and routings
      c.vm.provision "shell", inline: <<-SHELL
cat >/etc/hosts <<EOF
127.0.0.1       localhost

# KTHW Vagrant machines
10.240.0.10     controller-0
10.240.0.11     controller-1
10.240.0.12     controller-2
10.240.0.20     worker-0
10.240.0.21     worker-1
10.240.0.22     worker-2
EOF
case "$(hostname)" in
  worker-0)
    route add -net 10.200.1.0/24 gw 10.240.0.21
    route add -net 10.200.2.0/24 gw 10.240.0.22
    ;;
  worker-1)
    route add -net 10.200.0.0/24 gw 10.240.0.20
    route add -net 10.200.2.0/24 gw 10.240.0.22
    ;;
  worker-2)
    route add -net 10.200.0.0/24 gw 10.240.0.20
    route add -net 10.200.1.0/24 gw 10.240.0.21
    ;;
  *)
    route add -net 10.200.0.0/24 gw 10.240.0.20
    route add -net 10.200.1.0/24 gw 10.240.0.21
    route add -net 10.200.2.0/24 gw 10.240.0.22
    ;;
esac
      SHELL
    end
  end

  # plugin settings
  config.teraterm.exe_path = 'C:\Program Files (x86)\teraterm\ttermpro.exe'
end
```

**【補足】**

* ```Vagrantfile``` は適当なフォルダに配置し、```vagrant up``` で起動してください
* OS は ubuntu で構築されます
* 各サーバはストレージとして2つ(10GBと10MB)アタッチされます
* 各サーバのネットワークはアダプター1にNAT、アダプター2にホストオンリーアダプター(10.240.0.XXのアドレス)がアタッチされます
* ```config.vm.boot_timeout = 600```：Box のダウンロードの準備や ubuntu の初回起動時に時間がかかるため、タイムアウトの時間を増やしています<br>
⇒タイムアウトでエラーになっても再実行などで問題ないです
* ```vb.cpus = "1" / vb.memory = "512"```：CPU 数とメモリサイズ(MB)の指定です。必要に応じて変更してください<br>
⇒ メモリに余裕があったので 2048(MB) で実施しましたが十分でした
* ```config.vm.define "lb-0" ～```：LB 兼 作業サーバの設定です
  * ```c.vm.provision "shell", ～```：haproxy のインストールと設定ファイルの配置、haproxy の再起動を行っています
* ```config.vm.define "controller-#{n}" ～```：Controller (Master) サーバの設定です
  * ```c.vm.provision "shell", ～```：hosts ファイルに各サーバのホスト名 / ip アドレスを登録しています
* ```config.vm.define "worker-#{n}" ～```：Worker (minion) サーバの設定です
  * ```c.vm.provision "shell", ～```：Controller と同様の hosts ファイルの更新とルーティングの設定を行っています
* ```config.teraterm.exe_path```：teraterm plugin の設定を行っています。不要であればコメントアウト(先頭#)でもよいです
* 停止・再起動行うとルーティングの設定は消えてしまうため、日をまたがって作業する際には起動後に ```vagrant provision``` で再設定を行っておきましょう

## 02. クライアントツールのインストール
**The Hard Way**
[02-client-tools](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/02-client-tools.md)

lb-0 のサーバに作業用ツール、クライアントツールを準備していきます。
※各サーバへの ssh 接続は ```vagrant ssh サーバ名``` か ```vagrant teraterm サーバ名``` でアクセスします

```bash:lb-0
# lb-0 に ssh 接続
> vagrant teraterm lb-0

# CFSSL のインストール
# ダウンロード
$ wget -q --show-progress --https-only --timestamping \
  https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/linux/cfssl \
  https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/linux/cfssljson

# 実行権限付与
$ chmod +x cfssl cfssljson

# パスが通っているディレクトリに配置
$ sudo mv cfssl cfssljson /usr/local/bin/

$ ls -l /usr/local/bin/
-rwxrwxr-x 1 vagrant vagrant 20574840 Sep 15  2019 cfssl
-rwxrwxr-x 1 vagrant vagrant 12670032 Sep 15  2019 cfssljson

# 実行確認
$ cfssl version
Version: 1.3.4
Revision: dev
Runtime: go1.13

$ cfssljson --version
Version: 1.3.4
Revision: dev
Runtime: go1.13

# kubectl のインストール
# ダウンロード
$ wget https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl

# 実行権限付与
$ chmod +x kubectl

# パスが通っているディレクトリに配置
$ sudo mv kubectl /usr/local/bin/

# 実行確認
$ kubectl version --client
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.3", GitCommit:"2d3c76f9091b6bec110a5e63777c332469e0cba2", GitTreeState:"clean", BuildDate:"2019-08-19T11:13:54Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"linux/amd64"}
```

## 03. 認証局のプロビジョニングと TLS 証明書の生成
**The Hard Way**
[04-certificate-authority](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/04-certificate-authority.md)

ここでは認証局のプロビジョニングと、各コンポーネントで必要な TLS 証明書を生成します。
同じような作業が続くので間違えないように・・・

**秘密鍵と証明書の作成**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/af26b37c-38d5-7b09-7b57-92988240be73.png)

**配置する証明書と秘密鍵**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/776734b0-ebbc-c5c4-3e4d-2dd42377ad67.png)



※証明書、秘密鍵のいくつかは、04. 以降でコンポーネントの設定ファイルに含めます

```console
# lb-0 に SSH 接続
> vagrant teraterm lb-0

# または
> vagrant ssh lb-0
```

### 03-01. CA(認証局)のプロビジョニング
```bash:lb-0
# 認証局の設定ファイル生成(証明書作成する際に指定)
$ cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

# 認証局用の CSR(証明書発行要求)生成
$ cat > ca-csr.json <<EOF
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Oregon"
    }
  ]
}
EOF

# 認証局のプロビジョニング(認証局の証明書と秘密鍵を生成)
$ cfssl gencert -initca ca-csr.json | cfssljson -bare ca
YYYY/MM/DD hh:mm:ss [INFO] generating a new CA key and certificate from CSR
YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial number xxxx 

# 証明書、秘密鍵を確認
$ ls ca*.pem
ca-key.pem  ca.pem
```

### 03-02. admin ユーザのクライアント証明書の生成
```bash:lb-0
# CSR 生成
$ cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:masters",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

# 証明書、秘密鍵の生成
$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  admin-csr.json | cfssljson -bare admin

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial xxxx
YYYY/MM/DD hh:mm:ss [WARNING] This certificate lacks a "hosts" field. ～

# 証明書、秘密鍵を確認
$ ls admin*.pem
admin-key.pem  admin.pem
```

### 03-03. Kubelet のクライアント証明書の生成
```bash:lb-0
# worker 毎の証明書生成
# 10.240.0.40：LB のIPアドレス
$ for instance in worker-0 worker-1 worker-2; do
cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

EXTERNAL_IP=10.240.0.40

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${EXTERNAL_IP} \
  -profile=kubernetes \
  ${instance}-csr.json | cfssljson -bare ${instance}
done

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial number xxx
～

# 証明書、秘密鍵を確認
$ ls worker*.pem
worker-0-key.pem  worker-0.pem  worker-1-key.pem  worker-1.pem  worker-2-key.pem  worker-2.pem
```

### 03-04. Controller Manager のクライアント証明書の生成
```bash:lb-0
# cat > kube-controller-manager-csr.json <<EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial xxxx
YYYY/MM/DD hh:mm:ss [WARNING] This certificate lacks a "hosts" field. ～

# 証明書、秘密鍵を確認
$ ls kube-controller-manager*.pem
kube-controller-manager-key.pem  kube-controller-manager.pem
```

### 03-05. Kube Proxy のクライアント証明書の生成
```bash:lb-0
# cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:node-proxier",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-proxy-csr.json | cfssljson -bare kube-proxy

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial xxxx
YYYY/MM/DD hh:mm:ss [WARNING] This certificate lacks a "hosts" field. ～

# 証明書、秘密鍵を確認
$ ls kube-proxy*.pem
kube-proxy-key.pem  kube-proxy.pem
```

### 03-06. Scheduler のクライアント証明書の生成
```bash:lb-0
# cat > kube-scheduler-csr.json <<EOF
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:kube-scheduler",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  kube-scheduler-csr.json | cfssljson -bare kube-scheduler

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial xxxx
YYYY/MM/DD hh:mm:ss [WARNING] This certificate lacks a "hosts" field. ～

# 証明書、秘密鍵を確認
$ ls kube-scheduler*.pem
kube-scheduler-key.pem  kube-scheduler.pem
```

### 03-07. Kubernetes API のサーバ証明書生成
```bash:lb-0
# 環境変数設定(パブリックアドレス(LB-0))
$ KUBERNETES_PUBLIC_ADDRESS=10.240.0.40

# 環境変数設定(kubernetes ホスト名)
$ KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local

# CSR 生成
$ cat > kubernetes-csr.json <<EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

# 証明書、秘密鍵の生成
$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=10.32.0.1,10.240.0.10,10.240.0.11,10.240.0.12,${KUBERNETES_PUBLIC_ADDRESS},127.0.0.1,${KUBERNETES_HOSTNAMES} \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial xxxx

# 証明書、秘密鍵を確認
$ ls kubernetes*.pem
kubernetes-key.pem  kubernetes.pem
```

### 03-08. サービスアカウント用の Key Pair 作成
※Controller Manager でサービスアカウントを作成する際に使用する Key Pair を生成

```bash:lb-0
# CSR 生成
$ cat > service-account-csr.json <<EOF
{
  "CN": "service-accounts",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

# 証明書、秘密鍵の生成
$ cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -profile=kubernetes \
  service-account-csr.json | cfssljson -bare service-account

YYYY/MM/DD hh:mm:ss [INFO] generate received request
YYYY/MM/DD hh:mm:ss [INFO] received CSR
YYYY/MM/DD hh:mm:ss [INFO] generating key: rsa-2048
YYYY/MM/DD hh:mm:ss [INFO] encoded CSR
YYYY/MM/DD hh:mm:ss [INFO] signed certificate with serial xxxx
YYYY/MM/DD hh:mm:ss [WARNING] This certificate lacks a "hosts" field. ～

# 証明書、秘密鍵を確認
$ ls service-account*.pem
service-account-key.pem  service-account.pem
```

### 03-08. 証明書の配布
作成した証明書を lb-0 からダウンロードし、各サーバに配布します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/da288d94-c0fe-4826-0747-a6384250cc65.png)

```bash:lb-0
# 証明書、秘密鍵をアーカイブファイルにまとめる
$ tar cvf /tmp/pems.tar *.pem
admin-key.pem
・・・
worker-2.pem

# アーカイブファイル確認
$ ls /tmp/*.tar
/tmp/pems.tar
```

Vagrantfile を配置しているフォルダに、上記ファイルをダウンロードする。
アンアーカイブ後、以下のスクリプトを使って、各サーバに配布する。

```console:0301.bat（証明書配置後、実行）
@echo on

for %%i in (worker-0 worker-1 worker-2) do (
  vagrant scp pems\ca.pem      %%i:/home/vagrant/ca.pem 
  vagrant scp pems\%%i-key.pem %%i:/home/vagrant/%%i-key.pem
  vagrant scp pems\%%i.pem     %%i:/home/vagrant/%%i.pem 
)
```

```console:0302.bat（証明書配置後、実行）
@echo on

for %%i in (controller-0 controller-1 controller-2) do (
  vagrant scp pems\ca.pem                  %%i:/home/vagrant/ca.pem
  vagrant scp pems\ca-key.pem              %%i:/home/vagrant/ca-key.pem
  vagrant scp pems\kubernetes-key.pem      %%i:/home/vagrant/kubernetes-key.pem
  vagrant scp pems\kubernetes.pem          %%i:/home/vagrant/kubernetes.pem
  vagrant scp pems\service-account-key.pem %%i:/home/vagrant/service-account-key.pem
  vagrant scp pems\service-account.pem     %%i:/home/vagrant/service-account.pem
)
```

```console:開発端末(Windows_10)
# アーカイブファイルを配置し、アンアーカイブする
> dir /B
.vagrant
0301.bat   ★証明書ファイル配布バッチ(worker)
0302.bat   ★証明書ファイル配布バッチ(controller)
pems       ★アンアーカイブした証明書ファイル
pems.tar   ★アーカイブしてダウンロードした証明書ファイル
Vagrantfile

# 配置内容確認
> dir /B pems
admin-key.pem
admin.pem
ca-key.pem
ca.pem
kube-controller-manager-key.pem
kube-controller-manager.pem
kube-proxy-key.pem
kube-proxy.pem
kube-scheduler-key.pem
kube-scheduler.pem
kubernetes-key.pem
kubernetes.pem
service-account-key.pem
service-account.pem
worker-0-key.pem
worker-0.pem
worker-1-key.pem
worker-1.pem
worker-2-key.pem
worker-2.pem

# worker に証明書を配布
> 0301.bat

for %i in (worker-0 worker-1 worker-2) do (
 vagrant scp pems\ca.pem      %i:/home/vagrant/ca.pem
 vagrant scp pems\%i-key.pem  %i:/home/vagrant/%i-key.pem
 vagrant scp pems\%i.pem      %i:/home/vagrant/%i.pem
)

(
 vagrant scp pems\ca.pem            worker-0:/home/vagrant/ca.pem
 vagrant scp pems\worker-0-key.pem  worker-0:/home/vagrant/worker-0-key.pem
 vagrant scp pems\worker-0.pem      worker-0:/home/vagrant/worker-0.pem
)
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2203' (ECDSA) to the list of known hosts.
pems\ca.pem                                        100% 1318   225.3KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2203' (ECDSA) to the list of known hosts.
pems\worker-0-key.pem                              100% 1675   288.2KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2203' (ECDSA) to the list of known hosts.
pems\worker-0.pem                                  100% 1484   254.2KB/s   00:00

・・・

# controller に証明書を配布
>0302.bat

for %i in (controller-0 controller-1 controller-2) do (
 vagrant scp pems\ca.pem                  %i:/home/vagrant/ca.pem
 vagrant scp pems\ca-key.pem              %i:/home/vagrant/ca-key.pem
 vagrant scp pems\kubernetes-key.pem      %i:/home/vagrant/kubernetes-key.pem
 vagrant scp pems\kubernetes.pem          %i:/home/vagrant/kubernetes.pem
 vagrant scp pems\service-account-key.pem %i:/home/vagrant/service-account-key.pem
 vagrant scp pems\service-account.pem     %i:/home/vagrant/service-account.pem
)

(
 vagrant scp pems\ca.pem                   controller-0:/home/vagrant/ca.pem
 vagrant scp pems\ca-key.pem               controller-0:/home/vagrant/ca-key.pem
 vagrant scp pems\kubernetes-key.pem       controller-0:/home/vagrant/kubernetes-key.pem
 vagrant scp pems\kubernetes.pem           controller-0:/home/vagrant/kubernetes.pem
 vagrant scp pems\service-account-key.pem  controller-0:/home/vagrant/service-account-key.pem
 vagrant scp pems\service-account.pem      controller-0:/home/vagrant/service-account.pem
)
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
pems\ca.pem                                   100% 1318   221.9KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
pems\ca-key.pem                               100% 1679   285.5KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
pems\kubernetes-key.pem                       100% 1675   431.0KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
pems\kubernetes.pem                           100% 1663   280.5KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
pems\service-account-key.pem                  100% 1675   284.3KB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
pems\service-account.pem                      100% 1440   241.1KB/s   00:00
・・・

# ファイルが配布されたことを確認
> vagrant ssh worker-0 -c "ls /home/vagrant"
ca.pem  worker-0-key.pem  worker-0.pem
Connection to 127.0.0.1 closed.

> vagrant ssh controller-0 -c "ls /home/vagrant"
ca-key.pem  ca.pem  kubernetes-key.pem  kubernetes.pem  service-account-key.pem  service-account.pem
Connection to 127.0.0.1 closed.
```

## 04. Kubernetes 構成ファイルの生成
**The Hard Way**
[05-kubernetes-configuration-files](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/05-kubernetes-configuration-files.md)

ここでは「03.～」で作成した証明書ファイルを使って、kubernetes の各コンポーネントで使う構成ファイルを生成します。
「03.～」と同じく、同じような作業が続くので間違えないように・・・

**配置する構成ファイル**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/6bf715f1-7088-1d02-fe2d-7f68a4a3ea7b.png)

### 04-01. kubelet 用構成ファイル生成
```bash:lb-0
# 環境変数設定
# 10.240.0.40：lb-0 のパブリックIP
$ KUBERNETES_PUBLIC_ADDRESS=10.240.0.40

# 構成ファイル生成(worker 毎に作成)
$ for instance in worker-0 worker-1 worker-2; do
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=ca.pem \
    --embed-certs=true \
    --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-credentials system:node:${instance} \
    --client-certificate=${instance}.pem \
    --client-key=${instance}-key.pem \
    --embed-certs=true \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-context default \
    --cluster=kubernetes-the-hard-way \
    --user=system:node:${instance} \
    --kubeconfig=${instance}.kubeconfig

  kubectl config use-context default --kubeconfig=${instance}.kubeconfig
done
# _/_/_/ ここまで for のコマンド、以下実行結果 _/_/_/
Cluster "kubernetes-the-hard-way" set.
User "system:node:worker-0" set.
Context "default" created.
Switched to context "default".
・・・

# 生成ファイル確認
$ ls worker-*.kubeconfig
worker-0.kubeconfig  worker-1.kubeconfig  worker-2.kubeconfig
```

### 04-02. kube-proxy 用構成ファイル生成
```bash:lb-0
# 環境変数設定
# 10.240.0.40：lb-0 のパブリックIP
$ KUBERNETES_PUBLIC_ADDRESS=10.240.0.40

# 構成ファイル生成
$ kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443 \
  --kubeconfig=kube-proxy.kubeconfig

Cluster "kubernetes-the-hard-way" set.

$ kubectl config set-credentials system:kube-proxy \
  --client-certificate=kube-proxy.pem \
  --client-key=kube-proxy-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-proxy.kubeconfig

User "system:kube-proxy" set.

$ kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-proxy \
  --kubeconfig=kube-proxy.kubeconfig

Context "default" created.

$ kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig

Switched to context "default".

# 生成ファイル確認
$ ls kube-proxy.kubeconfig
kube-proxy.kubeconfig
```

### 04-03. kube-controller-manager 用構成ファイル生成
```bash:lb-0
# 構成ファイル生成
$ kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=kube-controller-manager.kubeconfig

Cluster "kubernetes-the-hard-way" set.

$ kubectl config set-credentials system:kube-controller-manager \
  --client-certificate=kube-controller-manager.pem \
  --client-key=kube-controller-manager-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-controller-manager.kubeconfig

User "system:kube-controller-manager" set.

$ kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-controller-manager \
  --kubeconfig=kube-controller-manager.kubeconfig

Context "default" created.

$ kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig

Switched to context "default".

# 生成ファイル確認
$ ls kube-controller-manager.kubeconfig
kube-controller-manager.kubeconfig
```

### 04-04. kube-scheduler 用構成ファイル生成
```bash:lb-0
# 構成ファイル生成
$ kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=kube-scheduler.kubeconfig

Cluster "kubernetes-the-hard-way" set.

$ kubectl config set-credentials system:kube-scheduler \
  --client-certificate=kube-scheduler.pem \
  --client-key=kube-scheduler-key.pem \
  --embed-certs=true \
  --kubeconfig=kube-scheduler.kubeconfig

User "system:kube-scheduler" set.

$ kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=system:kube-scheduler \
  --kubeconfig=kube-scheduler.kubeconfig

Context "default" created.

$ kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig

Switched to context "default".

# 生成ファイル確認
$ ls kube-scheduler.kubeconfig
kube-scheduler.kubeconfig
```

### 04-05. admin ユーザ用ファイル生成
```bash:lb-0
# admin ユーザ用ファイル生成
$ kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://127.0.0.1:6443 \
  --kubeconfig=admin.kubeconfig

Cluster "kubernetes-the-hard-way" set.

$ kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem \
  --embed-certs=true \
  --kubeconfig=admin.kubeconfig

User "admin" set.

$ kubectl config set-context default \
  --cluster=kubernetes-the-hard-way \
  --user=admin \
  --kubeconfig=admin.kubeconfig

Context "default" created.

$ kubectl config use-context default --kubeconfig=admin.kubeconfig

Switched to context "default".

# 生成ファイル確認
$ ls admin.kubeconfig
admin.kubeconfig
```

### 04-06. 構成ファイルの配布
作成した構成ファイルを lb-0 からダウンロードし、各サーバに配布します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/acf1dedc-0c8d-8eae-5c8b-936e27918d9c.png)


```bash:lb-0
# 構成ファイルをアーカイブファイルにまとめる
$ tar cvf /tmp/kubeconfig.tar *.kubeconfig
admin.kubeconfig
・・・
worker-2.kubeconfig

# アーカイブファイル確認
$ ls /tmp/*.tar
/tmp/kubeconfig.tar  /tmp/pems.tar
```

Vagrantfile を配置しているフォルダに、上記ファイル(kubeconfig.tar)をダウンロードする。
アンアーカイブ後、以下のスクリプトを使って、各サーバに配布する。

```console:0401.bat（構成ファイル配置後、実行）
@echo on

for %%i in (worker-0 worker-1 worker-2) do (
  vagrant scp kubeconfig\%%i.kubeconfig        %%i:/home/vagrant/%%i.kubeconfig 
  vagrant scp kubeconfig\kube-proxy.kubeconfig %%i:/home/vagrant/kube-proxy.kubeconfig
)
```

```console:0402.bat（構成ファイル配置後、実行）
@echo on

for %%i in (controller-0 controller-1 controller-2) do (
  vagrant scp admin.kubeconfig                   %%i:/home/vagrant/admin.kubeconfig
  vagrant scp kube-controller-manager.kubeconfig %%i:/home/vagrant/kube-controller-manager.kubeconfig
  vagrant scp kube-scheduler.kubeconfig          %%i:/home/vagrant/kube-scheduler.kubeconfig
)
```

```console:開発端末(Windows_10)
# アーカイブファイルを配置し、アンアーカイブする
> dir /B
.vagrant
0401.bat        ★構成ファイル配布バッチ(worker)
0402.bat        ★構成ファイル配布バッチ(controller)
kubeconfig      ★アンアーカイブした証明書ファイル
kubeconfig.tar  ★アーカイブしてダウンロードした証明書ファイル
Vagrantfile
・・・

# 配置内容確認
> dir /B kubeconfig
admin.kubeconfig
kube-controller-manager.kubeconfig
kube-proxy.kubeconfig
kube-scheduler.kubeconfig
worker-0.kubeconfig
worker-1.kubeconfig
worker-2.kubeconfig

# worker に構成ファイルを配布
> 0401.bat

for %i in (worker-0 worker-1 worker-2) do (
 vagrant scp kubeconfig\%i.kubeconfig         %i:/home/vagrant/%i.kubeconfig
 vagrant scp kubeconfig\kube-proxy.kubeconfig %i:/home/vagrant/kube-proxy.kubeconfig
)

(
 vagrant scp kubeconfig\worker-0.kubeconfig   worker-0:/home/vagrant/worker-0.kubeconfig
 vagrant scp kubeconfig\kube-proxy.kubeconfig worker-0:/home/vagrant/kube-proxy.kubeconfig
)
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2203' (ECDSA) to the list of known hosts.
kubeconfig\worker-0.kubeconfig                     100% 6369     1.6MB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2203' (ECDSA) to the list of known hosts.
kubeconfig\kube-proxy.kubeconfig                   100% 6319     1.1MB/s   00:00
・・・

# controller に構成ファイルを配布
> 0402.bat

for %i in (controller-0 controller-1 controller-2) do (
 vagrant scp kubeconfig\admin.kubeconfig                   %i:/home/vagrant/admin.kubeconfig
 vagrant scp kubeconfig\kube-controller-manager.kubeconfig %i:/home/vagrant/kube-controller-manager.kubeconfig
 vagrant scp kubeconfig\kube-scheduler.kubeconfig          %i:/home/vagrant/kube-scheduler.kubeconfig
)

(
 vagrant scp kubeconfig\admin.kubeconfig                   controller-0:/home/vagrant/admin.kubeconfig
 vagrant scp kubeconfig\kube-controller-manager.kubeconfig controller-0:/home/vagrant/kube-controller-manager.kubeconfig
 vagrant scp kubeconfig\kube-scheduler.kubeconfig          controller-0:/home/vagrant/kube-scheduler.kubeconfig
)
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
kubeconfig\admin.kubeconfig                        100% 6265     1.0MB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
kubeconfig\kube-controller-manager.kubeconfig      100% 6391     1.1MB/s   00:00
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
kubeconfig\kube-scheduler.kubeconfig               100% 6341     1.0MB/s   00:00
・・・

# ファイルが配布されたことを確認
> vagrant ssh worker-0 -c "ls /home/vagrant
ca.pem  kube-proxy.kubeconfig  worker-0-key.pem  worker-0.kubeconfig  worker-0.pem
Connection to 127.0.0.1 closed.

> vagrant ssh controller-0 -c "ls /home/vagrant"
admin.kubeconfig  kube-controller-manager.kubeconfig  kubernetes.pem
ca-key.pem        kube-scheduler.kubeconfig           service-account-key.pem
ca.pem            kubernetes-key.pem                  service-account.pem
Connection to 127.0.0.1 closed.
```

## 05. データ暗号化用の鍵と構成ファイルの生成
**The Hard Way**
[06-data-encryption-keys](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/06-data-encryption-keys.md)

ここではデータ暗号化用の鍵と構成ファイルを生成します。

```bash:lb-0
# 暗号化用鍵生成
$ ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)

# 暗号化用の構成ファイル生成
$ cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF
```

作成したファイルをダウンロードし、controller に配布します。

Vagrantfile を配置しているフォルダに、上記ファイル (encryption-config.yaml) をダウンロードする。
ダウンロード後、以下のスクリプトを使って、各サーバに配布する。

```console:0501.bat（構成ファイル配置後、実行）
@echo on

for %%i in (controller-0 controller-1 controller-2) do (
  vagrant scp encryption-config.yaml %%i:/home/vagrant/encryption-config.yaml
)
```

```console:開発端末(Windows_10)
# 構成ファイルを配置する
> dir /B
.vagrant
0501.bat                ★暗号化用構成ファイル配布バッチ
encryption-config.yaml  ★暗号化用構成ファイル
Vagrantfile
・・・

# controller に構成ファイルを配布
> 0501.bat

for %i in (controller-0 controller-1 controller-2) do (
  vagrant scp encryption-config.yaml %i:/home/vagrant/encryption-config.yaml
)

(
  vagrant scp encryption-config.yaml controller-0:/home/vagrant/encryption-config.yaml
)
Could not create directory '/home/xxxx/.ssh'.
Warning: Permanently added '[127.0.0.1]:2200' (ECDSA) to the list of known hosts.
encryption-config.yaml                             100%  240    40.5KB/s   00:00
・・・

# ファイルが配布されたことを確認
> vagrant ssh controller-0 -c "ls /home/vagrant"
admin.kubeconfig  encryption-config.yaml              kubernetes-key.pem       service-account.pem
ca-key.pem        kube-controller-manager.kubeconfig  kubernetes.pem
ca.pem            kube-scheduler.kubeconfig           service-account-key.pem
Connection to 127.0.0.1 closed.
```

## 06. etcd クラスターの起動
**The Hard Way**
[07-bootstrapping-etcd](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/07-bootstrapping-etcd.md)

ここでは、controller に etcd をインストールし、クラスタ構成で起動させます。
contoroller-0～2 の3台それぞれで以下の手順を実行していきます。


### 06-01. ダウンロードとインストール

```bash:controller-0～2
# バイナリファイルのダウンロード
$ wget -q --show-progress --https-only --timestamping \
  "https://github.com/etcd-io/etcd/releases/download/v3.4.0/etcd-v3.4.0-linux-amd64.tar.gz"

# 解凍と配置
$ tar -xvf etcd-v3.4.0-linux-amd64.tar.gz
tar: Ignoring unknown extended header keyword 'SCHILY.dev'
tar: Ignoring unknown extended header keyword 'SCHILY.ino'
tar: Ignoring unknown extended header keyword 'SCHILY.nlink'
・・・

$ sudo mv etcd-v3.4.0-linux-amd64/etcd* /usr/local/bin/

$ ls /usr/local/bin/etcd*
/usr/local/bin/etcd  /usr/local/bin/etcdctl
```

### 06-02. etcd の設定

|パス|用途|
|:---|:---|
|/etc/etcd/|証明書/秘密鍵配置ディレクトリ|
|/etc/systemd/system/|Systemd Unitファイル配置ディレクトリ|
|/usr/local/bin/|バイナリファイル配置ディレクトリ|
|/var/lib/etcd/|データディレクトリ|

```bash:controller-0～2
# データディレクトリ作成
$ sudo mkdir -p /etc/etcd /var/lib/etcd

# 証明書配置
sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/

# 設定ファイル用の環境変数定義
# contoroller サーバの IP アドレス
$ INTERNAL_IP=$(ifconfig enp0s8 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')
# controller サーバのホスト名
$ ETCD_NAME=$(hostname -s)

# 設定確認 (controller-0)
$ echo $INTERNAL_IP / $ETCD_NAME
10.240.0.10 / controller-0

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos
[Service]
ExecStart=/usr/local/bin/etcd \\
  --name ${ETCD_NAME} \\
  --cert-file=/etc/etcd/kubernetes.pem \\
  --key-file=/etc/etcd/kubernetes-key.pem \\
  --peer-cert-file=/etc/etcd/kubernetes.pem \\
  --peer-key-file=/etc/etcd/kubernetes-key.pem \\
  --trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-trusted-ca-file=/etc/etcd/ca.pem \\
  --peer-client-cert-auth \\
  --client-cert-auth \\
  --initial-advertise-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-peer-urls https://${INTERNAL_IP}:2380 \\
  --listen-client-urls https://${INTERNAL_IP}:2379,https://127.0.0.1:2379 \\
  --advertise-client-urls https://${INTERNAL_IP}:2379 \\
  --initial-cluster-token etcd-cluster-0 \\
  --initial-cluster controller-0=https://10.240.0.10:2380,controller-1=https://10.240.0.11:2380,controller-2=https://10.240.0.12:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
```

### 06-03. etcd の起動
```bash:controller-0～2
# systemd 設定ファイル再読込み(06-02で生成したファイルを読込み)
sudo systemctl daemon-reload

# 自動起動設定
sudo systemctl enable etcd

# 起動
sudo systemctl start etcd

# ステータス確認
$ sudo systemctl status etcd
● etcd.service - etcd
   Loaded: loaded (/etc/systemd/system/etcd.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ ago
・・・
```

### 06-04. 稼働確認
```bash:controller-0
# 3 ノードが startedであることを確認
$ sudo ETCDCTL_API=3 etcdctl member list \
>   --endpoints=https://127.0.0.1:2379 \
>   --cacert=/etc/etcd/ca.pem \
>   --cert=/etc/etcd/kubernetes.pem \
>   --key=/etc/etcd/kubernetes-key.pem
3a57933972cb5131, started, controller-2, https://10.240.0.12:2380, https://10.240.0.12:2379, false
f98dc20bce6225a0, started, controller-0, https://10.240.0.10:2380, https://10.240.0.10:2379, false
ffed16798470cab5, started, controller-1, https://10.240.0.11:2380, https://10.240.0.11:2379, false
```

etcd のセットアップが完了です。

## 07. Kubernetes Control Plane の起動
**The Hard Way**
[08-bootstrapping-kubernetes-controllers](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/08-bootstrapping-kubernetes-controllers.md)

ここでは、controller に Kubernetes Control Plane のコンポーネントをインストールします。
主に以下のサービスをセットアップしていきます

* Kubernetes Controller Manager
* Kubernetes API Server
* Kubernetes Scheduler

|パス|用途|
|:------|:-------------------------|
|/usr/local/bin/|バイナリファイル配置ディレクトリ|
|/var/lib/kubernetes/|構成ファイル、証明書/秘密鍵配置ディレクトリ|
|/etc/systemd/system/|Systemd Unit ファイル配置ディレクトリ|
|/etc/kubernetes/config/|設定ファイル配置ディレクトリ|

|IP/Port|オプション|用途|
|:------|:------------|:-------------------------|
|10.200.0.0/16|--cluster-cidr|クラスタ内の Pod の CIDR 範囲|
|10.32.0.0/24|--service-cluster-ip-range|Service(Cluster IP)の CIDR 範囲|
|30000-32767|--service-node-port-range|Service(NodePort) で利用できるポートの範囲|

参考サイト：
[kube-apiserver](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/)
[kube-controller-manager](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-controller-manager/)
[kube-scheduler](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-scheduler/)

contoroller-0～2 の3台それぞれで以下の手順を実行していきます。

### 07-01. ダウンロードとインストール

```bash:controller-0～2
# Kubernetes 設定ファイルディレクトリ作成
$ sudo mkdir -p /etc/kubernetes/config

# バイナリのダウンロード
$ wget -q --show-progress --https-only --timestamping \
  "https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-apiserver" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-controller-manager" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-scheduler" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl"

# バイナリの配置と実行権限付与
$ chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
$ sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
```

### 07-02. Kubernetes API Server の設定
```bash:controller-0～2
# 証明書・設定ファイル配置用ディレクトリ作成
$ sudo mkdir -p /var/lib/kubernetes/

# 証明書の配置
$ sudo mv ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
  service-account-key.pem service-account.pem \
  encryption-config.yaml /var/lib/kubernetes/

# 設定ファイル用の環境変数定義
# contoroller サーバの IP アドレス
$ INTERNAL_IP=$(ifconfig enp0s8 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/kube-apiserver.service
[Unit]
Description=Kubernetes API Server
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-apiserver \\
  --advertise-address=${INTERNAL_IP} \\
  --allow-privileged=true \\
  --apiserver-count=3 \\
  --audit-log-maxage=30 \\
  --audit-log-maxbackup=3 \\
  --audit-log-maxsize=100 \\
  --audit-log-path=/var/log/audit.log \\
  --authorization-mode=Node,RBAC \\
  --bind-address=0.0.0.0 \\
  --client-ca-file=/var/lib/kubernetes/ca.pem \\
  --enable-admission-plugins=NamespaceLifecycle,NodeRestriction,LimitRanger,ServiceAccount,DefaultStorageClass,ResourceQuota \\
  --etcd-cafile=/var/lib/kubernetes/ca.pem \\
  --etcd-certfile=/var/lib/kubernetes/kubernetes.pem \\
  --etcd-keyfile=/var/lib/kubernetes/kubernetes-key.pem \\
  --etcd-servers=https://10.240.0.10:2379,https://10.240.0.11:2379,https://10.240.0.12:2379 \\
  --event-ttl=1h \\
  --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --kubelet-certificate-authority=/var/lib/kubernetes/ca.pem \\
  --kubelet-client-certificate=/var/lib/kubernetes/kubernetes.pem \\
  --kubelet-client-key=/var/lib/kubernetes/kubernetes-key.pem \\
  --kubelet-https=true \\
  --runtime-config=api/all \\
  --service-account-key-file=/var/lib/kubernetes/service-account.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --service-node-port-range=30000-32767 \\
  --tls-cert-file=/var/lib/kubernetes/kubernetes.pem \\
  --tls-private-key-file=/var/lib/kubernetes/kubernetes-key.pem \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

### 07-03. Kubernetes Controller Manager の設定
```bash:controller-0～2
# 構成ファイルの配置
$ sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/kube-controller-manager.service
[Unit]
Description=Kubernetes Controller Manager
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-controller-manager \\
  --address=0.0.0.0 \\
  --cluster-cidr=10.200.0.0/16 \\
  --cluster-name=kubernetes \\
  --cluster-signing-cert-file=/var/lib/kubernetes/ca.pem \\
  --cluster-signing-key-file=/var/lib/kubernetes/ca-key.pem \\
  --kubeconfig=/var/lib/kubernetes/kube-controller-manager.kubeconfig \\
  --leader-elect=true \\
  --root-ca-file=/var/lib/kubernetes/ca.pem \\
  --service-account-private-key-file=/var/lib/kubernetes/service-account-key.pem \\
  --service-cluster-ip-range=10.32.0.0/24 \\
  --use-service-account-credentials=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

### 07-04. Kubernetes Scheduler の設定
```bash:controller-0～2
# 設定ファイルの配置
$ sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/

# Kubernetes Scheduler 設定ファイル生成
$ cat <<EOF | sudo tee /etc/kubernetes/config/kube-scheduler.yaml
apiVersion: kubescheduler.config.k8s.io/v1alpha1
kind: KubeSchedulerConfiguration
clientConnection:
  kubeconfig: "/var/lib/kubernetes/kube-scheduler.kubeconfig"
leaderElection:
  leaderElect: true
EOF

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/kube-scheduler.service
[Unit]
Description=Kubernetes Scheduler
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-scheduler \\
  --config=/etc/kubernetes/config/kube-scheduler.yaml \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

### 07-05. Controller Service の起動
```bash:controller-0～2
# systemd 設定ファイル再読込み
$ sudo systemctl daemon-reload

# 自動起動設定
$ sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler

Created symlink /etc/systemd/system/multi-user.target.wants/kube-apiserver.service → /etc/systemd/system/kube-apiserver.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-controller-manager.service → /etc/systemd/system/kube-controller-manager.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-scheduler.service → /etc/systemd/system/kube-scheduler.service.

# 起動
$ sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler

# 起動確認
$ sudo systemctl status kube-apiserver kube-controller-manager kube-scheduler

● kube-apiserver.service - Kubernetes API Server
   Loaded: loaded (/etc/systemd/system/kube-apiserver.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ ago
・・・

● kube-controller-manager.service - Kubernetes Controller Manager
   Loaded: loaded (/etc/systemd/system/kube-controller-manager.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ago
・・・

● kube-scheduler.service - Kubernetes Scheduler
   Loaded: loaded (/etc/systemd/system/kube-scheduler.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ago
・・・
```

### 07-06. 稼働確認
稼働確認するために lb-0 に nginx をセットアップします。

```bash:lb-0
# nginx のインストール
$ sudo apt-get update
$ sudo apt-get install -y nginx

# 設定ファイルの生成
$ cat > kubernetes.default.svc.cluster.local <<EOF
server {
  listen      80;
  server_name kubernetes.default.svc.cluster.local;

  location /healthz {
     proxy_pass                    https://10.240.0.40:6443/healthz;
     proxy_ssl_trusted_certificate /var/lib/kubernetes/ca.pem;
  }
}
EOF

# 設定ファイルの配置
$ sudo mv kubernetes.default.svc.cluster.local \
    /etc/nginx/sites-available/kubernetes.default.svc.cluster.local

$ sudo ln -s /etc/nginx/sites-available/kubernetes.default.svc.cluster.local /etc/nginx/sites-enabled/

$ ls -l /etc/nginx/sites-enabled/
・・・ default -> /etc/nginx/sites-available/default
・・・ kubernetes.default.svc.cluster.local -> /etc/nginx/sites-available/kubernetes.default.svc.cluster.local

# nginx の自動起動設定と再起動
$ sudo systemctl enable nginx
Synchronizing state of nginx.service with SysV service script with /lib/systemd/systemd-sysv-install.
Executing: /lib/systemd/systemd-sysv-install enable nginx

$ sudo systemctl restart nginx

$ sudo systemctl status nginx
● nginx.service - A high performance web server and a reverse proxy server
   Loaded: loaded (/lib/systemd/system/nginx.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ago
・・・
```

準備が整ったので、稼働確認します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/317159/1fd8829b-6af6-a0c3-9670-1f33ba89fbb0.png)

```bash:lb-0
# Cluter の接続先設定変更
$ kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://10.240.0.40:6443 \
  --kubeconfig=admin.kubeconfig

Cluster "kubernetes-the-hard-way" set.

# コンポーネントの稼働状況確認
$ kubectl get componentstatuses --kubeconfig admin.kubeconfig

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

# Kubernetes API Server の疎通確認
$ curl -H "Host: kubernetes.default.svc.cluster.local" -i http://127.0.0.1/healthz
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Wed, 25 Mar 2020 17:27:01 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
X-Content-Type-Options: nosniff

ok

# Kubernetes のバージョン情報取得
$ curl --cacert ca.pem https://10.240.0.40:6443/version

{
  "major": "1",
  "minor": "15",
  "gitVersion": "v1.15.3",
  "gitCommit": "2d3c76f9091b6bec110a5e63777c332469e0cba2",
  "gitTreeState": "clean",
  "buildDate": "2019-08-19T11:05:50Z",
  "goVersion": "go1.12.9",
  "compiler": "gc",
  "platform": "linux/amd64"
}
```

※Kubelet 承認用の RBAC をここで作成します

```bash:lb-0
# lb-0 の kubectl で実行
# ClusterRole の作成
$ cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  annotations:
    rbac.authorization.kubernetes.io/autoupdate: "true"
  labels:
    kubernetes.io/bootstrapping: rbac-defaults
  name: system:kube-apiserver-to-kubelet
rules:
  - apiGroups:
      - ""
    resources:
      - nodes/proxy
      - nodes/stats
      - nodes/log
      - nodes/spec
      - nodes/metrics
    verbs:
      - "*"
EOF

clusterrole.rbac.authorization.k8s.io/system:kube-apiserver-to-kubelet created

# ClusterRoleBinding の作成(kubernetes ユーザに 作成した ClusterRole を割当てる)
$ cat <<EOF | kubectl apply --kubeconfig admin.kubeconfig -f -
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: system:kube-apiserver
  namespace: ""
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:kube-apiserver-to-kubelet
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: kubernetes
EOF

clusterrolebinding.rbac.authorization.k8s.io/system:kube-apiserver created
```

## 08. Kubernetes Worker Node の起動
**The Hard Way**
[09-bootstrapping-kubernetes-workers](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/09-bootstrapping-kubernetes-workers.md)

ここでは、以下の流れでセットアップを行います。

* パッケージのインストール
* CNI (Container Network Interface) の設定
* containerd の設定
* Kubelet の設定
* Kubernetes Proxy の設定
* サービスの起動と確認

|パス|用途|
|:---|:---|
|/bin/|containerd バイナリファイル配置ディレクトリ|
|/etc/cni/net.d|CNI 設定ファイル格納ディレクトリ|
|/etc/containerd/|containerd 設定ファイル配置ディレクトリ|
|/etc/systemd/system/|Systemd Unit ファイル配置ディレクトリ|
|/opt/cni/bin/|CNI バイナリファイル格納ディレクトリ|
|/usr/local/bin/|バイナリファイル配置ディレクトリ|
|/var/lib/kubelet/|kubelet 構成/設定ファイル、証明書/秘密鍵配置ディレクトリ|
|/var/lib/kubernetes/|CA 証明書配置ディレクトリ|
|/var/lib/kube-proxy|kube-proxy 構成/設定ファイル配置ディレクトリ|

|IP/Port|用途|
|:------|:-------------------------|
|10.200.X.0/24|各 Worker で起動する Pod の CIDR|
|10.32.0.10|DNS Pod のアドレス|

worker-0～2 の3台それぞれで以下の手順を実行していきます。

### 08-01. パッケージのインストール
```bash:worker-0～2
# 依存パッケージのインストール
$ sudo apt-get update
$ sudo apt-get -y install socat conntrack ipset

# 本来の手順では swap を無効化する手順がありますが、
# デフォルトで無効化となっているためスキップ

# バイナリファイルのダウンロード
$ wget -q --show-progress --https-only --timestamping \
  https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.15.0/crictl-v1.15.0-linux-amd64.tar.gz \
  https://github.com/opencontainers/runc/releases/download/v1.0.0-rc8/runc.amd64 \
  https://github.com/containernetworking/plugins/releases/download/v0.8.2/cni-plugins-linux-amd64-v0.8.2.tgz \
  https://github.com/containerd/containerd/releases/download/v1.2.9/containerd-1.2.9.linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/linux/amd64/kubelet

# インストールディレクトリの作成
$ sudo mkdir -p \
  /etc/cni/net.d \
  /opt/cni/bin \
  /var/lib/kubelet \
  /var/lib/kube-proxy \
  /var/lib/kubernetes \
  /var/run/kubernetes

# バイナリファイル解凍
$ mkdir containerd

$ tar -xvf crictl-v1.15.0-linux-amd64.tar.gz
crictl

$ tar -xvf containerd-1.2.9.linux-amd64.tar.gz -C containerd
bin/
bin/containerd-shim-runc-v1
bin/ctr
bin/containerd
bin/containerd-stress
bin/containerd-shim

$ sudo tar -xvf cni-plugins-linux-amd64-v0.8.2.tgz -C /opt/cni/bin/
./
./flannel
./ptp
./host-local
./firewall
./portmap
./tuning
./vlan
./host-device
./bandwidth
./sbr
./static
./dhcp
./ipvlan
./macvlan
./loopback
./bridge

# バイナリファイルへの実行権限付与
$ sudo mv runc.amd64 runc
$ chmod +x crictl kubectl kube-proxy kubelet runc

# バイナリファイルの配置
$ sudo mv crictl kubectl kube-proxy kubelet runc /usr/local/bin/
$ ls -l /usr/local/bin/
total 232968
-rwxr-xr-x 1 vagrant vagrant  28655652 Jul  3  2019 crictl
-rwxrwxr-x 1 vagrant vagrant  36987488 Aug 19  2019 kube-proxy
-rwxrwxr-x 1 vagrant vagrant  42985504 Aug 19  2019 kubectl
-rwxrwxr-x 1 vagrant vagrant 119645232 Aug 19  2019 kubelet
-rwxrwxr-x 1 vagrant vagrant  10270256 Apr 25  2019 runc

$ sudo mv containerd/bin/* /bin/
$ ls -l /bin/containerd* /bin/ctr
-rwxr-xr-x 1 vagrant vagrant 49077568 Sep  6  2019 /bin/containerd
-rwxr-xr-x 1 vagrant vagrant  6124352 Sep  6  2019 /bin/containerd-shim
-rwxr-xr-x 1 vagrant vagrant  7703456 Sep  6  2019 /bin/containerd-shim-runc-v1
-rwxr-xr-x 1 vagrant vagrant 22433152 Sep  6  2019 /bin/containerd-stress
-rwxr-xr-x 1 vagrant vagrant 26211648 Sep  6  2019 /bin/ctr
```

### 08-02. CNI(Container Network Interface)の設定
```bash:worker0～2
# 設定ファイル用の環境変数の設定
$ INTERNAL_IP=$(ifconfig enp0s8 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

$ case "$INTERNAL_IP" in
10.240.0.20)
  POD_CIDR="10.200.0.0/24"
  ;;
10.240.0.21)
  POD_CIDR="10.200.1.0/24"
  ;;
10.240.0.22)
  POD_CIDR="10.200.2.0/24"
  ;;
*)
  echo "invalid ip"
  ;;
esac

# 設定確認 (worker-0)
$ echo $INTERNAL_IP / $POD_CIDR
10.240.0.20 / 10.200.0.0/24

# bridge ネットワークの設定ファイル生成
$ cat <<EOF | sudo tee /etc/cni/net.d/10-bridge.conf
{
    "cniVersion": "0.3.1",
    "name": "bridge",
    "type": "bridge",
    "bridge": "cnio0",
    "isGateway": true,
    "ipMasq": true,
    "ipam": {
        "type": "host-local",
        "ranges": [
          [{"subnet": "${POD_CIDR}"}]
        ],
        "routes": [{"dst": "0.0.0.0/0"}]
    }
}
EOF

# loopback のネットワーク設定ファイル生成
$ cat <<EOF | sudo tee /etc/cni/net.d/99-loopback.conf
{
    "cniVersion": "0.3.1",
    "name": "lo",
    "type": "loopback"
}
EOF
```

### 08-03. containerd の設定
```bash:worker0～2
# containerd の設定ファイル格納ディレクトリ作成
$ sudo mkdir -p /etc/containerd/

# 設定ファイル用の環境変数の設定
$ INTERNAL_IP=$(ifconfig enp0s8 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

# 設定ファイル生成
$ cat <<EOF | sudo tee /etc/containerd/config.toml
[plugins]
  [plugins.cri]
  stream_server_address = "${INTERNAL_IP}"
  [plugins.cri.containerd]
    snapshotter = "overlayfs"
    [plugins.cri.containerd.default_runtime]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runc"
      runtime_root = ""
EOF

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/containerd.service
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target

[Service]
ExecStartPre=/sbin/modprobe overlay
ExecStart=/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=1048576
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
EOF
```

### 08-04. Kubelet の設定
```bash:worker0～2
# 証明書、設定ファイルの配置
$ sudo mv ${HOSTNAME}-key.pem ${HOSTNAME}.pem /var/lib/kubelet/
$ sudo mv ${HOSTNAME}.kubeconfig /var/lib/kubelet/kubeconfig
$ sudo mv ca.pem /var/lib/kubernetes/

# 設定ファイル用の環境変数の設定
$ INTERNAL_IP=$(ifconfig enp0s8 | grep -Eo 'inet (addr:)?([0-9]*\.){3}[0-9]*' | grep -Eo '([0-9]*\.){3}[0-9]*' | grep -v '127.0.0.1')

$ case "$INTERNAL_IP" in
10.240.0.20)
  POD_CIDR="10.200.0.0/24"
  ;;
10.240.0.21)
  POD_CIDR="10.200.1.0/24"
  ;;
10.240.0.22)
  POD_CIDR="10.200.2.0/24"
  ;;
*)
  echo "invalid ip"
  ;;
esac

# kubelet の設定ファイル生成
$ cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: "/var/lib/kubernetes/ca.pem"
authorization:
  mode: Webhook
clusterDomain: "cluster.local"
clusterDNS:
  - "10.32.0.10"
podCIDR: "${POD_CIDR}"
resolvConf: "/run/systemd/resolve/resolv.conf"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${HOSTNAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${HOSTNAME}-key.pem"
EOF

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
[Unit]
Description=Kubernetes Kubelet
Documentation=https://github.com/kubernetes/kubernetes
After=containerd.service
Requires=containerd.service

[Service]
ExecStart=/usr/local/bin/kubelet \\
  --config=/var/lib/kubelet/kubelet-config.yaml \\
  --container-runtime=remote \\
  --container-runtime-endpoint=unix:///var/run/containerd/containerd.sock \\
  --image-pull-progress-deadline=2m \\
  --kubeconfig=/var/lib/kubelet/kubeconfig \\
  --network-plugin=cni \\
  --register-node=true \\
  --v=2
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

### 08-05. Kubernetes Proxy の設定
```bash:worker0～2
# 設定ファイルの配置
$ sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig

# kubernetes proxy の設定ファイル生成
$ cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
kind: KubeProxyConfiguration
apiVersion: kubeproxy.config.k8s.io/v1alpha1
clientConnection:
  kubeconfig: "/var/lib/kube-proxy/kubeconfig"
mode: "iptables"
clusterCIDR: "10.200.0.0/16"
EOF

# Systemd Unit ファイル生成
$ cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
[Unit]
Description=Kubernetes Kube Proxy
Documentation=https://github.com/kubernetes/kubernetes

[Service]
ExecStart=/usr/local/bin/kube-proxy \\
  --config=/var/lib/kube-proxy/kube-proxy-config.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
```

### 08-06. サービスの起動と確認
```bash:worker0～2
# systemd 設定ファイル再読込み
$ sudo systemctl daemon-reload

# 自動起動設定
$ sudo systemctl enable containerd kubelet kube-proxy
Created symlink /etc/systemd/system/multi-user.target.wants/containerd.service → /etc/systemd/system/containerd.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kubelet.service → /etc/systemd/system/kubelet.service.
Created symlink /etc/systemd/system/multi-user.target.wants/kube-proxy.service → /etc/systemd/system/kube-proxy.service.

# 起動
$ sudo systemctl start containerd kubelet kube-proxy

# 起動確認
$ sudo systemctl status containerd kubelet kube-proxy
● containerd.service - containerd container runtime
   Loaded: loaded (/etc/systemd/system/containerd.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ ago
・・・

● kubelet.service - Kubernetes Kubelet
   Loaded: loaded (/etc/systemd/system/kubelet.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ ago
・・・

● kube-proxy.service - Kubernetes Kube Proxy
   Loaded: loaded (/etc/systemd/system/kube-proxy.service; enabled; vendor preset: enabled)
   Active: active (running) since ・・・ ago
・・・

# 稼働確認(lb-0 の kubectl で確認)
$ kubectl get nodes --kubeconfig admin.kubeconfig
NAME       STATUS   ROLES    AGE     VERSION
worker-0   Ready    <none>   2m52s   v1.15.3
worker-1   Ready    <none>   2m54s   v1.15.3
worker-2   Ready    <none>   2m57s   v1.15.3
```

## 09. リモートアクセス用の kubectl 設定
**The Hard Way**
[10-configuring-kubectl](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/10-configuring-kubectl.md)

lb-0 にて admin ユーザで接続する際の kubectl の設定を行います。

### 09-01. kubectl 設定
```bash:lb-0
# 設定用の環境変数定義
$ KUBERNETES_PUBLIC_ADDRESS=10.240.0.40

# kubectl 設定
$ kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://${KUBERNETES_PUBLIC_ADDRESS}:6443

Cluster "kubernetes-the-hard-way" set.

$ kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=admin-key.pem

User "admin" set.

$ kubectl config set-context kubernetes-the-hard-way \
  --cluster=kubernetes-the-hard-way \
  --user=admin

Context "kubernetes-the-hard-way" created.

$ kubectl config use-context kubernetes-the-hard-way

Switched to context "kubernetes-the-hard-way".
```

### 09-02. 設定確認
```bash:lb-0
$ kubectl get componentstatuses
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-1               Healthy   {"health":"true"}
etcd-0               Healthy   {"health":"true"}
etcd-2               Healthy   {"health":"true"}

$ kubectl get nodes
NAME       STATUS   ROLES    AGE   VERSION
worker-0   Ready    <none>   14m   v1.15.3
worker-1   Ready    <none>   14m   v1.15.3
worker-2   Ready    <none>   14m   v1.15.3
```

## 10. Pod ネットワークのルーティング設定
**The Hard Way**
[11-pod-network-routes](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/11-pod-network-routes.md)

vagrant provision で設定しているため、スキップします。

## 11. DNS のデプロイ
**The Hard Way**
[12-dns-addon](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/12-dns-addon.md)

ここでは Service / Pod 用に DNS Pod をデプロイします。
DNS Pod の yaml は提供されているものを利用します。

```bash:lb-0
# DNS(coredns) Pod と関連のリソースをデプロイ
$ kubectl apply -f https://storage.googleapis.com/kubernetes-the-hard-way/coredns.yaml

serviceaccount/coredns created
clusterrole.rbac.authorization.k8s.io/system:coredns created
clusterrolebinding.rbac.authorization.k8s.io/system:coredns created
configmap/coredns created
deployment.apps/coredns created
service/kube-dns created

# デプロイ確認
kubectl get pods -l k8s-app=kube-dns -n kube-system
NAME                     READY   STATUS    RESTARTS   AGE
coredns-5fb99965-m5mn5   1/1     Running   0          38s
coredns-5fb99965-w74dj   1/1     Running   0          38s

# DNS 確認用に busybox の Pod をデプロイ
$ kubectl run --generator=run-pod/v1 busybox --image=busybox:1.28 --command -- sleep 3600
pod/busybox created

# デプロイ確認
$ kubectl get pods -l run=busybox
NAME      READY   STATUS    RESTARTS   AGE
busybox   1/1     Running   0          35s

# busybox Pod の名前を環境変数に設定
$ POD_NAME=$(kubectl get pods -l run=busybox -o jsonpath="{.items[0].metadata.name}")
$ echo $POD_NAME
busybox

# busybox Pod から 名前が引けるか確認
$ kubectl exec -ti $POD_NAME -- nslookup kubernetes
Server:    10.32.0.10
Address 1: 10.32.0.10 kube-dns.kube-system.svc.cluster.local

Name:      kubernetes
Address 1: 10.32.0.1 kubernetes.default.svc.cluster.local
```

ここまでで kubernetes の構築が完了です。

## 12. 稼働試験
**The Hard Way**
[13-smoke-test](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/13-smoke-test.md)

構築した kubernetes の稼働・機能試験を行います。

### 12-01. データ暗号化
Secret を作成し、データが etcd 内で暗号化されているか確認します

```bash:lb-0
# Secret の作成
kubectl create secret generic kubernetes-the-hard-way \
  --from-literal="mykey=mydata"

secret/kubernetes-the-hard-way created
```

controller で作成した Secret の内容を確認します。

```bash:controller-0
# etcdctl コマンドで確認
$ sudo ETCDCTL_API=3 etcdctl get \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/etcd/ca.pem \
  --cert=/etc/etcd/kubernetes.pem \
  --key=/etc/etcd/kubernetes-key.pem\
  /registry/secrets/default/kubernetes-the-hard-way | hexdump -C

00000000  2f 72 65 67 69 73 74 72  79 2f 73 65 63 72 65 74  |/registry/secret|
00000010  73 2f 64 65 66 61 75 6c  74 2f 6b 75 62 65 72 6e  |s/default/kubern|
00000020  65 74 65 73 2d 74 68 65  2d 68 61 72 64 2d 77 61  |etes-the-hard-wa|
00000030  79 0a 6b 38 73 3a 65 6e  63 3a 61 65 73 63 62 63  |y.k8s:enc:aescbc|
00000040  3a 76 31 3a 6b 65 79 31  3a 1b ee 4c 64 31 26 03  |:v1:key1:..Ld1&.|
00000050  2d 80 3e e3 b4 9f 0b 2d  0b 5b f2 a6 a0 d4 3c d3  |-.>....-.[....<.|
00000060  6f 25 bd 50 04 31 8a e0  4f d1 de fe 1f 14 d2 b7  |o%.P.1..O.......|
00000070  c7 25 19 7e 7a 98 e5 d6  7e b1 86 24 87 d8 c3 75  |.%.~z...~..$...u|
00000080  16 ed 6b 77 b1 1b 41 02  b6 b0 c8 0f 5e bf e7 6e  |..kw..A.....^..n|
00000090  c4 3a e3 39 9c 5c a3 5d  da 68 08 ae eb a4 6d 6c  |.:.9.\.].h....ml|
000000a0  51 c4 c2 50 4b e3 d4 20  c3 8d e5 de b4 b8 b0 2f  |Q..PK.. ......./|
000000b0  f8 d2 11 51 15 75 5c ec  9a 57 1c ec 31 e1 2f 38  |...Q.u\..W..1./8|
000000c0  c9 55 55 73 5e ed 4e e3  e4 7b f2 1f 84 cd d4 97  |.UUs^.N..{......|
000000d0  28 ab a7 27 89 9d 9f 5c  4a 7d ed 68 8f 24 24 94  |(..'...\J}.h.$$.|
000000e0  44 6b 97 db 31 ce f4 63  93 0a                    |Dk..1..c..|
000000ea
```

**【/registry/secrets/default/kubernetes-the-hard-way】**で登録されているデータが、**【k8s:enc:aescbc:v1:key1】**で aescbc プロバイダを使って暗号化されたことを示しています。

### 12-02. Deployment

```bash:lb-0
# nginx Deployment の作成と確認
$ kubectl create deployment nginx --image=nginx
deployment.apps/nginx created

$ kubectl get pods -l app=nginx
NAME                     READY   STATUS    RESTARTS   AGE
nginx-554b9c67f9-67vqv   1/1     Running   0          37s
```

### 12-03. Port Forwading

```bash:lb-0
# nginx Pod の名前を環境変数に設定
$ POD_NAME=$(kubectl get pods -l app=nginx -o jsonpath="{.items[0].metadata.name}")
$ echo $POD_NAME
nginx-554b9c67f9-67vqv

# Port Forwad 開始
# ※開始するとCtrl+cするまでプロンプトは戻らないため、確認は別のターミナルを開いて lb-0 にアクセス
$ kubectl port-forward $POD_NAME 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80

# Port Forwad の確認（上記とは別のターミナル）
# curl で nginx pod にアクセス
$ curl --head http://127.0.0.1:8080

HTTP/1.1 200 OK
Server: nginx/1.17.9
Date: Sun, 29 Mar 2020 17:13:58 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 03 Mar 2020 14:32:47 GMT
Connection: keep-alive
ETag: "5e5e6a8f-264"
Accept-Ranges: bytes

# Port Forward 元にもログが出力される
$ kubectl port-forward $POD_NAME 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080

# Port Forwarding の停止は「Ctrl+c」
```

### 12-04. Logs

```bash:lb-0
# ログの確認
$ kubectl logs $POD_NAME
127.0.0.1 - - [XX/XXX/XXXX:XX:XX:XX +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.58.0" "-"
```

### 12-05. Exec

```bash:lb-0
# コンテナ内でコマンド実行
$ kubectl exec -ti $POD_NAME -- nginx -v
nginx version: nginx/1.17.9
```

### 12-06. Service

NodePort の Service を作成して nginx にアクセスします。

```bash:lb-0
# Service(NodePort) の作成
$ kubectl expose deployment nginx --port 80 --type NodePort
service/nginx exposed

$ kubectl get svc nginx
NAME    TYPE       CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
nginx   NodePort   10.32.0.121   <none>        80:30124/TCP   51s

# Service のポートを環境変数に設定
$ NODE_PORT=$(kubectl get svc nginx \
  --output=jsonpath='{range .spec.ports[0]}{.nodePort}')

$ echo $NODE_PORT
30124

# アクセス確認(各 Worker へのリクエスト)
# ※NodePort へのリバプロは設定していないため、
#   lb-0 経由ではなく Worker のポートに直接アクセス
$ curl -I http://10.240.0.20:${NODE_PORT}
HTTP/1.1 200 OK
Server: nginx/1.17.9
Date: Sun, 29 Mar 2020 17:22:57 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 03 Mar 2020 14:32:47 GMT
Connection: keep-alive
ETag: "5e5e6a8f-264"
Accept-Ranges: bytes

# どの Worker にアクセスしても確認可能
$ curl -I http://10.240.0.21:${NODE_PORT}
HTTP/1.1 200 OK
・・・

$ curl -I http://10.240.0.22:${NODE_PORT}
HTTP/1.1 200 OK
・・・
```

## 13. お片付け
**The Hard Way**
[14-cleanup](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/14-cleanup.md)

最後に環境の削除です。コマンドプロンプトで ```vagrant destroy``` を実行することで作成したノードが削除できます。

# おわりに
お疲れさまでした。なかなか長い道のりだったかと思います。実際にファイルの配置をすることで、kubernetes の構成については理解が進んだかと思います。
また、構成・設定ファイルを読むことでオプションやファイルの配置先についても学習できたかと思います。

やってみてわかったことは、デプロイメントツールはやはり偉大です・・・（苦笑）