自宅の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)を構成して、異なるノードにデプロイされたポッドでも、お互いに疎通できるものを目指します。
ネットワークの説明
この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プラグインを利用して構成します。
参考にした資料
- Installing kubeadm https://kubernetes.io/docs/setup/independent/install-kubeadm/
- Using kubeadm to Create a Cluster https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/
- Troubleshooting kubeadm https://kubernetes.io/docs/setup/independent/troubleshooting-kubeadm/
- kubeadm init https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-init/
- kubeadm reset https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-reset/
- kubelet https://kubernetes.io/docs/reference/generated/kubelet/
- CoreOS flannel https://github.com/coreos/flannel
- Pod Overview https://kubernetes.io/docs/concepts/workloads/pods/pod-overview/
- Services https://kubernetes.io/docs/concepts/services-networking/service/
- Customizing DNS Service https://kubernetes.io/docs/tasks/administer-cluster/dns-custom-nameservers/
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行目以降 起動した仮想サーバーのセットアップのシェルで、初回の起動時に一回だけ実行されます。 このセットアップは、このリストの後に、詳しく開設したいと思います。
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のセットアップから抜粋してきたコマンドも、このファイルに書いておき、設定のスピードアップを図ります。
- 32行目 ポッドネットワークの構成に資料する flannel が動作するための設定です。 https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt によれば、デフォルト値が1なので、設定不要かもしれませんが、意図的に設定を入れておきます。
- 40〜43行目 これは https://kubernetes.io/docs/setup/independent/install-kubeadm/#installing-docker に書かれた Docker-CEをインストールするために、リポジトリを追加するものです。
- 46〜49行目 Kubernetesのリポジトリを追加します。 これは、https://kubernetes.io/docs/setup/independent/install-kubeadm/#installing-kubeadm-kubelet-and-kubectl に記載されています。
- 52行目 Docker-CEをインストールします。 Kubernetesの最新版が確認しているDockerのバージョンは17.03 ですから、指定してインストールします。
- 58行目 Kubernetesのインストールです。 バージョンを指定したい場合は、59行目の様にパッケージ名の後にバージョンを指定します。
リポジトリに登録されたバージョンを確認するには、次のコマンドを実行します。
# 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/ にあります。
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 へ配置されたポッドとはアクセスできない結果となります。
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
が一致するラベルを持ったデプロイメントに対応します。
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のノードからそれぞれ返ってくる事を確認します。 もし、何度かに一回アクセスが失敗する様な事があれば、ポッドネットワークの疎通に問題があるかもしれません。その時は、前章の各ポッドのアクセスを確認します。
/ # 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によって、均一にアクセスが分散されます。
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番ポートへ中継します。
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, ストレージインタフェース、ロードバランサーインタフェースなど、運用するために必要な全てが、必要最小限の操作で、自動的に構成されるので、改めて便利さが良くわかりました。