Edited at

Kubernetes v1.10 クラスタをVagrantで構築したメモ

More than 1 year has passed since last update.

自宅のMac上のVagrant環境で、K8s最新版 v1.10(2018年4月15日現在)をマスターノード1台とワーカーノード2台で構築したメモです。

macOSやWindows10の両方で動作する様に、自宅環境と会社のネット環境でも利用できる様に、改良を加えました。


k8s v1.10 システム構成

MacやWindwosの上で動かすと言えば、minikube?と思われのですが、そうではなく、本格的なK8sクラスタの構成にチャレンジします。 環境を作るためのハードウェアのスペック、ソフトウェアの名前とバージョン、そして、ネットワークについて、以下に書いておきます。


主な実行環境のスペックとバージョン


  • パソコン環境


    • MacOS: macOS High Sierra 10.13.4 RAM 16GB, 3.4GHz Intel Core i5

    • Windwos10 RAM 16GB, 3.4GHz Intel Core i5



  • Vagrant: 2.0.3

  • VirutalBox: 5.1.10

  • box: ubuntu/xenial64 (virtualbox, 20180413.0.0)

  • Kubernetes: 1.10 amd64


システム構成

Vagrant上の作るK8sのクラスタ環境は、マスターノード1台、ワーカーノード2台の構成で、全ノード横断でポッドネットワーク(10.244.0.0/16)を構成して、異なるノードにデプロイされたポッドでも、お互いに疎通できるものを目指します。

スクリーンショット 2018-05-14 22.43.29.png


ネットワークの説明

このK8sクラスタでは、3種類のネットワークがあり、以下に、それぞれの役割と概要を書きます。



  • Public Network(192.168.1.0/24)は、自宅のブロードバンドルータが接続されるプライベートネットワークで、NATによってインターネットへアクセスできます。Vagrantの各ノードは個々にIPアドレスを与えて、ブリッジ接続で、自宅プライベートネットワークに接続します。 ノードにアドレスを付与する事で、同じプライベートネットワークのPCなどからアクセスできる様にして、NodePortで開いたサービスを利用できる様に設定します。 Public Network上のIPアドレスの付与は、企業内LANでの利用を想定して廃止しました。 家庭内のLANで利用する場合には、コメントを外せば利用できる様にしてあります。


  • Private Network(172.42.42.0/24)は、ノード間の通信だけに利用するネットワークです。これは、ポッドネットワークの仮想回線の通信経路として利用します。

  • Private Network(172.16.20.0/24)は、ホストオンリー(パソコン内部のみ)の通信に利用できるネットワークです。ポッドネットワークの仮想回線の通信経路としても利用します。

  • NAT 10.0.2.15 は、それぞれのノード(仮想サーバー)に対して、vagrant ssh でホストからアクセスするための経路になります。

  • pod-network (10.244.0.0/16) ノード横断で、ポッド同士を繋ぐネットワークです。今回は、flannel というCNIプラグインを利用して構成します。


参考にした資料


Vagrantfileの説明

3台の仮想サーバーと各ネットワークは、次のVagrantfileを使って作成します。 このVagrantfileは、https://github.com/takara9/vagrant-k8s/blob/master/Vagrantfile に置いてありますので、ご自由にご利用ください。


  • 3行目 このVagrantfileを作る際に参考したURLです。 原型を殆ど残さないくらい、変わってしまいました。

  • 8行目以降 3回のループで3台の仮想サーバーを起動します。 10行目でOSを指定で、https://app.vagrantup.com/ubuntu/boxes/xenial64 を参照すれば、Ubuntu16.04であることが解ります。 public_ipとprivate_ipの開始アドレスからループ回数を加えてアドレスをアサインします。 混乱しそうですが、13行目パブリックIPは、自宅のブロードバンドルータの繋がったプライベートIPアドレスです。そして、 16行目のprivate_ipは、仮想サーバー間で利用するネットワークです。

  • 5行目〜70行目まで、ループで3台の仮想サーバーを起動します。 8行目でOSを指定は、Ubuntu16.04です。次のURL https://app.vagrantup.com/ubuntu/boxes/xenial64 にバージョンが記載されています。18行目がホストオンリーのネットワークです。

  • 21行目 ホスト名 node-1〜node-3をそれぞれ付与します。マスターにRAM 2GB、ワーカーノードにRAM 1GBを与えました。 もう少し少なくても動作すると思いますが、Macのホスト環境が許す限り、大きな値を与えると良いと思います。

  • 32行目以降 起動した仮想サーバーのセットアップのシェルで、初回の起動時に一回だけ実行されます。 このセットアップは、このリストの後に、詳しく開設したいと思います。


Vagrantfile

     1  # -*- mode: ruby -*-

2 # # vi: set ft=ruby :
3 #
4
5 Vagrant.configure(2) do |config|
6 (1..3).each do |i|
7 config.vm.define "node-#{i}" do |s|
8 s.vm.box = "ubuntu/xenial64"
9 s.vm.hostname = "node-#{i}"
10
11 #public_ip = "192.168.1.#{i+90}"
12 #s.vm.network :public_network, ip: public_ip, bridge: "en0: Ethernet"
13 #if i == 1 then
14 # s.vm.network :forwarded_port, host: 8001, guest: 8001
15 #end
16
17 private_ip = "172.16.20.#{i+10}"
18 s.vm.network "private_network", ip: private_ip
19
20 s.vm.provider "virtualbox" do |v|
21 v.cpus = 1
22 v.gui = false
23 if i == 1 then
24 v.memory = 2048
25 else
26 v.memory = 1024
27 #v.memory = 2048
28 end
29 end
30
31 s.vm.provision "shell", inline: <<-EOF
32 echo net.bridge.bridge-nf-call-iptables = 1 >> /etc/sysctl.conf
33 sysctl -p
34
35 #
36 apt-get update
37 apt-get install -y apt-transport-https ca-certificates curl software-properties-common
38
39 #
40 # add repo Docker-CE
41 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | apt-key add -
42 add-apt-repository "deb https://download.docker.com/linux/$(. /etc/os-release; echo "$ID") $(lsb_release -cs) stable"
43 curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
44
45 #
46 # add repo Kubernetes
47 cat <<EOF2 >/etc/apt/sources.list.d/kubernetes.list
48 deb http://apt.kubernetes.io/ kubernetes-xenial main
49 EOF2
50
51 #
52 # install Docker-CE
53 apt-get update
54 apt-get install -y docker-ce=$(apt-cache madison docker-ce | grep 17.03 | head -1 | awk '{print $3}')
55 usermod -aG docker vagrant
56
57 #
58 # install Kubernetes
59 #apt-get install -y kubelet=1.9.6-00 kubeadm=1.9.6-00 kubectl=1.9.6-00
60 apt-get install -y kubelet kubeadm kubectl
61 #
62 EOF
63 end
64 end
65
66 if Vagrant.has_plugin?("vagrant-cachier")
67 config.cache.scope = :box
68 end
69
70 end



セットアップ・シェル部分の解説

何度も実行する様な事は、Vagrantfileのs.vm.provision "shell"に書いて置くと便利です。 Kubernetesのセットアップから抜粋してきたコマンドも、このファイルに書いておき、設定のスピードアップを図ります。

リポジトリに登録されたバージョンを確認するには、次のコマンドを実行します。

# apt-cache madison kubeadm

kubeadm | 1.10.0-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.6-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.5-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.4-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.3-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.2-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.1-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.9.0-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
省略
kubeadm | 1.6.4-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.6.3-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.6.2-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.6.1-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.6.0-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.5.7-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages
kubeadm | 1.5.6-00 | http://apt.kubernetes.io/ kubernetes-xenial/main armhf Packages

このVagrantファイルは、次の様に利用します。

次のコマンドで、GitHubからローカルにクローン(複製)を作ります。

$ git clone https://github.com/takara9/vagrant-k8s

次にディレクトリを移動して、vagrant up とするだけで、仮想サーバーが3台、Docker-CE v17.03 と Kubernetes v1.10が導入された状態で起動します。 Vagrantは本当に便利ですよね。 

$ cd vagrant-k8s

$ vagrant up


仮想マシンの起動と個別設定

もし、vagrant box listして、該当の仮想マシン(Box)のテンプレートが、ローカル環境になければ、 次のコマンでダウンロードします。

$ vagrant box add ubuntu/xenial64

git clone https://github.com/takara9/vegrant-k8s で作成されたディレクトリで、次のコマンドで、仮想サーバーを起動します。

maho$ vagrant up

Bringing machine 'node-1' up with 'virtualbox' provider...
Bringing machine 'node-2' up with 'virtualbox' provider...
Bringing machine 'node-3' up with 'virtualbox' provider...
==> node-1: Importing base box 'ubuntu/xenial64'...
==> node-1: Matching MAC address for NAT networking...
==> node-1: Checking if box 'ubuntu/xenial64' is up to date...
==> node-1: Setting the name of the VM: node-1
==> node-1: Clearing any previously set network interfaces...
==> node-1: Preparing network interfaces based on configuration...
node-1: Adapter 1: nat
node-1: Adapter 2: bridged
node-1: Adapter 3: intnet
==> node-1: Forwarding ports...
node-1: 22 (guest) => 2222 (host) (adapter 1)

仮想マシンが3台起動するので、node-1にログインして、マスターノードになる様に設定します。


マスターノード node-1の個別設定

node-1仮想マシンへログインするには、sshの後にホスト名を付与します。

$ vagrant ssh node-1

ノードの起動IPを設定ます。

vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

次のEnvironment="KUBELET_DNS_ARGS=から始まる行に、dnsのIPアドレス、--node-ipを追加します。 この仮想サーバーのIPアドレスですが、--node-ip=172.16.20.11を設定しないと、vagrantがホストと接続する際に利用する 10.0.2.15が最初のインターフェースなので、 Kubernetesのデーモンが、このアドレスを掴んでしまい、正しく動作しません。 この10.0.2.15は、各仮想サーバーに同じアドレスで存在し、仮想サーバー間の通信のために利用できないためです。

Environment="KUBELET_DNS_ARGS=--cluster-dns=10.244.0.10 --cluster-domain=cluster.local --node-ip=172.16.20.11"

この変更を反映させるために、以下のコマンドを実行します。

systemctl daemon-reload

systemctl restart kubelet


マスターノードのセットアップ実行

Using kubeadm to Create a Cluster https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/#14-installing-kubeadm-on-your-hosts に従って、セットアップを進めるのですが、簡単そうで、上手く行かないので、補足します。

ノード横断で、ポッド・ネットワークを構成するには、Calico, Canal, Fannelが良く利用される様ですが、今回は、一番実績が多いとされるFannelを利用します。 Fannelにはネットワークアドレス(10.244.0.0/16)が、ハードコーディングされていて、それ以外を指定すると、動作しないので、--pod-network-cidr=10.244.0.0/16--service-cidr=10.244.0.0/16は、変更してはいけません。 それから、--apiserver-advertise-address=172.16.20.11は、マスターノードのprivateアドレスを指定しておきます。 これはマスターノードのIPアドレスであれば、良いのでパブリック側からアクセスしたければ、そちらのアドレスを設定します。

このコマンドは、ルートで実行します。

# kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=172.16.20.11 --service-cidr=10.244.0.0/16

実行結果のアウトプットを以下に添付します。 このアウトプットには、kubectlの環境設定、kubeadm joinのコマンドが表示されているので、メモ帳などにコピペして、保管しておきます。

root@node-1:~# kubeadm init --pod-network-cidr=10.244.0.0/16 --apiserver-advertise-address=172.16.20.11 --service-cidr=10.244.0.0/16

[init] Using Kubernetes version: v1.10.1
[init] Using Authorization modes: [Node RBAC]
[preflight] Running pre-flight checks.
[WARNING FileExisting-crictl]: crictl not found in system path
Suggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl
[certificates] Generated ca certificate and key.
[certificates] Generated apiserver certificate and key.
[certificates] apiserver serving cert is signed for DNS names [node-1 kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.244.0.1 172.16.20.11]
[certificates] Generated apiserver-kubelet-client certificate and key.
[certificates] Generated etcd/ca certificate and key.
[certificates] Generated etcd/server certificate and key.
[certificates] etcd/server serving cert is signed for DNS names [localhost] and IPs [127.0.0.1]
[certificates] Generated etcd/peer certificate and key.
[certificates] etcd/peer serving cert is signed for DNS names [node-1] and IPs [172.16.20.11]
[certificates] Generated etcd/healthcheck-client certificate and key.
[certificates] Generated apiserver-etcd-client certificate and key.
[certificates] Generated sa key and public key.
[certificates] Generated front-proxy-ca certificate and key.
[certificates] Generated front-proxy-client certificate and key.
[certificates] Valid certificates and keys now exist in "/etc/kubernetes/pki"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/admin.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/kubelet.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/controller-manager.conf"
[kubeconfig] Wrote KubeConfig file to disk: "/etc/kubernetes/scheduler.conf"
[controlplane] Wrote Static Pod manifest for component kube-apiserver to "/etc/kubernetes/manifests/kube-apiserver.yaml"
[controlplane] Wrote Static Pod manifest for component kube-controller-manager to "/etc/kubernetes/manifests/kube-controller-manager.yaml"
[controlplane] Wrote Static Pod manifest for component kube-scheduler to "/etc/kubernetes/manifests/kube-scheduler.yaml"
[etcd] Wrote Static Pod manifest for a local etcd instance to "/etc/kubernetes/manifests/etcd.yaml"
[init] Waiting for the kubelet to boot up the control plane as Static Pods from directory "/etc/kubernetes/manifests".
[init] This might take a minute or longer if the control plane images have to be pulled.
[apiclient] All control plane components are healthy after 50.002958 seconds
[uploadconfig] Storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[markmaster] Will mark node node-1 as master by adding a label and a taint
[markmaster] Master node-1 tainted and labelled with key/value: node-role.kubernetes.io/master=""
[bootstraptoken] Using token: c5b771.jeqtelo0ux27upvk
[bootstraptoken] Configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] Configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] Configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] Creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: kube-dns
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
https://kubernetes.io/docs/concepts/cluster-administration/addons/

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

kubeadm join 172.16.20.11:6443 --token c5b771.jeqtelo0ux27upvk --discovery-token-ca-cert-hash sha256:f1fa8b0f197792f7b7a97d2798a262573a7d058959aa7aff42e65d3b2a679450


kubectlの環境設定

上記のkubeadm initのアウトプットを参考にしながら、vagrantユーザーで以下を実行します。

mkdir -p $HOME/.kube

sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

vagrant ssh node-1でログインして、上記コマンドを実行後に、kubectl get nodeでマスターノードが NotReadyの状態で表示されたら完了です。

imac:k8s-cluster maho$ vagrant ssh node-1

省略
vagrant@node-1:~$ mkdir -p $HOME/.kube
vagrant@node-1:~$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
vagrant@node-1:~$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
vagrant@node-1:~$ kubectl get node
NAME STATUS ROLES AGE VERSION
node-1 NotReady master 8m v1.10.1


ポッド・ネットワークの設定

Flannelを利用してポッドネットワークを設定するのですが、Vagrantの環境では一つの障害があります。 https://kubernetes.io/docs/setup/independent/troubleshooting-kubeadm/ の Default NIC When using flannel as the pod network in Vagrant に記載されいる通り、もし、設定を追加しなければ、Vagrantのホストからvagrant ssh でログインするためのNICが Flannelのインターフェースとして割り当てられてしまい、ポッドネットワークがノードで分断された状態になります。

そこで、次の順番で修正をしていきます。 まず、ローカルに設定ファイルをダウンロードします。

vagrant@node-1:~$ curl -O https://raw.githubusercontent.com/coreos/flannel/v0.9.1/Documentation/kube-flannel.yml

% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 2801 100 2801 0 0 7374 0 --:--:-- --:--:-- --:--:-- 7390

エディタでkube-flannel.ymlを編集します。 次の127行目の様に、"- --iface=enp0s8"を追加します。

   119        containers:

120 - name: kube-flannel
121 image: quay.io/coreos/flannel:v0.10.0-amd64
122 command:
123 - /opt/bin/flanneld
124 args:
125 - --ip-masq
126 - --kube-subnet-mgr
127 - --iface=enp0s8
128 resources:

インターフェースの名前の取得は、ifconfigコマンドで、172.16.20.0/24 は プライベートネットワーク ホストオンリーに接続されたインタフェースの名前になります。 この名前は、3台の仮想サーバーで同じインターフェース名である必要があります。

vagrant@node-1:~$ ifconfig

cni0 Link encap:Ethernet HWaddr 0a:58:0a:f4:00:01
inet addr:10.244.0.1 Bcast:0.0.0.0 Mask:255.255.255.0
inet6 addr: fe80::4c56:9dff:fe21:d857/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:139624 errors:0 dropped:0 overruns:0 frame:0
TX packets:172900 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:34478740 (34.4 MB) TX bytes:37214713 (37.2 MB)

docker0 Link encap:Ethernet HWaddr 02:42:31:8b:40:ff
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:d4:20:7b:c2:aa
inet addr:10.0.2.15 Bcast:10.0.2.255 Mask:255.255.255.0
inet6 addr: fe80::d4:20ff:fe7b:c2aa/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:180913 errors:0 dropped:0 overruns:0 frame:0
TX packets:85413 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:253978977 (253.9 MB) TX bytes:5476438 (5.4 MB)

enp0s8 Link encap:Ethernet HWaddr 08:00:27:b3:d2:58
inet addr:172.16.20.11 Bcast:172.16.20.255 Mask:255.255.255.0
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:253555 errors:0 dropped:0 overruns:0 frame:0
TX packets:349308 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:26329597 (26.3 MB) TX bytes:390833376 (390.8 MB)

これは、マスターノードに設定すると、後からジョインするワーカーノードへも自動的に反映されます。

vagrant@node-1:/vagrant/yaml$ kubectl apply -f kube-flannel.yml 

clusterrole.rbac.authorization.k8s.io "flannel" created
clusterrolebinding.rbac.authorization.k8s.io "flannel" created
serviceaccount "flannel" created
configmap "kube-flannel-cfg" created
daemonset.extensions "kube-flannel-ds" created

このコマンドが完了すると、マスターノードの状態が Readyに変わります。

vagrant@node-1:/vagrant/yaml$ kubectl get node

NAME STATUS ROLES AGE VERSION
node-1 Ready master 26m v1.10.1

以上で、マスターノードの設定が完了しました。 ここでK8sのネームスペース kube-system のデーモンが正しく プライベートネットワークのIPアドレスを掴んでいるかを確認しておきます。 マスターノードのIPアドレス 172.16.20.11 にバインドされていれば、正しく設定されています。もしも、10.0.2.15 にバインドされていれば、ポッドネットワークがノードで分断されることになりますので、確認しておきます。 もしも、意図通りでなければ、kubeadm restを実行して、kubeadm init... の前の個別設定のステップから再度見直します。

vagrant@node-1:/vagrant/yaml$ kubectl get po -o wide -n kube-system

NAME READY STATUS RESTARTS AGE IP NODE
etcd-node-1 1/1 Running 0 25m 172.16.20.11 node-1
kube-apiserver-node-1 1/1 Running 0 26m 172.16.20.11 node-1
kube-controller-manager-node-1 1/1 Running 0 26m 172.16.20.11 node-1
kube-dns-86f4d74b45-5dckd 3/3 Running 0 27m 10.244.0.2 node-1
kube-flannel-ds-d86bt 1/1 Running 0 2m 172.16.20.11 node-1
kube-proxy-pd9mz 1/1 Running 0 27m 172.16.20.11 node-1
kube-scheduler-node-1 1/1 Running 0 26m 172.16.20.11 node-1
vagrant@node-1:/vagrant/yaml$ kubectl get svc -o wide -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kube-dns ClusterIP 10.244.0.10 <none> 53/UDP,53/TCP 27m k8s-app=kube-dns


ワーカーノードの設定

次にワーカーノードの2つをマスターノードに接続して、クラスタを構成します。


ノードIPの設定

node-2 へ次の要領で、ログインします。 そして rootユーザーになって、ファイルを編集します。

vagrant ssh node-2

vagrant@node-2:~$ sudo -s

root@node-2:~# vi vi /etc/systemd/system/kubelet.service.d/10-kubeadm.conf

下記の行を編集して、

Environment="KUBELET_DNS_ARGS=--cluster-dns=10.96.0.10 --cluster-domain=cluster.local"

dnsのIPアドレスを10.244.0.10に修正、--node-ip=172.16.20.12 を追加します。 このIPアドレスは、ifconfig で確認して インタフェース enp0s8 の IPアドレスを設定します。

Environment="KUBELET_DNS_ARGS=--cluster-dns=10.244.0.10 --cluster-domain=cluster.local --node-ip=172.16.20.12"

編集が終わったら、次の二つのコマンドを実行して、kubeletの設定を有効にします。

systemctl daemon-reload

systemctl restart kubelet


k8sクラスタへのジョイン

rootユーザーで kubeadm join ... コマンドを実行して、クラスタへ参加させます。 このコマンドのパラメータは、kubeadm initの実行結果として、アウトプットされた表示をコピペして実行します。

root@node-2:~# kubeadm join 172.16.20.11:6443 --token c5b771.jeqtelo0ux27upvk --discovery-token-ca-cert-hash sha256:f1fa8b0f197792f7b7a97d2798a262573a7d058959aa7aff42e65d3b2a679450

[preflight] Running pre-flight checks.
[WARNING FileExisting-crictl]: crictl not found in system path
Suggestion: go get github.com/kubernetes-incubator/cri-tools/cmd/crictl
[discovery] Trying to connect to API Server "172.42.42.11:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://172.16.20.11:6443"
[discovery] Requesting info from "https://172.42.42.11:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "172.42.42.11:6443"
[discovery] Successfully established connection with API Server "172.16.20.11:6443"

This node has joined the cluster:
* Certificate signing request was sent to master and a response
was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

これでマスターノード側で kubectl get nodes を実行して、クラスタの状態を確認します。

vagrant@node-1:~$ kubectl get node

NAME STATUS ROLES AGE VERSION
node-1 Ready master 3h v1.10.1
node-2 Ready <none> 1m v1.10.1

今回追加したノードのROLESが設定されていないので、ROLESを設定します。 コマンドは、kubectl label node <ノード名> node-role.kubernetes.io/node=で、ノード名をホスト名で置き換えて実行します。

vagrant@node-1:~$ kubectl label node node-2 node-role.kubernetes.io/node=

node "node-2" labeled
vagrant@node-1:~$ kubectl get node
NAME STATUS ROLES AGE VERSION
node-1 Ready master 3h v1.10.1
node-2 Ready node 2m v1.10.1

同じ要領で、node-3もクラスタへジョインします。

vagrant@node-1:~$ kubectl get node

NAME STATUS ROLES AGE VERSION
node-1 Ready master 3h v1.10.1
node-2 Ready node 34m v1.10.1
node-3 Ready node 1m v1.10.1

クラスタが完成した処で、確認しておきます。

vagrant@node-1:~$ kubectl get po -o wide -n kube-system

NAME READY STATUS RESTARTS AGE IP NODE
etcd-node-1 1/1 Running 0 3h 172.16.20.11 node-1
kube-apiserver-node-1 1/1 Running 0 3h 172.16.20.11 node-1
kube-controller-manager-node-1 1/1 Running 0 3h 172.16.20.11 node-1
kube-dns-86f4d74b45-5dckd 3/3 Running 0 3h 10.244.0.2 node-1
kube-flannel-ds-5lsd4 1/1 Running 0 35m 172.16.20.12 node-2
kube-flannel-ds-d86bt 1/1 Running 0 3h 172.16.20.11 node-1
kube-flannel-ds-r4jmb 1/1 Running 0 2m 172.16.20.13 node-3
kube-proxy-7j4vm 1/1 Running 0 2m 172.16.20.13 node-3
kube-proxy-n82vs 1/1 Running 0 35m 172.16.20.12 node-2
kube-proxy-pd9mz 1/1 Running 0 3h 172.16.20.11 node-1
kube-scheduler-node-1 1/1 Running 0 3h 172.16.20.11 node-1



k8sクラスタの機能テスト


デプロイメントのテスト

nginxのウェブサーバーをデプロイするためのYAMLファイルを準備します。 ここではYAMLファイルの概説として、spec.replicas: 6によって、6個のウェブサーバーが起動します。 ウェブサーバーのコンテナは、spec.template.spec.containers.image:の値フィールド nginx:aplineとなり、このコンテナの詳細な説明は、Docker Hub https://hub.docker.com/_/nginx/ にあります。


deploy.yaml

apiVersion: extensions/v1beta1

kind: Deployment
metadata:
name: nginx-deployment
spec:
replicas: 6
template:
metadata:
labels:
app: nginx-web
spec:
containers:
- name: nginx-container
image: nginx:alpine
ports:
- containerPort: 80

前述のYAMLファイルを利用して、nginxのサーバーを起動します。

vagrant@node-1:/vagrant/yaml$ kubectl create -f deploy.yaml 

deployment.extensions "nginx-deployment" created


デプロイメントの結果確認

上記のコマンドが成功すれば、デプロイメント nginx-deployment の配下に、6個のポッドが作成されます。 この確認のために、次のコマンドを実行します。

vagrant@node-1:/vagrant/yaml$ kubectl get deployment

NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx-deployment 6 6 6 6 30s

次に、ワーカーノードに均等に分散配置されている事を確認します。 NODE列を見れば node-2, node-3へ 3個づつ配置されている事が確認できます。

vagrant@node-1:/vagrant/yaml$ kubectl get po -o wide

NAME READY STATUS RESTARTS AGE IP NODE
nginx-deployment-69878cbf8d-2xtqb 1/1 Running 0 34s 10.244.1.5 node-2
nginx-deployment-69878cbf8d-4rhwr 1/1 Running 0 34s 10.244.2.7 node-3
nginx-deployment-69878cbf8d-djv9k 1/1 Running 0 34s 10.244.2.6 node-3
nginx-deployment-69878cbf8d-m7flc 1/1 Running 0 34s 10.244.1.6 node-2
nginx-deployment-69878cbf8d-mkp6z 1/1 Running 0 34s 10.244.1.7 node-2
nginx-deployment-69878cbf8d-r7nsh 1/1 Running 0 34s 10.244.2.5 node-3


ポッドへのアクセステスト

これらのポッドは、ポッドネットワーク上に配置されており、k8sクラスタの外部からアクセスする事はできません。そこで、ポッドネットワーク上に対話型のポッドを配置して、それぞれのnginxのポッドへアクセスして、動作を確認します。


テスト用ポッドの起動

対話型として利用するコンテナは、busybox https://store.docker.com/images/busybox を利用します。コンテナサイズは、1.2MBと軽量で ping, wget, nslookupなどが同梱されており、アクセステストに適したコンテナです。

vagrant@node-1:/vagrant/yaml$ kubectl run bbox --image=busybox:latest -- tail -f /dev/null

deployment.apps "bbox" created

次のコマンドで、ポッドの名前を取得します。

vagrant@node-1:~$ kubectl get po -o wide

NAME READY STATUS RESTARTS AGE IP NODE
bbox-7ff8d695b4-mprbz 1/1 Running 0 12m 10.244.2.8 node-3


ポッドのコンテナへのログイン

コンテナ1個だけのポッドなので、次のポッド名を指定する事で、ログインする事ができます。 このコマンドのオプションは、kubeclt exec -it <ポッド名> <コマンド> の様になっています。

vagrant@node-1:~$ kubectl exec -it bbox-7ff8d695b4-mprbz sh

/ #


各ポッドへのアクセステスト

busyboxには、curlが入っていないので、wgetを利用します。 node-2とnode-3に配置されたポッドから各1個を選んでアクセステストを実施します。もしも、ポッドネットワークがノードを跨って構成できていなければ、このbusyboxのポッドが配置された node-3 のnginxポッドへはアクセスは出来るが、node-2 へ配置されたポッドとはアクセスできない結果となります。

スクリーンショット 2018-05-14 22.34.05.png


node-2 へ配置されたポッドへのアクセス

/ # wget -q -O - http://10.244.1.5/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
省略


node-3 へ配置されたポッドへのアクセス

/ # wget -q -O - http://10.244.2.5/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>

上記の結果から、ノードを跨るポッドネットワークの構成に成功している事が確認できました。


サービス ClusterIP

nginxのポッド群を代表するクラスタIPのサービスを設定して、アクセスのテストを実施します。 このサービスを定義するためのYAMLファイルは、次の様になります。 先にデプロイしたデプロイメントとの対応づけは、spec.selector: の値フィールド app: nginx-web が一致するラベルを持ったデプロイメントに対応します。


service_cluster_ip.yaml

apiVersion: v1

kind: Service
metadata:
name: nginx-cluster-ip-service
spec:
type: ClusterIP
selector:
app: nginx-web
ports:
- protocol: TCP
port: 80

次のコマンドでサービスを作成します。

vagrant@node-1:/vagrant/yaml$ kubectl create -f service_cluster_ip.yaml 

service "nginx-cluster-ip-service" created

デプロイの結果、10.244.220.233 にクラスタIPが作成され、セレクターが、app=nginx-webとなっています。

vagrant@node-1:/vagrant/yaml$ kubectl get svc -o wide

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
省略
nginx-cluster-ip-service ClusterIP 10.244.220.233 <none> 80/TCP 9s app=nginx-web


ClusterIP サービスへのアクセス

ClusterサービスIPは、Kube-Proxyによって、セレクターで連携するポッドに均等にアクセスが分散されます。 このクラスタIPをターゲットにして、アクセスして結果が返させる事を確認します。 連続してアクセスして、node-2,node-3のノードからそれぞれ返ってくる事を確認します。 もし、何度かに一回アクセスが失敗する様な事があれば、ポッドネットワークの疎通に問題があるかもしれません。その時は、前章の各ポッドのアクセスを確認します。

スクリーンショット 2018-04-15 22.28.54.png

/ # wget -q -O - http://10.244.220.233/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
省略

DNS名でのアクセスを試みましたが、現在の設定だけでは、サービス名がkube-dnsへ登録されておらず、アドレスを解決できませんでした。


サービス NodePort

k8sクラスタの外部からアクセスためにNodePortのサービスを定義して、アクセステストを実施します。 spec.type: NodePort を指定すると、各ノードのIPアドレスの spec.ports.nodeport で指定するポート番号に、サービスが開きます。 このnodePortは、Kube-Proxyによって、均一にアクセスが分散されます。


service_nodeport.yaml

apiVersion: v1

kind: Service
metadata:
name: nginx-nodeport-service
spec:
type: NodePort
selector:
app: nginx-web
ports:
- protocol: TCP
port: 80
nodePort: 31514

上記のYAMLファイルを適用すると、ワーカーノードのIPアドレスに nodePort: 31514 で指定される TCP 31514が開きます。内部に対しては Kube-Proxyで分散して 80番ポートへ中継します。

スクリーンショット 2018-05-14 22.37.39.png


NodePortサービスの作成

前述のYAMLファイルを適応して、NodePortサービスを作成します。

vagrant@node-1:/vagrant/yaml$ kubectl create -f service_nodeport.yaml 

service "nginx-nodeport-service" created
vagrant@node-1:/vagrant/yaml$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
省略
nginx-nodeport-service NodePort 10.244.60.160 <none> 80:31514/TCP 8s app=nginx-web


ノードIPからのアクセス

MacOSのターミナルから各ノードのIPアドレスとポート番号に向けて、curlでアクセスして結果を確認します。 ワーカーノードだけでなく、マスターノードにもkubeproxyが動作していますから、全てのノードで、Nginxのポッドへアクセスできる事が確認できました。


node-1 のNodePortへのアクセス

imac:~ maho$ curl http://172.16.20.11:31514/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>


node-2 のNodePortへのアクセス

imac:~ maho$ curl http://172.16.20.12:31514/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>


node-3 のNodePortへのアクセス

imac:~ maho$ curl http://172.16.20.13:31514/

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>


ポッドネットワークからのアクセス

NodePortを作成すると、クラスタIPも開設しますので、ポッドからアクセスを試してみました。

vagrant@node-1:/vagrant/yaml$ kubectl exec -it bbox-7ff8d695b4-mprbz sh

/ # wget -q -O - http://10.244.60.160/
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>


まとめ

Vagrantの仮想サーバー環境で、K8s v1.10が動作する事が確認できました。 パブリッククラウドのKubernetesでは kubeadmを利用して、内部構造に関わる部分を触って学ぶ事ができないですし、minikubeでは限定的になります。 そして、ラズパイを使っても、部品代や場所など、そこそこコストがかかりますから、無料でパソコンの仮想環境上で、クラスタが検証できるのは、本当に助かりますね。

パブリック・クラウドのサービスやK8sをコアにしたプライベート・クラウドのソフトウェア製品では、Kubernetesのコア部分だけでなく、コンテナ・リポジトリ、Grafana, Prometheus, ElasticSearch, Kibana, Helm, ストレージインタフェース、ロードバランサーインタフェースなど、運用するために必要な全てが、必要最小限の操作で、自動的に構成されるので、改めて便利さが良くわかりました。