KVMでおうちKubernetes


ぷりぷりあぷりけーしょんずインフラ担当による ぷりぷりあぷりけーしょんず Advent Calendar 2019 の3日目

1.目標

これを作ります。

  • Master1台、Worker2台
  • CNIにはCalicoを利用してPod間通信
  • NginxPodをデプロイしてPod間通信を検証

2.環境

ホストOS

  • メモリ:16G
  • OS:Ubuntu18.04.2
  • KVM:
$ virsh version
Compiled against library: libvirt 4.0.0
Using library: libvirt 4.0.0
Using API: QEMU 4.0.0
Running hypervisor: QEMU 2.11.1

ゲストOS(Master,Worker01,Worker02)

  • メモリ:4G
  • OS:Ubuntu18.04.2
  • Kubernetes:1.15.3

3.仮想マシンの作成

3.1.必要パッケージのインストール

$ sudo apt install qemu-kvm libvirt-bin virtinst bridge-utils libosinfo-bin libguestfs-tools virt-top

3.2.仮想マシンの作成

3.2.1.インスタンスイメージの作成

sudo virt-install \
    --name master \
    --ram 4096 \
    --vcpus 2 \
    --disk path=/var/lib/libvirt/images/master.img,size=20,format=qcow2 \
    --os-type linux \
    --os-variant ubuntu18.04 \
    --network bridge=virbr0 \
    --graphics none \
    --console pty,target_type=serial \
    --location 'http://jp.archive.ubuntu.com/ubuntu/dists/bionic/main/installer-amd64/' \
    --extra-args 'console=ttyS0,115200n8 serial'

3.2.2.仮想マシンのコンソールへアクセスする設定

最終的には仮想マシンへSSHで接続しますが、SSHやNWの基本設定の操作をするために下記の手順を踏みます。

$ sudo virsh shutdown master

$ sudo guestmount -d master -i /mnt

$ sudo ln -s /mnt/lib/systemd/[email protected] /mnt/etc/systemd/system/getty.target.wants/[email protected]

$ sudo umount /mnt

3.2.3.仮想マシンの諸設定

  • 仮想マシンのIPアドレスを手動で設定します

    ホストOSで以下を実行

    $ sudo virsh start master --console

    ゲストOSへログイン後以下を実行

    $ sudo su -

    # vi /etc/netplan/01-netcfg.yaml

    # This file describes the network interfaces available on your system
    # For more information, see netplan(5).
    network:
    version: 2
    renderer: networkd
    ethernets:
        ens2:
        dhcp4: false
        addresses:
        - 192.168.122.2/24
        gateway4: 192.168.122.1
        nameservers:
            addresses:
            - 192.168.122.1
    

    # netplan apply

  • Kubernetesを利用可能にする

    # swapoff -a

    # vi /etc/fstab

    ## 以下の行をコメントアウト
    /swapfile                                 none            swap    sw              0       0
    

    # vi /etc/hosts

    # 作成した仮想マシンの情報を追記
    # IPアドレスは各自読み替えてください
    192.168.122.2 master
    192.168.122.3 node01
    192.168.122.4 node02
    

    # echo net.bridge.bridge-nf-call-iptables = 1 >> /etc/sysctl.conf

    # echo net.bridge.bridge-nf-call-ip6tables = 1 >> /etc/sysctl.conf

    # echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf

またこの際にホストOSから仮想マシンへSSHするユーザを作成したり、SSHのための公開鍵のコピーを行なってください。

3.2.4.仮想マシンへDocker,Kubernetesのインストール

上記に引き続き仮想マシンへログインした状態で作業してください。

公式サイトにある手順を実施していますが、バージョンを明示するため冗長ですが手順を示します。

  • Docker

    # apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common

    # curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

    # apt-key fingerprint 0EBFCD88

    # add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

    # apt-get update

    # apt-get install docker-ce=5:18.09.9~3-0~ubuntu-bionic docker-ce-cli=5:18.09.9~3-0~ubuntu-bionic containerd.io

  • Kubernetes

    # apt-get update && sudo apt-get install -y apt-transport-https curl

    # curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -

    # echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee /etc/apt/sources.list.d/kubernetes.list

    # apt-get update

    # apt-get install -y kubelet kubeadm kubectl

    # apt-mark hold kubelet kubeadm kubectl

3.3.仮想マシンを複製

ここまでの手順で作成したMaster用仮想マシンを雛形として、Worker用の仮想マシンを作成します。

$ sudo virsh shutdown master

$ sudo virt-clone --original master --name node01 --file /var/lib/libvirt/images/node01.img

これでWorker用仮想マシンが作成されたので3.2.3.のIPアドレスの手動設定だけして、仮想マシンの作成は終了です。

4.Kubernetesクラスタ作成

Masterノード

Master用仮想マシンへログインし、以下のコマンドを実行します。

--pod-network-cidrオプションはPodの所属するネットワークの指定に利用します。Calicoのデフォルトが192.168.0.0/16でKVMのネットワークとバッティングするため10.x.x.x/16で設定すると良いでしょう。

$ sudo kubeadm init --apiserver-advertise-address=192.168.122.2 --pod-network-cidr=10.64.0.0/16

(前略)
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/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.122.2:6443 --token 0bno9n.2h84n6b0nhl71iyy \
    --discovery-token-ca-cert-hash sha256:5fc061867c95d29841675ec575af7787a84316167a5b92e73b7f7a483579c100 

コマンドの結果にある、mkdir~chownまでのコマンドを実行することでkubectlによるKubernetesの操作が可能になります。

Workerノード

Masterノードで実行したコマンドの結果の最終行にあるコマンドを各Workerノードで実行します。

$ sudo kubeadm join 192.168.122.2:6443 --token 0bno9n.2h84n6b0nhl71iyy --discovery-token-ca-cert-hash sha256:5fc061867c95d29841675ec575af7787a84316167a5b92e73b7f7a483579c100

クラスタの確認

Masterノードで下記のコマンドを実行してNodeの状態を確認します。

$ kubectl get nodes

NAME     STATUS     ROLES    AGE    VERSION
master   NotReady   master   101s   v1.15.3
node01   NotReady   <none>   21s    v1.15.3
node02   NotReady   <none>   5s     v1.15.3

CNIを導入していないのでNodeのStatusがNotReadyですが、kubectlでNodeの情報を取得できていることがわかります。

5.Calicoのインストール

Calicoとは

DockerでデプロイしたコンテナはそのDockerのあるホスト上でのみ通信可能です。(緑矢印)

複数のホスト上でDockerを構築した場合、コンテナが外方向で通信可能な範囲は他のホストのインターフェイスまでになります。(青矢印)

ネットワークの異なる他のホストのコンテナへはアクセスできません。(紫矢印)

Calicoを利用することで、Kubernetesクラスタ作成時に指定したpod-network-cidrのネットワーク内でコンテナ(Pod)間の通信が可能になります。

コンテナのIPアドレスの共有はetcdとBGPを利用します。

etcdのインストール

Calicoの設定情報やIPや各コンテナのIPを保存するためのetcdPodをデプロイします。

$ wget https://docs.projectcalico.org/v3.5/getting-started/kubernetes/installation/hosted/etcd.yaml

$ kubectl apply -f etcd.yaml

Calicoのインストール

$ wget https://docs.projectcalico.org/v3.5/getting-started/kubernetes/installation/hosted/calico.yaml

$ vi calico.yaml

IPIPモードをOFFに、Calicoの利用するネットワークの設定にpod-network-cidrで指定した値を書き込みます。

217             - name: CALICO_IPV4POOL_IPIP
218               value: "off"

225             # The default IPv4 pool to create on startup if none exists. Pod IPs will be           
226             # chosen from this range. Changing this value after installation will have
227             # no effect. This should fall within `--cluster-cidr`.
228             - name: CALICO_IPV4POOL_CIDR
229               value: "10.64.0.0/16"

$ kubectl apply -f calico.yaml

configmap/calico-config created
secret/calico-etcd-secrets created
daemonset.extensions/calico-node created
serviceaccount/calico-node created
deployment.extensions/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created

$ kubectl get pods -A

Calicoのコンポーネントがデプロイされていることがわかります。

またCalicoをデプロイするまではステータスがPendingであった、corednsPodもRunningになっています。

NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-etcd-4m6k2                          1/1     Running   0          73s
kube-system   calico-kube-controllers-5d57bd4b96-4bj8s   1/1     Running   1          100s
kube-system   calico-node-jmsmd                          1/1     Running   2          100s
kube-system   calico-node-mhlnk                          1/1     Running   1          100s
kube-system   calico-node-v9ll2                          1/1     Running   1          100s
kube-system   coredns-5c98db65d4-j2h6b                   1/1     Running   0          39m
kube-system   coredns-5c98db65d4-x2shs                   1/1     Running   0          39m
kube-system   etcd-master                                1/1     Running   0          38m
kube-system   kube-apiserver-master                      1/1     Running   0          38m
kube-system   kube-controller-manager-master             1/1     Running   0          38m
kube-system   kube-proxy-2xggm                           1/1     Running   0          38m
kube-system   kube-proxy-j9j9m                           1/1     Running   0          38m
kube-system   kube-proxy-p6xnw                           1/1     Running   0          39m
kube-system   kube-scheduler-master                      1/1     Running   0          38m

6.Pod間通信の確認

冒頭にある通り、NginxPodを利用してPod間通信の確認をしていきます。

Nginxのデプロイ

$ kubectl run my-nginx --image=nginx --replicas=2 --port=80

kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/my-nginx created

$ kubectl get pods -o wide

Worker01,Worker02のそれぞれにNginxがデプロイされています。

Podの持つIPアドレスがCalicoで指定したネットワークになっていることも確認できます。

NAME                        READY   STATUS    RESTARTS   AGE   IP              NODE     NOMINATED NODE   READINESS GATES
my-nginx-756fb87568-f9bvd   1/1     Running   0          17h   10.64.196.130   node01   <none>           <none>
my-nginx-756fb87568-gpg99   1/1     Running   0          17h   10.64.140.64    node02   <none>           <none>

疎通確認

  • curl用Podのデプロイ

    $ kubectl run curl --image=radial/busyboxplus:curl -i --tty --rm

    別ターミナルでMasterノードへログイン後$ kubectl get podsを実行するとcurl用Podがデプロイされたことがわかります。

    今回はWorker02にデプロイされました。

    NAME                        READY   STATUS    RESTARTS     AGE   IP              NODE     NOMINATED NODE   READINESS GATES
    curl-6bf6db5c4f-vfwfd       1/1     Running   0          47s   10.64.140.65    node02   <none>           <none>
    my-nginx-756fb87568-f9bvd   1/1     Running   0          17h   10.64.196.130   node01   <none>           <none>
    my-nginx-756fb87568-gpg99   1/1     Running   0          17h   10.64.140.64    node02   <none>           <none>
    
  • 疎通確認

    [ root@curl-6bf6db5c4f-vfwfd:/ ]$ curl -I 10.64.196.130

    HTTP/1.1 200 OK
    Server: nginx/1.17.6
    Date: Sun, 24 Nov 2019 07:38:15 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
    Connection: keep-alive
    ETag: "5dd3e500-264"
    Accept-Ranges: bytes
    

    [ root@curl-6bf6db5c4f-vfwfd:/ ]$ curl -I 10.64.140.64

    HTTP/1.1 200 OK
    Server: nginx/1.17.6
    Date: Sun, 24 Nov 2019 07:42:30 GMT
    Content-Type: text/html
    Content-Length: 612
    Last-Modified: Tue, 19 Nov 2019 12:50:08 GMT
    Connection: keep-alive
    ETag: "5dd3e500-264"
    Accept-Ranges: bytes
    

    同一ホスト(Worker02)のPodはもちろん、異なるホスト(Worker01)への疎通についても確認できました。

Calicoの確認

Calicoの説明箇所でetcdBGPを利用していると書きましたが、それの確認をしてみます。

etcdctlのインストール

  • calico-etcdのバージョン確認

    $ curl http://10.96.232.136:6666/version | jq .

    % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                    Dload  Upload   Total   Spent    Left  Speed
    100    44  100    44    0     0  11000      0 --:--:-- --:--:-- --:--:-- 14666
    {
    "etcdserver": "3.3.9",
    "etcdcluster": "3.3.0"
    }
    
  • etcdctlのインストール

    $ ETCD_VER=v3.3.9

    $ GOOGLE_URL=https://storage.googleapis.com/etcd

    $ DOWNLOAD_URL=${GOOGLE_URL}

    $ curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o etcd-${ETCD_VER}-linux-amd64.tar.gz

    $ tar zxvf etcd-v3.3.9-linux-amd64.tar.gz

    $ sudo mv etcd-v3.3.9-linux-amd64/etcdctl /usr/local/bin/

  • etcdctlのセットアップ

    calico-etcdがVersion3を利用しているため、etcdctlでもVersion3を利用するように設定します。

    --endpointsオプションには先ほどデプロイしたetcd.yamlにあるClusterIPを指定します。

    $ alias etcd='ETCDCTL_API=3 etcdctl --endpoints=10.96.232.136:6666'

    $ etcd version

    etcdctl version: 3.3.9
    API version: 3.3
    

calico-etcdの確認

下記のコマンドでetcdに格納されているキーの一覧を取得可能です。

$ etcd get / --prefix --keys-only

先ほどデプロイしたNginxPodの情報についても格納されていることがわかります(JSONは整形してあります)。

$ etcd get /calico/resources/v3/projectcalico.org/workloadendpoints/default/node01-k8s-my--nginx--756fb87568--f9bvd-eth0

{
  "kind": "WorkloadEndpoint",
  "apiVersion": "projectcalico.org/v3",
  "metadata": {
    "name": "node01-k8s-my--nginx--756fb87568--f9bvd-eth0",
    "generateName": "my-nginx-756fb87568-",
    "namespace": "default",
    "uid": "e2b350ab-0df7-11ea-a8fd-525400f19390",
    "creationTimestamp": "2019-11-23T13:48:09Z",
    "labels": {
      "pod-template-hash": "756fb87568",
      "projectcalico.org/namespace": "default",
      "projectcalico.org/orchestrator": "k8s",
      "projectcalico.org/serviceaccount": "default",
      "run": "my-nginx"
    }
  },
  "spec": {
    "orchestrator": "k8s",
    "node": "node01",
    "containerID": "0baf28fb5c448b6850091691ad79c713f665e4915862c4d7713304414f02c210",
    "pod": "my-nginx-756fb87568-f9bvd",
    "endpoint": "eth0",
    "ipNetworks": [
      "10.64.196.130/32"
    ],
    "profiles": [
      "kns.default",
      "ksa.default.default"
    ],
    "interfaceName": "cali4ecc9d1d445",
    "mac": "ae:61:8a:0a:7b:4d"
  }
}

Calicoについてはまだまだ理解が浅いので断言はできませんが、これらの情報を利用してPod間の通信を可能にしているものと思われます。(たぶんね)

7.まとめ

ここまでで構築した環境があれば、Kubernetes上に適当なアプリケーションをデプロイするもサービスメッシュを実現するも、なんでも可能になります!

さらにこの記事で構築した環境については12/11or19のIstio構築に続きます!

参考URL