Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
56
Help us understand the problem. What is going on with this article?
@MahoTakara

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, ストレージインタフェース、ロードバランサーインタフェースなど、運用するために必要な全てが、必要最小限の操作で、自動的に構成されるので、改めて便利さが良くわかりました。

56
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
MahoTakara
Docker/Kuberneresの学習本を書きました。15ステップあるのですが、1ステップ完結型なので好きな所から学習できます。https://amzn.to/2mgCRya

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
56
Help us understand the problem. What is going on with this article?