VirtualBox上でkubeadmを使用してnodeを追加するとエラーが発生する

kubenetes1.4から導入された`kubeadm（alpha版）`を使用して、ローカルのVirtualBox上にkuebernetesクラスタを構築した際に発生したエラーの解決方法の覚書きです。

`kubeadm`を使用してkubernetesクラスタにNodeを追加しようとした際に、Node側からMasterへの接続ができないというのがエラーの内容になります。

環境はVagrantを使用して、VirtualBox上にUbuntu16.04のMasterとNodeを用意しています。

# 前提
PC：MacBook Pro（OS X El Capitan 10.11.6）
VirtualBox：バージョン 5.0.26 r108824
Vagrant：バージョン 1.8.6

# 構成
VirtualBox上に下記２環境を用意。
Master
・OS：Ubuntu16.04
・ネットワークアダプター1：NAT
・ネットワークアダプター2：ホストオンリーアダプター

Node
・OS：Ubuntu16.04
・ネットワークアダプター1：NAT
・ネットワークアダプター2：ホストオンリーアダプター

# 仮想環境構築 - Vagrant
Vagrantを使用して仮想環境を構築するために、まず下記コマンドを実行してVagrantfileを作成します。実行するとカレントディレクトリにVagrantfileが作成されるので、必要に応じてカレントディレクトリを移動しておきます。

```
$ vagrant init ubuntu/xenial64
```

次に、作成したVagrantfileを編集です。
編集する内容は

- `ubuntu/xenial64`のBoxを使用して、MasterとNodeを立ち上げる
- MasterとNode間で通信ができるように、private_networkを設定する

の2点になります。

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

 Vagrant.configure(2) do |config|
   config.vm.box = "ubuntu/xenial64"

   # Masterの設定
   # 固定IPアドレス 192.168.33.10を指定
   config.vm.define :master do | master |
     master.vm.hostname = "master"
     master.vm.network :private_network, ip: "192.168.33.10"
   end

   # Nodeの設定
　　　　　　# 固定IPアドレス 192.168.33.11を指定
   config.vm.define :node1 do | node1 |
     node1.vm.hostname = "node1"
     node1.vm.network :private_network, ip: "192.168.33.11"
   end
end
```

Vagrantfileの編集が終われば、`vagrant up`を実行して仮想環境を立ち上げます。

# kubeadmのインストール
仮想環境が立ち上がったら、まずkubernetクラスタを構築するのに必要な`kubeadm`、およびその他の必須パッケージを[公式ドキュメント](http://kubernetes.io/docs/getting-started-guides/kubeadm/)に従ってMasterとNodeの両方共にインストールします。

`vagrant ssh master`、`vagrant ssh node1`で仮想環境に接続してrootで作業開始です。
公式ドキュメントの通り下記コマンドを実行します。

```shell
# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
# cat <<EOF > /etc/apt/sources.list.d/kubernetes.list
deb http://apt.kubernetes.io/ kubernetes-xenial main
EOF
# apt-get update
# apt-get install -y docker.io kubelet kubeadm kubectl kubernetes-cni
```

# Masterの初期化 - Node追加（エラー発生箇所）
問題があったのはここです。
公式ドキュメントの通り`kubeadm init`を実行すると下記のようにNode側で実行するjoinコマンドが表示されます。（トークンの値は適当なものに置き換えてます）

```
master# kubeadm init
<master/tokens> generated token: "123abc.123456789abcdefg"
<master/pki> created keys and certificates in "/etc/kubernetes/pki"
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"
<util/kubeconfig> created "/etc/kubernetes/admin.conf"
<master/apiclient> created API client configuration
<master/apiclient> created API client, waiting for the control plane to become ready
<master/apiclient> all control plane components are healthy after 44.814120 seconds
<master/apiclient> waiting for at least one node to register and become ready
<master/apiclient> first node is ready after 4.230195 seconds
<master/discovery> created essential addon: kube-discovery, waiting for it to become ready
<master/discovery> kube-discovery is ready after 31.503093 seconds
<master/addons> created essential addon: kube-proxy
<master/addons> created essential addon: kube-dns

Kubernetes master initialised successfully!

You can now join any number of machines by running the following on each node:

kubeadm join --token 123abc.123456789abcdefg 10.0.2.15
```

本来であれば表示された`join`コマンドをNode側で使用すれば、Nodeをkubernetesクラスタに追加することができるのですが、実行してみたところ下記のようなエラーになってしまいました。

```
node1# kubeadm join --token 123abc.123456789abcdefg 10.0.2.15
<util/tokens> validating provided token
<node/discovery> created cluster info discovery client, requesting info from "http://10.0.2.15:9898/cluster-info/v1/?token-id=629bfc"
error: <node/discovery> failed to request cluster info [Get http://10.0.2.15:9898/cluster-info/v1/?token-id=123abc: dial tcp 10.0.2.15:9898: getsockopt: connection refused]
```

`join`コマンドで指定した`10.0.2.15`に接続できないと接続できないと怒られているようです。

どうも`kubeadm`は`join`コマンドを表示する際のIPアドレスを、自動で検出してくれるみたいなのですが、今回の場合IPアドレスが2つあり、MasterとNode間で通信できるように指定した固定IPアドレスを検出していなかった為に、Node側からMasterへの通信に失敗していたみたいです。
`10.0.2.15`はVirtualBoxのNAT用のIPアドレスでした。
Master側の`ifconfig`の実行結果は下記になります。

```
master# ifconfig
docker0   Link encap:Ethernet  HWaddr 02:42:40:e4:03:c2
          inet addr:172.17.0.1  Bcast:0.0.0.0  Mask:255.255.0.0
          UP BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

enp0s3    Link encap:Ethernet  HWaddr 02:3b:10:fe:82:8d
          inet addr:10.0.2.15  Bcast:10.0.2.255  Mask:255.255.255.0
          inet6 addr: fe80::3b:10ff:fefe:828d/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:255853 errors:0 dropped:0 overruns:0 frame:0
          TX packets:94600 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:366452042 (366.4 MB)  TX bytes:5874999 (5.8 MB)

enp0s8    Link encap:Ethernet  HWaddr 08:00:27:65:8e:49
          inet addr:192.168.33.10  Bcast:192.168.33.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe65:8e49/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:9 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:828 (828.0 B)  TX bytes:648 (648.0 B)

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:65536  Metric:1
          RX packets:66878 errors:0 dropped:0 overruns:0 frame:0
          TX packets:66878 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1
          RX bytes:31873809 (31.8 MB)  TX bytes:31873809 (31.8 MB)
```

# 解決方法
いろいろ調べてみところ同じような状況になっている人がいました。[kubernetes issue#33562](https://github.com/kubernetes/kubernetes/issues/33562)。
解決方法としては、複数IPアドレスを持っている場合は`kubeadm init`を実行する時に、オプションの`--api-advertise-addresses`を使用してIPアドレスを指定してあげればいいみたいです。

念のため仮想環境をもう一度作り直してから、`--api-advertise-addresses`を使用して再度`kubeadm init`を実行してみると、

```
master# kubeadm init --api-advertise-addresses=192.168.33.10
・・・
・・・
kubeadm join --token 123xyz.123456789abcdefg 192.168.33.10　
```
指定したIPアドレスが表示されました!

表示された`join`コマンドをNode側で実行した場合もエラーとならずに成功です。

```
node1# kubeadm join --token 123xyz.123456789abcdefg 192.168.33.10
<util/tokens> validating provided token
<node/discovery> created cluster info discovery client, requesting info from "http://192.168.33.10:9898/cluster-info/v1/?token-id=123xyz"
<node/discovery> cluster info object received, verifying signature using given token
<node/discovery> cluster info signature and contents are valid, will use API endpoints [https://192.168.33.10:443]
<node/csr> created API client to obtain unique certificate for this node, generating keys and certificate signing request
<node/csr> received signed certificate from the API server, generating kubelet configuration
<util/kubeconfig> created "/etc/kubernetes/kubelet.conf"

Node join complete:
* Certificate signing request sent to master and response
  received.
* Kubelet informed of new secure connection details.

Run 'kubectl get nodes' on the master to see this machine join.
```

Master側で確認してみると、ちゃんとNodeが追加されていることが確認できます。

```
master# kubectl get nodes
NAME      STATUS    AGE
master    Ready     3m
node1     Ready     18s
```

# 未解決の問題
上記エラーは解決しましたが、この環境ではまだ下記2点の問題を確認しています。

- Master側でkube-dnsのPodが起動されない（STATUSが「ContainerCreating」から進まない）
- Pod間通信にWeave Netを使用した際に、Weave NetのPodの1つが「CrashLoopBackOff」になる

設定の問題か`kubeadm`の問題なのかは調査中ですが、GitHubを見るとVirtualBoxに関連した`kubeadm`のissueがいくつかあったので、`kubeadm`の問題の可能性も高そうな気がします。
まだ`kubeadm`はalpha版なので、まだまだこれからに期待というところでしょうか。