Introduction to Kubernetes on Windows Server 2019

Kubernetes is the industry-standard container orchestration platform that automates deployment, scaling, and management of containerized applications. Windows Server 2019 can participate in Kubernetes clusters as a worker node, running Windows containers alongside Linux-based control plane components. As of Kubernetes 1.14 and later, Windows worker nodes are officially supported, enabling organizations to run both Linux and Windows workloads in the same Kubernetes cluster. The control plane components including the API server, etcd, scheduler, and controller manager must run on Linux nodes. Windows Server 2019 nodes run the kubelet, kube-proxy, and a container runtime such as containerd to execute Windows container workloads.

Prerequisites and Architecture Overview

A typical hybrid Kubernetes cluster for Windows Server 2019 workloads requires at least one Linux master node running the Kubernetes control plane, and one or more Windows Server 2019 worker nodes. For production, use three Linux master nodes for high availability. The Linux master can run Ubuntu 20.04, CentOS 8, or similar. The Windows worker nodes run Windows Server 2019. All nodes must have static IP addresses, network connectivity to each other, and unique hostnames. Disable Windows Firewall or configure it to allow Kubernetes traffic on ports 6443, 10250, 10251, 10252, and 2379-2380. Disable IPv6 if not in use.

Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
New-NetFirewallRule -DisplayName "Kubernetes API" -Direction Inbound -Protocol TCP -LocalPort 6443 -Action Allow
New-NetFirewallRule -DisplayName "Kubelet" -Direction Inbound -Protocol TCP -LocalPort 10250 -Action Allow

Preparing Windows Server 2019 as a Kubernetes Worker Node

Prepare the Windows Server 2019 node by enabling the Containers feature and ensuring the system meets Kubernetes requirements:

Install-WindowsFeature Containers -Restart
Get-ComputerInfo | Select-Object OsName, OsVersion
hostname

Disable Windows Defender real-time monitoring temporarily during installation to avoid performance issues (re-enable in production):

Set-MpPreference -DisableRealtimeMonitoring $true

Set a unique hostname for the node:

Rename-Computer -NewName "k8s-win-worker01" -Restart

Installing containerd on Windows Server 2019

Containerd is the recommended container runtime for Kubernetes on Windows Server 2019, replacing Docker for Kubernetes workloads. Install containerd:

$Version = "1.7.0"
curl.exe -L "https://github.com/containerd/containerd/releases/download/v${Version}/containerd-${Version}-windows-amd64.tar.gz" -o containerd.tar.gz
tar.exe -xvf containerd.tar.gz -C "C:Program Files"
mkdir C:Programdatacontainerdstate
mkdir C:Programdatacontainerdroot
containerd.exe config default | Out-File C:Programdatacontainerdconfig.toml

Register and start the containerd service:

& "C:Program Filesbincontainerd.exe" --register-service
Start-Service containerd
Set-Service containerd -StartupType Automatic
containerd --version

Installing crictl for containerd

crictl is the command-line interface for CRI-compatible container runtimes and is used by Kubernetes to manage containers and images through the CRI interface:

$Version = "v1.26.0"
curl.exe -L "https://github.com/kubernetes-sigs/cri-tools/releases/download/${Version}/crictl-${Version}-windows-amd64.tar.gz" -o crictl.tar.gz
tar.exe -xvf crictl.tar.gz -C "C:Windowssystem32"
crictl --version

Configure crictl to use the containerd socket:

$config = @"
runtime-endpoint: npipe:////./pipe/containerd-containerd
image-endpoint: npipe:////./pipe/containerd-containerd
timeout: 10
"@
$config | Out-File -FilePath "C:ProgramDatacrictl.yaml" -Encoding ascii

Installing kubelet and kube-proxy

Download and install the Kubernetes node components. Choose a Kubernetes version compatible with your control plane:

$KubeVersion = "v1.27.0"
$KubePath = "C:k"
mkdir $KubePath

curl.exe -L "https://dl.k8s.io/${KubeVersion}/bin/windows/amd64/kubelet.exe" -o "$KubePathkubelet.exe"
curl.exe -L "https://dl.k8s.io/${KubeVersion}/bin/windows/amd64/kube-proxy.exe" -o "$KubePathkube-proxy.exe"
curl.exe -L "https://dl.k8s.io/${KubeVersion}/bin/windows/amd64/kubectl.exe" -o "$KubePathkubectl.exe"

$env:PATH = "$env:PATH;C:k"
[Environment]::SetEnvironmentVariable("PATH", $env:PATH, "Machine")

Joining the Windows Node to the Kubernetes Cluster

Copy the kubeconfig file from the Linux master node to the Windows node. This file contains the cluster API server address and credentials. Store it at C:kconfig:

# From the Linux master, copy the join token
kubeadm token create --print-join-command
# This outputs a command like:
# kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:yyy

On Windows Server 2019, use the SIG Windows tooling to join the node to the cluster. Download and run the sig-windows-tools:

curl.exe -L "https://github.com/kubernetes-sigs/sig-windows-tools/releases/latest/download/PrepareNode.ps1" -o PrepareNode.ps1
.PrepareNode.ps1 -KubernetesVersion "v1.27.0"

kubeadm join 192.168.1.10:6443 --token your_token_here --discovery-token-ca-cert-hash sha256:your_hash_here

Verify the Windows node joined the cluster from the Linux master:

kubectl get nodes -o wide
kubectl describe node k8s-win-worker01

Installing a CNI Plugin for Windows

A Container Network Interface (CNI) plugin is required for pod networking. For Windows nodes, Flannel with the vxlan backend or Calico are commonly used. Install Flannel’s Windows-compatible overlay network. From the Linux master, apply the Flannel manifest for hybrid Windows/Linux clusters:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/sig-windows-tools/master/hostprocess/flannel/flanneld.yml

On the Windows node, verify the CNI is functioning:

ls C:kcniconfig
ipconfig | findstr "vEthernet"

Deploying Windows Workloads

Deploy Windows containers to the Windows worker node using node selectors to ensure Windows pods are scheduled on Windows nodes. Create a deployment manifest for a Windows IIS application:

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: iis-deployment
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: iis
  template:
    metadata:
      labels:
        app: iis
    spec:
      nodeSelector:
        kubernetes.io/os: windows
      containers:
      - name: iis
        image: mcr.microsoft.com/windows/servercore/iis:windowsservercore-ltsc2019
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: iis-service
spec:
  selector:
    app: iis
  ports:
  - port: 80
    targetPort: 80
  type: LoadBalancer
EOF

Verify deployment status:

kubectl get pods -o wide
kubectl get deployments
kubectl get services
kubectl describe pod -l app=iis

Troubleshooting Windows Kubernetes Nodes

Check kubelet and kube-proxy status on Windows worker nodes:

Get-Service kubelet, kube-proxy, containerd | Select-Object Name, Status
Get-WinEvent -LogName System -Source kubelet -MaxEvents 20 | Format-List TimeCreated, Message
kubectl logs -n kube-system -l component=kube-proxy
kubectl describe node k8s-win-worker01 | findstr -i "taints|conditions"

Common issues include pod networking failures from incorrect CNI configuration, certificate mismatch errors from clock skew (sync time with w32tm), and image pull failures. Check the kubelet logs:

Get-WinEvent -LogName "System" -MaxEvents 50 | Where-Object {$_.ProviderName -eq "kubelet"} | Format-List