[Oracle Cloud] Oracle CloudでKubernetes the Hard Wayする (その①)

# はじめに

Kubernetesを触ってみたかったので、まずはKubernetest the Hard Wayにチャレンジした。GCPのアカウントは持っていないので、Oracle Cloud上で試しました。

[本家](https://github.com/kelseyhightower/kubernetes-the-hard-way)に記載の手順を参考にしていますが、以下のように一部環境を変更している箇所もあります。

* クライアント・ノードを、同じVCNの別サブネットに作成
* OSは、UbuntuではなくOELを使用

めちゃくちゃ長くなったので、2部構成です

[[Oracle Cloud] Oracle CloudでKubernetes the Hard Wayする (その①)](https://qiita.com/NSO-KC/items/9b827603d186c10e9b37
)に記載している範囲

* Prerequisites
* Installing the Client Tools
* Provisioning Compute Resources
* Provisioning the CA and Generating TLS Certificates
* Generating Kubernetes Configuration Files for Authentication
* Generating the Data Encryption Config and Key
* Bootstrapping the etcd Cluster
* Bootstrapping the Kubernetes Control Plane

[[Oracle Cloud] Oracle CloudでKubernetes the Hard Wayする (その②)](https://qiita.com/NSO-KC/items/333adec90b0934face33)に記載している範囲

* Bootstrapping the Kubernetes Worker Nodes
* Configuring kubectl for Remote Access
* Provisioning Pod Network Routes
* Deploying the DNS Cluster Add-on
* Smoke Test
* Cleaning Up

# Kubenets the Hard Way in OCI (コントロール・プレーンの作成まで)
## 事前準備
OCIのWebコンソールにログインします。私は、Administratorsグループに所属しているユーザでログインして操作しました。画面上部のメニューから、Cloud Shellを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/64be0888-0e36-7461-89d3-e8df11e4609e.png)

以降の手順中、(CShell):$ と表記されているプロンプトは、Cloud Shellから実行していることを意味しています。

### プロファイルを設定
各種OCIDを都度指定するのが面倒なため、/etc/oci/configにプロファイルを追記しました。私は、[k8s]という名前のプロファイルを作成しました。

※**Cloud Shellの再接続が発生した際、/etc/oci/configが初期化されます。初期化されてしまった場合は、再度追記する必要があります。これが面倒な場合は、cloud shellのデフォルト・プロファイル(私の環境では、[ap-tokyo-1]でした)を使った方がよいかもしれません**

```CONSOLE
## /etc/oci/configに[k8s]プロファイルの追記
(CShell):$ vi /etc/oci/config
…
[k8s]
tenancy=ocid1.tenancy.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxx
region=ap-tokyo-1
delegation_token_file=/etc/oci/delegation_token

## 追記する内容は、同ファイルに記載されている[ap-tokyo-1]プロファイルのコピーです(東京リージョンに作成したい場合)。
…
```

### デフォルト・コンパートメントの設定
oci_cli_rcファイルを作成して、k8sプロファイルを使用した場合のデフォルト・コンパートメントOCIDを指定します。これを指定することで、都度compartment-idを指定する必要がなくなります


```CONSOLE
## ~/.oci/oci_cli_rcファイルを作成
(CShell):$ oci setup oci-cli-rc
________________< コマンド実行後 出力 >________________
Predefined queries written under section OCI_CLI_CANNED_QUERIES
Command aliases written under section OCI_CLI_COMMAND_ALIASES
Parameter aliases written under section OCI_CLI_PARAM_ALIASES

## k8sプロファイルを作成し、コンパートメントのOCIDを追記 # コンパートメントを指定しなかった場合のデフォルト
## compartment OCIDは、Webコンソールから調べます
(CShell):$ vi ~/.oci/oci_cli_rc
________________< 追記内容 >________________
[k8s]
compartment-id = ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

//省略//
```

## ネットワーク・リソースの作成
ComputeやLoad Balancerを配置する仮想クラウドネットワークを作成していきます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/3477776e-769f-ac96-3bb3-e4071a33e826.png)


### VCNの作成とデフォルト設定への追記
まずは、VCNを作成します

```CONSOLE
## VCNを作成
(CShell):$ oci --profile k8s network vcn create \
 --display-name k8s \
 --cidr-block 10.240.0.0/23 \
 --dns-label k8s
________________< 出力は省略 >________________

## 作成したVCNのOCIDをoci_cli_rcに追記する。# vcn-idを指定しなかった場合のデフォルト値とする
(CShell):$ oci --profile k8s network vcn list \
  --raw-output \
  --query 'data[?"display-name"==`k8s`].id |[0]'
________________< コマンド実行後 出力 >________________
ocid1.vcn.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
⇒ ocid1.vcnから始まるIDが出力されること

(CShell):$ vi ~/.oci/oci_cli_rc
[k8s]
compartment-id = ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
________________< 追記内容 >________________
vcn-id = ocid1.vcn.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

```
### インターネットGWの作成
インターネットからクライアント・ノードにアクセスできるように、インターネットGWを作成します

```CONSOLE
(CShell):$ oci --profile k8s network internet-gateway create \
  --display-name k8s-ig \
  --is-enabled true
________________< 出力は省略 >________________

```


### NAT GWの作成
Privateサブネットからyumリポジトリなどにアクセスできるように、NAT GWを作成します。Oracle CloudはNAT GWに従量課金されないのでうれしいです。

```CONSOLE
(CShell):$ oci --profile k8s network nat-gateway create \
  --display-name k8s-ng
________________< 出力は省略 >________________
```

### ルート表の作成
Publicサブネット用のルート表を作成します

```CONSOLE
## インターネットGWのOCIDを取得する
(CShell):$ INTERNET_GW_OCID=$(oci --profile k8s network internet-gateway list --raw-output --query 'data[].id |[0]')
(CShell):$ echo ${INTERNET_GW_OCID}
________________< コマンド実行後 出力 >________________
ocid1.internetgateway.oc1.ap-tokyo-1.XXXXXXXXXXXXXXXXXXXXXXXXXX
⇒ ocid1.internetgatewayから始まるIDが出力されることを確認する

## Publicサブネット用ルート表の作成
## VCNの外に出る通信は、インターネットGWにルーティングするように設定する
(CShell):$ oci --profile k8s network route-table create \
  --display-name route-table-public \
  --route-rules '[{"cidrBlock":"0.0.0.0/0"
                  ,"networkEntityId":"'${INTERNET_GW_OCID}'"}]'
________________< 出力は省略 >________________
```

続いて、Privateサブネット用のルート表を作成します。

```CONSOLE
## NAT GWのOCIDを取得する
(CShell):$ NAT_GW_OCID=$(oci --profile k8s network nat-gateway list --raw-output --query 'data[].id |[0]')
(CShell):$ echo ${NAT_GW_OCID}
________________< コマンド実行後 出力 >________________
ocid1.natgateway.oc1.ap-tokyo-1.aaaaaaaa6i65puwgi23reds2pacxlt3zhfjn6rjnro7cdw4dysjz2vqjlaqq
⇒ ocid1.natgatewayから始まるIDが出力されることを確認する

## Privaeサブネット用ルート表の作成
## VCNの外に出る通信は、NAT GWにルーティングするように設定する。

(CShell):$ oci --profile k8s network route-table create \
  --display-name route-table-private \
  --route-rules '[{"cidrBlock":"0.0.0.0/0"
                  ,"networkEntityId":"'${NAT_GW_OCID}'"}]'
________________< 出力は省略 >________________
```

### サブネットの作成

Public サブネットを作成します。Publicサブネットには、クライアント・ノードを配置します

```CONSOLE
## Publicサブネット用ルート表のOCIDを取得する
(CShell):$ PUBLIC_ROUTE_OCID=$(oci --profile k8s network route-table list --raw-output --query 'data[?"display-name"==`route-table-public`].id |[0]')
(CShell):$ echo ${PUBLIC_ROUTE_OCID}
________________< コマンド実行後 出力 >________________
ocid1.routetable.oc1.ap-tokyo-1.aaaaaaaaiutmujtsmbxikno75fly33w2d47r3pnk42lkwc5djmwmcrqpeaia=$
⇒ ocid1.routetableから始まるIDが出力されることを確認する

## Publicサブネットを作成
(CShell):$ oci --profile k8s network subnet create \
 --display-name public \
 --cidr-block 10.240.1.0/24 \
 --prohibit-public-ip-on-vnic false \
 --dns-label public \
 --route-table-id ${PUBLIC_ROUTE_OCID}
________________< 出力は省略 >________________
```

続いて、Privateサブネットを作成します。Privateサブネットは、デフォルトとして設定します

```CONSOLE
## Privateサブネット用ルート表のOCIDを取得する
(CShell):$ PRIVATE_ROUTE_OCID=$(oci --profile k8s network route-table list --raw-output --query 'data[?"display-name"==`route-table-private`].id |[0]')
(CShell):$ echo ${PRIVATE_ROUTE_OCID}
________________< コマンド実行後 出力 >________________
ocid1.routetable.oc1.ap-tokyo-1.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
⇒ ocid1.routetableから始まるIDが出力されることを確認する

## Privateサブネットを作成
(CShell):$ oci --profile k8s network subnet create \
 --display-name private \
 --cidr-block 10.240.0.0/24 \
 --prohibit-public-ip-on-vnic true \
 --dns-label private \
 --route-table-id ${PRIVATE_ROUTE_OCID}
________________< 出力は省略 >________________

## 作成したPrivateサブネットのOCIDを取得
(CShell):$ oci --profile k8s network subnet list --raw-output --query 'data[?"display-name"==`private`].id |[0]'
________________< コマンド実行後 出力 >________________
ocid1.subnet.oc1.ap-tokyo-1.XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
⇒ ocid1.subnetから始まるIDが出力されることを確認する

## oci_cli_rcに追記する。# サブネットを指定しなかった場合のデフォルトとして設定する
(CShell):$ vi ~/.oci/oci_cli_rc
[k8s]
compartment-id = ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
vcn-id = ocid1.vcn.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
________________< 追記内容 subnet-idsはJSON形式 >________________
subnet-ids = ["ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
subnet-id = ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

### ネットワーク・セキュリティ・グループの作成
コントローラ/ワーカー・ノードに割り当てるネットワーク・セキュリティ・グループを作成します

```CONSOLE
## ネットワーク・セキュリティ・グループを作成
(CShell):$ oci --profile k8s network nsg create \
  --display-name nsg-k8s
________________< 出力は省略 >________________

## 作成したネットワーク・セキュリティ・グループのOCIDを取得
(CShell):$ oci --profile k8s network nsg list --raw-output --query 'data[?"display-name"==`nsg-k8s`].id |[0]'
________________< コマンド実行後 出力 >________________
ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
⇒ ocid1.networksecuritygroupから始まるIDが出力されることを確認する

## oci_cli_rcに追記する。# ネットワーク・セキュリティ・グループを指定しなかった場合のデフォルト値とする
(CShell):$ vi ~/.oci/oci_cli_rc
[k8s]
compartment-id = ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
vcn-id = ocid1.vcn.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
subnet-ids = ["ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
subnet-id = ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
________________< 追記内容 nsg-idsはJSON形式 >________________
nsg-ids = ["ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
nsg-id = ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```

つづいて、作成したネットワーク・セキュリティ・グループに許可ルールを追加します

```CONSOLE
## 10.240.0.0/24(Privateサブネット)、10.240.1.0/24(Publicサブネット)、10.200.0.0/16(Podネットワーク)からのアクセスをすべて許可する

(CShell):$ oci --profile k8s network nsg rules add \
  --security-rules '[{"direction": "INGRESS", "source": "10.240.0.0/24", "protocol": "all"}
                    ,{"direction": "INGRESS", "source": "10.240.1.0/24", "protocol": "all"}
                    ,{"direction": "INGRESS", "source": "10.200.0.0/16", "protocol": "all"}
                    ]'
________________< 出力は省略 >________________
```
Webコンソールから、作成したネットワーク・セキュリティ・グループに以下の3ルールが追加されていることを確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/d03150cc-a9ac-3178-e99b-99a55caaf454.png)

## Private LBの作成
API Serverを負荷分散するためのPrivate LBを作成します。
一番安い10Mbpsを使用します。このシェイプを使用する場合の2021年1月時点での従量課金額は、以下のとおりです (Always Free枠のLoad Balancerが使える場合は、それを利用します)

* Load Balancer(10Mbps)価格： (1.356円 + 0.012円×10Mbps = )/時間
※ 課金額が気になる方は[最新のプライスリスト](https://www.oracle.com/jp/cloud/networking/load-balancing-pricing.html)を確認してください

```
## Private LBの作成
(CShell):$ oci --profile k8s lb load-balancer create \
  --display-name k8s-api-lb\
  --shape-name 10Mbps \
  --is-private true \
  --wait-for-state SUCCEEDED
________________< 出力は省略 >________________
```

## Computeの作成
クライアント・ノード×1、コントローラ・ノード×3、ワーカー・ノード×3を作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/043a4586-9b08-d343-0968-97c5bcc03f8e.png)

一番安いE2.1インスタンスを使用します。このインスタンスを使用する場合の2021年1月時点での従量課金額は、以下のとおりです。

* Compute価格     ：時間当たり(3.6円×7台 = 25.2円/時間) - Computeを起動している間課金される
　※ 課金額が気になる方は[最新のプライスリスト](https://www.oracle.com/jp/cloud/compute/pricing.html)を確認してください
* Block Volume価格：月当たり (48GB×(3.06円+0.204×10)×7台 = 1713.6円) - Boot Volumeを削除するまで課金対象

　※ 課金額が気になる方は[最新のプライスリスト](https://www.oracle.com/jp/cloud/storage/pricing.html)を確認してください

### Compute作成用イメージのOCIDを取得する
```CONSOLE
(CShell):$ oci --profile k8s compute image list \
  --output=table \
  --query 'sort_by(data, &"time-created")[?"operating-system"==`Oracle Linux` && contains("display-name", `Oracle-Linux-7.9`)].{"display-name":"display-name", "id":"id"}'
________________< コマンド実行後 出力 >________________
+----------------------------------------+-----------------------------------------------------------------------------------------+
| display-name                           | id                                                                                      |
+----------------------------------------+-----------------------------------------------------------------------------------------+
| Oracle-Linux-7.9-2020.10.26-0          | ocid1.image.oc1.ap-tokyo-1.aaaaaaaai4m66scxa3cu3sro47fh57trgepby5httw3eyrxkao544rt25s5a |
| Oracle-Linux-7.9-Gen2-GPU-2020.10.28-0 | ocid1.image.oc1.ap-tokyo-1.aaaaaaaakshu36ii77ccp5qhwalchpneuatixv5z27bdnrn2lf7fzsmqtipq |
| Oracle-Linux-7.9-Gen2-GPU-2020.11.10-0 | ocid1.image.oc1.ap-tokyo-1.aaaaaaaaq7ypxho64hcariezzyfefgsoo6y6zh4waqv37pbpgwul6uuut7oa |
| Oracle-Linux-7.9-2020.11.10-1          | ocid1.image.oc1.ap-tokyo-1.aaaaaaaazgoy6klsxzbi5jh5kx2qwxw6l6mqtlbo4c4kak4zes7zwytd4z2q |
| Oracle-Linux-7.9-Gen2-GPU-2020.12.17-0 | ocid1.image.oc1.ap-tokyo-1.aaaaaaaatdfjnuqelvb3diaofq4jz7kl3joa6wzh5oxfigarbvfx77jlkslq |
★| Oracle-Linux-7.9-2021.01.12-0          | ocid1.image.oc1.ap-tokyo-1.aaaaaaaazitsi3g3qp4h3ww37vigol7qynaulhltpk6z6oub5d7dqjhznh4a |
| Oracle-Linux-7.9-Gen2-GPU-2021.01.12-0 | ocid1.image.oc1.ap-tokyo-1.aaaaaaaanmv2mktfyc6otbrtflf7ule7jsikqr62vm4asbq6de3uofnvlefa |
+----------------------------------------+-----------------------------------------------------------------------------------------+
⇒ Oracle-Linux-7.X-YYYY.MM.YY-Nという名前のイメージのOCIDをコピーする

## oci_cli_rcに追記する。# イメージIDを指定しなかった場合のデフォルト値とする
(CShell):$ vi ~/.oci/oci_cli_rc
[k8s]
compartment-id = ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
vcn-id = ocid1.vcn.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
subnet-ids = ["ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
subnet-id = ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
nsg-ids = ["ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
nsg-id = ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
________________< 追記内容 >________________
image-id = ocid1.image.oc1.ap-tokyo-1.aaaaaaaazitsi3g3qp4h3ww37vigol7qynaulhltpk6z6oub5d7dqjhznh4a

## 東京リージョンのAvailability Domain名を取得する
(CShell):$ oci iam --profile k8s availability-domain list --raw-output --query 'data[].name |[0]'
________________< コマンド実行後 出力 >________________
lihX:AP-TOKYO-1-AD-1

## oci_cli_rcに追記する。# availability-domainを指定しなかった場合のデフォルト値とする
(CShell):$ vi ~/.oci/oci_cli_rc
[k8s]
compartment-id = ocid1.compartment.oc1..xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
vcn-id = ocid1.vcn.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
subnet-ids = ["ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
subnet-id = ocid1.subnet.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
nsg-id = ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
nsg-ids = ["ocid1.networksecuritygroup.oc1.ap-tokyo-1.xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]
image-id = ocid1.image.oc1.ap-tokyo-1.aaaaaaaazitsi3g3qp4h3ww37vigol7qynaulhltpk6z6oub5d7dqjhznh4a
________________< 追記内容 >________________
availability-domain = lihX:AP-TOKYO-1-AD-1
```

### Computeアクセス用の公開鍵ペアを作成
```CONSOLE
## クライアント・ノード用の鍵を作成
(CShell):$ mkdir -p ~/k8s/ssh/client

(CShell):$ ssh-keygen -t rsa 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa): /home/<USERNAME>/k8s/ssh/client/id_rsa
↑ ★$HOME/k8s/ssh/client/id_rsaを入力★
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
________________< 以降の出力は省略 >________________

(CShell):$ ll ~/k8s/ssh/client
________________< コマンド実行後 出力 >________________
total 8
-rw-------. 1 xxx oci 1675 Jan 21 02:26 id_rsa
-rw-r--r--. 1 xxx oci  402 Jan 21 02:26 id_rsa.pub
⇒ 上記ファイルが存在すること

## コントローラおよびワーカー・ノード用の鍵を作成
(CShell):$ mkdir -p ~/k8s/ssh/nodes
(CShell):$ ssh-keygen -t rsa 
Generating public/private rsa key pair.
Enter file in which to save the key (/home/username/.ssh/id_rsa): /home/<USERNAME>/k8s/ssh/nodes/id_rsa
↑ ★$HOME/k8s/ssh/nodes/id_rsaを入力★
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
//省略//

(CShell):$ ll ~/k8s/ssh/nodes
________________< コマンド実行後 出力 >________________
total 8
-rw-------. 1 xxx oci 1675 Jan 21 02:26 id_rsa
-rw-r--r--. 1 xxx oci  402 Jan 21 02:26 id_rsa.pub
⇒ 上記ファイルが存在すること
```

### クライアント・ノードを作成

```CONSOLE
## PublicサブネットのOCIDを取得
(CShell):$ PUBLIC_OCID=$(oci --profile k8s network subnet list --raw-output --query 'data[?"display-name"==`public`].id |[0]')
(CShell):$ echo ${PUBLIC_OCID}
________________< コマンド実行後 出力 >________________
ocid1.subnet.oc1.ap-tokyo-1.XXXXXXXXXXXXXXXXXXXXXXXXX
⇒ ocid1.subnetから始まるIDが出力されることを確認する

## クライアント・ノードの作成
(CShell):$ oci --profile k8s compute instance launch \
    --shape VM.Standard.E2.1 \
    --subnet-id ${PUBLIC_OCID} \
    --display-name k8s-client \
    --hostname-label client \
    --ssh-authorized-keys-file $HOME/k8s/ssh/client/id_rsa.pub
________________< 出力は省略 >________________

## クライアント・ノードのPublic IPを確認
(CShell):$ oci --profile k8s compute instance list-vnics --query 'data[?"hostname-label"==`client`]."public-ip"'
________________< コマンド実行後 出力 >________________
[
  "140.238.48.64"
]
※表示されるようになるまでしばらく時間がかかります。

⇒ Computeノードの作成完了後、Cloud Shellの~/k8s/ssh/client/id_rsa鍵を使用し、上記で確認したPublic IPにインターネット経由でsshログインできることを確認する。

※~/k8s/ssh/client/id_rsaをcatで表示させた後にコピペするのが簡単です。(Chromeの場合は、Ctrl+Cでコピーできました。)
```

### クライアント・ノードに、コントローラ/ワーカー・ノード用の秘密鍵を配賦

```CONSOLE
(CShell):$ scp -p -i ~/k8s/ssh/client/id_rsa ~/k8s/ssh/nodes/id_rsa opc@<クライアントのPublic IP>:~/.ssh/
________________< 出力は省略 >________________
```

### コントローラノードを作成

コントローラ・ノードを作成します

```
(CShell):$ for instance in controller01 controller02 controller03
do
  oci --profile k8s compute instance launch \
    --shape VM.Standard.E2.1 \
    --display-name ${instance} \
    --hostname-label ${instance} \
    --ssh-authorized-keys-file $HOME/k8s/ssh/nodes/id_rsa.pub
done
________________< 出力は省略 >________________
```
### ワーカーノードの作成
ワーカー・ノードを作成します

```
(CShell):$ for instance in worker01 worker02 worker03
do
  oci --profile k8s compute instance launch \
    --shape VM.Standard.E2.1 \
    --display-name ${instance} \
    --hostname-label ${instance} \
    --ssh-authorized-keys-file $HOME/k8s/ssh/nodes/id_rsa.pub
done
________________< 出力は省略 >________________
```

クライアントから、それぞれのコントローラ/ワーカーノードにsshアクセスできることを確認します
※以降の手順中、(client):$ と表記されているプロンプトは、クライアント・ノードから実行していることを意味しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/f6c7f013-d621-c5ea-385c-b198db2b4b62.png)

```CONSOLE
(client):$ ssh controller01.private -o StrictHostKeyChecking=no hostname -s
________________< コマンド実行後 出力 >________________
Warning: Permanently added 'worker03.private,10.240.0.19' (ECDSA) to the list of known hosts.
controller01

(client):$ ssh controller02.private -o StrictHostKeyChecking=no hostname -s
________________< コマンド実行後 出力 >________________
Warning: Permanently added 'worker03.private,10.240.0.19' (ECDSA) to the list of known hosts.
controller02

(client):$ ssh controller03.private -o StrictHostKeyChecking=no hostname -s
________________< コマンド実行後 出力 >________________
Warning: Permanently added 'worker03.private,10.240.0.19' (ECDSA) to the list of known hosts.
controller03

(client):$ ssh worker01.private -o StrictHostKeyChecking=no hostname -s
________________< コマンド実行後 出力 >________________
Warning: Permanently added 'worker03.private,10.240.0.19' (ECDSA) to the list of known hosts.
worker01

(client):$ ssh worker02.private -o StrictHostKeyChecking=no hostname -s
________________< コマンド実行後 出力 >________________
Warning: Permanently added 'worker03.private,10.240.0.19' (ECDSA) to the list of known hosts.
worker02

(client):$ ssh worker03.private -o StrictHostKeyChecking=no hostname -s
________________< コマンド実行後 出力 >________________
Warning: Permanently added 'worker03.private,10.240.0.19' (ECDSA) to the list of known hosts.
worker03
```

## クライアントツールのインストール
cfssl、cfssljson、kubectlをインストールします

### cfsslのインストール
```CONSOLE
## ダウンロード
(client):$ mkdir ~/cfssl && cd ~/cfssl
(client):$ sudo wget -q \
  https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/1.4.1/linux/cfssl \
  https://storage.googleapis.com/kubernetes-the-hard-way/cfssl/1.4.1/linux/cfssljson

## パーミッションを付与
(client):$ sudo chmod +x cfssl cfssljson

## 確認
(client):$ ll
________________< コマンド実行後 出力 >________________
total 23772
-rwxr-xr-x. 1 root root 14842064 Jul 18  2020 cfssl
-rwxr-xr-x. 1 root root  9495504 Jul 18  2020 cfssljson
⇒ 上記2ファイルが存在していること

## /usr/local/binに移動
(client):$ sudo mv cfssl cfssljson /usr/local/bin/

## バージョン確認 (version 1.4.1以上がインストールされていること)
(client):$ cfssl version
________________< コマンド実行後 出力 >________________
Version: 1.4.1
Runtime: go1.12.12

(client):$ cfssljson --version
________________< コマンド実行後 出力 >________________
Version: 1.4.1
Runtime: go1.12.12
```

### kubectlのインストール
```CONSOLE
## ダウンロード
(client):$ mkdir ~/kubectl && cd ~/kubectl
(client):$ sudo wget -q \
  https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl

## パーミッションを付与
(client):$ sudo chmod +x kubectl

## 確認
(client):$ ll
________________< コマンド実行後 出力 >________________
total 43004
-rwxr-xr-x. 1 root root 44036096 Jul 15  2020 kubectl
⇒ 上記ファイルが存在していること

## 移動
(client):$ sudo mv kubectl /usr/local/bin/

## バージョン確認(version  1.18.6以上がインストールされていること)
(client):$ kubectl version --client
________________< コマンド実行後 出力 >________________
Client Version: version.Info{Major:"1", Minor:"18", GitVersion:"v1.18.6", GitCommit:"dff82dc0de47299ab66c83c626e08b245ab19037", GitTreeState:"clean", BuildDate:"2020-07-15T16:58:53Z", GoVersion:"go1.13.9", Compiler:"gc", Platform:"linux/amd64"}
```

## 証明書の作成
各種コンポーネント間通信で利用する証明書を作成します。たくさん作ります。。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/72e0d635-b972-7cb0-e5db-659269ee62b6.png)

### その他の証明書を作る際のベースとなる証明書(CA)を作成する
```CONSOLE
(client):$ mkdir -p ~/cfssl/cert && cd ~/cfssl/cert

## {～から～}までをコピーして実行
(client):$ {

cat > ca-config.json <<EOF
{
  "signing": {
    "default": {
      "expiry": "8760h"
    },
    "profiles": {
      "kubernetes": {
        "usages": ["signing", "key encipherment", "server auth", "client auth"],
        "expiry": "8760h"
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
  "CN": "Kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Tokyo"
    }
  ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:18:40 [INFO] generating a new CA key and certificate from CSR
2021/01/23 11:18:40 [INFO] generate received request
2021/01/23 11:18:40 [INFO] received CSR
2021/01/23 11:18:40 [INFO] generating key: rsa-2048
2021/01/23 11:18:41 [INFO] encoded CSR
2021/01/23 11:18:41 [INFO] signed certificate with serial number 448846874133971172847044723040193611015639796239

(client):$ ll
________________< コマンド実行後 出力 >________________
total 20
-rw-r--r--. 1 root root  232 Jan 21 07:55 ca-config.json
-rw-r--r--. 1 root root 1005 Jan 21 07:55 ca.csr
-rw-r--r--. 1 root root  211 Jan 21 07:55 ca-csr.json
-rw-------. 1 root root 1675 Jan 21 07:55 ca-key.pem
-rw-r--r--. 1 root root 1318 Jan 21 07:55 ca.pem
⇒ 上記ファイルが存在していること
```

### 各kubernetesコンポーネントのサーバ証明書とadminユーザ用のクライアント証明書を作成

```CONSOLE
## {～から～}までをコピーして実行
(client):$ {

cat > admin-csr.json <<EOF
{
  "CN": "admin",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "system:masters",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
EOF

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

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:25:51 [INFO] generate received request
2021/01/23 11:25:51 [INFO] received CSR
2021/01/23 11:25:51 [INFO] generating key: rsa-2048
2021/01/23 11:25:51 [INFO] encoded CSR
2021/01/23 11:25:51 [INFO] signed certificate with serial number 127350543274952557206548036128992397012238906812
2021/01/23 11:25:51 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
⇒ 上記のWARNINGは無視します

(client):$ ls -ltr admin*
________________< コマンド実行後 出力 >________________
-rw-r--r--. 1 root root  231 Jan 21 07:59 admin-csr.json
-rw-r--r--. 1 root root 1428 Jan 21 07:59 admin.pem
-rw-------. 1 root root 1675 Jan 21 07:59 admin-key.pem
-rw-r--r--. 1 root root 1033 Jan 21 07:59 admin.csr
⇒ 上記ファイルが存在していること
```

### Kubelet クライアント証明書の作成

まず、**Cloud Shellから**ワーカー・ノードのIPを確認します。
**ワーカー・ノードのIPアドレス一覧は以降も頻繁に利用するため、メモしておきます**

```CONSOLE
(CShell):$ oci --profile k8s compute instance list-vnics \
  --query 'sort_by(data, &"hostname-label")[?contains("hostname-label", `worker`)].{"hostname-label":"hostname-label","private-ip":"private-ip"}' \
  --output=table
________________< コマンド実行後 出力 >________________
+----------------+------------+
| hostname-label | private-ip |
+----------------+------------+
| worker01       | 10.240.0.X |
| worker02       | 10.240.0.X |
| worker03       | 10.240.0.X |
+----------------+------------+
```

続いて、Kubeletクライアント証明書を作成します。**クライアント・ノードから**実行します

```CONSOLE
## 連想配列を設定
(client):$ declare -A worker_nodes=(
   [worker01]="★worker01のIP★"
   [worker02]="★worker02のIP★"
   [worker03]="★worker03のIP★"
)

## 連想配列に設定した値が参照できること
(client):$ echo ${worker_nodes["worker01"]}
________________< コマンド実行後 出力 >________________
10.240.0.6
⇒ 設定したIPアドレスが出力されること

## Kubeletクライアント証明書の作成
(client):$ for instance in worker01 worker02 worker03
do
cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
EOF
cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=${instance},${worker_nodes["${instance}"]} \
  -profile=kubernetes \
   ${instance}-csr.json | cfssljson -bare ${instance}
done
________________< コマンド実行後 出力 >________________
2021/01/23 11:34:04 [INFO] generate received request
2021/01/23 11:34:04 [INFO] received CSR
2021/01/23 11:34:04 [INFO] generating key: rsa-2048
2021/01/23 11:34:04 [INFO] encoded CSR
2021/01/23 11:34:04 [INFO] signed certificate with serial number 335056572191797643324790149166052815291094643015
//以降の出力は省略//

(client):$ ls -ltr worker*
________________< コマンド実行後 出力 >________________
-rw-r--r--. 1 root root  244 Jan 21 08:33 worker01-csr.json
-rw-r--r--. 1 root root 1484 Jan 21 08:33 worker01.pem
-rw-------. 1 root root 1679 Jan 21 08:33 worker01-key.pem
-rw-r--r--. 1 root root 1110 Jan 21 08:33 worker01.csr
-rw-r--r--. 1 root root  244 Jan 21 08:33 worker02-csr.json
-rw-r--r--. 1 root root 1484 Jan 21 08:33 worker02.pem
-rw-------. 1 root root 1675 Jan 21 08:33 worker02-key.pem
-rw-r--r--. 1 root root 1110 Jan 21 08:33 worker02.csr
-rw-r--r--. 1 root root  244 Jan 21 08:33 worker03-csr.json
-rw-r--r--. 1 root root 1484 Jan 21 08:33 worker03.pem
-rw-------. 1 root root 1675 Jan 21 08:33 worker03-key.pem
-rw-r--r--. 1 root root 1110 Jan 21 08:33 worker03.csr
⇒ 上記ファイルが存在していること
```

### コントロール・マネージャ用のクライアント証明書を作成

```CONSOLE
## {～から～}までをコピーして実行
(client):$ {

cat > kube-controller-manager-csr.json <<EOF
{
  "CN": "system:kube-controller-manager",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "system:kube-controller-manager",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
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

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:36:42 [INFO] generate received request
2021/01/23 11:36:42 [INFO] received CSR
2021/01/23 11:36:42 [INFO] generating key: rsa-2048
2021/01/23 11:36:42 [INFO] encoded CSR
2021/01/23 11:36:42 [INFO] signed certificate with serial number 437969658639895259786858542488119943804913084608
2021/01/23 11:36:42 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
⇒ 上記のWARNINGは無視する

(client):$ ls -ltr kube-controller-manager*
________________< コマンド実行後 出力 >________________
-rw-rw-r--. 1 opc opc  272 Jan 22 00:27 kube-controller-manager-csr.json
-rw-rw-r--. 1 opc opc 1484 Jan 22 00:27 kube-controller-manager.pem
-rw-------. 1 opc opc 1675 Jan 22 00:27 kube-controller-manager-key.pem
-rw-r--r--. 1 opc opc 1090 Jan 22 00:27 kube-controller-manager.csr
⇒ 上記のファイルが存在していること
```

### Kube Proxy用のクライアント証明書の作成

```CONSOLE
(client):$ {

cat > kube-proxy-csr.json <<EOF
{
  "CN": "system:kube-proxy",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "system:node-proxier",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
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

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:38:16 [INFO] generate received request
2021/01/23 11:38:16 [INFO] received CSR
2021/01/23 11:38:16 [INFO] generating key: rsa-2048
2021/01/23 11:38:17 [INFO] encoded CSR
2021/01/23 11:38:17 [INFO] signed certificate with serial number 665981232123811806966320246150016405203111028560
2021/01/23 11:38:17 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
⇒ 上記のWARNINGは無視する

(client):$ ls -ltr kube-proxy*
________________< コマンド実行後 出力 >________________
-rw-rw-r--. 1 opc opc  248 Jan 22 00:47 kube-proxy-csr.json
-rw-rw-r--. 1 opc opc 1452 Jan 22 00:47 kube-proxy.pem
-rw-------. 1 opc opc 1679 Jan 22 00:47 kube-proxy-key.pem
-rw-r--r--. 1 opc opc 1058 Jan 22 00:47 kube-proxy.csr
⇒ 上記のファイルが存在していること
```

### スケジューラ用クライアント証明書の作成

```CONSOLE
(client):$ {

cat > kube-scheduler-csr.json <<EOF
{
  "CN": "system:kube-scheduler",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "system:kube-scheduler",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
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

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:39:44 [INFO] generate received request
2021/01/23 11:39:44 [INFO] received CSR
2021/01/23 11:39:44 [INFO] generating key: rsa-2048
2021/01/23 11:39:44 [INFO] encoded CSR
2021/01/23 11:39:44 [INFO] signed certificate with serial number 510062430613949412253417312915291488998104343574
2021/01/23 11:39:44 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").
⇒ 上記のWARNINGは無視する

(client):$ ls -ltr kube-scheduler*
________________< コマンド実行後 出力 >________________
-rw-rw-r--. 1 opc opc  254 Jan 22 00:48 kube-scheduler-csr.json
-rw-rw-r--. 1 opc opc 1460 Jan 22 00:48 kube-scheduler.pem
-rw-------. 1 opc opc 1675 Jan 22 00:48 kube-scheduler-key.pem
-rw-r--r--. 1 opc opc 1066 Jan 22 00:48 kube-scheduler.csr
⇒ 上記のファイルが存在していること
```

### API Serverの証明書
APIサーバの証明書には、IPアドレス情報を含めるため、コントローラ・ノードとLBのIPを最初に取得します
**コントローラ・ノードとLBのIPアドレスは以降も頻繁に利用するため、メモしておきます**

**Cloud Shellから**以下のコマンドを実行します

```CONSOLE
## コントローラ・ノードのIPアドレスを取得する
(CShell):$ oci --profile k8s compute instance list-vnics \
  --query 'sort_by(data, &"hostname-label")[?contains("hostname-label", `controller`)].{"hostname-label":"hostname-label","private-ip":"private-ip"}' \
  --output=table
________________< コマンド実行後 出力 >________________
+----------------+-------------+
| hostname-label | private-ip  |
+----------------+-------------+
| controller01   | 10.240.0.X |
| controller02   | 10.240.0.X |
| controller03   | 10.240.0.X |
+----------------+-------------+

## Private LBのIPアドレス
(CShell):$ oci --profile k8s lb load-balancer list \
  --query 'data[?"display-name"==`k8s-api-lb` && "lifecycle-state"==`ACTIVE`]."ip-addresses"[].{"ip-address":"ip-address"}' \
  --output=table
________________< コマンド実行後 出力 >________________
+-------------+
| ip-address  |
+-------------+
| 10.240.0.X |
+-------------+
```

続いて、API Serverの証明書を作成します。**クライアント・ノードから**実行します

```CONSOLE
(client):$ CONTROLLER01_IP=★controller01のIP★
(client):$ CONTROLLER02_IP=★controller02のIP★
(client):$ CONTROLLER03_IP=★controller03のIP★
(clennt)$ LB_IP=★LBのIP★

## The Kubernetes API server is automatically assigned the kubernetes internal dns name, which will be linked to the first IP address (10.32.0.1) from the address range (10.32.0.0/24) reserved for internal cluster services during the control plane bootstrapping lab.

(client):$ {

KUBERNETES_HOSTNAMES=kubernetes,kubernetes.default,kubernetes.default.svc,kubernetes.default.svc.cluster,kubernetes.svc.cluster.local

cat > kubernetes-csr.json <<EOF
{
  "CN": "kubernetes",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
EOF

cfssl gencert \
  -ca=ca.pem \
  -ca-key=ca-key.pem \
  -config=ca-config.json \
  -hostname=10.32.0.1,${CONTROLLER01_IP},${CONTROLLER02_IP},${CONTROLLER03_IP},${LB_IP},127.0.0.1,${KUBERNETES_HOSTNAMES} \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:49:46 [INFO] generate received request
2021/01/23 11:49:46 [INFO] received CSR
2021/01/23 11:49:46 [INFO] generating key: rsa-2048
2021/01/23 11:49:47 [INFO] encoded CSR
2021/01/23 11:49:47 [INFO] signed certificate with serial number 537031571052305730283328931147934591788676720432

(client):$ ls -ltr kubernetes*
________________< コマンド実行後 出力 >________________
-rw-rw-r--. 1 opc opc  232 Jan 22 04:01 kubernetes-csr.json
-rw-rw-r--. 1 opc opc 1497 Jan 22 04:01 kubernetes.pem
-rw-------. 1 opc opc 1679 Jan 22 04:01 kubernetes-key.pem
-rw-r--r--. 1 opc opc 1123 Jan 22 04:01 kubernetes.csr
⇒ 上記のファイルが存在していること
```

### The Service Account Key Pair
サービス・アカウント用の鍵ペアを作成します

```CONSOLE
(client):$ {

cat > service-account-csr.json <<EOF
{
  "CN": "service-accounts",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "JP",
      "L": "xxxx-ku",
      "O": "Kubernetes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Tokyo"
    }
  ]
}
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

}
________________< コマンド実行後 出力 >________________
2021/01/23 11:51:22 [INFO] generate received request
2021/01/23 11:51:22 [INFO] received CSR
2021/01/23 11:51:22 [INFO] generating key: rsa-2048
2021/01/23 11:51:22 [INFO] encoded CSR
2021/01/23 11:51:23 [INFO] signed certificate with serial number 309291580739659926733705836189403996413882112274
2021/01/23 11:51:23 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for
websites. For more information see the Baseline Requirements for the Issuance and Management
of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org);
specifically, section 10.2.3 ("Information Requirements").

(client):$ ls -ltr service-account*
-rw-rw-r--. 1 opc opc  238 Jan 22 04:04 service-account-csr.json
-rw-rw-r--. 1 opc opc 1440 Jan 22 04:04 service-account.pem
-rw-------. 1 opc opc 1679 Jan 22 04:04 service-account-key.pem
-rw-r--r--. 1 opc opc 1041 Jan 22 04:04 service-account.csr
⇒ 上記のWARNINGは無視する
```

### 各証明書をノードに配賦

```CONSOLE
## ワーカー・ノードへの証明書配賦
(client):$ for instance in worker01 worker02 worker03; do
  scp -p p ca.pem ${instance}-key.pem ${instance}.pem ${instance}.private:~/
done
________________< 出力は省略 >________________

### 確認
(client):$ for instance in worker01 worker02 worker03; do
  echo "[${instance}]"
  ssh ${instance}.private ls -l "*.pem"
done
________________< コマンド実行後 出力 >________________
[worker01]
-rw-rw-r--. 1 opc opc 1318 Jan 22 00:23 ca.pem
-rw-------. 1 opc opc 1675 Jan 22 00:25 worker01-key.pem
-rw-rw-r--. 1 opc opc 1484 Jan 22 00:25 worker01.pem
[worker02]
//以降の出力は省略//
⇒ 各ノードに上記ファイルが存在すること

## コントローラ・ノードへの証明書配賦
(client):$ for instance in controller01 controller02 controller03; do
  scp -p ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem ${instance}.private:~/
done
________________< 出力は省略 >________________

### 確認
(client):$ for instance in controller01 controller02 controller03; do
  echo "[${instance}]"
  ssh ${instance}.private ls -l "*.pem"
done
________________< コマンド実行後 出力 >________________
[controller01]
-rw-------. 1 opc opc 1679 Jan 22 00:23 ca-key.pem
-rw-rw-r--. 1 opc opc 1318 Jan 22 00:23 ca.pem
-rw-------. 1 opc opc 1679 Jan 22 04:01 kubernetes-key.pem
-rw-rw-r--. 1 opc opc 1497 Jan 22 04:01 kubernetes.pem
-rw-------. 1 opc opc 1679 Jan 22 04:04 service-account-key.pem
-rw-rw-r--. 1 opc opc 1440 Jan 22 04:04 service-account.pem
[controller02]
//以降の出力は省略//
⇒ 各ノードに上記ファイルが存在すること

```

## Kubenetes設定ファイル(kubeconfig)の生成

kubeconfigsと呼ばれるKubernetesの各種設定ファイルを生成して、各ノードに配賦します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/9b34c8b5-ff25-f5d9-edfe-a1080ffda852.png)


### kubelet用のkubeconfigを生成

クライアント・ノードから以下のコマンドを実行します

```CONSOLE
(client):$ mkdir -p ~/kubectl/kubeconfig && cd ~/kubectl/kubeconfig
(client):$ LB_IP=★LBのIPアドレス★
(client):$ for instance in worker01 worker02 worker03; do
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=$HOME/cfssl/cert/ca.pem \
    --embed-certs=true \
    --server=https://${LB_IP}:6443 \
    --kubeconfig=${instance}.kubeconfig

  kubectl config set-credentials system:node:${instance} \
    --client-certificate=$HOME/cfssl/cert/${instance}.pem \
    --client-key=$HOME/cfssl/cert/${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
________________< コマンド実行後 出力 >________________
Cluster "kubernetes-the-hard-way" set.
User "system:node:worker01" set.
Context "default" created.
//以降の出力は省略//

(client):$ ll
________________< コマンド実行後 出力 >________________
total 24
-rw-------. 1 opc opc 6369 Jan 22 04:34 worker01.kubeconfig
-rw-------. 1 opc opc 6369 Jan 22 04:34 worker02.kubeconfig
-rw-------. 1 opc opc 6373 Jan 22 04:34 worker03.kubeconfig
⇒上記ファイルが存在すること
```

### The kube-proxy用のkubeconfigを生成
```CONSOLE
(client):$ {
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=$HOME/cfssl/cert/ca.pem \
    --embed-certs=true \
    --server=https://${LB_IP}:6443 \
    --kubeconfig=kube-proxy.kubeconfig

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

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

  kubectl config use-context default --kubeconfig=kube-proxy.kubeconfig
}
________________< コマンド実行後 出力 >________________
Cluster "kubernetes-the-hard-way" set.
User "system:kube-proxy" set.
Context "default" created.
Switched to context "default".

(client):$ ll kube-proxy.kubeconfig
________________< コマンド実行後 出力 >________________
-rw-------. 1 opc opc 6323 Jan 22 04:37 kube-proxy.kubeconfig
⇒ 上記ファイルが存在すること
```

### コントロール・マネージャ用のkubeconfigを生成

```CONSOLE
(client):$ {
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=$HOME/cfssl/cert/ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1:6443 \
    --kubeconfig=kube-controller-manager.kubeconfig

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

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

  kubectl config use-context default --kubeconfig=kube-controller-manager.kubeconfig
}
________________< コマンド実行後 出力 >________________
Cluster "kubernetes-the-hard-way" set.
User "system:kube-controller-manager" set.
Context "default" created.
Switched to context "default".

(client):$ ll kube-controller-manager.kubeconfig
________________< コマンド実行後 出力 >________________
-rw-------. 1 opc opc 6387 Jan 22 04:39 kube-controller-manager.kubeconfig
⇒ 上記ファイルが存在することを確認
```

### スケジューラ用のkubeconfigファイルの生成

```CONSOLE
(client):$ {
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=$HOME/cfssl/cert/ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1:6443 \
    --kubeconfig=kube-scheduler.kubeconfig

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

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

  kubectl config use-context default --kubeconfig=kube-scheduler.kubeconfig
}
________________< コマンド実行後 出力 >________________
Cluster "kubernetes-the-hard-way" set.
User "system:kube-scheduler" set.
Context "default" created.
Switched to context "default".

(client):$ ll kube-scheduler.kubeconfig
________________< コマンド実行後 出力 >________________
-rw-------. 1 opc opc 6337 Jan 22 04:41 kube-scheduler.kubeconfig
⇒ 上記ファイルが存在すること
```

### adminユーザ用のkubeconfigファイルを生成

```CONSOLE
(client):$ {
  kubectl config set-cluster kubernetes-the-hard-way \
    --certificate-authority=$HOME/cfssl/cert/ca.pem \
    --embed-certs=true \
    --server=https://127.0.0.1:6443 \
    --kubeconfig=admin.kubeconfig

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

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

  kubectl config use-context default --kubeconfig=admin.kubeconfig
}
________________< コマンド実行後 出力 >________________
Cluster "kubernetes-the-hard-way" set.
User "admin" set.
Context "default" created.
Switched to context "default".

(client):$ ll admin.kubeconfig
________________< コマンド実行後 出力 >________________
-rw-------. 1 opc opc 6261 Jan 22 04:44 admin.kubeconfig
⇒ 上記ファイルが存在すること
```

### kubeconfigファイルを各ノードに配賦

ワーカー・ノード用のkubeconfigファイルを配賦します

```CONSOLE
(client):$ for instance in worker01 worker02 worker03; do
  scp -p ${instance}.kubeconfig kube-proxy.kubeconfig ${instance}.private:~/
done
________________< 出力は省略 >________________

## 確認
(client):$ for instance in worker01 worker02 worker03; do
  echo "[${instance}]"
  ssh ${instance}.private ls -l "*.kubeconfig"
done
________________< コマンド実行後 出力 >________________
[worker01]
-rw-------. 1 opc opc 6323 Jan 22 04:37 kube-proxy.kubeconfig
-rw-------. 1 opc opc 6369 Jan 22 04:34 worker01.kubeconfig
[worker02]
//以降の出力は省略//
⇒ 上記のファイルが配賦されていること
```

コントローラ・ノード用のkubeconfigファイルを配賦します

```CONSOLE
(client):$ for instance in controller01 controller02 controller03; do
  scp -p admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig ${instance}.private:~/
done
________________< 出力は省略 >________________

## 確認
(client):$ for instance in controller01 controller02 controller03; do
  echo "[${instance}]"
  ssh ${instance}.private ls -l "*.kubeconfig"
done
________________< コマンド実行後 出力 >________________
[controller01]
-rw-------. 1 opc opc 6261 Jan 22 04:44 admin.kubeconfig
-rw-------. 1 opc opc 6387 Jan 22 04:39 kube-controller-manager.kubeconfig
-rw-------. 1 opc opc 6337 Jan 22 04:41 kube-scheduler.kubeconfig
[controller02]
//以降の出力は省略//
⇒ 上記のファイルが配賦されていること
```

## データ暗号化用鍵の生成と設定
etcdに格納されるデータを暗号化するための鍵と設定ファイルを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/475f4eed-5701-d646-eb38-34453da59080.png)

### 鍵の生成と設定ファイルの作成
クライアント・ノードから以下のコマンドを実行します

```CONSOLE
## ディレクトリの作成
(client):$ mkdir -p ~/encryption && cd ~/encryption

## 暗号化文字列(鍵)の生成
(client):$ ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
(client):$ echo $ENCRYPTION_KEY
________________< コマンド実行後 出力 >________________
iscfx5Fd2CKOgF/hl4wp86dvzunWH+iiyZlG60D7+P0=
⇒ ランダム文字列が生成されていること

## 鍵設定用のyamlファイルを作成
(client):$ cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF
________________< 出力は省略 >________________

## 確認
(client):$ ll
________________< コマンド実行後 出力 >________________
-rw-rw-r--. 1 opc opc 240 Jan 22 05:04 encryption-config.yaml
⇒ 上記のファイルが存在すること
```

### コントローラ・ノードへ設定ファイルを配賦

```CONSOLE
(client):$ for instance in controller01 controller02 controller03; do
  scp -p encryption-config.yaml ${instance}.private:~/
done
________________< 出力は省略 >________________

## 確認
(client):$ for instance in controller01 controller02 controller03; do
   echo "[${instance}]"
   ssh ${instance}.private ls -l encryption-config.yaml
done
________________< コマンド実行後 出力 >________________
[controller01]
-rw-rw-r--. 1 opc opc 240 Jan 22 05:04 encryption-config.yaml
[controller02]
-rw-rw-r--. 1 opc opc 240 Jan 22 05:04 encryption-config.yaml
[controller03]
-rw-rw-r--. 1 opc opc 240 Jan 22 05:04 encryption-config.yaml
⇒ 上記のファイルが配賦されていること
```

## etcdクラスタのインストールと起動

### 各種バイナリのダウンロードと配賦

ここでは、etcd以外のバイナリも含めてダウンロードおよび配布します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/f585dbf6-a21f-6966-8704-70ef920461ed.png)


#### etcd用バイナリ取得と配賦

```CONSOLE
(client):$ mkdir -p ~/etcd && cd ~/etcd
(client):$ wget -q  \
  "https://github.com/etcd-io/etcd/releases/download/v3.4.10/etcd-v3.4.10-linux-amd64.tar.gz"

(client):$ ll
________________< コマンド実行後 出力 >________________
-rw-rw-r--. 1 opc opc 17370166 Jul 16  2020 etcd-v3.4.10-linux-amd64.tar.gz
⇒ 上記ファイルが存在すること

## コントローラ・ノードに配賦
(client):$ for instance in controller01 controller02 controller03; do
  scp -p etcd*.tar.gz ${instance}.private:~/
done
________________< 出力は省略 >________________

## 確認
(client):$ for instance in controller01 controller02 controller03; do
   echo "[${instance}]"
   ssh ${instance}.private ls -l "etcd-*.tar.gz"
done
________________< コマンド実行後 出力 >________________
[controller01]
-rw-rw-r--. 1 opc opc 17370166 Jul 16  2020 etcd-v3.4.10-linux-amd64.tar.gz
[controller02]
-rw-rw-r--. 1 opc opc 17370166 Jul 16  2020 etcd-v3.4.10-linux-amd64.tar.gz
[controller03]
-rw-rw-r--. 1 opc opc 17370166 Jul 16  2020 etcd-v3.4.10-linux-amd64.tar.gz
⇒ 上記ファイルが配賦されていること
```

#### kubenetes controllerバイナリの取得と配賦
```CONSOLE
(client):$ mkdir -p ~/k8scontroller && cd ~/k8scontroller 
(client):$ wget -q  \
  "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-apiserver" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-controller-manager" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-scheduler" \
  "https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl"

## 確認
________________< コマンド実行後 出力 >________________
(client):$ ll
-rw-rw-r--. 1 opc opc 120672256 Jul 15  2020 kube-apiserver
-rw-rw-r--. 1 opc opc 110063616 Jul 15  2020 kube-controller-manager
-rw-rw-r--. 1 opc opc  44036096 Jul 15  2020 kubectl
-rw-rw-r--. 1 opc opc  42954752 Jul 15  2020 kube-scheduler
⇒ 上記ファイルが存在すること

## 配賦
(client):$ for instance in controller01 controller02 controller03; do
  scp -p kube* ${instance}.private:~/
done
________________< 出力は省略 >________________

## 確認
(client):$ for instance in controller01 controller02 controller03; do
   echo "[${instance}]"
   ssh ${instance}.private ls -l kube-apiserver kube-controller-manager kubectl kube-scheduler
done
________________< コマンド実行後 出力 >________________
[controller01]
-rw-rw-r--. 1 opc opc 120672256 Jul 15  2020 kube-apiserver
-rw-rw-r--. 1 opc opc 110063616 Jul 15  2020 kube-controller-manager
-rw-rw-r--. 1 opc opc  44036096 Jul 15  2020 kubectl
-rw-rw-r--. 1 opc opc  42954752 Jul 15  2020 kube-scheduler
[controller02]
//以降の出力省略//
⇒ 各コントローラ・ノードに配賦されていること
```

#### workerノード用バイナリの取得と配賦

```CONSOLE
(client):$ mkdir -p ~/workers && cd ~/workers 
(client):$ wget -q \
  https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.18.0/crictl-v1.18.0-linux-amd64.tar.gz \
  https://github.com/opencontainers/runc/releases/download/v1.0.0-rc91/runc.amd64 \
  https://github.com/containernetworking/plugins/releases/download/v0.8.6/cni-plugins-linux-amd64-v0.8.6.tgz \
  https://github.com/containerd/containerd/releases/download/v1.3.6/containerd-1.3.6-linux-amd64.tar.gz \
  https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubectl \
  https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kube-proxy \
  https://storage.googleapis.com/kubernetes-release/release/v1.18.6/bin/linux/amd64/kubelet

## 確認
(client):$ ll
________________< コマンド実行後 出力 >________________
total 290804
-rw-rw-r--. 1 opc opc  36878412 May 13  2020 cni-plugins-linux-amd64-v0.8.6.tgz
-rw-rw-r--. 1 opc opc  39951749 Jun 30  2020 containerd-1.3.6-linux-amd64.tar.gz
-rw-rw-r--. 1 opc opc  12590605 Apr  1  2020 crictl-v1.18.0-linux-amd64.tar.gz
-rw-rw-r--. 1 opc opc  44036096 Jul 15  2020 kubectl
-rw-rw-r--. 1 opc opc 113292024 Jul 15  2020 kubelet
-rw-rw-r--. 1 opc opc  38383616 Jul 15  2020 kube-proxy
-rw-rw-r--. 1 opc opc  12642864 Jul 30 10:33 runc.amd64
⇒ 上記ファイルが存在すること

## 配賦
(client):$ for instance in worker01 worker02 worker03; do
  scp -p * ${instance}.private:~/
done
________________< 出力は省略 >________________

## 確認
(client):$ for instance in worker01 worker02 worker03; do
   echo "[${instance}]"
   ssh ${instance}.private ls -l "*gz" "kube*" "runc*"
done
________________< コマンド実行後 出力 >________________
[worker01]
-rw-rw-r--. 1 opc opc  36878412 May 13  2020 cni-plugins-linux-amd64-v0.8.6.tgz
-rw-rw-r--. 1 opc opc  39951749 Jun 30  2020 containerd-1.3.6-linux-amd64.tar.gz
-rw-rw-r--. 1 opc opc  12590605 Apr  1  2020 crictl-v1.18.0-linux-amd64.tar.gz
-rw-rw-r--. 1 opc opc  44036096 Jul 15  2020 kubectl
-rw-rw-r--. 1 opc opc 113292024 Jul 15  2020 kubelet
-rw-rw-r--. 1 opc opc  38383616 Jul 15  2020 kube-proxy
-rw-------. 1 opc opc      6323 Jan 22 04:37 kube-proxy.kubeconfig
-rw-rw-r--. 1 opc opc  12642864 Jul 30 10:33 runc.amd64
[worker02]
//以降の出力省略//
⇒ 各コントローラ・ノードに配賦されていること
```

### 各ノードのSELinuxポリシーとfirewalldデーモンを停止

いろいろ面倒なので停止させときます

```CONSOLE
(client):$ for instance in controller01 controller02 controller03 worker01 worker02 worker03; do
  echo "[${instance}]"
  ssh ${instance}.private sudo systemctl disable firewalld
  ssh ${instance}.private sudo systemctl stop firewalld
  ssh ${instance}.private sudo sed -i "s/^SELINUX=enforcing$/SELINUX=permissive/" /etc/selinux/config
  ssh ${instance}.private sudo setenforce Permissive
done
________________< コマンド実行後 出力 >________________
[controller01]
Removed symlink /etc/systemd/system/multi-user.target.wants/firewalld.service.
Removed symlink /etc/systemd/system/dbus-org.fedoraproject.FirewallD1.service.
[controller02]
//以降の出力は省略//
```

## etcdサービスのインストールと起動

各コントローラ・ノードにetcdをインストールして、etcdクラスタを作成します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/fdb913d4-89ff-97b5-97c2-1e043852e0e1.png)

**★ etcd設定：controller01/controller02/controller03で繰り返す**
**------------------------------------- < ここから > -------------------------------------**

### バイナリの展開
```CONSOLE
## controller01にログイン
(client):$ ssh controller01.private
※controller02/controller03作業時は、各ノードにログインして作業します。読み替えてください

## ファイルを展開
(controller):$ {
  tar -xvf etcd-v3.4.10-linux-amd64.tar.gz
  sudo mv etcd-v3.4.10-linux-amd64/etcd* /usr/local/bin/
}
________________< 出力は省略 >________________

## 確認
(controller):$ ll /usr/local/bin/
________________< コマンド実行後 出力 >________________
total 40496
-rwxr-xr-x. 1 opc opc 23843808 Jul 16  2020 etcd
-rwxr-xr-x. 1 opc opc 17620576 Jul 16  2020 etcdctl
⇒ 上記ファイルが存在していること
```

### etcdサーバの設定

```CONSOLE
## 各コントローラ・ノードのIPを設定
(controller):$ CONTROLLER01_IP=★controller01のIP★
(controller):$ CONTROLLER02_IP=★controller02のIP★
(controller):$ CONTROLLER03_IP=★controller03のIP★

## 証明書の配賦
(controller):$ {
  sudo mkdir -p /etc/etcd /var/lib/etcd
  sudo chmod 700 /var/lib/etcd
  sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/
}

## 確認
(controller):$ ll /etc/etcd/*.pem
________________< コマンド実行後 出力 >________________
-rw-r--r--. 1 root root 1318 Jan 22 05:17 /etc/etcd/ca.pem
-rw-------. 1 root root 1679 Jan 22 05:17 /etc/etcd/kubernetes-key.pem
-rw-r--r--. 1 root root 1497 Jan 22 05:17 /etc/etcd/kubernetes.pem
⇒ 上記ファイルが存在していること

## 自ノードの取得
(controller):$ INTERNAL_IP=$(curl -s -L http://169.254.169.254/opc/v1/vnics/0/privateIp)

## 確認
(controller):$ echo ${INTERNAL_IP}
________________< コマンド実行後 出力 >________________
10.240.0.15
⇒ IPアドレスが出力されること

## ホスト名の取得
(controller):$ ETCD_NAME=$(hostname -s)

## systemdサービス設定ファイルの作成
(controller):$ cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=etcd
Documentation=https://github.com/coreos

[Service]
Type=notify
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 controller01=https://${CONTROLLER01_IP}:2380,controller02=https://${CONTROLLER02_IP}:2380,controller03=https://${CONTROLLER03_IP}:2380 \\
  --initial-cluster-state new \\
  --data-dir=/var/lib/etcd
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
________________< 出力は省略 >________________

## 確認
(controller):$ cat /etc/systemd/system/etcd.service
________________< 出力は省略 >________________
⇒ 特に、変数部分が反映されていることを確認

## サービス起動
(controller):$ {
  sudo systemctl daemon-reload
  sudo systemctl enable etcd
  sudo systemctl start etcd
}
________________< コマンド実行後 出力 >________________
Created symlink from /etc/systemd/system/multi-user.target.wants/etcd.service to /etc/systemd/system/etcd.service.
⇒ controller01のみ作業完了時の出力。タイムアウト後以下のメッセージが出力される。controller02以降の作業時は出力されない
Job for etcd.service failed because a timeout was exceeded. See "systemctl status etcd.service" and "journalctl -xe" for details.

## ログアウトし、クライアント・ノードに戻る
(controller):$ exit
```
**------------------------------------- < ここまで > -------------------------------------**

上記作業をcontroller02とcontrolller03でも実行する

### 確認
controller01にログインし、etcdの状態確認コマンドを実行する

```CONSOLE
## controller01にログイン
(client):$ ssh controller01.private

## etcdの状態確認
(controller01):$ sudo su -
(controller01):# 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
________________< コマンド実行後 出力 >________________
dd109d6b41f108f5, started, controller02, https://10.240.0.16:2380, https://10.240.0.16:2379, false
e47f640004d9805a, started, controller03, https://10.240.0.17:2380, https://10.240.0.17:2379, false
e627cbe455efd849, started, controller01, https://10.240.0.15:2380, https://10.240.0.15:2379, false
⇒ 上記のように出力されること

## controller01からログアウトする
(controller01):# exit
(controller01):$ exit
```

## コントロール・プレーンのインストールと設定
コントロール・プレーン(APIサーバ、コントローラ・マネージャ、スケジューラ)を各ノードにインストールします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/3699fb27-aa9a-54c9-886d-fc4d9055c478.png)

**★ コントロール・プレーンの設定：controller01/controller02/controller03で繰り返す**
**------------------------------------- < ここから > -------------------------------------**

### APIサーバの設定と作成

```CONSOLE
## controller01にログイン
(client):$ ssh controller01.private
※controller02/controller03作業時は、各ノードにログインして作業します。読み替えてください

## ファイルを展開
(controller):$ {
  chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
  sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
}

## 確認
(controller):$ ll /usr/local/bin/kube*
________________< コマンド実行後 出力 >________________
-rwxrwxr-x. 1 opc opc 120672256 Jul 15  2020 /usr/local/bin/kube-apiserver
-rwxrwxr-x. 1 opc opc 110063616 Jul 15  2020 /usr/local/bin/kube-controller-manager
-rwxrwxr-x. 1 opc opc  44036096 Jul 15  2020 /usr/local/bin/kubectl
-rwxrwxr-x. 1 opc opc  42954752 Jul 15  2020 /usr/local/bin/kube-scheduler
⇒ 上記ファイルが存在していること

## 証明書の配賦
(controller):$ {
  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/
}

## 確認
(controller):$ ll /var/lib/kubernetes/
________________< コマンド実行後 出力 >________________
total 28
-rw-------. 1 opc opc 1679 Jan 22 00:23 ca-key.pem
-rw-rw-r--. 1 opc opc 1318 Jan 22 00:23 ca.pem
-rw-rw-r--. 1 opc opc  240 Jan 22 05:04 encryption-config.yaml
-rw-------. 1 opc opc 1679 Jan 22 04:01 kubernetes-key.pem
-rw-rw-r--. 1 opc opc 1497 Jan 22 04:01 kubernetes.pem
-rw-------. 1 opc opc 1679 Jan 22 04:04 service-account-key.pem
-rw-rw-r--. 1 opc opc 1440 Jan 22 04:04 service-account.pem
⇒ 上記ファイルが存在していること

## コントローラ・ノードのIPを変数に設定
(controller):$ CONTROLLER01_IP=★controller01のIP★
(controller):$ CONTROLLER02_IP=★controller02のIP★
(controller):$ CONTROLLER03_IP=★controller03のIP★

## 自ノードのIPを変数に設定
(controller):$ INTERNAL_IP=$(curl -s -L http://169.254.169.254/opc/v1/vnics/0/privateIp)
(controller):$ echo ${INTERNAL_IP}
________________< コマンド実行後 出力 >________________
10.240.0.15
⇒ IPアドレスが出力されること

## APIサーバ用のsystemdサービス設定ファイルを作成
## 本家のKubernetes the hard wayでは --runtime-config='api/all=true' がシングルクォートで囲まれているが、kこの状態では起動時にエラーになったので、シングルクォートを外している

(controller):$ 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://${CONTROLLER01_IP}:2379,https://${CONTROLLER02_IP}:2379,https://${CONTROLLER03_IP}: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=true \\
  --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
________________< 出力は省略 >________________

## 確認
(controller):$ cat /etc/systemd/system/kube-apiserver.service
________________< 出力は省略 >________________
⇒ 変数部分が意図した値に設定されていること

```

### コントロール・マネージャの設定

```CONSOLE
## kubeconfigファイルの配賦
(controller):$ sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/

## 確認
(controller):$ ll /var/lib/kubernetes/kube-controller-manager.kubeconfig 
________________< コマンド実行後 出力 >________________
-rw-------. 1 opc opc 6387 Jan 22 04:39 /var/lib/kubernetes/kube-controller-manager.kubeconfig
⇒ 上記ファイルが存在していること

## コントロール・マネージャ用のsystemdサービス設定ファイルを作成
(controller):$ 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 \\
  --bind-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
________________< 出力は省略 >________________

## 確認
(controller):$ cat /etc/systemd/system/kube-controller-manager.service
________________< 出力は省略 >________________
```

### スケジューラの設定

```CONSOLE
## スケジューラ用kubeconfigファイルの配賦
(controller):$ sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/

## 確認
(controller):$ ll /var/lib/kubernetes/kube-scheduler.kubeconfig
________________< コマンド実行後 出力 >________________
-rw-------. 1 opc opc 6337 Jan 22 04:41 /var/lib/kubernetes/kube-scheduler.kubeconfig
⇒ 上記ファイルが存在していること

## スケジューラ用の設定ファイルの作成
(controller):$ sudo mkdir -p /etc/kubernetes/config/

(controller):$ 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サービス設定ファイルの作成
(controller):$ 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
________________< 出力は省略 >________________

## 確認
(controller):$ cat /etc/systemd/system/kube-scheduler.service
________________< 出力は省略 >________________

```

### コントロール・プレーンのサービスを起動する
```CONSOLE
## サービスの起動
(controller):$ {
  sudo systemctl daemon-reload
  sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
  sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
}
________________< 出力は省略 >________________

## サービスステータスの確認
(controller):$ 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: disabled)
   Active: active (running) since Fri 2021-01-22 10:37:39 GMT; 3s ago
     Docs: https://github.com/kubernetes/kubernetes
 Main PID: 19576 (kube-apiserver)
//以降の出力省略//
● kube-controller-manager.service - Kubernetes Controller Manager
   Loaded: loaded (/etc/systemd/system/kube-controller-manager.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2021-01-22 10:19:34 GMT; 19min ago
//以降の出力省略//
● kube-scheduler.service - Kubernetes Scheduler
   Loaded: loaded (/etc/systemd/system/kube-scheduler.service; enabled; vendor preset: disabled)
   Active: active (running) since Fri 2021-01-22 10:19:34 GMT; 19min ago
//以降の出力省略//
⇒ すべてRUNNING状態であること

```

### HTTPヘルスチェックの実装
LBからAPIサーバへのヘルスチェックをするために、nginxをインストールしてリバースProxy接続できるようにします
※LBのヘルスチェック時にクライアント証明書を利用できないためと思われる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/542610ec-ac7d-05d0-aef5-a899fe4318e6.png)

```CONSOLE
## nginxのインストール
(controller):$ sudo yum --enablerepo=ol7_developer_EPEL install -y  nginx
________________< 出力は省略 >________________

## nginx設定ファイルの修正
(controller):$ sudo cp -pi /etc/nginx/nginx.conf /etc/nginx/nginx.conf.$(date '+%Y%m%d')
(controller):$ sudo vi /etc/nginx/nginx.conf
//...//
    server {
//★server_nameディレクティブを変更★//
#        server_name  _;
        server_name kubernetes.default.svc.cluster.local;

//★location /healthzを追加★//
        location /healthz {
            proxy_pass                    https://127.0.0.1:6443/healthz;
            proxy_ssl_trusted_certificate /var/lib/kubernetes/ca.pem;
        }
//...//

## nginxをリバースProxyの動作を許可するためのSE Linuxコンテクストを有効化する
(controller):$ sudo setsebool -P httpd_can_network_relay on
(controller):$ sudo setsebool -P httpd_can_network_connect on

## nginxを再起動し、自動起動を有効にする
(controller):$ sudo systemctl restart nginx
(controller):$ sudo systemctl enable nginx

## 動作確認
(controller):$ kubectl get componentstatuses --kubeconfig admin.kubeconfig
________________< コマンド実行後 出力 >________________
NAME                 STATUS    MESSAGE             ERROR
controller-manager   Healthy   ok
scheduler            Healthy   ok
etcd-1               Healthy   {"health":"true"}
etcd-2               Healthy   {"health":"true"}
etcd-0               Healthy   {"health":"true"}
⇒ 上記のように出力されること

(controller):$ curl -H "Host: kubernetes.default.svc.cluster.local" -i http://127.0.0.1/healthz
________________< コマンド実行後 出力 >________________
HTTP/1.1 200 OK
Server: nginx/1.12.2
Date: Fri, 22 Jan 2021 11:42:03 GMT
Content-Type: text/plain; charset=utf-8
Content-Length: 2
Connection: keep-alive
Cache-Control: no-cache, private
X-Content-Type-Options: nosniff

ok
⇒ Status 200が返ること

(controller):$ exit
```
**------------------------------------- < ここまで > -------------------------------------**

controller02とcontroller03でも同様に、Kubenetesコントロールノードを構成する

### RBAC for Kubelet Authorization
クラスタ・ロールを作成して、kubernetesユーザに作成したロール付与します
> In this section you will configure RBAC permissions to allow the Kubernetes API Server to access the Kubelet API on each worker node. Access to the Kubelet API is required for retrieving metrics, logs, and executing commands in pods.

controller01でのみ、以下のコマンドを実行します

```CONSOLE
## controller01にログイン
(client):$ ssh controller01.private

## クラスタ・ロールの作成
(controller01):$ 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

## 作成したクラスタ・ロールをkubenetesユーザに適用
(controller01):$ 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

## controller01からログアウトします
(controller01):$ exit
```

### ロードバランサの設定
APIサーバ用のロード・バランサ設定を行います

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/427ee49b-4444-b2a6-fe26-836bc6020683.png)

**Cloud Shellから**、以下のコマンドを実行し、バックエンド・サーバとしてAPIサーバを指定します(6443/tcp)。ヘルスチェック・ポートには、nginx(80/tcp)を使用します

```CONSOLE
## コントローラ・ノードのIPを設定
(CShell):$ CONTROLLER01_IP=★controller01のIP★
(CShell):$ CONTROLLER02_IP=★controller02のIP★
(CShell):$ CONTROLLER03_IP=★controller03のIP★

## LBのOCIDを取得
(CShell):$ LB_OCID=$(oci --profile=k8s lb load-balancer list --raw-output --query 'data[?"display-name"==`k8s-api-lb` && "lifecycle-state"==`ACTIVE`].id |[0]')

(CShell):$ echo ${LB_OCID}
________________< コマンド実行後 出力 >________________
ocid1.loadbalancer.oc1.ap-tokyo-1.XXXXXXXXXXXXXXXXXXXXXXXXXXXX
⇒ ocid1.loadbalancerから始まるIDが出力されること

## バックエンド・セットを作成
(CShell):$ oci --profile k8s lb backend-set create \
  --health-checker-protocol HTTP \
  --load-balancer-id ${LB_OCID}\
  --name backend-k8s-api \
  --policy ROUND_ROBIN \
  --backends '[{"ipAddress": "'${CONTROLLER01_IP}'", "port": "6443"}, {"ipAddress": "'${CONTROLLER02_IP}'", "port": "6443"}, {"ipAddress": "'${CONTROLLER03_IP}'", "port": "6443"}]' \
  --health-checker-port 80 \
  --health-checker-return-code 200 \
  --health-checker-url-path /healthz \
  --wait-for-state SUCCEEDED
________________< 出力は省略 >________________

```

続いて、6443/tcpをリッスンするリスナーを作成します

```CONSOLE
(CShell):$ oci --profile k8s lb listener create \
  --default-backend-set-name backend-k8s-api \
  --load-balancer-id ${LB_OCID}\
  --name k8s-listener \
  --port 6443 \
  --protocol TCP \
  --wait-for-state SUCCEEDED
________________< 出力は省略 >________________

```

Webコンソールから、ロードバランサのバックエンド・セットのページに遷移し、ステータスがすべてOKになっていることを確認します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/456818/b88960c4-28d9-4746-1857-98a02b9911eb.png)

最後に、LB経由でAPIサーバにアクセスできることを確認します。次のコマンドはクライアント・ノードから実行します

```CONSOLE
## クライアント証明書が存在するディレクトリに移動
(client):$ cd ~/cfssl/cert/

## LBのIPを設定
(client):$ LB_IP=★LBのIP★

## LB経由でAPIサーバにアクセス
(client):$ curl --cacert ca.pem https://${LB_IP}:6443/version
________________< コマンド実行後 出力 >________________
{
  "major": "1",
  "minor": "18",
  "gitVersion": "v1.18.6",
  "gitCommit": "dff82dc0de47299ab66c83c626e08b245ab19037",
  "gitTreeState": "clean",
  "buildDate": "2020-07-15T16:51:04Z",
  "goVersion": "go1.13.9",
  "compiler": "gc",
  "platform": "linux/amd64"
}
⇒ 上記のような応答が返ること
```

[[Oracle Cloud] Oracle CloudでKubernetes the Hard Wayする (その②)](https://qiita.com/NSO-KC/items/333adec90b0934face33)に続きます