Kubernetesの環境構築を自動化してみた

# はじめに

最近Kubernetesの環境構築で苦しんでいた新人SEです。さて、近頃コンテナという技術が流行っていると思いますが、そのコンテナのオーケストレーションシステムであるKubernetesも標準的な技術となりつつあります。しかし、Kubernetesを触ってみたくてもノートPC１台では中々試すことはできません。そこで、この記事ではVagrantとAnsibleを使ってVurtualBox上のVM３台にKubernetes環境を簡単に構築する方法について書いていきます。構成としてはMaster Node１台、Worker Node２台とします。
なお、この記事で作成されたコードは[GitHub](https://github.com/Yoshiaki-Kanazawa/k8s_env)に置いてあります。

# 環境

Mac OS Catalina 10.15.2
Vagrant 2.2.6
VurtualBox 6.0.12

# ディレクトリ構成

この記事で作成するファイルのディレクトリ構成は以下の通りです。（syncはディレクトリです）

```
.
├── Vagrantfile
├── ansible-playbook
│   ├── hosts
│   ├── k8s_master.yml
│   ├── k8s_workers.yml
│   ├── kubernetes.yml
├── sync
├── ansible.cfg
```

# 必要なプラグインのインストール
必要なvagrantのプラグインをインストールします。vagrantでvmとのディレクトリ共有を行うために必要です。

```shell
$ vagrant plugin install vagrant-vbguest
# プロキシ設定が必要な場合のみ
$ vagrant plugin install vagrant-proxyconf
$ vagrant reload 
```

# Vagrantfileの作成

まずは、作業ディレクトリに移動してVagrantfileの雛形を作成します。

```shell
$ vagrant init
```

すると、Vagrantfileが作成されますが、まずはその中身を全部消します。エディタ等を用いて何も描かれていない状態にします。
そして設定を書いていく前に以下のような雛形の状態にします。

```:Vagrantfile
# coding: utf-8
# -*- mode: ruby -*-
Vagrant.configure(2) do |config|

end
```

##  Master Nodeの設定

まずはMaster Nodeの設定を書いていきます。最初に完成形を示します。

```:Vagrantfile
# coding: utf-8
# -*- mode: ruby -*-
Vagrant.configure(2) do |config|
  # マスタ 仮想マシンの起動
  config.vm.define 'master' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'master'
    machine.vm.network :private_network,ip: "172.16.20.11"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 2
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]

    # マスタ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "master"
      ansible.inventory_path = "ansible-playbook/hosts"
    end

    # マスタのセットアップ
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/k8s_master.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.install = false
      ansible.limit = "master"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
  end
end
```

まずは作成するVMの基本設定が記述されている以下の部分についてです。

```:Vagrantfile
  # マスタ 仮想マシンの起動
  config.vm.define 'master' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'master'
    machine.vm.network :private_network,ip: "172.16.20.11"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 2
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]
```

それぞれの行について説明していきます。

 - machine.vm.box
使用するbox名。Dockerでいうところのイメージ名のようなもの。

 - machine.vm.hostname
VMのhostname。

 - machine.vm.network、ip
プライベートネットワークでのVMのIPアドレスを指定。

 - machine.vm.provider
使用する仮想化ソフトの指定。このブロックの中に仮想化ソフトでの設定を記述する。

 - machine.vm.synced_folder
ここでホストOSとゲストOSのディレクトリ共有を設定。ownerやグループも指定できる。デフォルトはSSHユーザー。

次にVMへdocker & k8sのインストール設定を行います。

```:Vagrantfile
    # マスタ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "master"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
```

 - machine.vm.provision
ここで"ansible_local"を指定することでVMにAnsibleがインストールされる。

 - ansible.playbook
VM内で実行するplaybookを指定。

 - ansible.version
Ansibleのバージョン指定。

 - ansible.verbose
ansible-playbook 実行時に詳細出力するかどうか。出力する場合はv,vv,vvvと指定する。（vの数が多いほど詳細に出力）

 - ansible.limit
ansible-playbook を実行するホストやグループ指定。

 - ansible.inventory_path
hostsファイルのパス。

マスタのセットアップも同様に記述します。

## Worker Nodeの作成

Worker Node2台の設定を書いていきます。以下に完成形を示します。

```:Vagrantfile
# coding: utf-8
# -*- mode: ruby -*-
Vagrant.configure(2) do |config|
...
省略（Master Nodeの設定）
...
  # Worker Node１ 仮想マシンの起動
  config.vm.define 'worker1' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'worker1'
    machine.vm.network :private_network,ip: "172.16.20.12"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 1
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]

    # Worker Node１ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "worker1"
      ansible.inventory_path = "ansible-playbook/hosts"
    end

    # ノードのセットアップ
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/k8s_workers.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.install = false
      ansible.limit = "worker1"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
  end

  # Worker Node２ 仮想マシンの起動
  config.vm.define 'worker2' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'worker2'
    machine.vm.network :private_network,ip: "172.16.20.13"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 1
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]

    # Worker Node２ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "worker2"
      ansible.inventory_path = "ansible-playbook/hosts"
    end

    # ノードのセットアップ
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/k8s_workers.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.install = false
      ansible.limit = "worker2"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
  end
end
```

Worker Nodeの設定は記法としてはMaster Nodeの方と変わりません。

## 完成形

Vagrantfileの完成形は以下の通りです。

```:Vagrantfile
# coding: utf-8
# -*- mode: ruby -*-
Vagrant.configure(2) do |config|
  # プロキシ設定
  #if Vagrant.has_plugin?("vagrant-proxyconf")
  # config.proxy.enabled  = true  # => true; all applications enabled, false; all applications disabled
  # config.proxy.http     = ""
  # config.proxy.https    = ""
  #  config.proxy.no_proxy = "localhost,127.0.0.1,172.16.20.11,172.16.20.12,172.16.20.13,10.96.0.0/12,10.244.0.0/16,10.32.0.10" 
  #end

  #if Vagrant.has_plugin?("vagrant-vbguest")
  #  config.vbguest.auto_update = true
  #end

  # マスタ 仮想マシンの起動
  config.vm.define 'master' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'master'
    machine.vm.network :private_network,ip: "172.16.20.11"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 2
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]

    # マスタ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "master"
      ansible.inventory_path = "ansible-playbook/hosts"
    end

    # マスタのセットアップ
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/k8s_master.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.install = false
      ansible.limit = "master"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
  end

  # Worker Node１ 仮想マシンの起動
  config.vm.define 'worker1' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'worker1'
    machine.vm.network :private_network,ip: "172.16.20.12"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 1
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]

    # Worker Node１ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "worker1"
      ansible.inventory_path = "ansible-playbook/hosts"
    end

    # ノードのセットアップ
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/k8s_workers.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.install = false
      ansible.limit = "worker1"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
  end

  # Worker Node２ 仮想マシンの起動
  config.vm.define 'worker2' do |machine|
    machine.vm.box = "centos/7"
    machine.vm.hostname = 'worker2'
    machine.vm.network :private_network,ip: "172.16.20.13"
    machine.vm.provider "virtualbox" do |vbox|
      vbox.gui = false
      vbox.cpus = 1
      vbox.memory = 1024
    end
    machine.vm.synced_folder "./sync", "/home/vagrant/sync", owner: "vagrant",
      group: "vagrant", mount_options: ["dmode=777", "fmode=777"]

    # Worker Node２ docker & k8sのインストール
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/kubernetes.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.limit = "worker2"
      ansible.inventory_path = "ansible-playbook/hosts"
    end

    # ノードのセットアップ
    machine.vm.provision "ansible_local" do |ansible|
      ansible.playbook = "ansible-playbook/k8s_workers.yml"
      ansible.version = "latest"
      ansible.verbose = false
      ansible.install = false
      ansible.limit = "worker2"
      ansible.inventory_path = "ansible-playbook/hosts"
    end
  end
end
```

# Playbookの作成

次にPlaybookの説明をしていきます。Vagrantfileで"machine.vm.provision "ansible_local""と指定しているので、vm上にansibleがインストールされvm上で実行されます。なので、ホストOSにansibleをインストールする必要はありません。

まずは設定ファイルとhostsファイルを以下のように作成します。

```:ansible.cfg
[defaults]
inventory= /vagrant/ansible-playbook/hosts

host_key_checking = no

[ssh_connection]
ssh_args = -o ControlMaster=auto -o ControlPersist=60s -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes
```

```:hosts
master     ansible_connection=local
worker1    ansible_connection=local
worker2    ansible_connection=local

[workers]
worker[1:2]
```

hostsファイルとは、ansibleを実行するリモートホストを設定するインベントリファイルで、インベントリファイル上でリモートホストはグループとしてまとめておくことができます。

## docker & k8sのインストール

docker & k8sをインストールする設定を書いていきます。

```yml:kubernetes.yml
- hosts: all
  become: yes
  gather_facts: True

  tasks:
  ## yum repositoryの設定
  - name: Install yum-utils
    yum:
      name: "{{ item }}"
      state: latest
    with_list:
      - yum-utils
      - device-mapper-persistent-data
      - lvm2

  - name: Add Docker repo
    get_url:
      url: https://download.docker.com/linux/centos/docker-ce.repo
      dest: /etc/yum.repos.d/docker-ce.repo
    become: yes

  - name: Add kubernetes repo
    yum_repository:
      name: kubernetes
      description: kubernetes repo
      baseurl: https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
      gpgcheck: no
      enabled: yes

  ## Dockerのインストール
  - name: Install docker
    yum:
      name: docker-ce
      state: present

  - name: Docker setting mkdir
    file:
      path: /etc/systemd/system/docker.service.d
      state: directory
      owner: root
      group: root
      mode: 0755

  - name: Docker setting option file
    copy:
      src: option.conf
      dest: /etc/systemd/system/docker.service.d/option.conf

  - name: Start service docker, and enable service  docker
    systemd:
      name: docker.service
      state: started
      daemon-reload: yes
      enabled: yes

  ## ユーザーvagrantをdockerグループにいれる
  - name: Usermod -aG docker vagrant
    user:
      name: vagrant
      groups: docker

  ## カーネル設定変更
  - name: Modprebe br_netfilter
    command: modprobe br_netfilter

  - name: Set sysctl
    sysctl:
      name: net.bridge.bridge-nf-call-iptables
      value: "1"
      sysctl_set: yes
      sysctl_file: /etc/sysctl.conf
      state: present
      reload: yes

  ## 事前設定
  - name: Disable SELinux
    command: setenforce 0

  - name: Disable SELinux on reboot
    selinux:
      state: disabled

  - name: Ensure net.bridge.bridge-nf-call-iptables is set to 1
    sysctl:
     name: net.bridge.bridge-nf-call-iptables
     value: 1
     state: present

  - name: Swap off
    command: swapoff -a

  - name: Diable firewalld
    systemd:
      name: firewalld
      state: stopped
      enabled: no

  ## Kubernetes のインストール
  - name: Install kubelet kubeadm
    yum:
      name: "{{ packages }}"
    vars:
      packages:
        - kubelet
        - kubeadm
      state: present

  - name: Start kubelet
    systemd:
      name: kubelet.service
      state: started
      enabled: yes

  - name: Install kubectl
    yum:
      name: kubectl
      state: present
      allow_downgrade: yes

  ## HostOnly Interface の IPアドレス取得
  - name: Install net-tools
    yum:
      name: net-tools
      state: present

  - name: Getting hostonly ip address
    command: ifconfig eth1
    register: ip
  - debug: var=ip.stdout_lines[1].split('inet')[1].split(' ')[1]

  ## 10-kubeadmin.conf に --node-ipを追加
  - name: Oopy /usr/lib/systemd/system/kubelet.service.d to /etc/systemd/system
    copy:
      src: /usr/lib/systemd/system/kubelet.service.d/
      dest: /etc/systemd/system/kubelet.service.d/
      owner: root
      group: root
      mode: 0755

  - name: Change 10-kubeadm.conf for v1.11 or later
    replace:
      dest: /etc/systemd/system/kubelet.service.d/10-kubeadm.conf
      regexp: 'KUBELET_EXTRA_ARGS$'
      replace: KUBELET_EXTRA_ARGS --node-ip={{ ip.stdout_lines[1].split('inet')[1].split(' ')[1] }} --cluster-dns=10.32.0.10

  ## 変更を反映
  - name: Daemon-reload and restart kubelet
    systemd:
      name: kubelet.service
      state: restarted
      daemon_reload: yes

  ## ホストとの共有ディレクトリ作成
  - name: Make sync directory
    file:
      path: /home/vagrant/sync
      state: directory
      owner: vagrant
      group: vagrant
      mode: '0755'
```

## マスタのセットアップ

マスタをセットアップする設定を書いていきます。

```yml:k8s_master.yml
- hosts: master
  become: yes
  become_user: root
  gather_facts: True

  tasks:
    ## k8sマスタを初期化
    - name: Kubeadm reset v1.11 or later
      command: kubeadm reset -f

    ## HostOnly Interface の IPアドレス取得
    - name: Getting hostonly ip address
      command: ifconfig eth1
      register: ip

    - name: Kubeadm init
      command: kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address={{ ip.stdout_lines[1].split('inet')[1].split(' ')[1] }} --service-cidr=10.32.0.0/24
      register: join

    ## k8sノード用コマンドを保存
    - name: Generate join command
      command: kubeadm token create --print-join-command
      register: join_command

    - name: Copy join command to sync directory
      copy:
        content: "{{ join_command.stdout_lines[0] }}"
        dest: /home/vagrant/sync/join-command.sh
        mode: 0777

    ## kubeletのDNSのIPアドレスを変更
    - name: Change config.yaml
      replace:
        dest: /var/lib/kubelet/config.yaml
        regexp: '10.96.0.10'
        replace: 10.32.0.10

    ## kubeletをリスタートして変更を反映
    - name: Daemon-reload and restart kubelet
      systemd:
        state: restarted
        daemon_reload: yes
        name: kubelet

    ## kubeconfigディレクトリ作成
    - name: Mkdir kubeconfig
      file:
        path:  /home/vagrant/.kube
        state: directory
        owner: vagrant
        group: vagrant
        mode:  '0755'

    ## configファイルのコピー
    - name: Chmod admin.conf
      file:
        path:  /etc/kubernetes/admin.conf
        owner: vagrant
        group: vagrant
        mode:  '0600'

    - name: Copy config to home dir
      copy:
        src:  /etc/kubernetes/admin.conf
        dest: /home/vagrant/.kube/config
        owner: vagrant
        group: vagrant
        mode:  '0600'

    ## Wgetのインストール
    - name: Install wget
      yum:
        name: wget
        state: latest

    ## Flannelのインストール
    - name: Install flannel
      get_url:
        url: "https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml"
        dest: /home/vagrant/kube-flannel.yml

    ## Flannelのデプロイ
    - name: Deploy Flannel
      become_user: vagrant
      command: kubectl apply -f /home/vagrant/kube-flannel.yml

    ## Gitのインストール
    - name: Install git
      yum:
        name: git
        state: latest

    ## Metrics Server インストール
    - name: Install Metrics Server
      git:
        repo: 'https://github.com/kubernetes-sigs/metrics-server'
        dest: /home/vagrant/metrics-server

    - name: add a new line
      blockinfile:
        path: /home/vagrant/metrics-server/deploy/kubernetes/metrics-server-deployment.yaml
        insertafter: '        args:'
        block: |
           # added lines
                     - --kubelet-insecure-tls
                     - --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
    - name: Deploy Metrics Server
      become_user: vagrant
      command: kubectl apply -f /home/vagrant/metrics-server/deploy/kubernetes

    ## Dashboard UI インストール
    - name: Download Dashboard Manifest
      get_url:
        url: https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0-beta6/aio/deploy/recommended.yaml
        dest: /home/vagrant/
        mode: '0644'
        owner: vagrant
        group: vagrant

    - name: Change Dashboard RBAC
      replace:
        path: /home/vagrant/recommended.yaml
        after: '  kind: ClusterRole'
        regexp: '^  name: kubernetes-dashboard'
        replace: '  name: cluster-admin'

    - name: Deploy Dashboard UI
      become_user: vagrant
      command: kubectl apply -f /home/vagrant/recommended.yaml

    - name: Setup kubeconfig
      become_user: vagrant
      shell: |
        TOKEN=$(kubectl -n kubernetes-dashboard describe secret $(kubectl -n kubernetes-dashboard get secret |grep kubernetes-dashboard-token-* | awk '{print $1}') |awk '$1=="token:"{print $2}')
        kubectl config set-credentials kubernetes-admin --token="${TOKEN}"
```

## ノードのセットアップ

ノードをセットアップする設定を書いていきます。

```yml:k8s_workers.yml
- hosts: workers
  become: yes
  become_user: root
  gather_facts: True

  tasks:
    ## k8sノードをリセット
    - name: Kubeadm reset v1.11 or later
      command: kubeadm reset -f

    ## k8sクラスタへ参加
    - name: Join the node to cluster
      command: sh /home/vagrant/sync/join-command.sh

    ## kubeletのDNSのIPアドレスを変更
    - name: Change config.yaml
      replace:
        dest: /var/lib/kubelet/config.yaml
        regexp: '10.96.0.10'
        replace: 10.32.0.10

    ## kubeletをリスタートして変更を反映
    - name: Daemon-reload and restart kubelet
      systemd:
        state: restarted
        daemon_reload: yes
        name: kubelet
```

# 実行

ここまでで必要なファイルを作り終えました。冒頭のディレクトリ構造通りにファイルを配置して、Vagrantfileのあるディレクトリに移動し、"**vagrant up**"を実行するとKubernetesの環境が出来上がります！（初回のみ１時間程度かかります）
かなりバッテリーを消費するので、使わない時は"**vagrant halt**"でシャットダウンしておきましょう。

# 注意事項

- CPU高負荷で失敗することがあるので、他のアプリケーションは起動させずに実行することをお勧めします。（ハイスペックマシンなら問題ありませんが笑）
- 何かしらの理由で失敗したり中断した場合、syncディレクトリ内の**join-command.sh**を削除してから、再実行するようにしてください。

# 再起動時の手順

"**vagrant halt**"でVMをシャットダウンした状態から"**vagrant up**"で起動した場合、Kubernetesクラスタを再び使用可能にするためにいくつか行うべきことがあります。

1: masterへログイン  

```shell
$ vagrant ssh master
```

2: swapをオフにする
 
```shell
[vagrant@master ~]$ sudo swapoff -a
```

3: coredns以外の管理用Podが起動するまで待機

```shell
[vagrant@master ~]$ kubectl get pods -n kube-system -o wide
NAME                             READY   STATUS    RESTARTS   AGE     IP             NODE      NOMINATED NODE   READINESS GATES
coredns-6955765f44-2qmjs         0/1     Completed   0          23m     10.244.0.10    master    <none>           <none>
coredns-6955765f44-dtmr8         0/1     Completed   0          3d12h   10.244.0.8     master    <none>           <none>
etcd-master                      1/1     Running   8          3d12h   172.16.20.11   master    <none>           <none>
kube-apiserver-master            1/1     Running   7          3d12h   172.16.20.11   master    <none>           <none>
kube-controller-manager-master   1/1     Running   11         3d12h   172.16.20.11   master    <none>           <none>
kube-flannel-ds-amd64-9p8wd      1/1     Running   7          3d12h   172.16.20.11   master    <none>           <none>
kube-flannel-ds-amd64-d6tqx      1/1     Running   0          3d11h   172.16.20.13   worker2   <none>           <none>
kube-flannel-ds-amd64-hwbcp      1/1     Running   0          3d12h   172.16.20.12   worker1   <none>           <none>
kube-proxy-j88r7                 1/1     Running   0          3d11h   172.16.20.13   worker2   <none>           <none>
kube-proxy-kmlpr                 1/1     Running   4          3d12h   172.16.20.11   master    <none>           <none>
kube-proxy-ptrp2                 1/1     Running   0          3d12h   172.16.20.12   worker1   <none>           <none>
kube-scheduler-master            1/1     Running   10         3d12h   172.16.20.11   master    <none>           <none>
metrics-server-988549d7f-bn92x   1/1     Running   0          12m     10.244.1.2     worker1   <none>           <none>
```

うまく起動するまでPodの再起動が繰り返されるのでかなり重くなります。15分ほど待機する必要があります。

4: クラスタへのjoin用コマンド取得

```shell
[vagrant@master ~]$ kubeadm token create --print-join-command
kubeadm join 172.16.20.11:6443 --token dvucb2.lekv9gr0xdppi8gj     --discovery-token-ca-cert-hash sha256:4eec307c5a512ef4fb88e11d90ee99fdcab18b52cfe945ba607fc15f64562358 
[vagrant@master ~]$ exit
```
あとで使うのでどこかにコピーしておく。

5: worker nodeをKubernetesクラスタに参加させる

この手順は全てのworker nodeで行う。例としてworker1で行う。

```shell
$ vagrant ssh worker1
[vagrant@worker1 ~]$ sudo swapoff -a
[vagrant@worker1 ~]$ sudo kubeadm reset -f
[vagrant@worker1 ~]$ sudo kubeadm join 172.16.20.11:6443 --token dvucb2.lekv9gr0xdppi8gj     --discovery-token-ca-cert-hash sha256:4eec307c5a512ef4fb88e11d90ee99fdcab18b52cfe945ba607fc15f64562358
[vagrant@worker1 ~]$ exit
```

6: Kubernetesクラスタが正常か確認する

```shell
$ vagrant ssh master
[vagrant@master ~]$ kubectl get nodes
NAME      STATUS   ROLES    AGE     VERSION
master    Ready    master   3d13h   v1.17.2
worker1   Ready    <none>   3d12h   v1.17.2
worker2   Ready    <none>   3d12h   v1.17.2
[vagrant@master ~]$ kubectl get pods -n kube-system -o wide
NAME                             READY   STATUS    RESTARTS   AGE     IP             NODE      NOMINATED NODE   READINESS GATES
coredns-6955765f44-2qmjs         1/1     Running   1          57m     10.244.0.11    master    <none>           <none>
coredns-6955765f44-dtmr8         1/1     Running   2          3d13h   10.244.0.13    master    <none>           <none>
etcd-master                      1/1     Running   11         3d13h   172.16.20.11   master    <none>           <none>
kube-apiserver-master            1/1     Running   10         3d13h   172.16.20.11   master    <none>           <none>
kube-controller-manager-master   1/1     Running   14         3d13h   172.16.20.11   master    <none>           <none>
kube-flannel-ds-amd64-9p8wd      1/1     Running   10         3d13h   172.16.20.11   master    <none>           <none>
kube-flannel-ds-amd64-d6tqx      1/1     Running   0          3d12h   172.16.20.13   worker2   <none>           <none>
kube-flannel-ds-amd64-hwbcp      1/1     Running   0          3d12h   172.16.20.12   worker1   <none>           <none>
kube-proxy-j88r7                 1/1     Running   0          3d12h   172.16.20.13   worker2   <none>           <none>
kube-proxy-kmlpr                 1/1     Running   6          3d13h   172.16.20.11   master    <none>           <none>
kube-proxy-ptrp2                 1/1     Running   0          3d12h   172.16.20.12   worker1   <none>           <none>
kube-scheduler-master            1/1     Running   12         3d13h   172.16.20.11   master    <none>           <none>
metrics-server-988549d7f-bn92x   1/1     Running   0          46m     10.244.1.2     worker1   <none>           <none>
```

Kubernetesクラスタの再起動には時間がかかりますが、VMを１から作り直すよりは少ない時間で済みます。（クラスタの再起動も自動化したいなぁ）