<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Kubernetes Deployment on Digi Hunch</title><link>https://static.digihunch.com/tag/kubernetes-deployment/</link><description>Recent content in Kubernetes Deployment on Digi Hunch</description><generator>Hugo -- gohugo.io</generator><language>en-US</language><lastBuildDate>Tue, 08 Apr 2025 14:59:35 -0400</lastBuildDate><atom:link href="https://static.digihunch.com/tag/kubernetes-deployment/index.xml" rel="self" type="application/rss+xml"/><item><title>Kubernetes with Multiple CPU Architectures 2 of 2 – Node and Workload</title><link>https://static.digihunch.com/2023/05/kubernetes-with-multiple-cpu-architectures/</link><pubDate>Sat, 20 May 2023 01:34:00 -0400</pubDate><guid>https://static.digihunch.com/2023/05/kubernetes-with-multiple-cpu-architectures/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-k8s-multi-cpu.webp" alt="Featured image of post Kubernetes with Multiple CPU Architectures 2 of 2 – Node and Workload" /&gt;&lt;p class="wp-block-paragraph"&gt;The most common server CPU architectures today are amd64 (aka x86_64) and arm64. Although AMD developed the former first, Intel names it as x86_64 (or x64 for short). In terms of compatibility, they are the same. In general, arm64 architecture consumes less power and therefore mobile systems first favour it. Its power efficiency now drives a trend towards computing infrastructure. For example, Apple&amp;#8217;s MacBook moved to M1 processor in 2020. Since 2018, Amazon&amp;#8217;s &lt;a href="https://aws.amazon.com/ec2/graviton/"&gt;Graviton&lt;/a&gt; processor has entered the third generation. In 2022, Azure also brought &lt;a href="https://azure.microsoft.com/en-us/blog/azure-virtual-machines-with-ampere-altra-arm-based-processors-generally-available/"&gt;Ampere Altra&lt;/a&gt; processor, and GCP introduced &lt;a href="https://cloud.google.com/compute/docs/instances/arm-on-compute"&gt;ARM based VMs&lt;/a&gt;. Less power consumption ultimately leads to less computing cost.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I can only see more workloads gradually move to servers with ARM architecture. With Kubernetes, we will most likely have a fleet of computing node consisting of hybrid CPU architectures. We can take a look at what the arm64 adoption entails for workloads on Kubernetes.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading" id="h-arm64-architecture"&gt;ARM64 architecture&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Graviton processor is on 64-bit Arm Neoverse cores, targeting for optimizing cloud-native workloads. Currently at AWS, the majority of arm64 instances use Graviton2 processor. &lt;a href="https://aws.amazon.com/blogs/aws/new-graviton3-based-general-purpose-m7g-and-memory-optimized-r7g-amazon-ec2-instances/"&gt;This&lt;/a&gt; AWS blog posted the news about Graviton3-based general purpose (m7g) and memory-optimized (r7g) EC2 instances. At the bottom, there is a chart that compares the performance of Graviton3 with Graviton2, x86 and M6g instances. We can expect that in a few months the services that supports Graviton2 processor to start supporting Graviton3 processor.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In the serverless landscape, you can &lt;a href="https://docs.aws.amazon.com/lambda/latest/dg/foundation-arch.html"&gt;specify CPU architecture&lt;/a&gt; for Lambda function. If your runtime supports arm64 architecture, you enjoy up to 34% price performance improvement according to &lt;a href="https://aws.amazon.com/blogs/aws/aws-lambda-functions-powered-by-aws-graviton2-processor-run-your-functions-on-arm-and-get-up-to-34-better-price-performance/"&gt;this&lt;/a&gt; post. In late 2021, &lt;a href="https://docs.aws.amazon.com/AmazonECS/latest/userguide/ecs-arm64.html"&gt;AWS Fargate for ECS&lt;/a&gt; also started to support Graviton2 Processor with arm64 workload. As to Fargate for EKS, it has not supported Graviton2 processor as of yet, but is on &lt;a href="https://github.com/aws/containers-roadmap/issues/1629"&gt;track&lt;/a&gt;. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;As to Kubernetes, I&amp;#8217;ve discussed how to get container registries to support platform-specific images. So we can assume image registries all support OCI format image index(aka fat manifest), which points platform-specific images for arm64 and amd64. In this post, I&amp;#8217;ll focus on the node and workload, with EKS as an example. Since control plane is a managed service, we will focus on the worker node, where the Pods are running. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Worker Node&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The &lt;a href="https://github.com/digihunch/cloudkube"&gt;cloudkube&lt;/a&gt; project uses Terraform to build our test EKS cluster. One of the node groups consists of the new &lt;a href="https://aws.amazon.com/blogs/aws/new-graviton3-based-general-purpose-m7g-and-memory-optimized-r7g-amazon-ec2-instances/"&gt;m7g&lt;/a&gt;.large instance (Graviton3 processor). For this new node group, the AMI type must be AL2_ARM_64, so it picks up an &lt;a href="https://github.com/awslabs/amazon-eks-ami/releases"&gt;EKS optimized AMI &lt;/a&gt;for arm64 during node provisioning. The IAM role of each node has SSM policy so we can use session manager and pre-installed SSM agents to connect to each node. One the m7g node, I would like to check a few things:&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;The node CPU&lt;/li&gt;&#10;&lt;li&gt;The containerd package &lt;/li&gt;&#10;&lt;li&gt;The kubelet executable. &lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;They all should be for the right CPU architecture, as the following commands clarifies:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ lscpu | grep -i arch&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Architecture: aarch64&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ yum list | grep containerd&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;containerd.aarch64 1.6.6-1.amzn2.0.2 @amzn2extra-docker&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;containerd-stress.aarch64 1.6.8-1.amzn2.0.1 amzn2extra-docker&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ file -b &lt;span style="color:#66d9ef"&gt;$(&lt;/span&gt;which kubelet&lt;span style="color:#66d9ef"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ELF 64-bit LSB executable, ARM aarch64, version &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;(&lt;/span&gt;SYSV&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, dynamically linked &lt;span style="color:#f92672"&gt;(&lt;/span&gt;uses shared libs&lt;span style="color:#f92672"&gt;)&lt;/span&gt;, BuildID&lt;span style="color:#f92672"&gt;[&lt;/span&gt;sha1&lt;span style="color:#f92672"&gt;]=&lt;/span&gt;5c7a059f13f8bece4ce30f3357d57631c28bdde2, &lt;span style="color:#66d9ef"&gt;for&lt;/span&gt; GNU/Linux 3.7.0, stripped&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;We can also check image pulling with containerd. Let&amp;#8217;s check what is the correct image first, by examining the image index with &lt;a href="https://github.com/estesp/manifest-tool"&gt;manifest-tool&lt;/a&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ manifest-tool inspect digihunch/colorapp:v0.1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The index in the response tells us that:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;the digest of the whole image index starts with 0fa335;&lt;/li&gt;&#10;&lt;li&gt;the manifest digest for the arm64 variant starts with 7479df;&lt;/li&gt;&#10;&lt;li&gt;the manifest digest for the amd64 variant start with 1bd198;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Now if we use &lt;a href="https://iximiuz.com/en/posts/containerd-command-line-clients/"&gt;ctr&lt;/a&gt; utility to pull image (&amp;#8220;sudo ctl pull image&amp;#8221;), we can see the correct digest for arm64:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="1024" height="179" src="https://static.digihunch.com/wp-content/uploads/2023/05/arm64-digest.webp" alt="" class="wp-image-12917" srcset="https://static.digihunch.com/wp-content/uploads/2023/05/arm64-digest.webp 1024w, https://static.digihunch.com/wp-content/uploads/2023/05/arm64-digest-300x52.webp 300w, https://static.digihunch.com/wp-content/uploads/2023/05/arm64-digest-768x134.webp 768w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The default behaviour of &amp;#8220;&lt;code&gt;ctr image &lt;a href="https://pkg.go.dev/github.com/ktock/containerd#Client.Pull"&gt;pull&lt;/a&gt;&lt;/code&gt;&amp;#8221; is to pull a platform-specific image, overridable with &lt;code&gt;--platform&lt;/code&gt; or &lt;code&gt;--all-platforms&lt;/code&gt;. On an amd64 node, I get the corresponding results as well.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Workload&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Let&amp;#8217;s ensure all workloads in the Namespaces are using correct images. We&amp;#8217;ll &lt;a href="https://github.com/digihunch/cloudkube/blob/main/eks_workshop/appmesh/workload.yaml#L14-L39"&gt;deploy&lt;/a&gt; the colorapp, and then examine that along with some system Pods.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;For example, DaemonSet aws-node has one pod per node. To verify the distribution, we can get to pods&amp;#8217; command shell and check CPU architecture with &lt;code&gt;uname&lt;/code&gt; command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n kube-system get po -l app.kubernetes.io/name&lt;span style="color:#f92672"&gt;=&lt;/span&gt;aws-node -o name | xargs -I&lt;span style="color:#f92672"&gt;{}&lt;/span&gt; kubectl -n kube-system -c aws-node exec &lt;span style="color:#f92672"&gt;{}&lt;/span&gt; -- uname -m&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The command above verifies that pods scheduled to arm64 nodes correctly. It does not however, proof that the arm64-specific image is being used. I find it pretty tricky to validate a container is using intended image on arm64 node. I have not found a working kubectl command. There is a plausible tag named imageID under container status. For kube-proxy Pod in kube-system namespace, there are two values. However, for colorapp pods, there is only one value with different format, even though they are scheduled to nodes of both architectures.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n kube-system get po -l k8s-app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;kube-proxy -o yaml | grep &lt;span style="color:#e6db74"&gt;&amp;#39;imageID:&amp;#39;&lt;/span&gt; | sort | uniq&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; imageID: sha256:04beb3b811d345722d689a70a30bafa27e0edd412613bee76c3648b024b25744&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; imageID: sha256:b9b6705d4ad6be861f0e98b7325e5106715ef21a82692f7e8a005a280f159518&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl -n default get po -l app&lt;span style="color:#f92672"&gt;=&lt;/span&gt;color -o yaml | grep &lt;span style="color:#e6db74"&gt;&amp;#39;imageID:&amp;#39;&lt;/span&gt; | sort | uniq&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; imageID: docker.io/digihunch/colorapp@sha256:0fa335fdbcc3b644d57c8debe075775b19011985b6342adfb430e7011456d12e&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This &lt;a href="https://github.com/kubernetes/kubernetes/issues/108689"&gt;issue&lt;/a&gt; reports such inconsistency and the issue unfortunately did not get attention. The reporter also asks to have sha256 of the actual image. However, the Kubernetes developers regard this as an CRI issue. Currently we cannot tell exactly which image is used.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I figured out a workaround, by getting on the node and dump the image on the node:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo ctr -n k8s.io image list&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ sudo ctr -n k8s.io image export /tmp/x.tar docker.io/digihunch/colorapp@sha256:0fa335fdbcc3b644d57c8debe075775b19011985b6342adfb430e7011456d12e&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;In the export tar file review the manifest.json file which contains layer digests. We should find these layer digests match those of the platform-specific image&amp;#8217;s. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Utilities&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since we can ensure that Pod running on a node can always pull the correct platform-specific image, we do not need to worry about Helm chart. We just need to make sure our container registry references an index digest that points to images of multiple architecture. For the same reason, we do not need to worry about pod autoscaling. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When it comes to &lt;a href="https://static.digihunch.com/2022/03/autoscaling-in-kubernetes-from-metric-based-to-event-driven/"&gt;node autoscaling&lt;/a&gt;, all node should support have &lt;code&gt;kubernetes.io/arch&lt;/code&gt; and &lt;code&gt;kubernetes.io/os&lt;/code&gt; labels (e.g. &lt;a href="https://karpenter.sh/preview/faq/#can-karpenter-deal-with-workloads-for-mixed-architecture-cluster-arm-vs-amd"&gt;Karpenter&lt;/a&gt;). However, we generally prefer to expand the arm64 node group since it is cheaper. With cluster autoscaler, we can use &lt;a href="https://github.com/kubernetes/autoscaler/blob/master/cluster-autoscaler/expander/priority/readme.md"&gt;priority based expander.&lt;/a&gt; With Karpenter, we can set &lt;a href="https://karpenter.sh/v0.20.0/concepts/provisioning/#specweight"&gt;weight&lt;/a&gt; so that the provisioner for arm64 node group carries higher weight. &lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Scheduling&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;With multi-arch image, the container runtime will pick up the right version of image. From deployment perspective, we do not worry about the difference between nodes in CPU architectures. However, in some use cases, we still want to schedule certain Pods to nodes with one CPU architecture over the other. I call these platform-specific workload.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;We mainly needs to control scheduling behaviour. There are two mechanisms, node affinity, and taints &amp;amp; tolerations. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Node Affinity is based on node labeling. From the &lt;a href="https://kubernetes.io/docs/reference/labels-annotations-taints/"&gt;well-known labels, annotations and taints&lt;/a&gt;, all Kubernetes distribution should label their nodes with the &lt;code&gt;kubernetes.io/arch&lt;/code&gt; and &lt;code&gt;kubernetes.io/os&lt;/code&gt; labels. The value for arch is either &lt;code&gt;arm64&lt;/code&gt; or &lt;code&gt;amd64&lt;/code&gt;. When we add a &lt;a href="https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/"&gt;node affinity&lt;/a&gt; of &lt;code&gt;requiredDuringSchedulingIgnoredDuringExecution&lt;/code&gt; type to Pods, scheduler takes &lt;code&gt;matchExpressions&lt;/code&gt; under &lt;code&gt;nodeSelectorTerms&lt;/code&gt; into consideration, when placing Pods to Nodes. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;When a Pod has lots of nodeSelectorTerms, it can be brain twisting to sort through the logic. In that case we can use Taints and Tolerations. The idea is that once we taint a node, the scheduler will not schedule any Pod to the Node, unless the Pod has a matching Toleration.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;In &lt;a href="https://cablespaghetti.dev/2021/02/20/managing-multi-arch-kubernetes-clusters/#taints-and-tolerations"&gt;this&lt;/a&gt; post, the author customized the bootstrap script so the node provisioning process automatically taints arm64 nodes with &lt;code&gt;arch=arm64:NoSchedule&lt;/code&gt;. Otherwise, we can manually taint a node:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl get no -o wide &lt;span style="color:#75715e"&gt;# and check KERNEL-VERSION column, taint the ones with aarch64&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;$ kubectl taint nodes ip-147-207-3-164.us-west-2.compute.internal arch&lt;span style="color:#f92672"&gt;=&lt;/span&gt;arm64:NoSchedule&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This can be a very useful technique when you&amp;#8217;re not sure if every workload image are capable of multi-arch, and you want to avoid scheduling any Pods without tolerations on the arm64 nodes. A Pod cannot get scheduled on those nodes until you confirm their container images, and add corresponding tolerations.&lt;/p&gt;&#10;&lt;h2 class="wp-block-heading"&gt;Summary&lt;/h2&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Given the power efficiency, a lot of workload will gradually migrate to arm64 architecture. However, software will take a while to get ready. For example, &lt;a href="https://github.com/kubernetes/minikube/issues/9224"&gt;hyperkit&lt;/a&gt; has not supported M1 processor and I still cannot use it on newer MacOS for &lt;a href="https://static.digihunch.com/2021/09/single-node-kubernetes-cluster-minikube/"&gt;Minikube&lt;/a&gt;. Hybrid architecture is here to stay and we need to have an end-to-end examination of our supply chain.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2023/04/kubernetes-multiple-cpu-architecture-container-image/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Kubernetes with Multiple CPU Architectures 1 of 2 – Container Image&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2023/06/connect-kubectl-to-private-kubernetes-cluster-in-eks-and-aks/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Connect kubectl to private Kubernetes cluster in EKS and AKS&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item><item><title>Single-node Kubernetes cluster – docker desktop</title><link>https://static.digihunch.com/2021/08/docker-desktop-a-single-node-kubernetes-cluster/</link><pubDate>Sun, 22 Aug 2021 00:24:00 -0400</pubDate><guid>https://static.digihunch.com/2021/08/docker-desktop-a-single-node-kubernetes-cluster/</guid><description>&lt;img src="https://static.digihunch.com/wp-content/uploads/2025/04/feature-single-node-k8s.webp" alt="Featured image of post Single-node Kubernetes cluster – docker desktop" /&gt;&lt;p class="wp-block-paragraph"&gt;While there are many tools to set up single-node Kubernetes cluster (e.g. &lt;a href="https://minikube.sigs.k8s.io/docs/start/"&gt;minikube&lt;/a&gt;, &lt;a href="https://microk8s.io/"&gt;MicroK8s&lt;/a&gt;, &lt;a href="https://kind.sigs.k8s.io/docs/user/quick-start/"&gt;kind&lt;/a&gt;, or &lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt; with the &lt;a href="https://k3d.io/"&gt;k3d&lt;/a&gt; wrapper), docker-desktop has a significant advantage: it comes with Docker installation, on MacOS, or on Windows. It is installed simply by enabling the option &amp;#8220;Enable Kubernetes&amp;#8221;. It can be blown away and reset in a heartbeat (with the button &amp;#8220;Reset Kubernetes Cluster&amp;#8221;). For its versatility, docker-desktop is a great development environment.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;However, there are always nuances, which motivates me to write this blog. I wanted to note down what is on earth different about Docker-desktop, because the instructions for applications might differ slightly between single-node cluster on MacOS/Windows and the &amp;#8220;real&amp;#8221; multi-node cluster. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;I will start with with a deep dive into the docker-desktop architecture, then we&amp;#8217;ll go through the steps to install some common applications with Kubernetes.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-docker-desktop-on-macos"&gt;Docker-Desktop on MacOS&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;There are a number of open-source and proprietary projects involved to bring docker-desktop to implementation. Let&amp;#8217;s begin with the following five:&lt;/p&gt;&#10;&lt;ul class="wp-block-list"&gt;&#10;&lt;li&gt;&lt;a href="https://developer.apple.com/documentation/hypervisor"&gt;Hypervisor Framework&lt;/a&gt;: Apple&amp;#8217;s APIs on MacOS that allows you to interact with virtualization technologies in user space.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://wiki.freebsd.org/bhyve"&gt;bhyve&lt;/a&gt;: A type-2 hypervisor initially written for FreeBSD (and was contributed to FreeBSD in May 2011).&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/machyve/xhyve"&gt;xhyve&lt;/a&gt;: A port of bhyve project to MacOS with integration via Apple&amp;#8217;s Hypervisor Framework. The Hypervisor Framework allows xhyve to run entirely in userspace. It is sometimes loosely referred to as xhyve/bhyve hypervisor, and is optimized for lightweight virtual machines and container deployment.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/moby/hyperkit"&gt;HyperKit&lt;/a&gt; is an open-source toolkit on macOS based on xhyve. HyperKit is lightweight and therefore allows you to embed hypervisor capabilities in your application. The hypervisor component in HyperKit is based on xhyve/bhyve. HyperKit is designed to be interfaced with higher-level components such as the VPNKit and DataKit. Docker-desktop and &lt;a href="https://minikube.sigs.k8s.io/docs/drivers/hyperkit/"&gt;MiniKube&lt;/a&gt; are built on HyperKit.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/moby/hyperkit"&gt;LinuxKit&lt;/a&gt; is a toolkit for building custom minimal, immutable and purpose-build Linux distributions. It supports several well-known hypervisor platforms, such as HyperKit, Hyper-V, qemu and VMware. &lt;a href="https://www.docker.com/blog/introducing-linuxkit-container-os-toolkit/"&gt;LinuxKit&lt;/a&gt; started as an internal project in Docker Inc and is now managed as a &lt;a href="https://mobyproject.org/"&gt;Moby Project&lt;/a&gt;.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Hyperkit is installed as part of docker desktop. The process can be found with ps command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;ps -Af | grep hyperkit&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Or in the activity monitor:&lt;/p&gt;&#10;&lt;figure class="wp-block-image size-full"&gt;&lt;img loading="lazy" decoding="async" width="942" height="303" src="https://static.digihunch.com/wp-content/uploads/2021/08/image-3.png" alt="" class="wp-image-2648"/&gt;&lt;figcaption class="wp-element-caption"&gt;Docker related processes&lt;/figcaption&gt;&lt;/figure&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker-deskop is essentially a LinuxKit virtual machine (as defined &lt;a href="https://github.com/linuxkit/linuxkit/blob/master/examples/docker-for-mac.yml"&gt;here&lt;/a&gt;). It runs containerd process inside of the virtual machine. &lt;a href="https://collabnix.com/how-docker-for-mac-works-under-the-hood/"&gt;This&lt;/a&gt; is an older article about this architecture. If Kubernetes is enabled, the virtual machine is also installed with kubelet, the agent process running on each Kubernetes node.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Since MacOS is not the direct host of the containers, there is no way to map MacOS file system to container&amp;#8217;s as you can with a Docker/Kubernetes host. Prior to Docker 20.10, there used to be a &lt;a href="https://timonweb.com/docker/getting-path-and-accessing-persistent-volumes-in-docker-for-mac/"&gt;trick&lt;/a&gt; to indirectly access host volume from MacOS terminal. It has stopped working according to &lt;a href="https://github.com/docker/for-mac/issues/4822"&gt;this&lt;/a&gt; issue but workarounds are provided &lt;a href="https://stackoverflow.com/questions/63445657/why-i-am-getting-screen-is-terminating-error-in-macos/63595817#63595817"&gt;here&lt;/a&gt;. This &lt;a href="https://gist.github.com/BretFisher/5e1a0c7bcca4c735e716abf62afad389#2021-update-easiest-option-is-justins-repo-and-image"&gt;post&lt;/a&gt; proposes some good alternatives to access the file system of LinuxKit VM. For example, use netcat:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; user@LinVM &lt;span style="color:#75715e"&gt;# nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/ &lt;span style="color:#75715e"&gt;# cat /etc/os-release&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat /etc/os-release&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PRETTY_NAME&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;&amp;#34;Docker Desktop&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;/ &lt;span style="color:#75715e"&gt;# cat /etc/kubernetes/current-version&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;cat /etc/kubernetes/current-version&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubeadm version: &amp;amp;version.Info&lt;span style="color:#f92672"&gt;{&lt;/span&gt;Major:&lt;span style="color:#e6db74"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;, Minor:&lt;span style="color:#e6db74"&gt;&amp;#34;21&amp;#34;&lt;/span&gt;, GitVersion:&lt;span style="color:#e6db74"&gt;&amp;#34;v1.21.2&amp;#34;&lt;/span&gt;, GitCommit:&lt;span style="color:#e6db74"&gt;&amp;#34;092fbfbf53427de67cac1e9fa54aaa09a28371d7&amp;#34;&lt;/span&gt;, GitTreeState:&lt;span style="color:#e6db74"&gt;&amp;#34;archive&amp;#34;&lt;/span&gt;, BuildDate:&lt;span style="color:#e6db74"&gt;&amp;#34;2021-06-18T05:24:26Z&amp;#34;&lt;/span&gt;, GoVersion:&lt;span style="color:#e6db74"&gt;&amp;#34;go1.16.5&amp;#34;&lt;/span&gt;, Compiler:&lt;span style="color:#e6db74"&gt;&amp;#34;gc&amp;#34;&lt;/span&gt;, Platform:&lt;span style="color:#e6db74"&gt;&amp;#34;linux/amd64&amp;#34;&lt;/span&gt;&lt;span style="color:#f92672"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Typing in this terminal session feels clunky. According to this &lt;a href="https://stackoverflow.com/questions/64530530/how-may-i-connect-to-a-docker-desktop-virtual-machine-on-mac-docker-desktop-ve"&gt;thread&lt;/a&gt;, we can connect to the LinuxKit VM with tty and sane auto completion, using the command below:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;stty -echo -icanon &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; stty sane&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;There are some other alternatives, using privileged Docker containers:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -it --privileged --pid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;host debian nsenter -t &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; -m -u -n -i sh&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The following command uses a smaller image:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;docker run -it --rm --privileged --pid&lt;span style="color:#f92672"&gt;=&lt;/span&gt;host justincormack/nsenter1&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;As with Kubernetes, to access the file system on the node is via a privileged container, you can follow the &lt;a href="https://docs.microsoft.com/en-us/azure/aks/ssh"&gt;tips&lt;/a&gt; from Azure, identify node name, and debug against the node using a special container:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl debug node/docker-desktop -it --image&lt;span style="color:#f92672"&gt;=&lt;/span&gt;mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;Note that the root directory on the host is mounted to container&amp;#8217;s file system as /host. This mapping renders a lot of symbolic link as dangled, even though they are actually not on the host file system. &lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-docker-desktop-on-windows"&gt;Docker-Desktop on Windows&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker works with Linux kernel. There have been a couple of efforts to run Linux virtual machine on Windows. For example, Hyper-V backend, and Windows Subsystem Linux (WSL) backend.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Traditionally, Docker on Windows was implemented with Hyper-V as the hypervisor. A LinuxKit distro is running on the Hypver-V VM, provider Linux kernel capabilities. Docker refers to containers running in this architecture as &amp;#8220;Windows Containers&amp;#8221;, which is a misnomer in my opinion.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The first release of &lt;a href="https://www.zdnet.com/article/under-the-hood-of-microsofts-windows-subsystem-for-linux/"&gt;WSL &lt;/a&gt;provides a Linux-compatible kernel interface and runs a GNU user space on top of the interface. Neither the Linux kernel code, or a hypervisor is involved. The user space contains GNU Bash shell, command language, command-line tools and interpreters. The absence of Linux kernel in WSL, makes it useless for Docker setup. At that time The Hypver-V backend was still the only option to host docker container during the first version of WSL. This &lt;a href="https://www.docker.com/blog/new-docker-desktop-wsl2-backend/"&gt;post &lt;/a&gt;has a diagram of Docker on Windows with Hyper-V backend.&lt;/p&gt;&#10;&lt;div class="wp-block-image"&gt;&#10;&lt;figure class="aligncenter size-full"&gt;&lt;img loading="lazy" decoding="async" width="741" height="192" src="https://static.digihunch.com/wp-content/uploads/2021/08/image-1.png" alt="" class="wp-image-2616"/&gt;&lt;/figure&gt;&#10;&lt;/div&gt;&#10;&lt;p class="wp-block-paragraph"&gt;&lt;a href="https://static.digihunch.com/2020/06/wsl2-environment-on-windows-10/"&gt;WSL2&lt;/a&gt; comes with a real Linux Kernel (also on top of Hyper-V), making WSL2 a better alternative than the legacy Hyper-V as the backend of Docker on Windows. It can be turned on as the screenshot shows above. The rest of this post assumes WSL2 as backend. In this setup, we run a Bootstrapping distro independent of the WSL2 Linux distro, although both inside of the lightweight Linux Utility VM. Below is the diagram:&lt;/p&gt;&#10;&lt;p class="has-text-align-center wp-block-paragraph"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="691px" viewBox="-0.5 -0.5 691 371" style="max-width:100%;max-height:371px;"&gt;&lt;defs&gt;&lt;/defs&gt;&lt;g&gt;&lt;rect x="0" y="0" width="690" height="370" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;rect x="10" y="30" width="150" height="230" fill="#fad9d5" stroke="#ae4132" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 148px; height: 1px; padding-top: 27px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Windows&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="85" y="27" fill="#000000" font-family="Helvetica" font-size="17px" text-anchor="middle"&gt;Windows&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="10" y="280" width="650" height="70" rx="10.5" ry="10.5" fill="#dae8fc" stroke="#6c8ebf" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 648px; height: 1px; padding-top: 315px; margin-left: 11px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "&gt;Hypervisor (Hyper-V)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="335" y="320" fill="#000000" font-family="Helvetica" font-size="17px" text-anchor="middle" font-weight="bold"&gt;Hypervisor (Hyper-V)&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="170" y="30" width="490" height="230" fill="#fff2cc" stroke="#d6b656" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-end; justify-content: unsafe center; width: 488px; height: 1px; padding-top: 27px; margin-left: 171px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "&gt;Lightweight Linux Utility VM&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="415" y="27" fill="#000000" font-family="Helvetica" font-size="17px" text-anchor="middle" font-weight="bold"&gt;Lightweight Linux Utility VM&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="190" y="180" width="450" height="60" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 448px; height: 1px; padding-top: 210px; margin-left: 191px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "&gt;WSL2 Linux Kernel&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="415" y="215" fill="#333333" font-family="Helvetica" font-size="17px" text-anchor="middle" font-weight="bold"&gt;WSL2 Linux Kernel&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="180" width="120" height="60" fill="#f5f5f5" stroke="#666666" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 210px; margin-left: 31px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #333333; line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; word-wrap: normal; "&gt;NT Kernel&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="90" y="215" fill="#333333" font-family="Helvetica" font-size="17px" text-anchor="middle" font-weight="bold"&gt;NT Kernel&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="30" y="80" width="120" height="60" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 110px; margin-left: 31px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Windows Usermode&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="90" y="115" fill="#000000" font-family="Helvetica" font-size="17px" text-anchor="middle"&gt;Windows Usermo&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="190" y="70" width="250" height="80" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 248px; height: 1px; padding-top: 110px; margin-left: 191px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;&lt;div&gt;WSL2-compatible Linux Distro&lt;/div&gt;&lt;div&gt;in Usermode (e.g. Ubuntu)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="315" y="115" fill="#000000" font-family="Helvetica" font-size="17px" text-anchor="middle"&gt;WSL2-compatible Linux Distro&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;rect x="470" y="70" width="170" height="80" fill="#d5e8d4" stroke="#82b366" pointer-events="all"&gt;&lt;/rect&gt;&lt;g transform="translate(-0.5 -0.5)"&gt;&lt;switch&gt;&lt;foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 168px; height: 1px; padding-top: 110px; margin-left: 471px;"&gt;&lt;div style="box-sizing: border-box; font-size: 0; text-align: center; "&gt;&lt;div style="display: inline-block; font-size: 17px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; "&gt;Docker desktop&lt;br&gt;(Bootstrapping distro)&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;text x="555" y="115" fill="#000000" font-family="Helvetica" font-size="17px" text-anchor="middle"&gt;Docker desktop&amp;#8230;&lt;/text&gt;&lt;/switch&gt;&lt;/g&gt;&lt;path d="M 414.96 274.61 L 414.99 246.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 414.95 279.86 L 411.46 272.86 L 414.96 274.61 L 418.46 272.87 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 415 241.12 L 418.49 248.12 L 414.99 246.37 L 411.49 248.11 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 555.67 173.63 L 555.18 156.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 555.82 178.88 L 552.12 171.98 L 555.67 173.63 L 559.12 171.79 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 555.03 151.12 L 558.73 158.02 L 555.18 156.37 L 551.73 158.21 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 249.88 173.63 L 249.97 156.37" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 249.86 178.88 L 246.39 171.86 L 249.88 173.63 L 253.39 171.9 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 249.99 151.12 L 253.46 158.14 L 249.97 156.37 L 246.46 158.1 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 156.37 110 L 183.63 110" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 151.12 110 L 158.12 106.5 L 156.37 110 L 158.12 113.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 188.88 110 L 181.88 113.5 L 183.63 110 L 181.88 106.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 90 73.63 L 90 60 Q 90 50 100 50 L 545 50 Q 555 50 555 56.82 L 555 63.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 90 78.88 L 86.5 71.88 L 90 73.63 L 93.5 71.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 555 68.88 L 551.5 61.88 L 555 63.63 L 558.5 61.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 446.37 110 L 463.63 110" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 441.12 110 L 448.12 106.5 L 446.37 110 L 448.12 113.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 468.88 110 L 461.88 113.5 L 463.63 110 L 461.88 106.5 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 90 146.37 L 90 173.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 90 141.12 L 93.5 148.12 L 90 146.37 L 86.5 148.12 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 90 178.88 L 86.5 171.88 L 90 173.63 L 93.5 171.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 89.84 246.37 L 89.16 273.63" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"&gt;&lt;/path&gt;&lt;path d="M 89.97 241.12 L 93.3 248.2 L 89.84 246.37 L 86.3 248.03 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;path d="M 89.03 278.88 L 85.7 271.8 L 89.16 273.63 L 92.7 271.97 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"&gt;&lt;/path&gt;&lt;/g&gt;&lt;switch&gt;&lt;g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"&gt;&lt;/g&gt;&lt;a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank" rel="noopener"&gt;&lt;text text-anchor="middle" font-size="10px" x="50%" y="100%"&gt;Viewer does not support full SVG 1.1&lt;/text&gt;&lt;/a&gt;&lt;/switch&gt;&lt;/svg&gt;&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The innovative component is the lightweight Linux Utility VM. It is called a VM, but very different from the traditional sense of VM such as VirtualBox or VMware. Traditional VM is isolated from host OS, slow to boot and has large memory footprint. The lightweight Utility VM on the other hand, is integrated with host OS, super fast to boot (i.e. ~1 second), and comes with small memory footprint. It is not turned on until needed. The VM runs both a WSL2 Linux Kernel and GNU/Linux usermode (known as &amp;#8220;distribution&amp;#8221;, for example, Ubuntu). When an end-user say WSL2, s/he most likely refers to the distribution. Similarly, the so called &amp;#8220;docker-desktop on Windows with WSL2 backend&amp;#8221;, is also managed as two WSL2 distros: the bootstrapping distro (docker-desktop) and the data store distro (docker-desktop-data). &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The detailed components in the Bootstrapping distro is in the second diagram in &lt;a href="https://www.docker.com/blog/new-docker-desktop-wsl2-backend/"&gt;this &lt;/a&gt;post, which has a detailed discussion. With this architecture, you don&amp;#8217;t even need the WSL2 Linux Distro for Docker desktop to function. You can even run docker CLI command from Windows PowerShell without any Linux distro (although this is implemented only for backward compatibility and not recommended anymore) . In the following session, we first list out the WSL2 distros. Notice that the docker-desktop distro is not the default. We then connect to the distro using -d switch. Last, we run docker info from windows user space.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PS C:\WINDOWS\system32&amp;gt; wsl -l -v&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; NAME STATE VERSION&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;* Ubuntu Running &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; docker-desktop Running &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; docker-desktop-data Running &lt;span style="color:#ae81ff"&gt;2&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PS C:\WINDOWS\system32&amp;gt; wsl -d docker-desktop&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WINLAPTOP&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;/mnt/host/c/WINDOWS/system32&lt;span style="color:#75715e"&gt;# cd ~&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WINLAPTOP&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;~&lt;span style="color:#75715e"&gt;# printenv|grep DIST&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WSL_DISTRO_NAME=docker-desktop&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;WINLAPTOP&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;~&lt;span style="color:#75715e"&gt;# exit&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;PS C:\WINDOWS\system32&amp;gt; docker info&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Client&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Context&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Debug Mode&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; false&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Plugins&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; buildx&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Build with BuildKit (Docker Inc., v0.5.&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;-docker)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; compose&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Docker Compose (Docker Inc., v2.0.&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;-beta.&lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; scan&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Docker Scan (Docker Inc., v0.8.&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Server&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Containers&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;93&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Running&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;80&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Paused&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Stopped&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;13&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Images&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;28&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Server Version&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;20.10&lt;/span&gt;.7&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Storage Driver&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; overlay2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Backing Filesystem&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; extfs&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Supports d_type&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Native Overlay Diff&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; true&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; userxattr&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; false&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Logging Driver&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; json-file&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Cgroup Driver&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; cgroupfs&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Cgroup Version&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;1&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Plugins&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Volume&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; local&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Network&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; bridge host ipvlan macvlan null overlay&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Log&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Swarm&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; inactive&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Runtimes&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; io.containerd.runc.v2 io.containerd.runtime.v1.linux runc&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;Default&lt;/span&gt; Runtime&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; runc&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Init Binary&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; docker-init&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; containerd version&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; d71fcd7d8303cbf684402823e425e9dd2e99285d&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runc version&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; init version&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; de40ad0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Security Options&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; seccomp&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Profile&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;default&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Kernel Version&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;5.10&lt;/span&gt;.16.&lt;span style="color:#ae81ff"&gt;3&lt;/span&gt;-microsoft-standard-WSL2&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Operating System&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; Docker Desktop&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; OSType&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; linux&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Architecture&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; x86_64&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; CPUs&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;4&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Total Memory&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;12&lt;/span&gt;.32GiB&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Name&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; docker-desktop&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ID&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; WHDE&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;PJF3&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;HVFC&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;AZJA&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;EDKH&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;VUZR&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;RRUJ&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;HHXX&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;TDV5&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;4UJG&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;XY4E&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;PK4F&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Docker Root Dir&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; /var/lib/docker&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Debug Mode&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; false&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Registry&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; https&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;//index.docker.io/v1/&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Labels&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Experimental&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; false&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Insecure Registries&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#ae81ff"&gt;127.0&lt;/span&gt;.0.&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;/&lt;span style="color:#ae81ff"&gt;8&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Live Restore Enabled&lt;span style="color:#960050;background-color:#1e0010"&gt;:&lt;/span&gt; false&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The wsl -d command as illustrated above is a good way to connect to the docker-desktop distro. The alternative to get to the distro is via privileged container (Docker) or helper pod (Kubernetes), which is the same as in Docker-desktop on MacOS. Refer to the section above for specific steps.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-application-install-on-docker-desktop"&gt;Application Install on docker desktop&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The followings are my notes to install commonly used applications in Docker Desktop with Kubernetes. They work on both MacOS or WSL2, requiring Kubernetes enabled.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-metric-server"&gt;Metric server&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The metric server is provided in the &lt;a href="https://github.com/kubernetes-sigs/metrics-server"&gt;official&lt;/a&gt; repository. Releases are publish &lt;a href="https://github.com/kubernetes-sigs/metrics-server/releases/latest"&gt;here&lt;/a&gt;, which provides the installation step as follows:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.5.0/components.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;However, there is some issues when deploying it on MacOS, the deployment will fail due to certificates not matching the hostname. To fix the issue, it is recommended to download the yaml file (components.yaml), and edit the file by adding &amp;#8211;kubelet-insecure-tls to the args section of the container named &lt;em&gt;metrics-server&lt;/em&gt;. This is sufficient to fix the issue. Some people are not comfortable with port 443 being insecure TLS, and would rather change the port to 4443. This is completely unnecessary but if that&amp;#8217;s the case, make sure the named port for https is also updated to 4443.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Once metric server has been installed, the following two commands should return meaningful results:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl top no&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl top po&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;This should also allow application (Pods) to query for cluster resource usage. When working with single-node cluster on MacOS or WSL2, multiple Pods might come up with a single command and the memory can be easily over-subscribed. The two commands above allows you to check and make adjustment on the node configuration (in Docker preference).&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-dashboard"&gt;Dashboard&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similar to metric server, the dashboard is kept in the official &lt;a href="https://github.com/kubernetes/dashboard"&gt;repo&lt;/a&gt;, in the the path of aio/deploy/recommended.yaml. &lt;a href="https://github.com/kubernetes/dashboard/releases/latest"&gt;Here&lt;/a&gt; is the published release, where the instruction says:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.3.1/aio/deploy/recommended.yaml&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;However, this is not directly applicable either because the login page requires token or kubeconfig. We need to be able to bypass that. To do so, download the yaml file (recommended.yaml), and add parameter &amp;#8211;enable-skip-login to the args section for the container named &lt;em&gt;kubernetes-dashboard&lt;/em&gt;.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To display the login page properly, we need to start the proxy using this command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl proxy&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;The login page will then be available at &lt;a href="http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/"&gt;this&lt;/a&gt; URL. The URL reflects the namespace and service name. On the login page, the skip button will be available.&lt;/p&gt;&#10;&lt;h4 class="wp-block-heading" id="h-rancher"&gt;Rancher&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;The official installation guide of Rancher 2.5x recommends RKE Kubernetes. If you prefer not to run a separate cluster on MacOS, you can install it on docker desktop (with Kubernetes enabled). The installation steps require Helm 3 and are completed in three helm commands.&lt;/p&gt;&#10;&lt;ol class="wp-block-list"&gt;&#10;&lt;li&gt;Install Nginx Ingress controller using Helm, following the three commands &lt;a href="https://kubernetes.github.io/ingress-nginx/deploy/#using-helm"&gt;here&lt;/a&gt;. Alternatively, you can apply the rendered template as posted &lt;a href="https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.48.1/deploy/static/provider/cloud/deploy.yaml"&gt;here&lt;/a&gt;. The controller will later be used by the ingress that Rancher&amp;#8217;s chart creates.&lt;/li&gt;&#10;&lt;li&gt;Follow the steps on &lt;a href="https://ranchermanager.docs.rancher.com/getting-started/installation-and-upgrade/install-upgrade-on-a-kubernetes-cluster"&gt;this&lt;/a&gt; page to install Rancher, even though the page does not say it applies to docker desktop. If you do not have TLS certificate, the Rancher helm chart can generate one for you, using cert-manager.&lt;/li&gt;&#10;&lt;li&gt;The installation exposes rancher application on port 443 of the MacBook, and the cert is issued to &amp;#8220;rancher.my.org&amp;#8221; by default. To access it, add &amp;#8220;127.0.0.1 rancher.my.org&amp;#8221; to the host file.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p class="wp-block-paragraph"&gt;To back out from the steps above, just uninstall with helm. For example:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm uninstall rancher -n cattle-system&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm uninstall cert-manager -n cert-manager&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm uninstall ingress-nginx&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h4 class="wp-block-heading" id="h-jenkins"&gt;Jenkins&lt;/h4&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Similar to Rancher, Jenkins instruction assumes minikube cluster instead of docker desktop.&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo add jenkinsci https://charts.jenkins.io&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm repo update&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl create namespace jenkins&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm install jenkins -n jenkins -f https://raw.githubusercontent.com/jenkinsci/helm-charts/main/charts/jenkins/values.yaml jenkinsci/jenkins&#10;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl --namespace jenkins port-forward svc/jenkins 8080:8080&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To find out the default password for admin user:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl -n jenkins get secrets jenkins -o jsonpath&lt;span style="color:#f92672"&gt;={&lt;/span&gt;.data.jenkins-admin-password&lt;span style="color:#f92672"&gt;}&lt;/span&gt; | base64 -D&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;To uninstall Jenkins:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;helm uninstall jenkins -n jenkins&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 class="wp-block-heading" id="h-container-runtime"&gt;Container Runtime&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Although I seem to be a proponent of docker desktop thus far, this post would be incomplete to not discuss what is missing with docker desktop. One key difference between docker desktop and minikube is the container runtime being used. Docker desktop uses docker as the runtime, and it does not support other runtime as of now. Minikube allows user to choose runtime, including containerd. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Because of this difference, Kubernetes nodes with Docker as runtime and with containerd as runtime place pod log files in different locations. To find out the runtime, use the following command:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;kubectl describe node &amp;lt;node_name&amp;gt; | grep Runtime&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p class="wp-block-paragraph"&gt;If the runtime is docker, the stdout of container is placed in /var/lib/docker/containers/&amp;lt;sha&amp;gt;/. If the runtime is containerd, the stdout log of pods are stored in /var/log/containers/. This is important to know when you configure log shipping and needs to get stdout from node. The log path used in containerd is the standard path in compliance with Container Runtime Interface (CRI) so you should develop log shipping solution based on that.&lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker&amp;#8217;s refusal to comply to CRI also caused Kubernetes to stop supporting it as container runtime as of Dec 2020. For more background, refer to &lt;a href="https://kubernetes.io/blog/2020/12/02/dockershim-faq/"&gt;this&lt;/a&gt; and &lt;a href="https://kubernetes.io/blog/2020/12/02/dont-panic-kubernetes-and-docker/"&gt;this&lt;/a&gt;. &lt;a href="https://ink.insertcoin.dev/news/dockershim-deprecation"&gt;Here&lt;/a&gt; is also an article with great diagrams on the removal of docker-shim.&lt;/p&gt;&#10;&lt;h3 class="wp-block-heading" id="h-bottom-line"&gt;Bottom line&lt;/h3&gt;&#10;&lt;p class="wp-block-paragraph"&gt;Docker-desktop is a great tool for a quick single-node Kubernetes environment. As of docker 20.10, docker-desktop still uses docker as runtime. This limits its use case to development only. &lt;/p&gt;&#10;&lt;p class="wp-block-paragraph"&gt;If you need a CRI compliant environment, docker-desktop is not a good choice. We will discuss alternatives in the next post.&lt;/p&gt;&#10;&lt;nav class="wp-post-navigation" aria-label="Post navigation"&gt;&#10;&lt;a rel="prev" href="https://static.digihunch.com/2021/08/scalable-infrastructure-deployment-in-terraform/"&gt;&lt;span class="wp-post-navigation-label"&gt;Previous Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Infrastructure deployment in Terraform 1/2&lt;/strong&gt;&lt;/a&gt;&#10;&lt;a rel="next" href="https://static.digihunch.com/2021/08/creating-tls-certificate-kubernetes/"&gt;&lt;span class="wp-post-navigation-label"&gt;Next Post&lt;/span&gt;&lt;strong class="wp-post-navigation-title"&gt;Creating X.509 TLS certificate for workload on Kubernetes&lt;/strong&gt;&lt;/a&gt;&#10;&lt;/nav&gt;&#10;</description></item></channel></rss>