Kubernetes for Windows 實做

提醒:本篇文章包含很多 Kubernetes 技術名詞與步驟。因為筆者用 Kubernetes 有一段時間了,如果解釋不夠,歡迎留言互動。

承 Kubernetes for Windows 心得文章,筆者在這分享 Kubernetes for Windows 實做。本篇文章包含下述主題。

  • 架構
  • 安裝
  • 驗證
  • 實做心得

架構

Kubernetes for Windows 組成如下:

  • 至少一台的 Control Plane node。
  • 至少一台的 Linux node。
  • 至少一台的 Windows node。

筆者在這提醒,不是掛著 Windows 名稱就不用 Linux OS 了,Kubernetes 許多原件仍然是跑在 Linux OS 的 Control Plane,再者神奇的 gMSA 其中一個服務也是跑在 Linux node 上,所以古早年代純 Windows 環境已經不存在了,連 Microsoft 也在大力的擁抱 Linux 中。這就是為什麼筆者會用 Kubernetes for Windows 技術名詞,不會用 Windows Kubernetes,因為前者更能代表原來的 Kubernetes 加上 Windows node。

有關於 Kubernetes components 介紹可以參考官方文件(註一)。筆者在這篇文章將主要介紹 Windows node 上 Kubernetes components 是怎麼運作的。

一個 Windows node 要可以運作需要二個 Kubernetes 元件 kubelet, kube-proxy 與一個 network add-on (註二)。

kubelet,在 Windows node 是用 service 方式在 Cluster 內的每一台 node 運作,讀者可以透過 PowerShell command 如下得到狀態。根據官方文件 kubelet 是確保 PodSpecs 有 container 的記錄,以及確保 Container 是 running and healthy,由此可見 Container 的管理就是透過 kubelet。

get-service kubelet

kube-proxy,筆者在 1.14 版時,kube-proxy 如同 kubelet 一樣是用 service 方式在 Cluster 內的每一台 Windows node 運行,到了 1.16 OR 1.18 版之後,kube-proxy 就是以 Pod 的方式運作,這相對簡單非常的多,也就是說 kubelet service 有問題時,kube-proxy 就不會正常的運作。kube-proxy 主要是維護 Kubernetes network policy,這些 network policy 允許 Cluster 內部與 Cluster 外部可以跟 Pod 溝通。

Network add-on,有了 kube-proxy 後,node 與 node 之間就知道怎麼分配網路資源,但跨 node 的 Pod 與 Pod 之間要真正連接就需要 Network add-on 來實作。目前筆者建議地端的 Kubernetes 用 flannel network add-on,至於雲端 Kubernetes 就依照供應商方案。

安裝

Kubernetes for Windows 的安裝,筆者分為下述二部份:

  • 雲端供應商 Kubernetes for Windows
  • 地端 Kubernetes for Windows

雲端供應商 Kubernetes for Windows

雖然筆者有在用 KOps (一種運作在 AWS 上的 Kubernetes),但筆者目前只實做 EKS for Windows,等未來有機會接觸到 KOps for Windows,筆者會另外寫一篇介紹。

回到在 EKS 上要實做 Windows 其實非常的簡單,筆者建議可以透過 eksctl (一種 AWS 提供的 EKS cli)實做,過去筆者在 1.14 版時有嚐試過 CloudFormation,但那真的是吃裡不討好的做法。另外如果要實作 gMSA 就有點難度了,因為這會需要實做 Windows node auto join AD domain 來搭配 Node AutoScaling,而相關的介紹筆者將會在 gMSA for your Windows Container 文章中介紹,本篇只專注在如何建立 Windows node,其步驟如下:

  • 準備一台有 EKS 服務權限的跳板機,這需要 attached role 給一台 EC2。
  • 透過 eksctl 建立 EKS Cluster 與至少一台的 Linux node。
  • 確認 EKS 與 Linux node ready
  • 透過 eksctl 建立 Windows NodeGroup
eksctl create nodegroup \
--cluster=$CLUST_NAME \
--name $NODE_NAME \
--node-ami-family WindowsServer2019CoreContainer \
--node-type=t3.large \
--nodes-min=1 \
--nodes-max=1 \
--node-private-networking \
--ssh-public-key=$SSHKEY \
--node-volume-size=100 \
--asg-access \
--external-dns-access \
--full-ecr-access \
--alb-ingress-access
eksctl utils install-vpc-controllers --name=$CLUST_NAME --approve
  • 更新或新增跳板機上的 kubeconfig。
aws eks  — region $YOUR_REGION update-kubeconfig  — name $CLUST_NAME
  • 大約十分鐘後讀者就可以用下述 command line 檢查你的 Windows node
kubectl get nodes

地端 Kubernetes for Windows

地端的安裝方法相對雲端困難很多,如果要做到 Multiple Control Plane 難度又增加了一點,如果安裝的環境又要指定 http proxy 才能對外連線,那難度就更大了。關於要怎麼要怎麼安裝原生 Kubernetes,筆者會寫一篇文章介紹,這邊讀者可以用下面的步驟清單確認安裝好 Control Plane & Linux node。

Docker service

  • OS: CentsOS 7 or 8
  • install yum-utils, device-mapper-persistent-data, lvm2
  • download and add yum docker repo
  • install docker-ce, docker-ce-cli, containerd.io
  • start docker service

Control Plane (google search the following action, or refer to 註三)

  • OS: CentOS 7 or 8
  • install nfs-utils, wget
  • disable firewalld
  • disable swap from /etc/fstab
  • download official yum repo for kubctl, kubeadm, kubelet
  • yum install kubectl, kubeadm, kubelet
  • enable kubelet service
  • disable SELinux
  • change docker cgroup to systemd in /etc/docker/daemon.json
  • change k8s cgroup to systemd in /etc/default/kubelet
  • restart docker service
  • change up tables & disable swappiness
  • reload sysctl
  • start/restart kubelet service
  • pull Kubernetes component docker image
  • download kube-flannel.yml, kube-flannel-overlay, kube-proxy-win (你可以在 github 搜尋筆者公開的)
  • reboot server
  • kubeadm init,請參考下述的參數,因為這跟上述的 flannel 有關,建議採用筆者建議設定。
kubeadm init --pod-network-cidr=172.31.0.0/16 --service-cidr=172.30.0.0/16 --kubernetes-version=1.18.6
  • install flannel (從筆者的公開 github repo 下載的)
kubectl apply -f kube-flannel.yml

Linux node,在完成上述的 Control Plane 後就可以安裝 Linux node

  • OS 建議與 Control Plane 相同
  • install nfs-utils
  • disable firewalld
  • disable swap from /etc/fstab
  • download official yum repo for kubctl, kubeadm, kubelet
  • yum install kubectl, kubeadm, kubelet
  • enable kubelet service
  • disable SELinux
  • change docker cgroup to systemd in /etc/docker/daemon.json
  • change k8s cgroup to systemd in /etc/default/kubelet
  • restart docker service
  • change up tables & disable swappiness
  • reload sysctl
  • start/restart kubelet service
  • reboot server
  • generate kubeadm join command on Control Plane node
kubeadm token create --print-join-command
  • join Kubernetes Cluster,用上述 join-command 產生的 command,範例如下:
kubeadm join {API SERVER} --token XXXXXXXXXXXXXXXX --discovery-token-ca-cert-hash sha256:xxxxxxxxxxxxxxxxxxxxxx

Docker for Windows,在完成上述的 Control Plane node 與 Linux node 安裝後就可以開始 Windows node。

  • disable windows firewall by PowerShell
  • install Docker Msft Module
  • install Docker
  • reboot
  • enable docker service and reboot again

Windows node,筆者很建議要用 Windows Server Core,筆者從 1.14 開始用 Windows node,當時步驟更為複雜。

  • OS: Windows 1809 build or greater
  • disable firewall firewall by PowerShell
netsh firewall set opmode mode=disable
  • 先下載需要的 container image
docker pull sigwindowstools/kube-proxy:v1.18.6
docker pull sigwindowstools/flannel:0.12.0
docker pull mcr.microsoft.com/windows/nanoserver:1809
docker tag mcr.microsoft.com/windows/nanoserver:1809 microsoft/nanoserver:latest
docker run microsoft/nanoserver:latest
docker pull mcr.microsoft.com/k8s/core/pause:1.2.0
  • 下載與套用 kube-flannel-overlay.yml and kube-proxy-win.yml(可以從筆者的 GitHub repo 下載)
kubectl apply -f kube-flannel-overlay.yml
kubectl apply -f kube-proxy-win.yml
  • install PowerShell activedirectory modules (for gMSA,如果不需要可以跳過)
Install-WindowsFeature RSAT-AD-PowerShell
  • Install Powershell CredentialSpec module (for gMSA,如果不需要可以跳過)
Install-Module -Name CredentialSpec
  • change NIC Ethernet0 card name to be Ethernet,如果本來網卡是別的名稱可以調整 Ethernet0
Rename-NetAdapter "Ethernet0" -NewName Ethernet -ErrorAction SilentlyContinue
  • disable 第一張網卡的其他網卡,這步要很確定沒有 disable 到正在連線的網卡
Get-NetAdapter | ? {$_.name -ne "Ethernet"} | Disable-NetAdapter
  • create c:\k directory (從 1.14 版以來的習慣)
mkdir c:\k
  • generate kubeadm join command on Control Plane node
kubeadm token create --print-join-command
  • 請保留下面的 command line,在每次 1.18.6 版本 Windows node 重啟後都需要執行。
powershell -command "if((docker network ls) -match 'host                nat'){$null}else{docker network create -d nat host}"
  • 讀者就可以用下述 command line 檢查你的 Windows node
kubectl get nodes

驗證

在驗證之前讀者需要安裝 kubectl 在你的電腦裡,你才能操作 Kubernetes Cluster,並且需要把複製 kubeconfig 內容檔案到你的電腦 ~/.kube/config 裡,在這筆者非常推廌 Kubernetes Lensapp,它是一套非常容易上手的 Kubernetes IDE app,它支援 Windows Desktop, MacOS, Linux Desktop。

在安裝完 Windows node 後,是時來佈署一個簡單的 Web on Windows Pod。

kubectl apply -f aspnet.yaml
  • 透過 kubectl 檢查 Pod 的狀態,並可以用 get pods 看到的 pod name 檢查 Pod 狀態的細節。因為 Windows Pod 大小常常都是 3GB 以上,所以如下圖所示,Kubernetes 正在拉 mcr.microsoft.com/dotnet/framework/aspnet:4.8 image
# get pods
kubectl get pods
# output
NAME READY STATUS RESTARTS AGE
aspnet-56887957d-4m5xg 0/1 ContainerCreating 0 10m
# describe pods
kubectl describe pods aspnet-56887957d-4m5xg
# output
Name: aspnet-56887957d-4m5xg
Namespace: default
Priority: 0
Node: <Windows NODE NAME>
Start Time: Sat, 28 Nov 2020 11:43:56 +0800
Labels: app=aspnet
pod-template-hash=56887957d
Annotations: <none>
Status: Pending
IP:
Controlled By: ReplicaSet/aspnet-56887957d
Containers:
aspnet:
Container ID:
Image: mcr.microsoft.com/dotnet/framework/aspnet:4.8
Image ID:
Port: 80/TCP
Host Port: 0/TCP
State: Waiting
Reason: ContainerCreating
Ready: False
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from default-token-72dc8 (ro)
Conditions:
Type Status
Initialized True
Ready False
ContainersReady False
PodScheduled True
Volumes:
default-token-72dc8:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-72dc8
Optional: false
QoS Class: BestEffort
Node-Selectors: beta.kubernetes.io/os=windows
Tolerations: node.kubernetes.io/not-ready:NoExecute for 300s
node.kubernetes.io/unreachable:NoExecute for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled <unknown> default-scheduler Successfully assigned default/aspnet-56887957d-4m5xg to xx-xxx-xxxxxx
Normal Pulling 10m kubelet, xx-xxx-xxxxxx Pulling image "mcr.microsoft.com/dotnet/framework/aspnet:4.8"
  • 過了十分鐘後 (取決你的網路下載速度)再檢查一次 Pod status,這次就可以看到 aspnet-56887957d-4m5xg pods 的狀態是 running。
# get pods
kubectl get pods
# output
NAME READY STATUS RESTARTS AGE
aspnet-56887957d-4m5xg 1/1 Running 0 15m
  • 得到 aspnet service 的 port number,如果要正式佈署就需要有完整的網路規劃,這邊先用 node port 的方式連線,如下指令的輸出你可以得到一個 tcp 31114 的 port number。
# get servicekubectl get service# outputNAME         TYPE        CLUSTER-IP    EXTERNAL-IP   PORT(S)        AGE
aspnet NodePort 172.30.2.14 <none> 80:31114/TCP 19m
  • 接下來可以透過瀏覽器開啟 http://Node_IP:31114 得到下述熟悉的畫面,至於要怎麼充實 Web service 那又是另外的故事了。

實做心得

筆者第一次實做 Windows node 是 1.14 版本,當時的 Windows node 的安裝方法比 1.18 版本複雜很多,現在 Windows node 可以跟 Linux node 一樣的用 kubeadm join 加入 Cluster 相信是很多人的付出才有這樣的便利。

在完成 Windows node 後,接下來的挑戰就是如何佈署 Windows Application 到 Kubernetes,並且集中 Windows Container log ,有關 Application 方面的探討筆者會另外寫一個 Kubernetes for Windows Application 篇。

至於真的需要 Windows node 嗎?這個問題其實要讀者們自行評估,筆者自己的 Cluster 是混合模式,因為 Windows 仍然有像 gMSA 這個優點,是 Linux node 沒有的,筆者在這是把過去所學與所遭遇到一一分享各位。

參考與註釋