{"id":1433,"date":"2020-11-17T19:08:51","date_gmt":"2020-11-18T03:08:51","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=1433"},"modified":"2020-11-17T19:09:04","modified_gmt":"2020-11-18T03:09:04","slug":"running-windows-containers-on-the-azure-kubernetes-service-aks","status":"publish","type":"post","link":"https:\/\/nillsf.com\/index.php\/2020\/11\/17\/running-windows-containers-on-the-azure-kubernetes-service-aks\/","title":{"rendered":"Running Windows containers on the Azure Kubernetes Service (AKS)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Containers and Kubernetes have traditionally been the area of Linux-based workloads. However, things have changed. Windows has supported Docker containers <a href=\"https:\/\/www.docker.com\/blog\/dockerforws2016\/\">for a while now<\/a>, and since <a href=\"https:\/\/kubernetes.io\/blog\/2019\/03\/25\/kubernetes-1-14-release-announcement\/\">Kubernetes 1.14<\/a>, Windows support has been generally available in Kubernetes as well.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this blog post, we&#8217;ll explore how you can add Windows nodes to a Kubernetes cluster running on Azure. After we&#8217;ve set up the cluster, we&#8217;ll have a look at how actual Windows containers can be created on the cluster.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up the cluster<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To run Windows containers on AKS, we&#8217;ll need the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>An AKS cluster<\/li><li>(at least) 1 Linux Nodepool. This is used for running system components such as CoreDNS.<\/li><li>A Windows Nodepool<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s create all of this using the Azure CLI:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Create a resource group\naz group create -n win-aks -l westus2\n\n# Create the cluster, with the default linux nodepool\naz aks create -g win-aks -n win-aks \\\n  --node-count 2 --ssh-key-value ~\/.ssh\/id_rsa.pub \\\n  --windows-admin-username nilfranadmin \\\n  --windows-admin-password superSecret123! \\\n  --network-plugin azure\n\n# Create a second nodepool using Windows\n az aks nodepool add -g win-aks \\\n  --cluster-name win-aks \\\n  --os-type Windows --name winnp \\\n  --node-count 2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The second command will take some time to run (about 15 minutes), but after a while, we will be able to go ahead and schedule Windows containers. While you&#8217;re waiting for the node pool to be added, let&#8217;s explore how we need to tell Kubernetes to schedule a Windows workload on Windows nodes.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A little info about labels and nodeSelectors<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Once you have a cluster with both Linux and Windows nodes, you should be able to run <code>kubectl get nodes -o wide<\/code> and see you now have nodes with a Windows operating system:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"97\" src=\"\/wp-content\/uploads\/2020\/11\/image-1024x97.png\" alt=\"\" class=\"wp-image-1436\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1024x97.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-300x28.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-768x73.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1536x145.png 1536w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image.png 1809w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>You should have nodes with a Windows OS<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">What&#8217;s more, these nodes are also labeled with their OS information. To see those labels, run a <code>kubectl describe node &lt;windows-node-name&gt;<\/code>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"541\" src=\"\/wp-content\/uploads\/2020\/11\/image-1-1024x541.png\" alt=\"\" class=\"wp-image-1437\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1-1024x541.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1-300x158.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1-768x406.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1.png 1333w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Labels on Windows nodes<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To schedule pods on a Windows node (or a Linux node for that matter) you&#8217;ll have to set a <code>nodeSelector <\/code>in the pod definition. In the node selector, you define which labels on the nodes need to be met to schedule pods on certain nodes. In case of the operating system, we&#8217;ll set the <code>kubernetes.io\/os<\/code> label to Windows in the <code>nodeSelector<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">What&#8217;s important to note here: if you run in a mixed cluster (with both Linux and Windows nodes and workloads), you need to include the <code>nodeSelector <\/code>for both Windows and Linux workloads. Otherwise, Kubernetes might schedule Linux pods on Windows nodes (or vice-versa), and that will lead to issues.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s have a look at how to do that:<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to schedule pods on Windows nodes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">As mentioned in the previous section, to schedule a pod on a Windows node, you need to include a <code>nodeSelector <\/code>in your workload definition. An example of that below (<a href=\"https:\/\/github.com\/NillsF\/blog\/tree\/master\/aks-windows\">code is also available on GitHub<\/a>):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  labels:\n    app: win-webserver\n  name: win-webserver\nspec:\n  replicas: 2\n  selector:\n    matchLabels:\n      app: win-webserver\n  template:\n    metadata:\n      labels:\n        app: win-webserver\n      name: win-webserver\n    spec:\n     containers:\n      - name: windowswebserver\n        ports:\n        - containerPort: 80\n          name: http\n          protocol: TCP\n        - containerPort: 443\n          name: https\n        image: mcr.microsoft.com\/windows\/servercore\/iis:windowsservercore-ltsc2019\n     nodeSelector:\n      kubernetes.io\/os: windows<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will create a deployment, containing 2 IIS web servers. To verify that things work well, let&#8217;s also include a service to route traffic to these IIS servers. Note how the service definition isn&#8217;t any different for Windows vs Linux workloads.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: v1\nkind: Service\nmetadata:\n  name: win-webserver\n  labels:\n    app: win-webserver\nspec:\n  selector:\n    app: win-webserver\n  ports:\n  - port: 80\n    targetPort: 80\n  type: LoadBalancer<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We can deploy both using: <code>kubectl create -f .<\/code> .<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It will take a while for the pods to spin up since Windows images are generally a bit bigger than Linux images. But after a couple of minutes, you should see your Windows pods running, which you can confirm using <code>kubectl get pods -o wide<\/code>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"86\" src=\"\/wp-content\/uploads\/2020\/11\/image-2-1024x86.png\" alt=\"\" class=\"wp-image-1438\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-2-1024x86.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-2-300x25.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-2-768x65.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-2.png 1405w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Getting the windows server pods.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And we can now also browse to the service. To get its public IP, use <code>kubectl get svc<\/code>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"131\" src=\"\/wp-content\/uploads\/2020\/11\/image-3-1024x131.png\" alt=\"\" class=\"wp-image-1439\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-3-1024x131.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-3-300x38.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-3-768x98.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-3.png 1134w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Getting the service&#8217;s public IP address<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And if we browse to that IP, you can see a glorious IIS web server running on Kubernetes:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"678\" src=\"\/wp-content\/uploads\/2020\/11\/image-4-1024x678.png\" alt=\"\" class=\"wp-image-1440\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-4-1024x678.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-4-300x199.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-4-768x509.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-4.png 1469w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>A glorious IIS web server running on AKS.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And that&#8217;s how you run Windows containers on AKS.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In this blog post, we looked into how to run Windows containers on the Azure Kubernetes Service (AKS). We created a new AKS cluster, and we added a Windows nodepool. After that, we scheduled an actual workload on the nodes in that pool. To do this, we used the <code>nodeSelector <\/code>in the pod definition.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you&#8217;re interested to take this one step further and also run Windows pods on ACI instances using the virtual kubelet, check <a href=\"https:\/\/blog.nillsf.com\/index.php\/2020\/10\/26\/creating-windows-azure-container-instances-using-the-virtual-kubelet-from-the-azure-kubernetes-service\/\">out this blog post<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Containers and Kubernetes have traditionally been the area of Linux-based workloads. However, things have changed. Windows has supported Docker containers for a while now, and since Kubernetes 1.14, Windows support has been generally available in Kubernetes as well. In this blog post, we&#8217;ll explore how you can add Windows nodes to a Kubernetes cluster running [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":1437,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,58,41],"tags":[115,154,18,42],"class_list":["post-1433","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-kubernetes","category-windows","tag-azure-kubernetes-service","tag-cont","tag-kubernetes","tag-windows"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2020\/11\/image-1.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1433","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=1433"}],"version-history":[{"count":2,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1433\/revisions"}],"predecessor-version":[{"id":1441,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/1433\/revisions\/1441"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/1437"}],"wp:attachment":[{"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=1433"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=1433"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=1433"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}