kubernetes the hard way を AWS でやってみた（全文和訳有り）

kubernetes the hard wayをご存じでしょうか？
kubernetesクラスターを手作業で構築することによってkubernetesへの理解を深めようという意図で作成されたチュートリアルです。

自分は今まで何となくの知識でkubernetesを使っていたので、今回復習のためにkubernetes the hard wayをやってみました。

[オリジナルのチュートリアル](https://github.com/kelseyhightower/kubernetes-the-hard-way)はGCP環境で構築を行っていますが、有志の方が[AWS環境用のチュートリアル](https://github.com/prabhatsharma/kubernetes-the-hard-way-aws/blob/master/README.md)を作成されていたので今回はそちらをやってみました。
それとせっかくなのでAWS用チュートリアルの日本語版を作ってみました。

個人的にはやってみてとても勉強になったのでお勧めです。皆さんもよければぜひ。

なお自分は以下のエントリーを見つつチュートリアルをやりました。こちらもとても勉強になるのでぜひ。

[Kubernetes The Hard Wayする](https://qiita.com/sotoiwa/items/2b22dc21d88c7822e44b)
[Kubernetes: 構成コンポーネント一覧](https://qiita.com/tkusumi/items/c2a92cd52bfdb9edd613)
[KubernetesのTLS証明書](https://qiita.com/nsawa/items/4f11ac89707aad2c3d4a)


**※このチュートリアルは[オリジナル](https://github.com/kelseyhightower/kubernetes-the-hard-way)、[その日本語訳(GCP環境)](https://springmt.github.io/kubernetes-the-hard-way-ja/kubernetes_the_hard_way)、[AWS用チュートリアル](https://github.com/prabhatsharma/kubernetes-the-hard-way-aws/blob/master/README.md)を基に作成したものであり、私がゼロから作成したものではありません。**

---

#00-kubernetes the hard way (はじめに)

Kubernetes Hard Wayでは、Kubernetesの設定を一から進めます。

なので、このラボは、完全に自動化されて管理されたKubernetesクラスタを作るコマンドを探している人のためのものではありません。

このKubernetes The Hard Wayは学習することに最適化されています。
Kubernetesクラスタを立ち上げるための必要な各タスクを確実に理解するための、長い道のりが示されています。

## 想定読者
本番用のKubernetesクラスタをサポートすることを計画していて、かつ、Kubenretesクラスタのすべての部品がどのように組み合わさっているかを理解したいという人向けです。

## 今回出来上がるクラスタの詳細
Kubernetes The Hard Wayでは、コンポーネント間のエンドツーエンドの暗号化とRBAC認証ができる、可用性の高いKubernetesクラスタを作ります。

使うコンポーネントとそのバージョン一覧は下記の通りです

Kubernetes 1.15.3
containerd Container Runtime 1.2.9
gVisor 08879266fef3a67fac1a77f1ea133c3ac75759dd
CNI Container Networking 0.8.2
etcd 3.3.10

このチュートリアルではAWSを使用します

では、はじめていきましょう！



#01-前提条件

## Amazon Web Services
このチュートリアルではAmazon Web Service(以下AWS)を利用して、kubernetesクラスターを立ち上げます。
このチュートリアルを通して掛かる費用は、24時間あたり2ドル未満です。

また、今回使用するリソースはAWSの無料枠を超えるので、チュートリアル終了後には作成したリソースをクリーンアップし、不要なコストが発生しないよう注意してください。

## Amazon Web Services CLI

以降の手順は、デプロイ用インスタンス（適当なEC2インスタンスを立ち上げる）で実行してください。
このデプロイ用インスタンスはクラスター立ち上げのための各種設定を行うためのものであり、kubernetesクラスターのコンポーネントではありません。

### AWS CLIのインストール
[AWSの公式ドキュメント](https://aws.amazon.com/cli/)に従って、AWS CLIをインストールし、必要な設定を行います。

インストールが終了したら、以下のコマンドでAWS CLIが有効であることを確認します。

    aws --version

### デフォルトリージョンの設定
このチュートリアルで使用するデフォルトのリージョンを設定します

    AWS_REGION=us-west-1
    aws configure set default.region $AWS_REGION

## tmuxを使ってパラレルにコマンドを打てるようにする
一応おすすめ設定として書かれているので興味がある人は下記を見てみてください。

https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md#running-commands-in-parallel-with-tmux


#02-クライアントツールのインストール

この手順では`cfssl`, `cfssljson`, `kubectl`のインストールを行います。

## CFSSLとCFSSLJSONのインストール

`cfssl`と`cfssljson`は[PKIの環境](https://en.wikipedia.org/wiki/Public_key_infrastructure)構築とTLSの証明書発行に使います。

### OS Xでのインストール

```
curl -o cfssl https://pkg.cfssl.org/R1.2/cfssl_darwin-amd64
curl -o cfssljson https://pkg.cfssl.org/R1.2/cfssljson_darwin-amd64
```

```
chmod +x cfssl cfssljson
```

```
sudo mv cfssl cfssljson /usr/local/bin/
```
OS Xを使用し、pre-buildでインストールしようとした場合、問題が生じる場合があります。
その場合は[Homebrew](https://brew.sh)を使用してインストールするしてください。


```
brew install cfssl
```

### Linuxでのインストール

```
wget -q --show-progress --https-only --timestamping \
  https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 \
  https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
```

```
chmod +x cfssl_linux-amd64 cfssljson_linux-amd64
```

```
sudo mv cfssl_linux-amd64 /usr/local/bin/cfssl
```

```
sudo mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
```

### 確認

`cfssl` のバージョンが 1.2.0 以上であることを確認します

```
% cfssl version
Version: 1.2.0
Revision: dev
Runtime: go1.6
```

(cfssljsonにはバージョンを表示するコマンドがありません）

## kubectlのインストール

`kubectl`はKubernetes API Serverとの通信に使用されるコマンドラインツールです。
今回はバージョン1.15.3以上が必要になります。

### OS Xでのインストール

```
curl -o kubectl https://storage.googleapis.com/kubernetes-release/release/v1.15.3/bin/darwin/amd64/kubectl
```

```
chmod +x kubectl
```

```
sudo mv kubectl /usr/local/bin/
```

### Linuxでのインストール

```
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のバージョンが1.15.3以上であることを確認します。

```
% kubectl version --client
Client Version: version.Info{Major:"1", Minor:"15", GitVersion:"v1.15.3", GitCommit:"2d3c76f9091b6bec110a5e63777c332469e0cba2", GitTreeState:"clean", BuildDate:"2019-08-19T12:38:00Z", GoVersion:"go1.12.9", Compiler:"gc", Platform:"darwin/amd64"}
```




#03-計算リソースのプロビジョニング

リソースについての説明は[元ネタ](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/03-compute-resources.md)か、[こちらの日本語訳](https://springmt.github.io/kubernetes-the-hard-way-ja/kubernetes_the_hard_way/#4)の方を確認してください。
以下、デプロイ用インスタンスで実行するコマンドを記載します。

## Networking

### VPC

```sh
VPC_ID=$(aws ec2 create-vpc --cidr-block 10.240.0.0/24 --output text --query 'Vpc.VpcId')
aws ec2 create-tags --resources ${VPC_ID} --tags Key=Name,Value=kubernetes
aws ec2 modify-vpc-attribute --vpc-id ${VPC_ID} --enable-dns-support '{"Value": true}'
aws ec2 modify-vpc-attribute --vpc-id ${VPC_ID} --enable-dns-hostnames '{"Value": true}'
```

### Subnet

```sh
SUBNET_ID=$(aws ec2 create-subnet \
  --vpc-id ${VPC_ID} \
  --cidr-block 10.240.0.0/24 \
  --output text --query 'Subnet.SubnetId')
aws ec2 create-tags --resources ${SUBNET_ID} --tags Key=Name,Value=kubernetes
```

### Internet Gateway

```sh
INTERNET_GATEWAY_ID=$(aws ec2 create-internet-gateway --output text --query 'InternetGateway.InternetGatewayId')
aws ec2 create-tags --resources ${INTERNET_GATEWAY_ID} --tags Key=Name,Value=kubernetes
aws ec2 attach-internet-gateway --internet-gateway-id ${INTERNET_GATEWAY_ID} --vpc-id ${VPC_ID}
```

### Route Tables

```sh
ROUTE_TABLE_ID=$(aws ec2 create-route-table --vpc-id ${VPC_ID} --output text --query 'RouteTable.RouteTableId')
aws ec2 create-tags --resources ${ROUTE_TABLE_ID} --tags Key=Name,Value=kubernetes
aws ec2 associate-route-table --route-table-id ${ROUTE_TABLE_ID} --subnet-id ${SUBNET_ID}
aws ec2 create-route --route-table-id ${ROUTE_TABLE_ID} --destination-cidr-block 0.0.0.0/0 --gateway-id ${INTERNET_GATEWAY_ID}
```

### Security Groups

```sh
SECURITY_GROUP_ID=$(aws ec2 create-security-group \
  --group-name kubernetes \
  --description "Kubernetes security group" \
  --vpc-id ${VPC_ID} \
  --output text --query 'GroupId')
aws ec2 create-tags --resources ${SECURITY_GROUP_ID} --tags Key=Name,Value=kubernetes
aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP_ID} --protocol all --cidr 10.240.0.0/24
aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP_ID} --protocol all --cidr 10.200.0.0/16
aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP_ID} --protocol tcp --port 22 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP_ID} --protocol tcp --port 6443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP_ID} --protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id ${SECURITY_GROUP_ID} --protocol icmp --port -1 --cidr 0.0.0.0/0
```

### Kubernetes Public Access - ロードバランサーの作成

```sh
  LOAD_BALANCER_ARN=$(aws elbv2 create-load-balancer \
    --name kubernetes \
    --subnets ${SUBNET_ID} \
    --scheme internet-facing \
    --type network \
    --output text --query 'LoadBalancers[].LoadBalancerArn')
  TARGET_GROUP_ARN=$(aws elbv2 create-target-group \
    --name kubernetes \
    --protocol TCP \
    --port 6443 \
    --vpc-id ${VPC_ID} \
    --target-type ip \
    --output text --query 'TargetGroups[].TargetGroupArn')
  aws elbv2 register-targets --target-group-arn ${TARGET_GROUP_ARN} --targets Id=10.240.0.1{0,1,2}
  aws elbv2 create-listener \
    --load-balancer-arn ${LOAD_BALANCER_ARN} \
    --protocol TCP \
    --port 443 \
    --default-actions Type=forward,TargetGroupArn=${TARGET_GROUP_ARN} \
    --output text --query 'Listeners[].ListenerArn'
```

```sh
KUBERNETES_PUBLIC_ADDRESS=$(aws elbv2 describe-load-balancers \
  --load-balancer-arns ${LOAD_BALANCER_ARN} \
  --output text --query 'LoadBalancers[].DNSName')
```

## 各種インスタンス

### Instance Image

```sh
IMAGE_ID=$(aws ec2 describe-images --owners 099720109477 \
  --filters \
  'Name=root-device-type,Values=ebs' \
  'Name=architecture,Values=x86_64' \
  'Name=name,Values=ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*' \
  | jq -r '.Images|sort_by(.Name)[-1]|.ImageId')
```

### SSH Key Pair

```sh
aws ec2 create-key-pair --key-name kubernetes --output text --query 'KeyMaterial' > kubernetes.id_rsa
chmod 600 kubernetes.id_rsa
```

### Kubernetes コントローラーノード

今回は `t3.micro` インスタンスを使用します

```sh
for i in 0 1 2; do
  instance_id=$(aws ec2 run-instances \
    --associate-public-ip-address \
    --image-id ${IMAGE_ID} \
    --count 1 \
    --key-name kubernetes \
    --security-group-ids ${SECURITY_GROUP_ID} \
    --instance-type t3.micro \
    --private-ip-address 10.240.0.1${i} \
    --user-data "name=controller-${i}" \
    --subnet-id ${SUBNET_ID} \
    --block-device-mappings='{"DeviceName": "/dev/sda1", "Ebs": { "VolumeSize": 50 }, "NoDevice": "" }' \
    --output text --query 'Instances[].InstanceId')
  aws ec2 modify-instance-attribute --instance-id ${instance_id} --no-source-dest-check
  aws ec2 create-tags --resources ${instance_id} --tags "Key=Name,Value=controller-${i}"
  echo "controller-${i} created "
done
```

### Kubernetes ワーカーノード

```sh
for i in 0 1 2; do
  instance_id=$(aws ec2 run-instances \
    --associate-public-ip-address \
    --image-id ${IMAGE_ID} \
    --count 1 \
    --key-name kubernetes \
    --security-group-ids ${SECURITY_GROUP_ID} \
    --instance-type t3.micro \
    --private-ip-address 10.240.0.2${i} \
    --user-data "name=worker-${i}|pod-cidr=10.200.${i}.0/24" \
    --subnet-id ${SUBNET_ID} \
    --block-device-mappings='{"DeviceName": "/dev/sda1", "Ebs": { "VolumeSize": 50 }, "NoDevice": "" }' \
    --output text --query 'Instances[].InstanceId')
  aws ec2 modify-instance-attribute --instance-id ${instance_id} --no-source-dest-check
  aws ec2 create-tags --resources ${instance_id} --tags "Key=Name,Value=worker-${i}"
  echo "worker-${i} created"
done
```


#04-認証局(CA)のプロビジョニングとTLS証明書の生成

この手順では、CloudFlareのPKIツールキットである [cfssl](https://github.com/cloudflare/cfssl)を使用して[PKI基盤](https://en.wikipedia.org/wiki/Public_key_infrastructure)をプロビジョニングします。
そして、PKI基盤を利用して認証局を作り、`admin`用のTLS証明書、etcd、kube-apiserver、kube-controller-manager、kube-scheduler、 kubelet、kube-proxyの各コンポーネント用TLS証明書を生成します。



## 認証局(CA)の立ち上げ

このステップでは、TLS証明書を生成するための認証局(CA)をプロビジョニングします。

まずは、CA設定ファイル、CA自身の証明書、および秘密鍵を生成します。

```
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": "US",
      "L": "Portland",
      "O": "Kubernetes",
      "OU": "CA",
      "ST": "Oregon"
    }
  ]
}
EOF

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

生成物

```
ca-key.pem
ca.pem
```

## クライアントとサーバーの証明書発行

このステップでは、各Kubernetesコンポーネントで使うクライアント証明書とサーバー証明書、Kubernetes `admin` ユーザー用のクライアント証明書を発行します。

### Admin用のクライアント証明書

まずは、 `admin` 用のクライアント証明書と秘密鍵を生成します。

```
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
```

生成物

```
admin-key.pem
admin.pem
```

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

Kubernetesは、Node Authorizerと呼ばれる[特別な用途向けの認可モード](https://kubernetes.io/docs/admin/authorization/node/)を利用します。

これは特に[Kubelets](https://kubernetes.io/docs/concepts/overview/components/#kubelet)からのAPIリクエストの認証を行います。

Node Authorizerの認可のためには、Kubeletは、`system:nodes group`の中の `system:node:<nodeName>` というユーザー名で認証されるように証明書を作成しなければなりません。

このステップでは、KubernetesワーカーノードごとにNode Authorizerの要求を満たす証明書と秘密鍵を発行します。

```
for i in 0 1 2; do
  instance="worker-${i}"
  instance_hostname="ip-10-240-0-2${i}"
  cat > ${instance}-csr.json <<EOF
{
  "CN": "system:node:${instance_hostname}",
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "US",
      "L": "Portland",
      "O": "system:nodes",
      "OU": "Kubernetes The Hard Way",
      "ST": "Oregon"
    }
  ]
}
EOF

  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  internal_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PrivateIpAddress')

  cfssl gencert \
    -ca=ca.pem \
    -ca-key=ca-key.pem \
    -config=ca-config.json \
    -hostname=${instance_hostname},${external_ip},${internal_ip} \
    -profile=kubernetes \
    worker-${i}-csr.json | cfssljson -bare worker-${i}
done
```

生成物

```
worker-0-key.pem
worker-0.pem
worker-1-key.pem
worker-1.pem
worker-2-key.pem
worker-2.pem
```

### kube-contorller-manager クライアントの証明書

`kube-controller-manager` クライアントの証明書と秘密鍵を発行します。

```
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
```

生成物

```
kube-controller-manager-key.pem
kube-controller-manager.pem
```


### kube-proxy クライアントの証明書

`kube-proxy` クライアント用に証明書と秘密鍵を発行します。

```
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
```

生成物

```
kube-proxy-key.pem
kube-proxy.pem
```

### kube-scheduler クライアント用の証明書

`kube-scheduler` クライアント用の証明書と秘密鍵を発行します。

```
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

```

生成物

```
kube-scheduler-key.pem
kube-scheduler.pem
```


### Kubernetes API サーバー用証明書
kubernetes-the-hard-way のstatic IPアドレスは、Kubernetes API サーバーの証明書のSAN(subject alternative names、サブジェクトの別名)のリストに含める必要があります。

これにより、外部のクライアントでも証明書を使った検証を行います。

Kubernetes API サーバーの証明書と秘密鍵を生成します。

```
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.default \
  -profile=kubernetes \
  kubernetes-csr.json | cfssljson -bare kubernetes
```

生成物

```
kubernetes-key.pem
kubernetes.pem
```

## サービスアカウントのキーペア
サービスアカウントの管理のドキュメントにあるように、[Kubernetes Controller Manager](https://kubernetes.io/docs/admin/service-accounts-admin/)は、サービスアカウントのトークンの生成と署名をするためにキーペアを使用します。

`service-account` の証明書と秘密鍵を発行します。

```
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

```

生成物

```
service-account-key.pem
service-account.pem
```


## クライアントとサーバーの証明書の配置
証明書と秘密鍵をコピーし、各ワーカーインスタンスに配置します。
（配置するもの：CAの証明書、APIサーバの証明書、ワーカーノードの証明書と秘密鍵）

```
for instance in worker-0 worker-1 worker-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  scp -i kubernetes.id_rsa ca.pem ${instance}-key.pem ${instance}.pem ubuntu@${external_ip}:~/
done
```

コントローラーインスタンスにも同様に配置します。
（配置するもの：CAの証明書、APIサーバの証明書と秘密鍵、サービスアカウント振り出し用キーペア）

```
for instance in controller-0 controller-1 controller-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  scp -i kubernetes.id_rsa \
    ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem \
    service-account-key.pem service-account.pem ubuntu@${external_ip}:~/
done
```

>kube-proxy、kube-controller-manager、kube-scheduler、kubelet のクライアント証明書は次の手順のクライアント認証設定ファイルの生成で使います。


#05-認証用kubeconfigの生成 

この手順では、Kubernetes APIサーバーがKubernetesのクライアントを配置、認証できるようにするための[kubeconfigs(Kubernetes構成ファイル)](https://kubernetes.io/docs/concepts/configuration/organize-cluster-access-kubeconfig/)を生成します。 


## クライアントの認証設定

まずは、`conttoller-manager`、`kubelet`、`kube-proxy`、`scheduler`、そして`admin`ユーザー用のkubeconfigファイルを生成します。

### KubernetesのPublic DNSアドレス
各kubeconfigはKubernetes APIサーバと接続できることが要求されます。
高可用性を実現するために、Kubernetes APIサーバの前に設置されている外部ロードバランサーのIPアドレスを使用します。

`kubernetes-the-hard-way` のDNSアドレスを取得して設定します。

```
KUBERNETES_PUBLIC_ADDRESS=$(aws elbv2 describe-load-balancers \
  --load-balancer-arns ${LOAD_BALANCER_ARN} \
  --output text --query 'LoadBalancers[0].DNSName')
```

### kubeletのkubeconfigsの生成
kubelet用のkubeconfigファイルを生成するときは、kubeletのノード名と同じクライアント証明書を使用する必要があります。
これにより、kubeletがKubernetesの[Node Authorizer](https://kubernetes.io/docs/admin/authorization/node/)によって認可されるようになります。

ワーカーノード毎にkubeconfigを生成します。

```
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}:443 \
    --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
```

生成物

```
worker-0.kubeconfig
worker-1.kubeconfig
worker-2.kubeconfig
```

### kube-proxyのkubeconfigの生成

`kube-proxy`のkubeconfigも生成します。

```
kubectl config set-cluster kubernetes-the-hard-way \
  --certificate-authority=ca.pem \
  --embed-certs=true \
  --server=https://${KUBERNETES_PUBLIC_ADDRESS}:443 \
  --kubeconfig=kube-proxy.kubeconfig

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

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
```

生成物

```
kube-proxy.kubeconfig
```

### kube-controller-managerのkubeconfig

`kube-controller-manager`のkubeconfigを生成します。

```
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

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

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
```

生成物

```
kube-controller-manager.kubeconfig
```


### kube-schedulerのkubeconfig

`kube-scheduler`のkubeconfigを生成します。

```
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

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

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
```

生成物

```
kube-scheduler.kubeconfig
```

### adminユーザー用のkubeconfig

`admin`ユーザーのkubeconfigを生成します。

```
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

kubectl config set-credentials admin \
  --client-certificate=admin.pem \
  --client-key=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
```

生成物

```
admin.kubeconfig
```



## kubeconfigの配布
`kubelet`と`kube-proxy`のkubecnofigをコピーし、各ワーカーノードに配置します

```
for instance in worker-0 worker-1 worker-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  scp -i kubernetes.id_rsa \
    ${instance}.kubeconfig kube-proxy.kubeconfig ubuntu@${external_ip}:~/
done
```

`kube-controller-manager`と`kube-scheduler`の`kubeconfig`をコピーし、各コントローラーノードに配置します。

```
for instance in controller-0 controller-1 controller-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')
  
  scp -i kubernetes.id_rsa \
    admin.kubeconfig kube-controller-manager.kubeconfig kube-scheduler.kubeconfig ubuntu@${external_ip}:~/
done
```



#06-暗号化の設定とキーの生成

Kubernetesは、クラスタの状態、アプリケーションの設定、秘匿情報などを含むさまざまなデータが格納されます。

Kubernetesは、クラスタ内で保持しているデータを[暗号化](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data)する機能が提供されています。

このステップでは、Kubernetes Secretsの暗号化に合わせた暗号化鍵と[暗号化の設定](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#understanding-the-encryption-at-rest-configuration) を生成します。


## 暗号化鍵

暗号化に使用する鍵を作成します。

```
ENCRYPTION_KEY=$(head -c 32 /dev/urandom | base64)
```

## 暗号化の設定ファイル
暗号化の設定のための`encryption-config.yaml`を生成します。

```
cat > encryption-config.yaml <<EOF
kind: EncryptionConfig
apiVersion: v1
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: ${ENCRYPTION_KEY}
      - identity: {}
EOF
```

この`encryption-config.yaml`をコピーし、各コントローラーノードに配置します。

```sh

for instance in controller-0 controller-1 controller-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')
  
  scp -i kubernetes.id_rsa encryption-config.yaml ubuntu@${external_ip}:~/
done
```

#07-etcdの起動

Kubernetesの各コンポーネントはステートレスになっており、クラスタの状態はetcdに格納され管理されています。(つまりetcdが超重要)

この手順では、3ノードのetcdクラスタを構築して、高可用性と安全な外部アクセスを実現します。


## 準備
このステップのコマンドは、controller-0、controller-1、controller-2の各コントローラインスタンスで実行する必要があります。

以下の様に`ssh`コマンドを使用して各コントローラーノードにログインしてください。

```
for instance in controller-0 controller-1 controller-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  echo ssh -i kubernetes.id_rsa ubuntu@$external_ip
done
```

ここからの手順は、直前のコマンドによって出力されたそれぞれのIPアドレスにssh接続して行います。
（つまり3台のインスタンス全てで同じコマンドを実行する必要があります）


### tmuxを使って並列にコマンドを走らせる

[tmux](https://github.com/tmux/tmux/wiki) を使えば、容易に複数のインスタンスで同時にコマンドを実行できます。[こちら](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md#running-commands-in-parallel-with-tmux) をご覧ください.

## etcdのクラスタメンバーの起動
※繰り返しになりますが、ここからの手順は各コントローラーインスタンスで行ってください※

### etcdのダウンロードとインストール

etcdのバイナリを[github](https://github.com/coreos/etcd)からDLします。

```
wget -q --show-progress --https-only --timestamping \
  "https://github.com/etcd-io/etcd/releases/download/v3.3.10/etcd-v3.3.10-linux-amd64.tar.gz"
```

DLしたファイルを展開して`etcd`サーバーと`etcdctl`コマンドラインユーティリティを取り出します。

```
tar -xvf etcd-v3.3.10-linux-amd64.tar.gz
sudo mv etcd-v3.3.10-linux-amd64/etcd* /usr/local/bin/
```

### etcdサーバーの設定

```
sudo mkdir -p /etc/etcd /var/lib/etcd
sudo cp ca.pem kubernetes-key.pem kubernetes.pem /etc/etcd/
```

インスタンスの内部IPアドレスは、クライアントのリクエストを受け付けて、etcdクラスタ間で通信するために使います。

現在のEC2インスタンスの内部IPアドレスを取得します。


```
INTERNAL_IP=$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)
```

各etcdのメンバーは、etcdクラスター内で名前はユニークにする必要があります。
そのため、現在使用しているEC2インスタンスのホスト名をetcdの名前として設定します。

```
ETCD_NAME=$(curl -s http://169.254.169.254/latest/user-data/ \
  | tr "|" "\n" | grep "^name" | cut -d"=" -f2)
echo "${ETCD_NAME}"
```

`etcd.service`としてsystemdのユニットファイルを作成します。

```
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
```

### etcdサーバーの起動

```
sudo systemctl daemon-reload
sudo systemctl enable etcd
sudo systemctl start etcd
```

> 念押しですが、ここまでのことを、各コントローラーノード `controller-0`、`controller-1`、`controller-2`で実行してください！

## 確認

etcdのクラスタの確認しておきます。


```
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
f98dc20bce6225a0, started, controller-0, https://10.240.0.10:2380, https://10.240.0.10:2379
ffed16798470cab5, started, controller-1, https://10.240.0.11:2380, https://10.240.0.11:2379
```








#08-Kubernetesコントロールプレーンの起動

この手順では、3つのインスタンスを使って可用性の高いKubernetesコントロールプレーンを作ります。

合わせて、`Kubernetes API Server`を外部クライアントに公開する外部ロードバランサーも作成します。

各ノードに、`Kubernetes API Server`、`Scheduler`、および`Controller Manager`のコンポーネントをインストールします。


## Prerequisites

前のステップと同じくこのステップでも、`controller-0`、`controller-1`、`controller-2`の各コントローラインスタンスで実行する必要があります。

全てのコントローラーノードに`ssh`コマンドでログインしてコマンドを実行します

**※既に各コントローラーノードにログインしている状態であれば、次の「Kubernetesコントロールプレーンのプロビジョニング」に飛んでください※**

```
for instance in controller-0 controller-1 controller-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  echo ssh -i kubernetes.id_rsa ubuntu@$external_ip
done
```

ここからの手順は、直前のコマンドによって出力されたそれぞれのIPアドレスにssh接続して行います。
（つまり3台のインスタンス全てで同じコマンドを実行する必要があります）

### tmuxを使って並列にコマンドを走らせる

[tmux](https://github.com/tmux/tmux/wiki) を使えば、容易に複数のインスタンスで同時にコマンドを実行できます。[こちら](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md#running-commands-in-parallel-with-tmux) をご覧ください.

## Kubernetesコントロールプレーンのプロビジョニング

Kubernetesの設定をおくディレクトリを作成します。

```
sudo mkdir -p /etc/kubernetes/config
```

### KubernetesコントローラーのバイナリのDLとインストール

Kubernetesの公式のリリースバイナリをDLします

```
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"
```

DLしたバイナリをインストールします。

```
chmod +x kube-apiserver kube-controller-manager kube-scheduler kubectl
sudo mv kube-apiserver kube-controller-manager kube-scheduler kubectl /usr/local/bin/
```

### KubernetesAPIサーバーの設定

```
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/
```

APIサーバーをクラスターのメンバーに知らせるための設定として、インスタンスの内部IPアドレスを使います。

現在のEC2インスタンスの内部IPアドレスを取得します。

```
INTERNAL_IP=$(curl -s http://169.254.169.254/latest/meta-data/local-ipv4)
```

`kube-apiserver.service`のsystemdのユニットファイルを生成します。

```
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 \\
  --enable-swagger-ui=true \\
  --encryption-provider-config=/var/lib/kubernetes/encryption-config.yaml \\
  --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 \\
  --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
```

参考：https://kubernetes.io/docs/reference/command-line-tools-reference/kube-apiserver/

### Kubernetesのコントローラーマネージャーの設定

`kube-controller-manager`のkubeconfigを移動させます。

```
sudo mv kube-controller-manager.kubeconfig /var/lib/kubernetes/
```

`kube-controller-manager.service`のsystemdユニットファイルを生成します。

```
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
```

### Kubernetesのschedulerの設定

```
sudo mkdir -p /etc/kubernetes/config/
```

kube-schedulerのkubeconfigを移動させます。

```
sudo mv kube-scheduler.kubeconfig /var/lib/kubernetes/
```

`kube-scheduler.yaml`を作ります。

```
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
```

`kube-scheduler.service`のsystemdユニットファイルを生成します。

```
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
```

### コントローラーサービスの起動

```
sudo systemctl daemon-reload
sudo systemctl enable kube-apiserver kube-controller-manager kube-scheduler
sudo systemctl start kube-apiserver kube-controller-manager kube-scheduler
```

> Kubernetes APIサーバーは初期化が完了するまで30秒くらいかかります。

コントローラーコンポーネントの状態を確認してみます。

```sh
kubectl get componentstatuses
```

出力

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

やったー！　コントロールプレーンが起動しています！

## kubelet認証のRBAC

このステップでは、Kubernetes APIサーバーが各ワーカーノードのKubelet APIにアクセスできるようにRBACによるアクセス許可を設定します。

メトリックやログの取得、Pod内でのコマンドの実行には、Kubernetes APIサーバーからKubelet APIへのアクセスが必要です。


> このチュートリアルでは、Kubelet ---authorization-modeフラグをWebhookに設定します。
Webhookモードは [SubjectAccessReview API](https://kubernetes.io/docs/admin/authorization/#checking-api-access)を使用して認証を行います

**ここで実行する以下のコマンドはクラスター全体に作用します。このため適当なコントローラーノードにログインして一度だけ実行してください。**
**以下では念のため、デプロイ用インスタンスから`controller-0`ノードにログインする所から手順を記載しています**

```
external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=controller-0" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

ssh -i kubernetes.id_rsa ubuntu@${external_ip}
```

kube-apiserver-to-kubeletという名前で[ClusterRole](https://kubernetes.io/docs/admin/authorization/rbac/#role-and-clusterrole)を作ります。

このロールに、Kubelet APIにアクセスしてポッドの管理に関連するタスクを実行する権限を付与します。

```
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
```

Kubernetes APIサーバーは、`--kubelet-client-certificate`フラグで定義したクライアント証明書を使って、`kubernetes`ユーザーとして`Kubelet`に対して認証を行います。

`system:kube-apiserver-to-kubelet`の`ClusterRole`を`kubernetes`ユーザーにバインドします。

```
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
```

### Kubernetesクラスターのパブリックエンドポイントを有効化する

以下のコマンドはデプロイ用インスタンス（各AWSリソースを作成するのに使用したインスタンス）で行ってください。

`kubernetes-the-hard-way`ロードバランサーのアドレスを取得する

```
KUBERNETES_PUBLIC_ADDRESS=$(aws elbv2 describe-load-balancers \
  --load-balancer-arns ${LOAD_BALANCER_ARN} \
  --output text --query 'LoadBalancers[].DNSName')
```

HTTPリクエストを作成しKubernetesのVersion情報を取得する

```
curl -k --cacert ca.pem https://${KUBERNETES_PUBLIC_ADDRESS}/version
```

出力例

```
{
  "major": "1",
  "minor": "13",
  "gitVersion": "v1.13.4",
  "gitCommit": "c27b913fddd1a6c480c229191a087698aa92f0b1",
  "gitTreeState": "clean",
  "buildDate": "2019-02-28T13:30:26Z",
  "goVersion": "go1.11.5",
  "compiler": "gc",
  "platform": "linux/amd64"
}
```


#09-ワーカーノードの起動

このステップでは、3つのKubernetesワーカーノードをブートストラップします。

下記のコンポーネントを各ノードにインストールします。

[runc](https://github.com/opencontainers/runc), [gVisor](https://github.com/google/gvisor), [container networking plugins](https://github.com/containernetworking/cni), [containerd](https://github.com/containerd/containerd), [kubelet](https://kubernetes.io/docs/admin/kubelet), [kube-proxy](https://kubernetes.io/docs/concepts/cluster-administration/proxies).

## 準備

この手順で記載されているコマンドは、`worker-0`、`worker-1`、`worker-2`の各ワーカーノードで実行する必要があります。

このため、まずは`ssh`コマンドで各ワーカーノードにログインします

```
for instance in worker-0 worker-1 worker-2; do
  external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

  echo ssh -i kubernetes.id_rsa ubuntu@$external_ip
done
```

ここからの手順は、直前のコマンドによって出力されたそれぞれのIPアドレスにssh接続して行います。
（つまり3台のインスタンス全てで同じコマンドを実行する必要があります）


### tmuxを使って並列にコマンドを走らせる

[tmux](https://github.com/tmux/tmux/wiki) を使えば、容易に複数のインスタンスで同時にコマンドを実行できます。[こちら](https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/master/docs/01-prerequisites.md#running-commands-in-parallel-with-tmux) をご覧ください.


## Kubernetesのワーカーノードのプロビジョング

使うライブラリをインストールします。

```
sudo apt-get update
sudo apt-get -y install socat conntrack ipset
```

> socatは`kubectl port-forward`コマンドに必要となります。

### ワーカーのバイナリをDLしてインストール

```
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://storage.googleapis.com/kubernetes-the-hard-way/runsc \
  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
```

ワーカーのバイナリをインストールします。

```
chmod +x kubectl kube-proxy kubelet runc.amd64 runsc
sudo mv runc.amd64 runc
sudo mv kubectl kube-proxy kubelet runc runsc /usr/local/bin/
sudo tar -xvf crictl-v1.15.0-linux-amd64.tar.gz -C /usr/local/bin/
sudo tar -xvf cni-plugins-linux-amd64-v0.8.2.tgz -C /opt/cni/bin/
sudo tar -xvf containerd-1.2.9.linux-amd64.tar.gz -C /
```

### CNIネットワーキングの設定

現在のEC2インスタンスのPodのCIDR範囲を取得します。

```
POD_CIDR=$(curl -s http://169.254.169.254/latest/user-data/ \
  | tr "|" "\n" | grep "^pod-cidr" | cut -d"=" -f2)
echo "${POD_CIDR}"
```

`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",
    "type": "loopback"
}
EOF
```

### containerdの設定

`containerd`の設定ファイルを作ります。

```
sudo mkdir -p /etc/containerd/
```

```
cat << EOF | sudo tee /etc/containerd/config.toml
[plugins]
  [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 = ""
    [plugins.cri.containerd.untrusted_workload_runtime]
      runtime_type = "io.containerd.runtime.v1.linux"
      runtime_engine = "/usr/local/bin/runsc"
      runtime_root = "/run/containerd/runsc"
EOF
```

> 信頼できないワークロードはgVisor(runsc)ランタイムが使われます

`containerd.service` systemdのユニットファイルを作ります。

```
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
```

### Kubeletの設定

```
WORKER_NAME=$(curl -s http://169.254.169.254/latest/user-data/ \
| tr "|" "\n" | grep "^name" | cut -d"=" -f2)
echo "${WORKER_NAME}"

sudo mv ${WORKER_NAME}-key.pem ${WORKER_NAME}.pem /var/lib/kubelet/
sudo mv ${WORKER_NAME}.kubeconfig /var/lib/kubelet/kubeconfig
sudo mv ca.pem /var/lib/kubernetes/
```

`kubelet-config.yaml`設定ファイルを作ります。

```
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}"
runtimeRequestTimeout: "15m"
tlsCertFile: "/var/lib/kubelet/${WORKER_NAME}.pem"
tlsPrivateKeyFile: "/var/lib/kubelet/${WORKER_NAME}-key.pem"
resolvConf: "/run/systemd/resolve/resolv.conf"
EOF
```

`kubelet.service`systemdユニットファイルを作ります。

```
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
```

### Kubernetes Proxyの設定

```
sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig
```

Create the `kube-proxy-config.yaml` configuration file:

```
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
```

`kube-proxy-config.yaml` 設定ファイルを作ります。

```
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
```

### ワーカーのサービス群の起動

```
sudo systemctl daemon-reload
sudo systemctl enable containerd kubelet kube-proxy
sudo systemctl start containerd kubelet kube-proxy
```

> 念押しですが、ここまでのことを、各ワーカーノード、`worker-0`、`worker-1`、`worker-2`で実行してください！

## 確認

> 現在作業中のインスタンス（ワーカーノード）には、このステップを完了できる権限がありません。
このため、以下のコマンドは任意のコントローラーノードにログインして行ってください。
以下のコマンドは、デプロイ用インスタンス（各AWSリソースを作成する際に使用したインスタンス）から`controller-0`ノードにログインする所から記載してあります。

登録されているKubernetesノードの一覧を表示させます。

```
external_ip=$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=controller-0" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')

ssh -i kubernetes.id_rsa ubuntu@${external_ip}

kubectl get nodes --kubeconfig admin.kubeconfig
```

出力

```
NAME             STATUS   ROLES    AGE   VERSION
ip-10-240-0-20   Ready    <none>   51s   v1.13.4
ip-10-240-0-21   Ready    <none>   51s   v1.13.4
ip-10-240-0-22   Ready    <none>   51s   v1.13.4
```


```
socat, conntrack, ipsetのそれぞれの役割がよくわかっていない

AWS、GCPともに169.254.169.254というIPアドレスはインスタンスメタデータの取得に使われる

CNIネットワークの設定の所、何やってるかもう一度復習する必要あり
```

#10-リモートアクセス用のkubectl設定

このステップでは、`admin`ユーザーのcredenialに基づいた、`kubectl`コマンドラインユーティリティ用のkubeconfigファイルを生成します。

> このステップでは、adminのクライアント証明書の生成に使用したディレクトリと同じディレクトリでコマンドを実行してください

## Admin Kubernetes設定ファイル

各kubeconfigは、Kubernetes APIサーバーと接続できる必要があります。

高可用性を実現するために、Kubernetes APIサーバーの前に設置した外部ロードバランサーに割り当てられたIPアドレスを使用します。

`admin`ユーザーとして認証するのに適したkubeconfigファイルを生成します。

```
KUBERNETES_PUBLIC_ADDRESS=$(aws elbv2 describe-load-balancers \
--load-balancer-arns ${LOAD_BALANCER_ARN} \
--output text --query 'LoadBalancers[].DNSName')

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

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

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

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

## 確認

remoteのKubernetesクラスターのヘルスチェックを確認します。

```
kubectl get componentstatuses
```

出力例

```
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"}
```

remoteのKubernetesクラスタのノードの一覧を取得します。

```
kubectl get nodes
```

出力例

```
NAME             STATUS   ROLES    AGE     VERSION
ip-10-240-0-20   Ready    <none>   3m35s   v1.13.4
ip-10-240-0-21   Ready    <none>   3m35s   v1.13.4
ip-10-240-0-22   Ready    <none>   3m35s   v1.13.4
```

#11-クラスタ内ネットワークの設定

ノードにスケジュールされたPodは、ノードのPod CIDR範囲からIPアドレスを受け取ります。

この時点では、ネットワーク[ルート](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Route_Tables.html)が見つからないため、Podは異なるノードで実行されている他のPodと通信できません。

この手順では、ノードのPod CIDR範囲をノードの内部IPアドレスにマップするための、各ワーカーノードのルートを作成します。

> Kubernetesのネットワークモデルの実装は[他にも](https://kubernetes.io/docs/concepts/cluster-administration/networking/#how-to-achieve-this)あります。

## ルーティングテーブルとルート群

このセクションでは、`kubernetes-the-hard-way`VPCネットワーク内でルートを作るために必要な情報を集めます。

通常、この機能は`flanne`, `calico`, `amazon-vpc-cin-k8s`等のCNIプラグインによって提供されます。
これを手作業で行うことにより、これらのプラグインがバックグラウンドで何をしているのかを理解しやすくなります。

まずは各ワーカーインスタンスの内部IPアドレスとPod CIDR範囲を表示させます。

```sh
for instance in worker-0 worker-1 worker-2; do
  instance_id_ip="$(aws ec2 describe-instances \
    --filters "Name=tag:Name,Values=${instance}" \
    --output text --query 'Reservations[].Instances[].[InstanceId,PrivateIpAddress]')"
  instance_id="$(echo "${instance_id_ip}" | cut -f1)"
  instance_ip="$(echo "${instance_id_ip}" | cut -f2)"
  pod_cidr="$(aws ec2 describe-instance-attribute \
    --instance-id "${instance_id}" \
    --attribute userData \
    --output text --query 'UserData.Value' \
    | base64 --decode | tr "|" "\n" | grep "^pod-cidr" | cut -d'=' -f2)"
  echo "${instance_ip} ${pod_cidr}"

  aws ec2 create-route \
    --route-table-id "${ROUTE_TABLE_ID}" \
    --destination-cidr-block "${pod_cidr}" \
    --instance-id "${instance_id}"
done
```

出力例

```
10.240.0.20 10.200.0.0/24
{
    "Return": true
}
10.240.0.21 10.200.1.0/24
{
    "Return": true
}
10.240.0.22 10.200.2.0/24
{
    "Return": true
}
```

## ルートの確認

各ワーカーインスタンスに対してネットワークルートを確認します。

```sh
aws ec2 describe-route-tables \
  --route-table-ids "${ROUTE_TABLE_ID}" \
  --query 'RouteTables[].Routes'
```

出力例

```
[
    [
        {
            "DestinationCidrBlock": "10.200.0.0/24",
            "InstanceId": "i-0879fa49c49be1a3e",
            "InstanceOwnerId": "107995894928",
            "NetworkInterfaceId": "eni-0612e82f1247c6282",
            "Origin": "CreateRoute",
            "State": "active"
        },
        {
            "DestinationCidrBlock": "10.200.1.0/24",
            "InstanceId": "i-0db245a70483daa43",
            "InstanceOwnerId": "107995894928",
            "NetworkInterfaceId": "eni-0db39a19f4f3970f8",
            "Origin": "CreateRoute",
            "State": "active"
        },
        {
            "DestinationCidrBlock": "10.200.2.0/24",
            "InstanceId": "i-0b93625175de8ee43",
            "InstanceOwnerId": "107995894928",
            "NetworkInterfaceId": "eni-0cc95f34f747734d3",
            "Origin": "CreateRoute",
            "State": "active"
        },
        {
            "DestinationCidrBlock": "10.240.0.0/24",
            "GatewayId": "local",
            "Origin": "CreateRouteTable",
            "State": "active"
        },
        {
            "DestinationCidrBlock": "0.0.0.0/0",
            "GatewayId": "igw-00d618a99e45fa508",
            "Origin": "CreateRoute",
            "State": "active"
        }
    ]
]
```

#12-DNSクラスタアドオンの導入
  
**この手順中、説明とコマンドが食い違っている箇所があったので修正してあります**

このステップでは、Kubernetesクラスタ内で動いているアプリケーションに、CoreDNSを使ったDNSベースのサービスディスカバリを提供する[DNSアドオン]((https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/))をデプロイします。

## DNSクラスターアドオン

`coredns`クラスターアドオンをデプロイします。

```
kubectl create -f https://raw.githubusercontent.com/prabhatsharma/kubernetes-the-hard-way-aws/master/deployments/core-dns.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
```

kube-dns deploymentによって作られたPodの確認

```
kubectl get pods -l k8s-app=kube-dns -n kube-system
```

出力例

```
NAME                      READY   STATUS    RESTARTS   AGE
coredns-7946767f6-trbvx   1/1     Running   0          42s
```

## 確認

`busybox` deploymentを作成します。

```
kubectl run busybox --image=busybox:1.28 --restart=Never -- sleep 3600
```

`busybox` deploymentによって作られてPodを確認します。

```
kubectl get pod busybox
```

出力

```
NAME       READY     STATUS    RESTARTS   AGE
busybox   1/1       Running   0          45s
```

`busybox` pod内から`kubernetes`serviceのDNS lookupを行います。

```
kubectl exec -it busybox -- 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
```

#13-スモークテスト

このステップでは、Kubernetesクラスタが正しく機能していることを確認するためのタスクを実行します。


## データの暗号化

このステップでは[保存されているデータの暗号化](https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/#verifying-that-data-is-encrypted)を確認します。

generic secretを作ります。

```sh
kubectl create secret generic kubernetes-the-hard-way --from-literal="mykey=mydata"
```

etcdに保存されている`kubernetes-the-hard-way`secretをhexdumpします。

```sh
external_ip=$(aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=controller-0" \
  --output text --query 'Reservations[].Instances[].PublicIpAddress')

ssh -i kubernetes.id_rsa ubuntu@${external_ip}
```

`controller-0`ノードで以下のコマンドを実行します

```sh
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 7b 8e 59 78 0f 59 09  |:v1:key1:{.Yx.Y.|
00000050  e2 6a ce cd f4 b6 4e ec  bc 91 aa 87 06 29 39 8d  |.j....N......)9.|
00000060  70 e8 5d c4 b1 66 69 49  60 8f c0 cc 55 d3 69 2b  |p.]..fiI`...U.i+|
00000070  49 bb 0e 7b 90 10 b0 85  5b b1 e2 c6 33 b6 b7 31  |I..{....[...3..1|
00000080  25 99 a1 60 8f 40 a9 e5  55 8c 0f 26 ae 76 dc 5b  |%..`.@..U..&.v.[|
00000090  78 35 f5 3e c1 1e bc 21  bb 30 e2 0c e3 80 1e 33  |x5.>...!.0.....3|
000000a0  90 79 46 6d 23 d8 f9 a2  d7 5d ed 4d 82 2e 9a 5e  |.yFm#....].M...^|
000000b0  5d b6 3c 34 37 51 4b 83  de 99 1a ea 0f 2f 7c 9b  |].<47QK....../|.|
000000c0  46 15 93 aa ba 72 ba b9  bd e1 a3 c0 45 90 b1 de  |F....r......E...|
000000d0  c4 2e c8 d0 94 ec 25 69  7b af 08 34 93 12 3d 1c  |......%i{..4..=.|
000000e0  fd 23 9b ba e8 d1 25 56  f4 0a                    |.#....%V..|
000000ea
```

etcdキーは、k8s:enc:aescbc:v1:key1というプレフィックスになっているはずです。

これは、`aescbc`プロバイダが`key1`という暗号化キーでデータを暗号化したことを表しています。

## Deployment  - To be run on local laptop

このステップでは[Deployment](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/)の作成と管理ができているかを確認します。

[nginx](https://nginx.org/en/) web serverのdeploymentを作成します。

```
kubectl create deployment nginx --image=nginx
```

`nginx` deploymentによってできたPodを確認します。

```
kubectl get pods -l app=nginx
```

出力

```
NAME                     READY     STATUS    RESTARTS   AGE
nginx-65899c769f-xkfcn   1/1       Running   0          15s
```

### Port Forwarding

このステップでは、[port forwarding](https://kubernetes.io/docs/tasks/access-application-cluster/port-forward-access-application-cluster/)を使って外部からアプリケーションにアクセスできるかを確認します。

`nginx` podのフルネームを取得します。

```
POD_NAME=$(kubectl get pods -l app=nginx -o jsonpath="{.items[0].metadata.name}")
```

ローカルの`8080`ポートを`nginx` Podの`80`番ポートにフォワードします。

```
kubectl port-forward $POD_NAME 8080:80
```

出力

```
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
```

別のターミナルからフォワードしたアドレスにHTTPリクエストを投げてみます。

```
curl --head http://127.0.0.1:8080
```

出力

```
HTTP/1.1 200 OK
Server: nginx/1.17.3
Date: Sat, 14 Sep 2019 13:54:34 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 13 Aug 2019 08:50:00 GMT
Connection: keep-alive
ETag: "5d5279b8-264"
Accept-Ranges: bytes
```

もとのターミナルに戻って`nginx` Podへのフォワーディングを止めます。

```
Forwarding from 127.0.0.1:8080 -> 80
Forwarding from [::1]:8080 -> 80
Handling connection for 8080
^C
```

### Logs

このステップでは、[コンテナのログの取得](https://kubernetes.io/docs/concepts/cluster-administration/logging/)ができるかを確認します。

`nginx` Podのログを表示します。

```
kubectl logs $POD_NAME
```

出力

```
127.0.0.1 - - [14/May/2018:13:59:21 +0000] "HEAD / HTTP/1.1" 200 0 "-" "curl/7.52.1" "-"
```

### Exec

このステップでは[コンテナ内でのコマンド実行](https://kubernetes.io/docs/tasks/debug-application-cluster/get-shell-running-container/#running-individual-commands-in-a-container)ができるかを確認します。

`nginx`コンテナに入って、`nginx -v`を実行してnginxのバージョンを表示します。

```
kubectl exec -ti $POD_NAME -- nginx -v
```

出力

```
nginx version: nginx/1.17.3
```

## Services

このステップでは、[Service](https://kubernetes.io/docs/concepts/services-networking/service/)を使ったアプリケーションの公開ができるかを確認します。

`nginx` deploymentを[NodePort](https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport) を使って公開します。

```
kubectl expose deployment nginx --port 80 --type NodePort
```

> クラスターが[クラウドプロバイダーインテグレーション](https://kubernetes.io/docs/getting-started-guides/scratch/#cloud-provider)の設定がされていないため、LoadBalancerは使用できません。

> この手順では、[クラウドプロバイダーインテグレーション](https://kubernetes.io/docs/getting-started-guides/scratch/#cloud-provider)の設定は対象外です。

`nginx` serviceでアサインされたノードのポート取得します。

```
NODE_PORT=$(kubectl get svc nginx \
  --output=jsonpath='{range .spec.ports[0]}{.nodePort}')
```

`nginx`ノードポートに外部からアクセスできるようにファイヤーウォールのルール追加します。

```
aws ec2 authorize-security-group-ingress \
  --group-id ${SECURITY_GROUP_ID} \
  --protocol tcp \
  --port ${NODE_PORT} \
  --cidr 0.0.0.0/0
```

ワーカーインスタンスから外部IPアドレスを取得します。

```
INSTANCE_NAME=$(kubectl get pod $POD_NAME --output=jsonpath='{.spec.nodeName}')
```

もしもあなたが今回クラスターをUS-EAST-1に立ち上げている場合、以下のコマンドを実行してください。

```
EXTERNAL_IP=$(aws ec2 describe-instances \
    --filters "Name=network-interface.private-dns-name,Values=${INSTANCE_NAME}.ec2.internal" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')
```

US-EAST-1以外のリージョンにクラスターを立ち上げている場合は、以下のコマンドを実行してください。

```
EXTERNAL_IP=$(aws ec2 describe-instances \
    --filters "Name=network-interface.private-dns-name,Values=${INSTANCE_NAME}.${AWS_REGION}.compute.internal" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')
```

外部IPアドレスと`nginx`ノードポート組わせてHTTPリクエストを投げてみます。

```
curl -I http://${EXTERNAL_IP}:${NODE_PORT}
```

出力

```
HTTP/1.1 200 OK
Server: nginx/1.17.3
Date: Sat, 14 Sep 2019 13:54:34 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Tue, 13 Aug 2019 08:50:00 GMT
Connection: keep-alive
ETag: "5d5279b8-264"
Accept-Ranges: bytes
```

## Untrusted Workloads

[gVisor](https://github.com/google/gvisor)を使って信頼されていないワークロードが動かせるかを確認します。

`untrustedPod`を作ります。

```
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
  name: untrusted
  annotations:
    io.kubernetes.cri.untrusted-workload: "true"
spec:
  containers:
    - name: webserver
      image: gcr.io/hightowerlabs/helloworld:2.0.0
EOF
```

### 確認

このセクションでは、割り当てられたワーカーノードを調べて、`untrustedPod`がgVisor(runsc)の下で実行されていることを確認します。

`untrustedPod`が実行されていることを確認します。

```
kubectl get pods -o wide
```

```
NAME                     READY     STATUS    RESTARTS   AGE       IP           NODE             NOMINATED NODE
busybox                  1/1       Running   0          5m        10.200.0.2   ip-10-240-0-20   <none>
nginx-64f497f8fd-l6b78   1/1       Running   0          3m        10.200.1.2   ip-10-240-0-21   <none>
untrusted                1/1       Running   0          8s        10.200.2.3   ip-10-240-0-22   <none>
```

`untrustedPod`が実行されているノードの名前を取得します。

```
INSTANCE_NAME=$(kubectl get pod untrusted --output=jsonpath='{.spec.nodeName}')
```

もしもあなたが今回クラスターをUS-EAST-1に立ち上げている場合、以下のコマンドを実行してください。

```
INSTANCE_IP=$(aws ec2 describe-instances \
    --filters "Name=network-interface.private-dns-name,Values=${INSTANCE_NAME}.ec2.internal" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')
```

US-EAST-1以外のリージョンにクラスターを立ち上げている場合は、以下のコマンドを実行してください。

```
INSTANCE_IP=$(aws ec2 describe-instances \
    --filters "Name=network-interface.private-dns-name,Values=${INSTANCE_NAME}.${AWS_REGION}.compute.internal" \
    --output text --query 'Reservations[].Instances[].PublicIpAddress')
```

ワーカーノードにSSH接続します

```
ssh -i kubernetes.id_rsa ubuntu@${INSTANCE_IP}
```

`gVisor`のもとで動いているコンテナの一覧を取得します。

```
sudo runsc --root  /run/containerd/runsc/k8s.io list
```

```
I0514 14:03:56.108368   14988 x:0] ***************************
I0514 14:03:56.108548   14988 x:0] Args: [runsc --root /run/containerd/runsc/k8s.io list]
I0514 14:03:56.108730   14988 x:0] Git Revision: 08879266fef3a67fac1a77f1ea133c3ac75759dd
I0514 14:03:56.108787   14988 x:0] PID: 14988
I0514 14:03:56.108838   14988 x:0] UID: 0, GID: 0
I0514 14:03:56.108877   14988 x:0] Configuration:
I0514 14:03:56.108912   14988 x:0]              RootDir: /run/containerd/runsc/k8s.io
I0514 14:03:56.109000   14988 x:0]              Platform: ptrace
I0514 14:03:56.109080   14988 x:0]              FileAccess: proxy, overlay: false
I0514 14:03:56.109159   14988 x:0]              Network: sandbox, logging: false
I0514 14:03:56.109238   14988 x:0]              Strace: false, max size: 1024, syscalls: []
I0514 14:03:56.109315   14988 x:0] ***************************
ID                                                                 PID         STATUS      BUNDLE                                                           CREATED                          OWNER
3528c6b270c76858e15e10ede61bd1100b77519e7c9972d51b370d6a3c60adbb   14766       running     /run/containerd/io.containerd.runtime.v1.linux/k8s.io/3528c6b270c76858e15e10ede61bd1100b77519e7c9972d51b370d6a3c60adbb   2018-05-14T14:02:34.302378996Z
7ff747c919c2dcf31e64d7673340885138317c91c7c51ec6302527df680ba981   14716       running     /run/containerd/io.containerd.runtime.v1.linux/k8s.io/7ff747c919c2dcf31e64d7673340885138317c91c7c51ec6302527df680ba981   2018-05-14T14:02:32.159552044Z
I0514 14:03:56.111287   14988 x:0] Exiting with status: 0
```

`untrustedPod`のIDを取得します。

```
POD_ID=$(sudo crictl -r unix:///var/run/containerd/containerd.sock pods --name untrusted -q)
```

`untrustedPod`の中で動いてる`webserver`コンテナのIDを取得します。

```
CONTAINER_ID=$(sudo crictl -r unix:///var/run/containerd/containerd.sock ps -p ${POD_ID} -q)
```

`gVisor`の`runsc`コマンドを使って、`webserver`コンテナの中で走っているプロセスを表示します。

```
sudo runsc --root /run/containerd/runsc/k8s.io ps ${CONTAINER_ID}
```

出力

```
I0514 14:05:16.499237   15096 x:0] ***************************
I0514 14:05:16.499542   15096 x:0] Args: [runsc --root /run/containerd/runsc/k8s.io ps 3528c6b270c76858e15e10ede61bd1100b77519e7c9972d51b370d6a3c60adbb]
I0514 14:05:16.499597   15096 x:0] Git Revision: 08879266fef3a67fac1a77f1ea133c3ac75759dd
I0514 14:05:16.499644   15096 x:0] PID: 15096
I0514 14:05:16.499695   15096 x:0] UID: 0, GID: 0
I0514 14:05:16.499734   15096 x:0] Configuration:
I0514 14:05:16.499769   15096 x:0]              RootDir: /run/containerd/runsc/k8s.io
I0514 14:05:16.499880   15096 x:0]              Platform: ptrace
I0514 14:05:16.499962   15096 x:0]              FileAccess: proxy, overlay: false
I0514 14:05:16.500042   15096 x:0]              Network: sandbox, logging: false
I0514 14:05:16.500120   15096 x:0]              Strace: false, max size: 1024, syscalls: []
I0514 14:05:16.500197   15096 x:0] ***************************
UID       PID       PPID      C         STIME     TIME      CMD
0         1         0         0         14:02     40ms      app
I0514 14:05:16.501354   15096 x:0] Exiting with status: 0
```

#14-crictlを使用してワーカーノードのイメージ・ポッド・コンテナをチェックする

ワーカーノードにログインし、リソース一覧を確認します。
この手順は、立ち上がっている3台のワーカーノード全てで実行可能です。


```sh
external_ip=$(aws ec2 describe-instances \
  --filters "Name=tag:Name,Values=worker-0" \
  --output text --query 'Reservations[].Instances[].PublicIpAddress')

ssh -i kubernetes.id_rsa ubuntu@${external_ip}
```

以下のコマンドを実行し、出力を確認します

```sh
sudo crictl -r unix:///var/run/containerd/containerd.sock images
```

出力例

```sh
IMAGE                                                  TAG                 IMAGE ID            SIZE
gcr.io/google_containers/k8s-dns-dnsmasq-nanny-amd64   1.14.7              5feec37454f45       10.9MB
gcr.io/google_containers/k8s-dns-kube-dns-amd64        1.14.7              5d049a8c4eec9       13.1MB
gcr.io/google_containers/k8s-dns-sidecar-amd64         1.14.7              db76ee297b859       11.2MB
k8s.gcr.io/pause                                       3.1                 da86e6ba6ca19       317kB
```

```sh
sudo crictl -r unix:///var/run/containerd/containerd.sock pods
```

出力

```sh
POD ID              CREATED             STATE               NAME                        NAMESPACE           ATTEMPT
9a304a19557f7       2 hours ago         Ready               kube-dns-864b8bdc77-c5vc2   kube-system         0
```

```sh
sudo crictl -r unix:///var/run/containerd/containerd.sock ps
```

出力

```sh
CONTAINER ID        IMAGE                                                                     CREATED             STATE               NAME                ATTEMPT
611bfea53997d       sha256:db76ee297b8597fc007b23a90619314b8405bb1df6dcad189df0a123a09e7ecc   2 hours ago         Running             sidecar             0
824f26368efc0       sha256:5feec37454f45d060c5f528c7d0bd4958df39e7ffd2e65ae42aae68bf78f69a5   2 hours ago         Running             dnsmasq             0
f3d35b783af1e       sha256:5d049a8c4eec92b21ca4be399c260166d96569a1a52d497f4a0365bb55c1a18c   2 hours ago         Running             kubedns             0
```


#15-後片付け

この手順では、今まで作ってきたリソースを削除します。

## EC2インスタンス

コントローラーノード、ワーカーノードを削除します。

```
aws ec2 terminate-instances \
  --instance-ids \
    $(aws ec2 describe-instances \
      --filter "Name=tag:Name,Values=controller-0,controller-1,controller-2,worker-0,worker-1,worker-2" \
      --output text --query 'Reservations[].Instances[].InstanceId')
aws ec2 delete-key-pair --key-name kubernetes
```

## Networking

外部ロードバランサー、VPC等のネットワークリソースを削除します。

```
aws elbv2 delete-load-balancer --load-balancer-arn "${LOAD_BALANCER_ARN}"
aws elbv2 delete-target-group --target-group-arn "${TARGET_GROUP_ARN}"
aws ec2 delete-security-group --group-id "${SECURITY_GROUP_ID}"
ROUTE_TABLE_ASSOCIATION_ID="$(aws ec2 describe-route-tables \
  --route-table-ids "${ROUTE_TABLE_ID}" \
  --output text --query 'RouteTables[].Associations[].RouteTableAssociationId')"
aws ec2 disassociate-route-table --association-id "${ROUTE_TABLE_ASSOCIATION_ID}"

aws ec2 delete-route-table --route-table-id "${ROUTE_TABLE_ID}"
aws ec2 detach-internet-gateway \
  --internet-gateway-id "${INTERNET_GATEWAY_ID}" \
  --vpc-id "${VPC_ID}"
aws ec2 delete-internet-gateway --internet-gateway-id "${INTERNET_GATEWAY_ID}"
aws ec2 delete-subnet --subnet-id "${SUBNET_ID}"
aws ec2 delete-vpc --vpc-id "${VPC_ID}"

```



おつかれさまでした！
以上でkubernetes the hard wayは終了です。