Skip to content

我的 K8S DevOps 實驗環境 - 節點安裝

Published: 27 分鐘

本文屬於「我的 K8S DevOps 實驗環境」文章系列,主要說明如何在實驗環境完整安裝 K8S 叢集節點。雖然透過 kubeadm 工具,叢集節點的安裝設定工作已經大幅簡化,但藉由了解各個安裝設定步驟除了可以對叢集架構有更具體的了解外,也可以奠定對叢集系統進行客製化的基礎。

本文所描述的安裝步驟,大致上是按照 Kubernetes 官方文件說明進行,您可以將本文視為官方文件在此實驗環境的安裝演練描述,也請做好文章會有點冗長的心理準備:-)。說真的,會手動安裝 K8S 叢集的人骨子裡應該都是很硬核(hardcore)吧!

相關連結 - Bootstraping clusters with kubeadm

Table of Contents

前題假設

下圖是此實驗環境的架構示範:

K8s Lab environment

此實驗環境的重要系統參數(在 .env 檔中設定):

參數名稱參數值說明
SPK_MASTER_COUNT2控制節點數量
SPK_MASTER_MEMORY20482GB
SPK_MASTER_CPUS2虛擬機 CPU 數量
SPK_WORKER_COUNT1工作節點數量
SPK_WORKER_MEMORY2048
SPK_WORKER_CPUS2虛擬機 CPU 數量
SPK_LAB_NETWORK172.42.42172.42.42.0/16, 叢集主機的網段
SPK_POD_CIDR10.217.0.0/16動態分配給 Pod 的 IP 網段

下面是一些假設條件:

  1. 在每個節點都有一個具備不用密碼便可使用 sudo 權限的管理帳戶存在,在此實驗環境就是 vagrant 帳戶。
  2. 安裝指令都是以 root 身份執行,所以需先以管理帳戶登入後轉成 root 身份。
  3. 節點作業系統都是 Ubuntu 18.04 x86_64 系統。
  4. 每個節點的 MAC 位址以及 product_uuid 都是唯一。
  5. 主機防火牆並未啟用,也就是節點之間可以任意 port 來通訊。
  6. 採用 Docker 作為容器執行環境。
  7. 節點間有機制可以分享資料檔案,例如:透過 SCP 來拷貝檔案到不同主機。此實驗環境是透過虛擬機與宿主機間共享的 /vagrant 目錄來達到共享檔案的目的。

準備工作

此實驗環境提供腳本在 Vagrant 建置虛擬環境時自動設定 K8S 叢集,如果您打算一步一步跟著進行安裝設定,可以按照本節說明準備一個用來手動安裝的環境。安裝需要一個全新的環境,所以請先將 spk-cluster-lab 在不同目錄複製一份。修改 .env 檔案內容來調整控制節點以及工作節點數量,手動安裝建議有2個控制節點以及至少一個工作節點,如此,可以練習主要控制節點、次要控制節點以及工作節點的安裝步驟。

主要(primary)跟次要(secondary)控制節點的劃分並非正式的說法,此實驗環境使用第一台控制節點來 bootstrap 整個叢集以及提供額外的工具,為了方便識別所以才給予這樣的名稱。

不管有沒有要使用手動安裝,首先使用下列指令建立虛擬環境:

vagrant up --no-provision

如此,Vagrant 只會建立指定數量與規格的虛擬機,而不會進行自動設定工作。虛擬機建置完成後,使用 vagrant ssh 登入主機,例如下面指令登入第一台工作節點:

vagrant spkworker-1

登入主機後就可以進行腳本無法自動完成的工作。

確保節點 MAC 位址以及 product_uuid 都是唯一

由於 Kubenetes 依賴網卡的 MAC 位址以及系統 product_uuid來識別節點,登入節點才能進行相關的檢查。

使用 ip link 查詢網卡 MAC 位址:

vagrant@spkworker-1:~$ ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:76:b9:09 brd ff:ff:ff:ff:ff:ff
3: eth1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
    link/ether 08:00:27:ef:7d:77 brd ff:ff:ff:ff:ff:ff
4: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN mode DEFAULT group default
    link/ether 02:42:87:ce:2f:3c brd ff:ff:ff:ff:ff:ff
7: tunl0@NONE: <NOARP,UP,LOWER_UP> mtu 1440 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/ipip 0.0.0.0 brd 0.0.0.0

其中, eth0 是第一張網卡名稱,使用 NAT 模式讓虛擬機可以透過宿主機連到網際網路; eth1 則是第二張網卡,提供節點以及宿主機之間的通訊用途。

注意:由於 eth0 網卡是以 NAT 模式模擬,實驗環境中的虛擬機,eth0 的 MAC 位址都會是相同值。

檢視 /sys/class/dmi/id/product_uuid 內容:

vagrant@spkworker-1:~$ sudo cat /sys/class/dmi/id/product_uuid
87A360CF-7EB8-4FE4-9789-AE3EFB855BAC

確保必要的通訊埠開通

下面是節選自官方文件但根據此實驗環境修改過的表格。

控制節點需要開通的通訊埠(不含 Network add-on 的需求, e.g. flannel):

ProtocolDirectionPort RangePurposeUsed By
TCPInbound16443Control-plane endpointAll
TCPInbound6443Kubernetes API serverSelf
TCPInbound2379-2380etcd server client APIkube-apiserver, etcd
TCPInbound10250Kubelet APISelf, Control plane
TCPInbound10251kube-schedulerSelf
TCPInbound10252kube-controller-managerSelf

工作節點需要開通的通訊埠(不含 Network add-on 的需求):

ProtocolDirectionPort RangePurposeUsed By
TCPInbound10250Kubelet APISelf, Control plane
TCPInbound30000-32767NodePort ServicesAll

按照此實驗環境的作法,確保通訊埠簡單地作法就是全部開通,也就是確保沒有防火牆規則在作用或者規則指定全部開通。此實驗環境所使用的虛擬機預設就是開通,所以,並不需要進行通訊埠開通與否的檢查工作。當然您要練習如何檢查,可以透過 telnet 工具來測試 TCP 通訊埠是否有真的開通,進階一點可以使用 nmap 來掃描。。

執行自動設定腳本

完成叢集節點相關檢查後,使用下列指令啟動自動設定腳本或者按照後續章節描述手動執行:

vagrant provision

也可以只執行特定虛擬機的自動腳本:

vagrant provision spkmaster-1

注意:如果要以一台一台方式執行自動設定腳本的話,要確保 spkmaster-1 最先正確完成,其它節點則可以任意順序加入。

基礎安裝設定

基礎安裝設定是由 bootstrap/spk-base.sh 這個腳本執行。

此叢集實驗環境由控制節點以及工作節點組成,基礎安裝設定需要在每個節點都要完成的設定工作。

關閉 swap

Kubelet 需要系統 swap 停用才能正確運作。

swapoff -a

將 swap 設定從 /etc/fstab 檔中移除:

sed -i '/swap/d' /etc/fstab

檢查 br_netfilter 核心模組是否已載入

使用 lsmod | grep br_netfilter 查詢:

# lsmod | grep br_netfilter
br_netfilter           24576  0
bridge                155648  1 br_netfilter

確保 br_netfilter 核心模組已載入

如果 br_netfilter 尚未載入,透過 modprobe 指令來載入:

modprobe br_netfilter

確保 iptables 可以檢視 bridged 的封包流量

需要修改 sysctl 設定以確保 iptables 可以檢查到經過橋接(bridged)的封包,執行這個動作前需要確認 br_netfilter 核心模組已經載入。

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

sysctl --system >/dev/null 2>&1

讓節點間可以透過域名尋訪

叢集內節點需要可以透過域名來解析服務的 IP 位址,其中最重要的是「控制平面端點」的域名 spkcluter.lab.local

先刪除原有的 spk 開頭的設定值(這些是由 Vagrant 根據主機名稱自動設定,會跟下面新增的衝突):

sed -i '/spk/d' /etc/hosts

加入共用的域名設定:

  echo "172.42.42.10    spkcluster    spkcluster.lab.local" >> /etc/hosts
  echo "172.42.42.11    spkmaster-1   spkmaster-1.lab.local" >> /etc/hosts
  echo "172.42.42.12    spkmaster-2   spkmaster-2.lab.local" >> /etc/hosts
  echo "172.42.42.13    spkmaster-3   spkmaster-3.lab.local" >> /etc/hosts
  echo "172.42.42.101   spkworker-1   spkworker-1.lab.local" >> /etc/hosts
  echo "172.42.42.102   spkworker-2   spkworker-2.lab.local" >> /etc/hosts
  echo "172.42.42.103   spkworker-3   spkworker-3.lab.local" >> /etc/hosts
  echo "172.42.42.104   spkworker-4   spkworker-4.lab.local" >> /etc/hosts
  echo "172.42.42.105   spkworker-5   spkworker-5.lab.local" >> /etc/hosts
  echo "172.42.42.106   spkworker-6   spkworker-6.lab.local" >> /etc/hosts
  echo "172.42.42.107   spkworker-7   spkworker-7.lab.local" >> /etc/hosts
  echo "172.42.42.108   spkworker-8   spkworker-8.lab.local" >> /etc/hosts
  echo "172.42.42.109   spkworker-9   spkworker-9.lab.local" >> /etc/hosts

在實際的部署環境,域名解析是由機房或者雲端 DNS 服務提供,在此實驗環境為了簡單起見直接以設定 /etc/hosts 來模擬。由於項目都已經是固定,這也是為何此實現環境的節點數量有明確限制。

讓 Ubuntu 系統可以透過 HTTPS 協定來安裝套件

apt-get update -y
apt-get install -y apt-transport-https ca-certificates curl software-properties-common gnupg2

安裝 Docker Engine

加入安裝 Docker engine 所需的 PGP key 以及套件庫:

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

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

apt-get update -y

安裝 Docker engine 相關套件:

apt-get install -y containerd.io=1.2.13-1 \
  docker-ce-cli=5:19.03.8~3-0~ubuntu-bionic \
  docker-ce=5:19.03.8~3-0~ubuntu-bionic

為了執行效率,自動設定腳本安裝 Docker 跟 Kubernetes 工具並非如本文描述般順序分明。雖然順序不同,但最終結果應該是相同的。

讓管理帳戶可以執行 Docker 命令列工具

為了讓 vagrant 這個管理帳戶可以執行 docker 工具,需要將該帳戶加入 docker 群組中:

usermod -aG docker vagrant

設定 Docker 使用的 cgroup 驅動器

提供給 Docker engine 使用的 /etc/docker/daemon.json 設定檔:

cat > /etc/docker/daemon.json <<EOF
{
  "exec-opts": ["native.cgroupdriver=systemd"],
  "log-driver": "json-file",
  "log-opts": {
    "max-size": "100m"
  },
  "storage-driver": "overlay2"
}
EOF

kubelet 跟容器環境(這裡是 Docker)使用的 cgroup 驅動器一定要相同。 此實驗環境使用的 kubelet 版本會自動辨識 Docker 使用的 cgroup 驅動器來自行調整,所以,不用另外做設定。如果使用其他容器環境,要檢查 kubelet 是否需要額外設定。

重啟 Docker 讓設定生效

systemctl daemon-reload
systemctl restart docker

官方文件還有這個步驟 mkdir -p /etc/systemd/system/docker.service.d,有人可以說明建立該目錄的用途嗎?

安裝 Kubernetes 叢集工具

加入 Kubernetes 套件庫以及相關 PGP key:

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

cat <<EOF | tee /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main
EOF

apt-get update -y

安裝 kubelet、kubeadm 以及 kubelet 套件:

apt-get install -y \
  kubelet=1.18.3.00 \
  kubeadm=1.18.3.00 \
  kubectl=1.18.3.00

注意:由於 kubelet 需要正確的設定檔才能正常執行,所以,到此步驟,kubelet 尚無法用來啟動靜態 Pod 的(透過 /etc/kubernetes/manifiests/ 目錄部署)。

預防 kubelet, kubeadm, kubectl 隨系統自動更新

因為除了安全修復外,本來就不應該隨意升級系統的套件。不確定此步驟是不是必要,Kubernetes 的升級的確不是只是簡單的套件更新這麼簡單,為了預防意外還是加了此步驟。

apt-mark hold kubelet kubeadm kubectl

容器存儲方案支援

OpenEBS 以及幾個想使用的容器存儲方案(支援CSI)需要在主機安裝額外套件,其中以 iSCSI 為最常見。Linstor(使用 DRBD)則需要額外啟用核心模組(kernel module),之後會視需要來新增。

安裝 open-iscsi 套件:

apt-get install -y open-iscsi

啟動 iscsid 服務:

systemctl enable iscsid
systemctl start iscsid

主要控制節點安裝設定

本節描述主要控制節點在基礎安裝完成後的設定工作,也就是 spkmaster-1 這台虛擬機。

控制節點安裝設定是由 bootstrap/spk-master.sh 這個腳本執行,呼叫時需要提供參數,分別為 節點編號POD CIDRAPI_ADV_ADDRESS

安裝控制平面端點

控制節點在完成基礎安裝設定後,需要進行「控制平面端點」的安裝設定工作,相關資訊請參考 「我的 K8S DevOps 實驗環境 - 控制平面端點」。

產生憑証金鑰(certificate key)

憑證金鑰在次要控制節點加入叢集時用來取得呼叫 API Server 所需的憑證。

CERTIFICATE_KEY=$(kubeadm alpha certs certificate-key)

初始化叢集

主要控制節點(spkmaster-1)負責整個叢集的初始化工作。

    kubeadm init --apiserver-advertise-address=${API_ADV_ADDRESS} \
               --pod-network-cidr="10.217.0.0/16" \
               --control-plane-endpoint=spkcluster.lab.local:16443 \
               --apiserver-cert-extra-sans spkcluster \
               --kubernetes-version=1.18.3 \
               --upload-certs \
               --certificate-key=${CERTIFICATE_KEY} | tee /root/kubeinit.log

其中,

  1. --apiserver-advertise-addres 明確指定此節點對外公告的 IP 位址,由於不是使用預設的網卡作為叢集通訊,需要特別指定。 ${API_ADV_ADDRESS}是控制節點的 IP 位址,主要控制節點的 IP 預設為 172.42.42.11
  2. --pod-network-cidr 指定POD 網路的 CIDR, 預設為 10.217.0.0/16
  3. --control-plane-endpoint 指定控制平面端點(API 服務的固定存取點)。
  4. --apiserver-cert-extra-sans 指定 API Server 憑證額外需要的別名,因為 spkcluster.lab.local 已經指定為控制平面端口,所以 kubeadm 會自動加入。
  5. --kubernetes-version 指定欲安裝 Kubernetes 的版號,此實驗環境都以指定明確版本來安裝。
  6. --upload-certskubeadm 將控制平面需要的憑證資訊以資源物件的形式存放在叢集資料庫(etcd)中,次要控制節點需要 certificate key 才能成功從API服務取得所需憑證。
  7. --certificate-key 指定加密憑證的金鑰。

相關連結 - kubeadm init: 想進一步了解 kubeadm init 內部運作可以參考此文件,可以看出 kubeadm 完成很多繁瑣的工作。

產生工作節點加入叢集腳本

產生給工作節點用來加入叢集的腳本:

kubeadm token create --print-join-command | tee /home/vagrant/worker-join.sh

複製到 /vagrant/.kube 共用目錄中:

cp /home/vagrant/worker-join.sh /vagrant/.kube/worker-join.sh

產生控制節點加入叢集腳本

產生給次要控制節點用來加入叢集的腳本,並複製到 /vagrant/.kube 共用目錄中:

kubeadm token create --certificate-key ${CERTIFICATE_KEY} --print-join-command > /home/vagrant/master-join.sh

cat /home/vagrant/master-join.sh | tr -d '\n' > /vagrant/.kube/master-join.sh

echo " --apiserver-advertise-address \${API_ADV_ADDRESS}" >> /vagrant/.kube/master-join.sh

其中,

  1. 需要使用 --apiserver-advertise-address 來明確指定加入節點的 IP 位址,不然次要控制節點的 etcd 程式會使用第一張網卡(eth0)的 IP 位址導致無法形成叢集而崩潰。如果只有主要控制節點時都可以正常運作,但在第二個控制節點加入後,主要控制節點的 etcd 就掛點,可能就是這個沒設定造成。
  2. \${API_ADV_ADDRESS} 在次要控制節點加入時才會被置換成對應的節點 IP 位址,’\’ 字元是必要的。

當初設定實驗環境時,因為 --apiserver-advertise-address 問題卡關好幾天。

安裝網路擴充套件(network add-on)

此實驗環境的原始碼中有包含部署所需的描述檔(manifest),底下以安裝 Calico 為例子:

kubectl create -f /vagrant/add-ons/calico/

山姆鍋也對 Cilium 網路擴充套件的使用感興趣。

讓 root 以及管理帳戶可以管理叢集

執行 kubeadm init 成功後,產生給 kubectl 使用的設定檔位於 /etc/kubernetes/admin.conf,下面指令設定讓 root 以及 vagrant 帳戶可以執行 kubelet 管理叢集的權限:

  mkdir -p /home/vagrant/.kube
  cp /etc/kubernetes/admin.conf /home/vagrant/.kube/config
  chown -R vagrant:vagrant /home/vagrant/.kube
  mkdir -p /root/.kube
  cp /etc/kubernetes/admin.conf /root/.kube/config

複製管理叢集的 kubeconfig 到共用目錄

/etc/kubernetes/admin.conf檔案拷貝到共用目錄讓其它節點可以存取:

  mkdir -p /vagrant/.kube
  cp /etc/kubernetes/admin.conf /vagrant/.kube/config

在其他部署環境可以透過 scp 來複製到其他節點。

次要控制節點安裝設定

本節說明次要節點的安裝設定步驟,也就是 spkmaster-2 以及 spkmaster-3 這兩個虛擬機。

控制節點安裝設定是由 bootstrap/spk-master.sh 這個腳本執行,腳本根據節點序號自動執行主要或次要控制節點的設定。

安裝控制平面端點

控制節點在完成共同安裝設定後,需要進行「控制平面端點」的安裝設定工作,相關資訊請參考 「我的 K8S DevOps 實驗環境 - 控制平面端點」。

加入叢集

次要控制節點透過類似下面指令加入叢集:

kubeadm join spkcluster.lab.local:16443 --token vauafs.gtr7lji99mgmyiko \
    --discovery-token-ca-cert-hash  sha256:9164d84f2003ecee4acef369c5a3b76274cc701a0ec9efa9da8e4b2e89eb3e1c \
    --control-plane --certificate-key 833b7ad00d901883dc624fc1e0d871c573e0573d65f9201eeaf70bcdcbd4141b \ --apiserver-advertise-address ${API_ADV_ADDRESS}

上述指令只是範例,實際需要根據安裝主要控制節點時產生的 /vagrant/.kube/master-join.sh 腳本內容。

讓 root 以及管理帳戶可以管理叢集

將由主要控制節點在執行 kubeadm init 時產生的 /etc/kubernetes/admin.conf 複製到適當位置,讓 root 以及 vagrant 帳戶可以使用 kubectl 命令管理叢集。

mkdir -p /home/vagrant/.kube
cp /vagrant/.kube/config /home/vagrant/.kube/config
chown -R vagrant:vagrant /home/vagrant/.kube
mkdir -p /root/.kube
cp /vagrant/.kube/config /root/.kube/config

其中,/vagrant/.kube/config 的檔案內容跟主要控制節點上的 /etc/kubernetes/admin.conf 相同。

工作節點安裝設定

工作節點安裝設定是由 bootstrap/spk-worker.sh 這個腳本執行,。

工作節點加入叢集

完成工作節點的共同安裝設定後,第一個動作就是將此工作節點加入叢集。下列指令只是個範例,實際的指令要依照 kubeadm init 時產生的指令為準:

kubeadm join spkcluster.lab.local:16443 --token f0nhjh.rmn6xni96z9i13hz \
    --discovery-token-ca-cert-hash sha256:9164d84f2003ecee4acef369c5a3b76274cc701a0ec9efa9da8e4b2e89eb3e1c

spk-worker.sh 自動腳本其實是執行下列指令:

bash /vagrant/.kube/worker-join.sh | tee /root/kubejoin.log

其中, /vagrant/.kube/worker-join.sh 的內容是由 spk-master.sh 產生。實際看其內容的話,就可以發現類似上述的指令。

讓 root 以及管理帳戶可以管理叢集

將由主要控制節點在執行 kubeadm init 時產生的 /etc/kubernetes/admin.conf 複製到適當位置,讓 root 以及 vagrant 帳戶可以使用 kubectl 命令管理叢集。

mkdir -p /home/vagrant/.kube
cp /vagrant/.kube/config /home/vagrant/.kube/config
chown -R vagrant:vagrant /home/vagrant/.kube
mkdir -p /root/.kube
cp /vagrant/.kube/config /root/.kube/config

其中,/vagrant/.kube/config 的檔案內容跟主要控制節點上的 /etc/kubernetes/admin.conf 相同。

選擇性安裝設定

此節說明有些非必要或者此實驗環境才需要的安裝設定工作,由於希望叢集的安裝設定步驟跟 Kubernetes 官方提供的文件相當,所以選擇性的設定不應該影響整體流程,您可以忽略此節的內容。

套件與容器映像檔快取

此實驗環境採用多節點叢集環境,如果每個節點都要從網路下載安裝套件無異是不必要的時間浪費。雖然虛擬機的映像檔不小,但只在第一次啟用時下載,且叢集內所有虛擬機共用相同映像檔,所以,虛擬機的映像檔不用特別處理。每次環境建立時會重複下載的有:

  1. Ubuntu 系統套件資訊:這是關於套件的中介資訊(meta-info),讓套件管理工具可以知道到哪裡下載實際套件檔案。並沒太多特別處理,所以每個節點都還是需要下載最新的套件資訊。目前只是改變自動腳本,減少 apt-get update 的動作次數。
  2. Ubuntu 系統套件檔案:也就是實際安裝用的 .deb 檔案。共享目錄 /vagrant/.kube/apt-cache/ 中的 .deb 檔會被複製到節點中的 /var/cache/apt/archives 目錄中。
  3. 容器映像檔:K8S 叢集元件,大多數以容器方式部署,容器映像檔的重複下載佔整個實驗環境整備不少時間。 控制跟工作節點根據預先知道要使用到的容器映像檔,自動從共享目錄 /vagrant/.kube/docker-images/ 載入對應的映像存檔(假如檔案存在的話),這些映像存檔可以透過 docker save 指令從現有已經拉回(pulled)的映像檔產生。

額外常用工具

除了 kubelet 外,預計將一些常使用的工具安裝在主要控制節點(spkmaster-1)中,目前只有安裝 helm 來方便安裝 K8S 第三方套件。

小結

使用虛擬機作為基礎設施,並完成控制平面端點以及叢集節點的安裝後,終於有了一個支援高可用以及可動態加入節點的K8S叢集實驗環境。但山姆鍋希望此實驗環境像雲端託管的 K8S 叢集一樣,可以宣告方式建立服務負載均衡器(service loadbalancer)以及動態供應永久存儲卷(persistent volumes)讓此實驗環境更像雲端服務。

郭信義 (Sam Kuo)

奔騰網路科技技術長,專長分散式系統、Web 應用與雲端服務架構、設計、開發、部署與維運。工作之餘,喜歡關注自由軟體的發展與應用,偶爾寫一下部落格文章。