在macOS中添加Windows节点用于开发调试

前提

在macOS下,已经通过UTM创建了两台Linux arm64的虚拟机

已经部署了k8s 1.28.5

想要添加一个windows节点运行负载

安装Windows虚拟机

首先下载Windows镜像

主要要选择Windows Server 2019 以后的版本(才支持Kubernetes)

这里使用了CrystalFetch下载的ISO镜像,并选择了Windows Server 23H2的版本

可以看到这里包含了Azure Stack HCI

azure stack HCI

安装系统

⚠️ UTM 安装的时候在加载镜像之后立即按ESC键,否则无法安装

安装的时候选择模拟(速度很慢) 因下载的iso镜像是X64架构的,而本机是M2的芯片是arm架构的,所以只能选择模拟

 emulate

安装一路默认,选择好镜像即可

default img

安装的时候看到这个提示尽快按下任意一个键盘(比如Enter/Esc),否则会进入UEFI启动菜单

press any key

如果没任何操作的话,是不会进入到windows 安装页面的,而是下面这个

UEFI menu

如果已经来到这里也没事,关闭虚拟机电源再进入重来一次

由于模拟的话实在太卡了,我安装的时候选择了

AzureStackHCICore, 这样安装的话是默认没有Windows的GUI管理界面的,但是可以通过命令行管理,甚至也能远程连接

install AzureStackHCICore

接下来的设置没啥特殊。

最后安装完成的话会有个菜单,按照提示按下4, 开启远程桌面

enable remote

网络设置

网卡和端口映射

编辑此虚拟机,这里添加了两个网络,一个模式选择了Emulated VLAN,这个是用于端口映射。另一个是Shared Network, 这个是用于和其他虚拟机(k8s互通的)

首先是Emulated VlAN网络设置如下映射,这是Windows的远程桌面

3389 mapping

远程连接

接下来用Microsoft Remote Desktop连接上去

账号是:Administrator,密码在安装的时候有设置

remote login

连接上之后按下15进入命令行模式,接下来就和SSH操作差不多了

remote cmd

调整K8s集群原有配置

Calico 网络配置 strict affinity

ref: https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/manual-install/quickstart#configure-strict-affinity-for-clusters-using-calico-networking Linux控制平面使用Calico网络的,为了防止Linux 节点从Windows节点借用IP地址,需要配置strict affinity 值为 true

kubectl patch ipamconfigurations default --type merge --patch='{"spec": {"strictAffinity": true}}'

关闭ipipMode

因Windows不支持此模式,所以修改ipipMode为Never:

kubectl edit ippools.crd.projectcalico.org

ipipMode

回到Windows继续安装K8s所需组件

安装containerd

在Windows上需要安装各种K8s需要的组件(containerd, kubelet, kube-proxy)

以下命令可能会出现网络不顺畅,重试几次

获取containerd安装脚本

Invoke-WebRequest -UseBasicParsing "https://raw.githubusercontent.com/microsoft/Windows-Containers/Main/helpful_tools/Install-ContainerdRuntime/install-containerd-runtime.ps1" -o install-containerd-runtime.ps1

如果上面获取失败,可以自己在网络顺畅的机器上打开链接,然后创建一个脚本并拷贝内容编辑 notepad install-containerd-runtime.ps1

保存后安装containerd, 包括nerdctl, cni二进制文件,默认安装的containerd版本为1.6.6, 可以自行修改,比如notepad install-containerd-runtime.ps1 修改$ContainerDVersion = "1.7.15"

.\install-containerd-runtime.ps1

查看当前K8s集群的配置,找一个linux的控制平面获取kubeconfig的内容,等下要用到

root@utm-debian2:~# cat ~/.kube/config 
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: xxx
    #server: https://apiserver.cluster.local:6443
    server: https://192.168.64.12:6443
  name: kubernetes
contexts:
- context:
    cluster: kubernetes
    user: kubernetes-admin
  name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: kubernetes-admin
  user:
    client-certificate-data: xxx
    client-key-data: xxx=

创建K8s的配置目录以及配置文件

mkdir c:\k
New-Item c:\k\config

notepad c:\k\config

这里打开notepad之后把上一步获取到的配置贴上并保存,注意server处可能是域名,自己替换成apiserver 的IP,比如server: https://192.168.64.12:6443或者把域名加入到windows 的hosts文件里面,比如 C:\Windows\System32\drivers\etc\Hosts

安装 Calico(和kubelet)

获取calico的windows安装脚本,这里用的是v3.25.1版本

Invoke-WebRequest -Uri https://github.com/projectcalico/calico/releases/download/v3.25.1/install-calico-windows.ps1 -OutFile c:\k\install-calico-windows.ps1

实际上calico的脚本不但能安装calico,还能安装kubernetes的二进制文件

这里指定了版本和CIDR,可以按需修改

c:\k\install-calico-windows.ps1 -ReleaseBaseURL "https://github.com/projectcalico/calico/releases/download/v3.25.0" -ReleaseFile "calico-windows-v3.25.0.zip" -KubeVersion "1.27.1" -DownloadOnly "yes" -ServiceCidr "10.96.0.0/22" -DNSServerIPs "10.96.0.10"

or 更新的版本

c:\k\install-calico-windows.ps1 -ReleaseBaseURL "https://github.com/projectcalico/calico/releases/download/v3.27.3" -ReleaseFile "calico-windows-v3.27.3.zip" -KubeVersion "1.28.5" -DownloadOnly "yes" -ServiceCidr "10.96.0.0/22" -DNSServerIPs "10.96.0.10"

因为这里需要从多个网站下载一些依赖组件比如

https://www.powershellgallery.com/packages/7Zip4Powershel

https://github.com/Microsoft/SDN/raw/master/Kubernetes/windows/hns.psm1

https://github.com/projectcalico/calico/releases/download/v3.27.2/

https://dl.k8s.io/release

是有可能失败的,可以多试几次

失败处理

最有可能失败的是7Zip4Powershell和K8s的二进制,而下载7zip其实是为了解压k8s的压缩包。

所以如果实在不行的话可以提前在网络顺畅的机器下载好,然后放在这台windows上即可规避

具体操作:

  1. 下载压缩包 https://cdn.dl.k8s.io/release/v1.27.1/kubernetes-node-windows-amd64.tar.gz

  2. 解压,得到目录kubernetes/node下若干二进制文件

  3. 复制kubernetes/node下所有文件(kube-log-runner.exe、kube-proxy.exe、kubeadm.exe、kubectl-convert.exe,kubectl.exe,kubelet.exe)到 windows机器的c:\k目录下

  4. 在Windows节点上执行notepad c:\k\install-calico-windows.ps1 进行编辑,将InstallK8sBinaries这一句注释掉,如下所示

    function PrepareKubernetes()
    {
        DownloadFiles
        ipmo -DisableNameChecking C:\k\hns.psm1
        #InstallK8sBinaries
    
        # Prepull and tag the pause image for docker
        if (-not (Get-IsContainerdRunning)) {
         #...
        }
    }
    
  5. 继续执行c:\k\install-calico-windows.ps1 …即可

执行完成后会下载并安装binary到c:\k目录下面

file

添加环境变量,然后安装calico插件

$ENV:CNI_BIN_DIR="c:\program files\containerd\cni\bin"
$ENV:CNI_CONF_DIR="c:\program files\containerd\cni\conf"
$ENV:CALICO_DATASTORE_TYPE="kubernetes"

c:\calicowindows\install-calico.ps1
c:\calicowindows\start-calico.ps1

耐心等待几分钟,看到下面输出说明完成


Starting Calico...
This may take several seconds if the vSwitch needs to be created.
Waiting for Calico initialisation to finish...
Waiting for Calico initialisation to finish...StoredLastBootTime , CurrentLastBootTime 5/21/2023 8:21:24 AM
Waiting for Calico initialisation to finish...StoredLastBootTime , CurrentLastBootTime 5/21/2023 8:21:24 AM
Calico initialisation finished.
Done, the Calico services are running:

Status   Name               DisplayName
------   ----               -----------
Running  CalicoFelix        Calico Windows Agent
Running  CalicoNode         Calico Windows Startup

接下来安装kubelet 和kube-proxy服务,

c:\calicowindows\kubernetes\install-kube-services.ps1

防火墙设置

涉及kubelet的一些操作比如kubectl logs和kubectl exec命令需要和windows nodes节点进行通信,所以这里要开放入站连接100250端口和kubelet windows 服务联通

New-NetFirewallRule -Name 'Kubelet-In-TCP' -DisplayName 'Kubelet (node)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 10250

定制kubelet的启动参数:

kubelet会选定一个网卡再启动,所以这里可以选择适当的网卡,如果有多个网卡,那就选和控制节点网络能互通的网卡。

先运行ipconfig,查看当前网络,在powershell中也可以执行Get-NetAdapter,获取网卡名

比如这里我获取到的是网卡名称为”vEthernet (Ethernet1)”

PS C:\Windows\system32> ipconfig

Windows IP Configuration

Ethernet adapter vEthernet (Ethernet1):

   Connection-specific DNS Suffix  . :
   IPv6 Address. . . . . . . . . . . : 2409:8a56:3443:7c30::1009
   IPv6 Address. . . . . . . . . . . : 2409:8a56:3443:7c30:4536:6e06:9675:9552
   Temporary IPv6 Address. . . . . . : 2409:8a56:3443:7c30:d31:18c9:a275:57f1
   Link-local IPv6 Address . . . . . : fe80::4536:6e06:9675:9552%3
   IPv4 Address. . . . . . . . . . . : 192.168.2.102
   Subnet Mask . . . . . . . . . . . : 255.255.255.0
   Default Gateway . . . . . . . . . : 192.168.2.1

Ethernet adapter vEthernet (Calico_ep):

   Connection-specific DNS Suffix  . :
   Link-local IPv6 Address . . . . . : fe80::5c13:f755:55fa:86e%25
   IPv4 Address. . . . . . . . . . . : 100.112.118.66
   Subnet Mask . . . . . . . . . . . : 255.255.255.192
   Default Gateway . . . . . . . . . : 100.112.118.65
PS C:\Windows\system32>
PS C:\Windows\system32>
PS C:\Windows\system32>
PS C:\Windows\system32> Get-NetAdapter

Name                      InterfaceDescription                    ifIndex Status       MacAddress             LinkSpeed
----                      --------------------                    ------- ------       ----------             ---------
Ethernet1                 Intel(R) 82574L Gigabit Network Co...#2      13 Up           00-0C-29-6F-7E-8A         1 Gbps
vEthernet (590be6e2a68...                                              36 Up           00-15-5D-89-68-93         1 Gbps
vEthernet (Ethernet1)     Hyper-V Virtual Ethernet Adapter              3 Up           00-0C-29-6F-7E-8A         1 Gbps

接下来用notepad或者其他编辑器打开kubelet的配置脚本  notepad c:\calicowindows\kubernetes\kubelet-service.ps1 修改默认的InterfaceName变量为相应的网卡名vEthernet (Ethernet1)

# c:\calicowindows\kubernetes\kubelet-service.ps1
Param(
    [string]$NodeIp="",
    [string]$InterfaceName="vEthernet (Ethernet1))"
)

另外:

  • K8s 1.26以上的版本,请移除或者注释掉参数-logtostderr=true
  • K8s 1.27以上的版本,请移除或者注释掉参数-container-runtime=remote

保存退出

最后启动Kubernetes的服务

Start-Service kubelet
Start-Service kube-proxy

错误处理以及调试指南

日志查看

如果启动不了,可以看看一些日志

比如kubelet的服务可以查看 c:\k\kubelet-error.log 以及c:\k\kubelet.out.log

containerd日志?

kube-proxy运行日志: c:\k\kube-proxy.out.log

Calico运行日志:C:\CalicoWindows\logs

例: 如果kubelet长时间未注册到集群,可以查看kubelet的日志

看到kubelet的错误:


cat c:\k\kubelet-error.log

E0324 07:45:12.792485    5936 run.go:74] "command failed" err="failed to parse kubelet flag: unknown flag: --logtostderr=true"

根据日志修正c:\calicowindows\kubernetes\kubelet-service.ps1 即可

正常来说过一会kubelet就会将当前的windows节点注册到集群,用kubectl get node就能看到

root@utm-debian2:~# kubectl get node -o wide
NAME              STATUS   ROLES           AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                         KERNEL-VERSION   CONTAINER-RUNTIME
utm-debian2       Ready    control-plane   9d    v1.28.5   192.168.2.101   <none>        Debian GNU/Linux 12 (bookworm)   6.1.0-13-arm64   containerd://1.6.26
win-nog6jk08mf6   Ready    <none>          23m   v1.28.5   192.168.2.102   <none>        Azure Stack HCI                  10.0.25398.1     containerd://1.7.15

修复dns fallback到Default模式

c:\calicowindows\kubernetes\kubelet-service.ps1 设置 kubelet: "--cluster-dns=10.96.0.10",`

替换google的pause镜像

另外以上方式安装的containerd默认用的sandbox镜像是k8s.gcr.io/pause:3.6

显然这个镜像是需要科学上网的,但是也可以修改默认的pause镜像

notepad "c:\Program Files\containerd\config.toml"

修改sandbox_image 为 mcr.microsoft.com/oss/kubernetes/pause:3.6

file

然后重启containerd服务

Stop-Service containerd
Start-Service containerd

或者自己在其他节点手动下载,然后导入其他节点下载的pause镜像

sudo ctr i pull registry.k8s.io/pause:3.6 --platform windows

sudo ctr i export pause.windows.tar registry.k8s.io/pause:3.6 --platform windows

还可以使用高版本的powershell设置代理后再拉取:

#pause镜像不替换,设置代理后手动拉一次
$env:HTTP_PROXY = "socks5://192.168.2.100:7891"
$env:HTTPS_PROXY = "socks5://192.168.2.100:7891"
ctr -n k8s.io image pull  registry.k8s.io/pause:3.8

验证:

kubectl run winpod-preview  --image mcr.microsoft.com/windows/server:win10-21h1-preview --overrides '{"spec": {"nodeSelector": {"kubernetes.io/os": "windows"}}}'

root@utm-debian2:~# kubectl get po
NAME             READY   STATUS        RESTARTS         AGE
winpod-preview   0/1     Running   114 (100m ago)   13h
root@utm-debian2:~# 

卸载

c:\calicowindows\kubernetes\uninstall-kube-services.ps1
c:\calicowindows\uninstall-calico.ps1
c:\k\kubectl.exe delete node  <node_name>

ref:

  • https://docs.tigera.io/calico/latest/getting-started/kubernetes/windows-calico/manual-install/quickstart
  • https://microk8s.io/docs/add-a-windows-worker-node-to-microk8s