VM2台にkubeadmでk8sクラスターを構築

表題の通り。
導入要件については以下のリンクを参照。
kubernetes.io

今回利用するVMのspecは2台とも以下。

CentOS 7.8
Disk: 50G
CPU: 4core
mem: 8G

必要なステップを以下に示す。
1. OS構成
2. 必要なコンポーネントの導入
3. masterノードの構成
4. workerノードの構成
5. podのデプロイ
6. Web UIの構成(optional)

1. OS構成

2台とも同一ネットワーク / VLAN上に存在している必要がある。

masterノード,workerノードでそれぞれユニークなhostnameを構成。
#master

$ hostnamectl set-hostname "k8s-master-1"

#worker

$ hostnamectl set-hostname "k8s-worker-1"

masterノード,workerノードでそれぞれユニークなMACアドレスが割り振られていることを確認。
以下のコマンドをそれぞれの環境で実行。

$ ip a

今回利用した環境ではDHCP等で自動的にIPが割り振られなかったためnmtuiコマンドでIPを構成。

masterノード,workerノードでそれぞれユニークなproduct_uuidが割り振られていることを確認。
以下のコマンドをそれぞれの環境で実行。

cat /sys/class/dmi/id/product_uuid

ブリッジ構成。ipv4,ipv6によるブリッジ通信の有効化。

cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system

参考:ブリッジ通信を制御するカーネルモジュールnetfilterがロードされていることは以下から確認できる。

$ lsmod |grep br_netfilter
br_netfilter           22256  0
bridge                151336  2 br_netfilter,ebtable_broute

port構成。以下リンクからmaster / workerでそれぞれ必要なポートを確認。
kubeadmのインストール | Kubernetes

必要に応じて構成。
今回の(私の)環境の場合は以下の通り新しくruleを追加構成した。
#master

$ iptables -I INPUT 1 -p tcp --dport 6443 -j ACCEPT
$ iptables -I INPUT 1 -p tcp --dport 2379:2380 -j ACCEPT
$ iptables -I INPUT 1 -p tcp --dport 10250:10252 -j ACCEPT

#worker

$ iptables -I INPUT 1 -p tcp --dport 10250 -j ACCEPT
$ iptables -I INPUT 1 -p tcp --dport 3000:32767 -j ACCEPT

余談:構築後にiptablesで設定した各ルール毎の該当したパケットカウントは以下。

#master

$ iptables -nvxL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination
    1215    75074 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:179
       0        0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10252
       0        0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10251
       0        0 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10250
 4566860 530689761 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpts:2379:2380
 3752632 451693115 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:6443

#worker

$ iptables -nvxL
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
    pkts      bytes target     prot opt in     out     source               destination
  556871 302247581 cali-INPUT  all  --  *      *       0.0.0.0/0            0.0.0.0/0            /* cali:Cz_u1IQiXIMmKD4c */
  137110 23345161 KUBE-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes service portals */
  137110 23345161 KUBE-EXTERNAL-SERVICES  all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate NEW /* kubernetes externally-visible serv
ice portals */
  530660 545070871 KUBE-FIREWALL  all  --  *      *       0.0.0.0/0            0.0.0.0/0
      19      836 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpts:30000:32767
     753    67427 ACCEPT     tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            tcp dpt:10250

master, workerともにSwapをoffにする。

$ swapoff -a

2. 必要なコンポーネントの導入

ランタイムをインストールする。今回はDockerを導入。手順は以下を参照。
kubernetes.io
#master, worker

$ yum install -y yum-utils device-mapper-persistent-data lvm2
$ yum-config-manager --add-repo \
  https://download.docker.com/linux/centos/docker-ce.repo
$ yum update -y && yum install -y \
  containerd.io-1.2.10 \
  docker-ce-19.03.4 \
  docker-ce-cli-19.03.4
$ mkdir /etc/docker
# すでにディレクトリが存在する場合は次のステップを実行
$ cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2",
  "storage-opts": [
    "overlay2.override_kernel_check=true"
  ]
}
EOF

$ systemctl daemon-reload
$ systemctl restart docker
$ systemctl status docker

kubeadm、kubelet、kubectlのインストール
#master, worker

cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF

# SELinuxをpermissiveモードに設定する(効果的に無効化する)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config

yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes

systemctl enable --now kubelet

3. masterノードの構成

#kubeadmでKubernetesクラスターのを構築を実施。初めにコントロールプレーンとなるmasterのみで構成し、続いてworkerをクラスターに追加する。
その前にmasterからコンテナイメージレジストリgcr.ioに繋がることを確認。
#master

kubeadm config images pull

コントロールプレーンを構成。
#master

kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=10.84.82.112

# podのネットワーク範囲について、Calicoは192.168.0.0/16をPodネットワークのCIDRを利用する。その場合、本コマンドの引数--pod-network-cidrにそのCIDRを指定する必要がある。
# デフォルトではデフォルトゲートウェイに関連するネットワークインターフェースを利用するが、念のため明記。

コマンドの実行が成功すると以下のような出力が表示される。

Your Kubernetes control-plane 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/

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

kubeadm join 10.84.82.112:6443 --token <token value> \
    --discovery-token-ca-cert-hash sha256:<hash value>
[root@k8s-master-1 ~]#

最終行当たりのkubeadm joinコマンドはクラスターに追加するworkerノードで実行するコマンドであるため、書き留めておく。
前段のmkdirからsudo chownコマンドまではmasterノードのkubectlコマンドを構成するために必要なコマンド。
#master

[root@k8s-master-1 ~]# mkdir -p $HOME/.kube
[root@k8s-master-1 ~]# cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@k8s-master-1 ~]# chown $(id -u):$(id -g) $HOME/.kube/config

# 他ユーザーがkubectlコマンドを利用する場合はそのユーザーのホームディレクトリ配下で上記同様にコマンド実行。

コマンドを実行して構成を確認。現在masterノードのみ。
# master

#node情報取得
[root@k8s-master-1 ~]# kubectl get nodes
NAME           STATUS     ROLES    AGE   VERSION
k8s-master-1   NotReady   master   16m   v1.19.2

ネットワークコンポーネントのcalicoを構成する。
# master

kubectl apply -f https://docs.projectcalico.org/v3.11/manifests/calico.yaml

以下コマンドでcalicoのpodがデプロイされ稼動していることを確認。

#pod情報
[kubeuser01@k8s-master-1 ~]$ kubectl  get pods -A
NAMESPACE     NAME                                       READY   STATUS    RESTARTS   AGE
kube-system   calico-kube-controllers-6b8f6f78dc-l9tcx   1/1     Running   0          2m7s
kube-system   calico-node-hzvcb                          1/1     Running   0          2m7s
kube-system   coredns-f9fd979d6-r5ldn                    1/1     Running   0          22m
kube-system   coredns-f9fd979d6-x5cgz                    1/1     Running   0          22m
kube-system   etcd-k8s-master-1                          1/1     Running   0          22m
kube-system   kube-apiserver-k8s-master-1                1/1     Running   0          22m
kube-system   kube-controller-manager-k8s-master-1       1/1     Running   0          22m
kube-system   kube-proxy-9lvfn                           1/1     Running   0          22m
kube-system   kube-scheduler-k8s-master-1                1/1     Running   0          22m
[kubeuser01@k8s-master-1 ~]$

4. workerノードの構成

workerノードをクラスターにjoinさせる。
先ほどコピーしておいたコマンドをworkerノード側で実行する。
#worker

kubeadm join 10.84.82.112:6443 --token <token value> \
    --discovery-token-ca-cert-hash sha256:<hash value>

token とhashの値を紛失してしまった場合は以下のコマンドから確認可能。
# master

kubeadm token list

5. Podのデプロイ

例えば以下コマンドで命令的にpodをデプロイ。(deprecatedなので本来はymlによる正当な宣言的方法が望ましいです)

$ kubectl run nginx --image=nginx:latest

podのデプロイを確認。

$ kubectl get po

6. Web UIの構成(optional)

Web UIの管理コンソールを利用する場合は以下コマンド実行。
#master, workerどちらでも可

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml

local PCのkubectlコマンドを構成する。
kubernetes.io

kubectlコマンドを導入し、$HOMEに.kubeディレクトリを作成、ディレクトリ内に先ほどmasterで構成したconfigファイルを転送しておく。
https://kubernetes.io/ja/docs/tasks/tools/install-kubectl/

masterでコンソールにログインするためのadminユーザー(のtoken)を作成する。
参考:
github.com

Service Accountを生成する。以下のコマンドを実行。

cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
  name: admin-user
  namespace: kubernetes-dashboard
EOF

ClusterRoleBindingを構成。↑のSAのadmin-userに対して、既存の"cluster-admin"のClusterRoleを設定。
kubectl get crで現在利用可能なClsuterRoleが確認可能。

cat <<EOF | kubectl apply -f -
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: admin-user
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin
subjects:
- kind: ServiceAccount
  name: admin-user
  namespace: kubernetes-dashboard
EOF

以上で設定作業は完了。
作成したservice accountを確認。

$ kubectl -n kubernetes-dashboard get sa/admin-user
NAME         SECRETS   AGE
admin-user   1         15m

作成したservice accountには、secretが自動的に与えられる。

$ kubectl -n kubernetes-dashboard get sa/admin-user  -o yaml
...
secrets:
- name: admin-user-token-6mn4x

このsecretの中にtokenが保存されているので、確認する。
secretの存在を確認。

$ kubectl  get secret -n kubernetes-dashboard
NAME                               TYPE                                  DATA   AGE
admin-user-token-6mn4x             kubernetes.io/service-account-token   3      18m

secretの値の確認。

$ kubectl  get secret -n kubernetes-dashboard admin-user-token-6mn4x -o yaml
token: <base64 encoded token value>

↑のtokenをコピーしてbase64 decodeする。decodeはecho -nしてbase64 -dに渡す。

local PCで以下のコマンドを実行。macはターミナル、windowsPowerShell

$ kubectl proxy

ブラウザを立ち上げ、
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/にアクセス。

tokenは先ほど取得・base64 decodeしておいた値を利用。
以上。