這是我在 2019 年建立的 lab:Kubernetes 1.15.3、Docker 19.3.1、Ubuntu 18.04 與 CentOS 7。下列指令保留當時環境;新建叢集請參考目前的 kubeadm 安裝文件,套件來源與 Docker 整合方式已有變更。

硬體與節點配置
我以一台實體主機執行整個多節點環境,配置是 Intel Core i7-8700(6 cores/12 threads)、32 GB RAM 與 512 GB SSD,另有供 GPU workloads 使用的 NVIDIA RTX 2070 O8G。這是當時的實機配置,不是 Kubernetes 最低需求。
Ubuntu host 擔任 control-plane node,四台 CentOS Vagrant VMs 擔任 workers。也可以把相同拓撲分布到多台實體機器。
Host 安裝 VirtualBox 與 Vagrant,並在 BIOS 啟用硬體虛擬化。
設定每台 VM
各 worker 的 Vagrantfile 如下,需分別調整 IP、bridge、MAC、hostname 與 VM 名稱:
# save this file as Vagrantfile
Vagrant.configure(2) do |config|
config.vm.box = "centos/7"
config.vm.synced_folder '.', '/vagrant', disabled: true
# change IP to your local IP address
config.vm.network "public_network", ip: "192.168.2.150",bridge: "eth1", :mac => "8CA3ABCE4001"
# change hostname to k8s-node1, k8s-node2...
config.vm.hostname = "k8s-node1"
config.vm.provision "shell",
inline: "sudo iptables -P FORWARD ACCEPT"
# load prepare_k8s.sh script
config.vm.provision "shell", path: "./../prepare_k8s.sh", keep_color: true
config.vm.provider :virtualbox do |virtualbox, override|
# change name, memory, cpu
virtualbox.name = "node1"
virtualbox.memory = 5000
virtualbox.cpus = 4
end
end
Memory 與 CPU 依主機能力調整。共同的 prepare_k8s.sh 安裝 Docker 與 Kubernetes 元件,設定 DNS、SELinux、bridge sysctl,並停用 swap:
#! /bin/sh
# give nodes a DNS server; otherwise DNS can fail during setup.
sudo cat <<EOF > /etc/resolv.conf
nameserver 8.8.8.8
EOF
# update and install Docker.
sudo yum update -y
sudo yum install -y vim yum-utils device-mapper-persistent-data lvm2 net-tools
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo -y
sudo yum install -y docker-ce docker-ce-cli containerd.io
sudo systemctl start docker.service
sudo systemctl enable docker.service
# install kubernetes
sudo 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
exclude=kube*
EOF
# set SELINUX enforcing -> permissive
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
sudo sed -i 's/^GRUB_DEFAULT=saved$/GRUB_DEFAULT=0/' /etc/default/grub
sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
sudo systemctl enable --now kubelet
sudo cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sudo sysctl --system
# important: turn off swap.
sudo swapoff -a
目錄配置如下:
kubernetes/node1/Vagrantfile
kubernetes/node2/Vagrantfile
kubernetes/node3/Vagrantfile
kubernetes/node4/Vagrantfile
kubernetes/prepare_k8s.sh
啟動並檢查 workers
進入各 node 目錄啟動 VM,再登入確認 swap 設定:
# cd node1, node2, node3, node4
$ sudo vagrant up
......some boot message......
# ssh login to node1
$ sudo vagrant ssh
# ensure swap is disabled.
[vagrant@k8s-node1 ~]$ cat /etc/fstab
#
# /etc/fstab
UUID=8ac075e3-1124-4bb6-bef7-xxxxxx / xfs defaults 0 0
#/swapfile none swap defaults 0 0
若 swap 仍開啟,執行 sudo swapoff -a,並修改 /etc/fstab,避免重開機後恢復。各 worker 與 control-plane node 都要確認自己的基礎設定。
查詢 worker 的網路介面:
# ssh to each node
$ ifconfig eth1
這個 lab 的節點位於同一個 subnet:
master: 192.168.2.110
node1: 192.168.2.150
node2: 192.168.2.151
node3: 192.168.2.152
node4: 192.168.2.153
Host 上執行 top 可以看到四個 VBoxHeadless processes。

準備 control-plane node
Ubuntu host 使用當時的 APT 套件來源安裝 Kubernetes:
# Ubuntu, Debian
apt-get update && apt-get install -y apt-transport-https curl
curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
cat <<EOF >/etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl
若 control-plane 使用 CentOS,當時對應的安裝方式是:
# CentOS
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
# Set SELinux in permissive mode (effectively disabling it)
setenforce 0
sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config
yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes
systemctl enable --now kubelet
Workers 已由 prepare_k8s.sh 安裝 kubeadm 與 kubelet,不必重複安裝。這些舊版套件來源不能用來推定今日可安裝的版本。
初始化 control plane
使用預定的 Pod CIDR 執行 kubeadm init:
$ sudo kubeadm init --pod-network-cidr=10.244.0.0/16
[init] Using Kubernetes version: vX.Y.Z
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
.........
kubeadm join --token <token> <master-ip>:<master-port> --discovery-token-ca-cert-hash sha256:<hash>
# you should get this kubeadm join information.
完成後設定 kubectl:
$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config
此時叢集只有 control-plane node。保留 init 輸出的 join 指令,供 worker 加入。
加入 workers 並指定正確的 node IP
登入每個 worker,使用 init 輸出的 server、token 與 CA hash:
# use `sudo vagrant ssh` to log in to each node.
$ sudo kubeadm join 192.168.2.xxxx:6443 --token xxxxxxxxx --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxx
多網卡 VM 要確認 kubelet 使用同一個 LAN 上的 node IP。以下以 node1 為例,在 kubeadm-flags.env 設定 --node-ip,再重啟 kubelet:
$ sudo vim /var/lib/kubelet/kubeadm-flags.env
KUBELET_KUBEADM_ARGS="--cgroup-driver=cgroupfs --network-plugin=cni --pod-infra-container-image=k8s.gcr.io/pause:3.1 --node-ip=192.168.2.150"
# restart kubelet
$ sudo systemctl restart kubelet
其餘 nodes 使用各自的位址。
安裝 Flannel
Pod network 不可與 host network 重疊。kubeadm init --pod-network-cidr 與 Flannel 的 Network 必須一致。本例預設是 10.244.0.0/16,bridge 的 net.bridge.bridge-nf-call-iptables 需為 1;前面的 script 已設定。
當時使用的 Flannel manifest:
# apply flannel to your cluster.
kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/62e44c867a2846fefb68bd5f178daf4da3095ccb/Documentation/kube-flannel.yml
其中的網段設定是:
# at kube-flannel.yml we can see this network config.
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
如果改用 10.32.0.0/16,初始化時的 CIDR 與 Flannel YAML 都要同步修改。套用後,worker 上會出現 flannel.1 與 cni0;使用該 CIDR 時,可看到 10.32.x.x 位址:
# I use CIDR 10.32.0.0/16. ifconfig will display 10.32.x.x
[vagrant@k8s-node1 ~]$ ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.32.4.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::24ae:5eff:xxxx:xxxx prefixlen 64 scopeid 0x20<link>
ether 26:ae:5e:29:57:d8 txqueuelen 0 (Ethernet)
RX packets 994894 bytes 1042168344 (993.8 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 361101 bytes 400393922 (381.8 MiB)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0
[vagrant@k8s-node1 ~]$ ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.32.4.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::1876:f4ff:xxxx:xxxx prefixlen 64 scopeid 0x20<link>
ether 1a:76:f4:0d:6c:5a txqueuelen 1000 (Ethernet)
RX packets 460962 bytes 353293465 (336.9 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 478311 bytes 1050286009 (1001.6 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
另一個 node 的介面範例:
[vagrant@k8s-node2 ~]$ ifconfig cni0
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.32.3.1 netmask 255.255.255.0 broadcast 0.0.0.0
inet6 fe80::c468:e0ff:xxxx:xxxx prefixlen 64 scopeid 0x20<link>
ether c6:68:e0:40:12:31 txqueuelen 1000 (Ethernet)
RX packets 592707 bytes 157355303 (150.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 522413 bytes 655781024 (625.4 MiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
[vagrant@k8s-node2 ~]$ ifconfig flannel.1
flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1450
inet 10.32.3.0 netmask 255.255.255.255 broadcast 0.0.0.0
inet6 fe80::c003:63ff:xxxx:xxxx prefixlen 64 scopeid 0x20<link>
ether c2:03:63:59:12:d1 txqueuelen 0 (Ethernet)
RX packets 291008 bytes 166468118 (158.7 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 300398 bytes 140955741 (134.4 MiB)
TX errors 0 dropped 8 overruns 0 carrier 0 collisions 0
確認 cluster 就緒
# at master node
$ kubectl get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
k8s-node1 Ready <none> 18h v1.15.3 192.168.2.150 <none> CentOS Linux 7 (Core) 3.10.0-957.27.2.el7.x86_64 docker://19.3.1
k8s-node2 Ready <none> 23h v1.15.3 192.168.2.151 <none> CentOS Linux 7 (Core) 3.10.0-957.27.2.el7.x86_64 docker://19.3.1
k8s-node3 Ready <none> 23h v1.15.3 192.168.2.152 <none> CentOS Linux 7 (Core) 3.10.0-957.27.2.el7.x86_64 docker://19.3.1
k8s-node4 Ready <none> 23h v1.15.3 192.168.2.153 <none> CentOS Linux 7 (Core) 3.10.0-957.27.2.el7.x86_64 docker://19.3.1
master Ready master 23h v1.15.3 192.168.2.110 <none> Ubuntu 18.04.3 LTS 5.0.0-25-generic docker://19.3.1
所有 nodes 應顯示 Ready。需要跨 Pod 重建保留資料時,可以接著閱讀 NFS StorageClass。
Node 或 Pod 網路失敗時,先檢查介面、Pod CIDR、CNI 與防火牆規則。不要把清空所有 iptables chains 當成通用修法,因為它也會移除 Kubernetes 與 host firewall 使用的其他規則。
