手把手從0到1:搭建Kubernetes叢集

Blackbinbin發表於2021-11-09

搭建 k8s 叢集網上很多教程,如果是手工部署或者實驗環境可以直接使用 MiniKube 或者 Kind,來在本地啟動簡單的 Kubernetes 叢集進行後面的學習即可。如果是使用 MiniKube 的話,阿里雲還維護了一個國內版的 MiniKube,這對於在國內的同學來說會比較友好。

下面介紹使用 kubeadm 這個 Kubernetes 半官方管理工具的工作原理。kubeadm 的初衷就是讓 Kubernetes 叢集的部署不再讓人頭疼,而且發展比較成熟,關於各種問題網上都有對應的文章來解決,我們就來使用它部署一個完整的 Kubernetes 叢集。

準備工作

首先準備三臺虛擬機器,或者是三臺伺服器,我是從企鵝☁️上買的三個輕量伺服器,還挺便宜,但是後來看文件才知道,這所謂的輕量伺服器原來就是k8s上的容器,所以我這屬於是在 k8s 的容器裡上搭建 k8s 叢集了,擱這擱這呢。 為什麼我肯定這個提供的就是一個有官方映象 linux 的容器呢,具體是因為我買了後發現他們所謂的內網在不同賬號下是不能互通的,也就是說不同賬號的輕量伺服器分配到了不同的 node 上,內網ip只能在同一個 node 上互通,不同的node之間想互通只能去配置路由,並且配置屬於自己的二層網路。所以這聽起來就知道是容器了。

在本次部署中,我準備的機器配置如下:

  1. 2 核 CPU、 4 GB 記憶體(三臺);
  2. 80 GB 磁碟;
  3. Centos 7.6;
  4. 內網互通;
  5. 外網訪問許可權不受限制。

這裡的內網互通我是直接通過 iptables 來進行配置的,因為三臺伺服器公網ip可以互通,但是內網ip是不互通的,在 k8s 中預設是用內網ip來互動的,所以我只能用如下命令來進行配置。

sudo iptables -t nat -A OUTPUT -d <另外伺服器的內網ip> -j DNAT --to-destination <另外伺服器的外網ip>

保證每個節點能內網ip都 ping 通其他節點就行了。

 

安裝 kubeadm 和 Docker

所有節點安裝Docker/kubeadm/kubelet

安裝 docker

$ wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
$ yum -y install docker-ce-18.06.1.ce-3.el7
$ systemctl enable docker && systemctl start docker
$ docker --version
Docker version 18.06.1-ce, build e68fc7a

配置一下映象地址

# cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": ["https://b9pmyelo.mirror.aliyuncs.com"]
}
EOF

新增 k8s 的 yum 源

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

安裝kubeadm,kubelet和kubectl

yum install -y kubelet-1.18.0 kubeadm-1.18.0 kubectl-1.18.0
systemctl enable kubelet

K8S叢集還未拉起,故這裡的kubelet是無法啟動的,等master初始化時會自動拉起

 

替換 k8s 所需元件的 docker 映象

因為國內的映象都拉不回來關於 k8s 所需元件的,所以需要替換一下映象

檢視 kubeadm 所需要的映象列表:

$ kubeadm config images list
I1108 19:54:40.085559    2997 version.go:252] remote version is much newer: v1.22.3; falling back to: stable-1.18
W1108 19:54:40.592794    2997 configset.go:202] WARNING: kubeadm cannot validate component configs for API groups [kubelet.config.k8s.io kubeproxy.config.k8s.io]
k8s.gcr.io/kube-apiserver:v1.18.20
k8s.gcr.io/kube-controller-manager:v1.18.20
k8s.gcr.io/kube-scheduler:v1.18.20
k8s.gcr.io/kube-proxy:v1.18.20
k8s.gcr.io/pause:3.2
k8s.gcr.io/etcd:3.4.3-0
k8s.gcr.io/coredns:1.6.7

編寫拉取shell,這裡指令碼里替換成所需要的元件版本就行了

$ vim ./pull_k8s_images.sh
set -o errexit
set -o nounset
set -o pipefail

##這裡定義版本,按照上面得到的列表自己改一下版本號

KUBE_VERSION=v1.18.20
KUBE_PAUSE_VERSION=3.2
ETCD_VERSION=3.4.3-0
DNS_VERSION=1.6.7

##這是原始倉庫名,最後需要改名成這個
GCR_URL=k8s.gcr.io

##這裡就是寫你要使用的倉庫
DOCKERHUB_URL=registry.aliyuncs.com/google_containers

##這裡是映象列表,新版本要把coredns改成coredns/coredns
images=(
kube-proxy:${KUBE_VERSION}
kube-scheduler:${KUBE_VERSION}
kube-controller-manager:${KUBE_VERSION}
kube-apiserver:${KUBE_VERSION}
pause:${KUBE_PAUSE_VERSION}
etcd:${ETCD_VERSION}
coredns:${DNS_VERSION}
)

##這裡是拉取和改名的迴圈語句
for imageName in ${images[@]} ; do
  docker pull $DOCKERHUB_URL/$imageName
  docker tag $DOCKERHUB_URL/$imageName $GCR_URL/$imageName
  docker rmi $DOCKERHUB_URL/$imageName
done

執行shell

$ chmod +x ./pull_k8s_images.sh
$ ./pull_k8s_images.sh

 

部署 k8s master 節點

因為我的版本是 1.18.20 的版本,所以關於 kubeadm 的配置檔案 config,內容如下

vim ./kubeadm/kubeadm.yaml
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
controllerManager:
    extraArgs:
        horizontal-pod-autoscaler-use-rest-clients: "true"
        horizontal-pod-autoscaler-sync-period: "10s"
        node-monitor-grace-period: "10s"
apiServer:
    extraArgs:
        runtime-config: "api/all=true"
kubernetesVersion: "stable-1.18"

這裡的檔案中 apiVersion 欄位要根據不同的 k8s 進行調整,具體可以看看官網,因為不同的版本api不同,會報錯~

具體可以看看這個:https://kubernetes.io/docs/reference/config-api/kubeadm-config.v1beta3/

kubeadm 初始化安裝 k8s

kubeadm init --config ./kubeadm/kubeadm.yaml

最後安裝完畢後會有提示:

[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

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 <master_id>:<master_port> --token <join_token> --discovery-token-ca-cert-hash <join_cert_hash>

這個 kubeadm join 命令,就是用來給這個 Master 節點新增更多工作節點(Worker)的命令。我們在後面部署 Worker 節點的時候馬上會用到它,所以找一個地方把這條命令記錄下來。

此外,kubeadm 還會提示我們第一次使用 Kubernetes 叢集所需要的配置命令:

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

而需要這些配置命令的原因是:Kubernetes 叢集預設需要加密方式訪問。所以,這幾條命令,就是將剛剛部署生成的 Kubernetes 叢集的安全配置檔案,儲存到當前使用者的.kube 目錄下,kubectl 預設會使用這個目錄下的授權資訊訪問 Kubernetes 叢集。如果不這麼做的話,我們每次都需要通過 export KUBECONFIG 環境變數告訴 kubectl 這個安全配置檔案的位置。

我個人更傾向於使用 export KUBECONFIG 環境變數告訴 kubectl 這個安全配置檔案的位置,因為當 k8s 發生錯誤重新啟動的時候,可會找不到配置檔案

所以

$ vim /etc/profile
在檔案最後新增
export KUBECONFIG=/etc/kubernetes/admin.conf

$ source /etc/profile

現在,我們就可以使用 kubectl get 命令來檢視當前唯一一個節點的狀態了

$ kubectl get nodes

NAME      STATUS     ROLES     AGE       VERSION
master    NotReady   master    1d        v1.18.0

在除錯 Kubernetes 叢集時,最重要的手段就是用 kubectl describe 來檢視這個節點(Node)物件的詳細資訊、狀態和事件(Event),我們來試一下:

$ kubectl describe node master

...
Conditions:
...

Ready   False ... KubeletNotReady  runtime network not ready: NetworkReady=false reason:NetworkPluginNotReady message:docker: network plugin is not ready: cni config uninitialized

通過 kubectl describe 指令的輸出,我們可以看到 NodeNotReady 的原因在於,我們尚未部署任何網路外掛。另外,我們還可以通過 kubectl 檢查這個節點上各個系統 Pod 的狀態,其中,kube-system 是 Kubernetes 專案預留的系統 Pod 的工作空間(Namepsace,注意它並不是 Linux Namespace,它只是 Kubernetes 劃分不同工作空間的單位):

$ kubectl get pods -n kube-system

NAME               READY   STATUS   RESTARTS  AGE
coredns-78fcdf6894-j9s52     0/1    Pending  0     1h
coredns-78fcdf6894-jm4wf     0/1    Pending  0     1h
etcd-master           1/1    Running  0     2s
kube-apiserver-master      1/1    Running  0     1s
kube-controller-manager-master  0/1    Pending  0     1s
kube-proxy-xbd47         1/1    NodeLost  0     1h
kube-scheduler-master      1/1    Running  0     1s

可以看到,CoreDNS、kube-controller-manager 等依賴於網路的 Pod 都處於 Pending 狀態,即排程失敗。這當然是符合預期的:因為這個 Master 節點的網路尚未就緒。

 

部署網路外掛

在 Kubernetes 專案“一切皆容器”的設計理念指導下,部署網路外掛非常簡單,只需要執行一句 kubectl apply 指令,以 Weave 為例:

$ kubectl apply -f "https://cloud.weave.works/k8s/net?k8s-version=$(kubectl version | base64 | tr -d '\n')"
serviceaccount/weave-net created
clusterrole.rbac.authorization.k8s.io/weave-net created
clusterrolebinding.rbac.authorization.k8s.io/weave-net created
role.rbac.authorization.k8s.io/weave-net created
rolebinding.rbac.authorization.k8s.io/weave-net created
daemonset.apps/weave-net created

部署完成後檢視所有的 pod 狀態

$ kubectl get pods -n kube-system

NAME                             READY     STATUS    RESTARTS   AGE
coredns-78fcdf6894-j9s52         1/1       Running   0          1d
coredns-78fcdf6894-jm4wf         1/1       Running   0          1d
etcd-master                      1/1       Running   0          9s
kube-apiserver-master            1/1       Running   0          9s
kube-controller-manager-master   1/1       Running   0          9s
kube-proxy-xbd47                 1/1       Running   0          1d
kube-scheduler-master            1/1       Running   0          9s
weave-net-cmk27                  2/2       Running   0          19s

可以看到,所有的系統 Pod 都成功啟動了,而剛剛部署的 Weave 網路外掛則在 kube-system 下面新建了一個名叫 weave-net-cmk27 的 Pod,一般來說,這些 Pod 就是容器網路外掛在每個節點上的控制元件。

除此之外我想讓pod被三個節點排程,因為k8s中預設master是不進行排程pod的,也就是此節點上不會部署pod,這個挺浪費

$ kubectl describe node master

Name:               master
Roles:              master
Taints:             node-role.kubernetes.io/master:NoSchedule

可以看到,Master 節點預設被加上了node-role.kubernetes.io/master:NoSchedule這樣一個“汙點”,其中“鍵”是node-role.kubernetes.io/master,而沒有提供“值”。

這個汙點是保證 master 不會被pod排程到

所以直接刪除這個 taints:

$ kubectl taint nodes --all node-role.kubernetes.io/master-

 

部署 k8s 的 worker 節點

首先按照上面的部署“安裝 kubeadm 和 Docker”

第二步,因為我希望這三個節點隨便哪個都能當 master 節點來使用,所以按照“替換 k8s 所需元件的 docker 映象”把所有master需要的元件映象都拿來了

第三步,複製master中的 /etc/kubernetes/admin.conf 到 worker 節點的同樣路徑下,然後 export KUBECONFIG 環境變數

除此之外我發現在 worker join 叢集的時候會出現錯誤:

[ERROR FileContent--proc-sys-net-bridge-bridge-nf-call-iptables]: /proc/sys/net/bridge/bridge-nf-call-iptables contents are not set to 1

解決方法:

echo "1" >/proc/sys/net/bridge/bridge-nf-call-iptables

最後在 woker 節點新增進入叢集命令:

 

就是之前 master 節點提示的 join 命令

 檢視節點

$ kubectl get nodes
NAME             STATUS   ROLES    AGE    VERSION
vm-20-4-centos   Ready    <none>   141m   v1.18.0
vm-20-6-centos   Ready    master   7h5m   v1.18.0
vm-20-9-centos   Ready    <none>   169m   v1.18.0

# 為 workder 節點新增 role
kubectl label nodes vm-20-4-centos kubernetes.io/role=worker
kubectl label nodes vm-20-9-centos kubernetes.io/role=worker

因為在k8s中根據 namespace 請求pod會請求host,我不希望請求 vm-20-4-centos 的時候會找不到解析的域名,所以我在沒個node節點中還新增了以下的配置:

cat >> /etc/hosts << EOF
10.0.20.4 vm-20-4-centos
10.0.20.6 vm-20-6-centos
10.0.20.9 vm-20-9-centos
EOF

將橋接的IPv4流量傳遞到iptables的鏈:
$ cat > /etc/sysctl.d/k8s.conf << EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
$ sysctl --system # 生效

 

大功告成,enjoy~~~

關於 k8s 的玩法我個人有以下幾個目標:

  1. 關於 k8s 的視覺化,可以給使用者提供一個視覺化的 Web 介面來檢視當前叢集的各種資訊
  2. 關於 k8s 的網路外掛,可以嘗試各種常用的網路外掛,看看他們二層網路是怎麼工作原理,理解 k8s 的 CNI
  3. 關於 k8s 的永續性儲存是怎麼實現的,看看 pvc 和 pv 
  4. 關於 k8s 中物件 operator 是怎麼工作的,實現細節是如何的

這裡先定一個小目標

 

相關文章