Add a Windows worker node to MicroK8s

This guide explains how to add Windows worker nodes to MicroK8s. This how-to guide explains how to join a Windows Server 2022 to an existing MicroK8s cluster running Calico.

Note: Currently Windows nodes may be affected by a bug in the Windows container code for Server 2022/July update. Please see this issue for more details and a workaround:Container networking on Kubernetes broken after Server 2022 July 2024 / KB5040437 (OS Build 20348.2582) update · Issue #516 · microsoft/Windows-Containers · GitHub

Requirements

  • A node running Windows (tested with Windows Server 2022).
  • A MicroK8s cluster using the Calico CNI (enabled by default since 1.19).

Prepare the MicroK8s cluster

  1. Determine the exact version of Kubernetes running in the cluster, e.g. 1.27.1. You can use the following command:

    microk8s kubectl get node -o wide
    
  2. Determine the exact version of Calico running in the MicroK8s cluster, e.g. 3.25.0. For this, you can inspect the image used by the calico-node containers:

    microk8s kubectl get ds/calico-node -n kube-system -o jsonpath='{.spec.template.spec.containers[?(.name=="calico-node")].image}{"\n"}'
    
  3. Generate a kubeconfig file for the MicroK8s cluster. You will need this to run calicoctl commands, and later copy it to the Windows node.

    mkdir -p ~/.kube
    microk8s config > ~/.kube/config
    
  4. In order for Windows pods to schedule, strict affinity must be set to true. This is required to prevent Linux nodes from borrowing IP addresses from Windows nodes. This can be set with the calicoctl binary. Install the calicoctl binary with:

    CALICO_VERSION="3.25.0"
    curl -L https://github.com/projectcalico/calico/releases/download/v$CALICO_VERSION/calicoctl-linux-amd64 -o calicoctl
    chmod +x ./calicoctl
    

    (set the environment variable to the appropriate version of Calico for your system)
    Then, set strict affinity to true with the following command:

    ./calicoctl ipam configure --strictaffinity=true --allow-version-mismatch
    

Prepare the Windows node

All the commands shown next have to be run in a PowerShell window as Administrator.

  1. Install the containerd container runtime. The machine might restart during this operation.

    Invoke-WebRequest -UseBasicParsing "https://raw.githubusercontent.com/microsoft/Windows-Containers/Main/helpful_tools/Install-ContainerdRuntime/install-containerd-runtime.ps1" -o install-containerd-runtime.ps1
    
    .\install-containerd-runtime.ps1
    
  2. Create directory c:\k, create the kubeconfig file c:\k\config. After creating, open the file with notepad, paste the contents of the kubeconfig file of the MicroK8s cluster and save.

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

Install Calico

  1. Retrieve the install-calico-windows.ps1 script from the Calico GitHub releases page. NOTE: do not worry about the calico version in the URL, we will pick the Calico version to install later.

    Invoke-WebRequest -Uri https://github.com/projectcalico/calico/releases/download/v3.25.1/install-calico-windows.ps1 -OutFile c:\k\install-calico-windows.ps1
    
  2. Download Calico and Kubernetes binaries using the following command (replace 1.27.1 with the Kubernetes version and 3.25.0 with the Calico version running in the cluster):

    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.152.183.0/24" -DNSServerIPs "10.152.183.10"
    
  3. Configure the CNI bin and configuration directories and then install the Calico services. If the vSwitch is not yet created, this will temporarily affect network connectivity for a few seconds.

    $ENV:CNI_BIN_DIR="c:\program files\containerd\cni\bin"
    $ENV:CNI_CONF_DIR="c:\program files\containerd\cni\conf"
    c:\calicowindows\install-calico.ps1
    c:\calicowindows\start-calico.ps1
    

    If successful, the output should look like this:

    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
    
  4. Install Kubernetes services (kubelet and kube-proxy):

    c:\calicowindows\kubernetes\install-kube-services.ps1
    
  5. Add a firewall rule for incoming connections to the Windows kubelet node service. This is for kubectl logs and kubectl exec commands to work with pods running in Windows nodes:

    New-NetFirewallRule -Name 'Kubelet-In-TCP' -DisplayName 'Kubelet (node)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 10250
    
  6. Configure the network adapter name expected by the kubelet service.

    Run ipconfig to retrieve the IP configuration of the machine’s network adapters. An example output is shown below, where the network adapter name is vEthernet (tapeb67a82f-a1):

    Windows IP Configuration
    
    
    Ethernet adapter vEthernet (tapeb67a82f-a1):
    
    Connection-specific DNS Suffix  . : teststack.internal
    Link-local IPv6 Address . . . . . : fe80::d17b:a2e:c02c:1163%8
    IPv4 Address. . . . . . . . . . . : 172.16.1.177
    Subnet Mask . . . . . . . . . . . : 255.255.255.0
    Default Gateway . . . . . . . . . : 172.16.1.1
    

    Then, open c:\calicowindows\kubernetes\kubelet-service.ps1 with notepad and edit the default value of the InterfaceName parameter to match the one above.

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

    Save the file and exit notepad.

  7. Clean service arguments for kubelet. Open c:\calicowindows\kubernetes\kubelet-service.ps1 with notepad and ensure that:

    • For MicroK8s version 1.26 or newer, remove the argument --logtostderr=true
    • For MicroK8s version 1.27 or newer, remove the argument --container-runtime=remote

    Save the file and exit notepad.

  8. Start Kubernetes services

    Start-Service kubelet
    Start-Service kube-proxy
    

Verify the Windows node

  1. Ensure that the Windows node appears in the output of microk8s kubectl get nodes -o wide, for example:

    NAME   STATUS   ROLES    AGE    VERSION   INTERNAL-IP    EXTERNAL-IP   OS-IMAGE                                    KERNEL-VERSION      CONTAINER-RUNTIME
    u1     Ready    <none>   3h3m   v1.27.1   172.16.1.49    <none>        Ubuntu 20.04.5 LTS                          5.4.0-139-generic   containerd://1.6.15
    w1     Ready    <none>   105m   v1.27.1   172.16.1.177   <none>        Windows Server 2022 Datacenter Evaluation   10.0.20348.587      containerd://1.6.6
    
  2. Start a nanoserver pod on the Windows node and wait for it to come up.

    microk8s kubectl run test-pod -it --image mcr.microsoft.com/windows/nanoserver:ltsc2022 --overrides '{"spec": {"nodeSelector": {"kubernetes.io/os": "windows"}}}'
    

    The default timeout for the pod to come up is 60 seconds, which might not be enough for the nanoserver image to be fetched and the pod to be started. In that case, simply wait for the pod to start and then attach using microk8s kubectl attach -it test-pod

  3. Ensure that you can curl the kubernetes service, e.g. with curl https://kubernetes --insecure

    Microsoft Windows [Version 10.0.20348.1726]
    (c) Microsoft Corporation. All rights reserved.
    
    C:\>curl https://kubernetes --insecure
    {
    "kind": "Status",
    "apiVersion": "v1",
    "metadata": {},
    "status": "Failure",
    "message": "Unauthorized",
    "reason": "Unauthorized",
    "code": 401
    }
    

Uninstall the Windows worker node

On the Windows machine:

  1. Stop and uninstall Kubernetes services:

    c:\calicowindows\kubernetes\uninstall-kube-services.ps1
    
  2. Stop and uninstall Calico services:

    c:\calicowindows\uninstall-calico.ps1
    

On the MicroK8s machine:

  1. Remove the node from MicroK8s (replace w1 with the name of the Windows node):

    microk8s kubectl delete node w1
    

Upgrade

There is no specific upgrade path for a Windows node. If you have upgraded MicroK8s to a later version, and wish the Windows node to remain compatible, you should first uninstall the node components (see above), then re-install the newer version.

Further reading

6 Likes

Thanks for the tutorial.
Was finally able to get the windows host joined into microk8s and deploy an example windows pod running mcr.microsoft.com/windows/servercore:1809 :slight_smile:

2 Likes

Hi @joedborg , @evilnick the official calico docs link is no longer available.

https://docs.projectcalico.org/getting-started/windows-calico/standard

Is it this one now?

Thanks

Thanks for noticing that. I believe you are right , it is now on https://docs.projectcalico.org/getting-started/windows-calico/quickstart

Oh, i updated the link in the doc too :wink:

Hey all, I am trying to set up a mk8s cluster with a windows worker node now and not getting any errors from the installer scripts, but once done my cluster never shows the Windows worker node. I reviewed some of the troubleshooting tips on the Canonical and Calico sites but can’t see what’s wrong here.

I am able to use the clusters kubeconfig file on the windows node to interact with the cluster via kubectl and I have reviewed the networking and believe all required ports are open correctly between worker node and control plane…

Do you guys have and troubleshooting suggestions for good troubleshooting articles or other places to look for assistance?

Hi, I can’t get this to work. I can get the windows node joined and can run a pod on it but there is no network connectivity. I didn’t get any errors during installation though I did have to change the default service cluster ip range to 10.152.183.0/24 to match microk8s and to set the service cluster ip of DNS to 10.152.183.10 to match the DNS Service. From the calico config file:

$env:K8S_SERVICE_CIDR = “10.152.183.0/24”
$env:DNS_NAME_SERVERS = “10.152.183.10”

I’m running microk8s on Ubuntu 20.04 (v1.20.6-34+e4abae43f6acde)
The windows node is Windows Server 1809 (Build 17763.1879)

I had similar problem. After a close look at the logs the installation script produces and the script itself I’ve found that the script fails to properly detect Calico backend - in my case bgp was used instead of vxlan. Specifying the backend explicitly resolved the problem for me:

 . C:\install-calico-windows.ps1 -DownloadOnly yes -CalicoBackend "vxlan"

thanks @zjklee for your fix. I will investigate though the Calico docs say the script should work with BGP or vxlan

I feel like I’m so close, but just not there yet. If someone can help me. I followed the described steps to a letter.
I managed to join Windows worker node. I can run windows containers there, but DNS isn’t working. If I use an IP address than pod from windows node can reach pod on linux node. But it doesn’t work with the service name. Same thing works for linux container. I tried to change DNS_NAME_SERVERS to 10.152.183.10 with no effect. I also tried -CalicoBackend “vxlan” suggestion with no luck. Is there anything else I can try?

I have missed ServiceCidr parameter (10.152.183.0/24)…now it’s working! This is so satisfying!

1 Like

Hi, I have gone thru the steps and set up 3 Microk8s nodes in Ubuntu and 1 node in MS Windows 2019. It’s kind of working that I could create and get the pod/deployment working in the windows node if it is a windows image. However, the cluster just randomly assigns the node regardless of the type of image windows / Linux. Any idea that I am going wrong please. Thanks!

Every node is labeled with the following

kubernetes.io/os and kubernetes.io/arch

Where kubernetes.io/os can be linux or windows. Using these labels you can use nodeSelector to make sure pods are scheduled into the right node.

For more details check this out.

1 Like

@balchua1 Thank you so much!

Hi there,

Not sure what happens but my hybrid cluster (ubuntu + windows) cannot deploy the pod to the windows node now. Any idea, please. Below is the message from describe pod. Thanks!

Warning FailedCreatePodSandBox 43s kubelet Failed to create pod sandbox: rpc error: code = Unknown desc = failed to setup network for sandbox “27c91bdff828f4b4e6c07bd1ef2ae67e28395f265812603d50e307cb1b79a7c4”: failed to look up reserved IPs: connection is unauthorized: ipreservations.crd.projectcalico.org is forbidden: User “system:serviceaccount:kube-system:calico-node” cannot list resource “ipreservations” in API group “crd.projectcalico.org” at the cluster scope

It seems that happens after the rbac is enabled so how. Is that true? Any way to resolve it? Thanks!

This is odd, the ClusterRole for calico-node includes this crd.
Do you also see this error in your linux node?

Not in the Linux node; only in windows

Can you do a kubectl get daemonset -A
Do you see 2 different daemonsets? One for linux and one for windows?

Im just guessing here, the ClusterRole may not be available for the serviceaccount used for the calico in windows.

Due to the urgency, I recreate the whole cluster. But this error happened twice before. I’ll try to reproduce it and provide more information here. Thanks indeed!