After installing Kubernetes on WSL, how do we connect to it from Windows using kubectl or any other developer tool we might be using? As usual it turns out it can be done with just few lines of good old scripting π
Table of Contents
Before we start
Not surprisingly most of you probably already have WSL installed, but in case you donβt you can quickly get it directly from Microsoft Store for Windows 10/11. For this tutorial we will be using Ubuntu 22.04 with systemd enabled and microk8s installed. If you are using minikube approach is very similar with some additional steps as described below.
- Install and Set Up kubectl on Windows | Kubernetes
- Windows Subsystem for Linux
- Ubuntu 22.04
- Windows Terminal β optional, but highly recommended
- How to run microk8s on your WSL Ubuntu 22.04? | DevOpsify Me
Extract Kube Config from WSL
First to notice is that after installing microk8s inside of a WSL distribution, kubectl is fully configured inside of that distribution. Therefore we will focus on getting that configuration transferred to a Windows host. In order to do that, execute ‘kubectl config view –raw’ and save command output to a file inside our profile folder .kube. Take a look at a sample response in second tab.
wsl -d <DISTRIBUTION_NAME> -e /snap/bin/microk8s kubectl config view --raw > "~/.kube/config"
apiVersion: v1 clusters: - cluster: certificate-authority-data: <BASE64_ENCODED_CERT> server: https://127.0.0.1:16443 name: microk8s-cluster contexts: - context: cluster: microk8s-cluster user: admin name: microk8s current-context: microk8s kind: Config preferences: {} users: - name: admin user: token: VVRvMXpPdmxVYWFtaENKMXZsOVdub3BDbWdTb09YdXZIa2pTWkR1L04wOD0K
Next open saved file and update address from 127.0.0.1 to localhost. I know, I know, it is the same… but it isn’t π Localhost is special to WSL and only that hostname ports are forwarded, so we have to use it.
$distributionConfig = Get-Content -path "~/.kube/config" -Raw $distributionConfig = $distributionConfig -replace '127.0.0.1','localhost' $distributionConfig | Set-Content -Path "~/.kube/config"
At this point you are ready to try to connect! Lets type ‘kubectl get all --all-namespaces
‘ and see what happens π Wait… what? Are you getting an error?!
Unable to connect to the server: x509: certificate is valid for kubernetes, kubernetes.default, kubernetes.default.svc, kubernetes.default.svc.cluster, kubernetes.default.svc.cluster.local, not localhost
Ah yes, we need to update the Kubernetes API certificate to support localhost… host π Lucky for us, the only thing we need to do is to edit one configuration file csr.conf.template, by adding localhost to alt_names.
sudo nano /var/snap/microk8s/current/certs/csr.conf.template
[ alt_names ] DNS.1 = kubernetes DNS.2 = kubernetes.default DNS.3 = kubernetes.default.svc DNS.4 = kubernetes.default.svc.cluster DNS.5 = kubernetes.default.svc.cluster.local DNS.6 = localhost IP.1 = 127.0.0.1 IP.2 = 10.152.183.1
Finally save the file, wait some seconds to allow the service to notice modification, generate new certificate and apply all the changes. Now, only thing left is to test again, so verify if you get similar results to the ones on the picture below.
Multiple Kube Configs
In this paragraph lets talk about situation when we work with multiple clusters, each of them requiring different set of credentials. In such case, traditional approach involves either replacing or merging configuration files, but there is a much simpler way. All we need to do is to add multiple files to environmental variable KUBECONFIG.
Personally I recommend to store one config file per environment we work with, so that we don’t clutter single file. In addition it becomes easy to add/remove configurations, especially for our test environments. Reference .kube directory following this recommendation might look like this:
~/.kube
- cache/
- config
- azure.config
- microk8s.config
- minikube.config
Now, to achieve this, use the sample script below to update KUBECONFIG variable for the current user. I would recommend saving it inside of a .kube folder, so that every time you are adding new config you can run this script to update the environmental variable quickly. The script works like this:
- First find all config files in ~/.kube folder
- Then get current value of KUBECONFIG (in case it points at files outside ~/.kube)
- Next merge two lists and remove duplicates, update current session’s variable
- Finally also update in registry for all other future sessions
$discoveredConfigPath = Get-ChildItem "~/.kube/*config" | % { $_.FullName} $currentConfigPath = $null -ne $env:KUBECONFIG ? @($env:KUBECONFIG -split ';') : @() $env:KUBECONFIG = ($currentConfigPath + $discoveredConfigPath) | Sort-Object -Unique | Join-String -Separator ';' Set-ItemProperty Registry::HKEY_CURRENT_USER\Environment -Name KUBECONFIG -Value $env:KUBECONFIG
After script finishes, view merged kube config by running kubectl config view
and choose active context by running kubectl config use-context <CONTEXT-NAME>
.
Minikube version – final
All the above examples are made based on microk8s, but what if you are running minikube? Not a big deal, the only thing extra we need to do is to inline base64 encoded certificates π
$distributionName = "Ubuntu-22.04" $distroConfigPath = "~/.kube/minikube.config" wsl -d $distributionName -e /snap/bin/kubectl config view --raw > $distroConfigPath # get base64 encoded certificates $caCert = wsl -d $distributionName -e bash -c "cat /root/.minikube/ca.crt | base64 -w0" $clientCert = wsl -d $distributionName -e bash -c "cat /root/.minikube/profiles/minikube/client.crt | base64 -w0" $clientKey = wsl -d $distributionName -e bash -c "cat /root/.minikube/profiles/minikube/client.key | base64 -w0" # inline certs, update host and naming $distributionConfig = Get-Content -path $distroConfigPath -Raw $distributionConfig = $distributionConfig -replace "client-certificate.*","client-certificate-data: $clientCert" $distributionConfig = $distributionConfig -replace "client-key.*","client-key-data: $clientKey" $distributionConfig = $distributionConfig -replace "certificate-authority.*","certificate-authority-data: $caCert" $distributionConfig = $distributionConfig -replace "minikube","minikube-$distributionName" $distributionConfig = $distributionConfig -replace '127.0.0.1','localhost' $distributionConfig | Set-Content -Path $distroConfigPath $distributionConfig #update kubectl config path $currentConfigPath = $null -ne $env:KUBECONFIG ? @($env:KUBECONFIG -split ';') : @() $discoveredConfigPath = Get-ChildItem "~/.kube/*config" | % { $_.FullName} $env:KUBECONFIG = ($currentConfigPath + $discoveredConfigPath) | Sort-Object -Unique | Join-String -Separator ';' Set-ItemProperty Registry::HKEY_CURRENT_USER\Environment -Name KUBECONFIG -Value $env:KUBECONFIG #set current distro as main kubectl config use-context "minikube-$distributionName"
Microk8s version – final
In case you just want to get going, below is the full script that will get you started in no time π
$distributionName = "Ubuntu-22.04" $distroConfigPath = "~/.kube/microk8s.config" # extract kube config from WSL wsl -d $distributionName -e /snap/bin/microk8s kubectl config view --raw > $distroConfigPath # patch configuration file $distributionConfig = Get-Content -path $distroConfigPath -Raw $distributionConfig = $distributionConfig -replace "microk8s","microk8s-$distributionName" $distributionConfig = $distributionConfig -replace "admin","admin-$distributionName" $distributionConfig = $distributionConfig -replace '127.0.0.1','localhost' $distributionConfig | Set-Content -Path $distroConfigPath # update kubectl config path $currentConfigPath = $null -ne $env:KUBECONFIG ? @($env:KUBECONFIG -split ';') : @() $discoveredConfigPath = Get-ChildItem "~/.kube/*config" | % { $_.FullName} $env:KUBECONFIG = ($currentConfigPath + $discoveredConfigPath) | Sort-Object -Unique | Join-String -Separator ';' Set-ItemProperty Registry::HKEY_CURRENT_USER\Environment -Name KUBECONFIG -Value $env:KUBECONFIG # set current distro as main kubectl config use-context "microk8s-$distributionName"
Summary
To sum it all up, it turns out that it is rather simple to get connected from Windows to WSL deployed Kubernetes. We were able to achieve it in 3 simple steps: copy the config file, adjust values, update environmental variable. At the end we can now easily access our cluster from any development tool we prefer. Personally I work quite a lot in Visual Studio Code, so it is rather important for me to have that as smooth as possible π