{"id":437,"date":"2019-10-16T12:17:45","date_gmt":"2019-10-16T19:17:45","guid":{"rendered":"http:\/\/blog.nillsf.com\/?p=437"},"modified":"2019-10-16T12:17:48","modified_gmt":"2019-10-16T19:17:48","slug":"setting-up-keda-and-running-a-first-application","status":"publish","type":"post","link":"https:\/\/blog.nillsf.com\/index.php\/2019\/10\/16\/setting-up-keda-and-running-a-first-application\/","title":{"rendered":"Setting up KEDA and running a first application"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">I&#8217;m presenting at a meetup tonight on the topic of Nodeless and Serverless on Kubernetes. My friend and colleague <a href=\"https:\/\/twitter.com\/richardespitz\">Richard Spitz <\/a>is presenting on nodeless kubernetes and the <a href=\"https:\/\/github.com\/virtual-kubelet\/virtual-kubelet\">virtual kubelet<\/a> &#8211; while I&#8217;ll be taking on the topic of Serverless Kubernetes, with a focus on <a href=\"https:\/\/github.com\/kedacore\/keda\">KEDA<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">KEDA (Kubernetes Event Driven Architecture) is a kubernetes component that allows event-driven scale to Kubernetes pods. It is an open-source project, initially developed by Microsoft and Red Hat.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The challenge KEDA solves, is the link between the Kubernetes horizontal pod autoscaler and event sources. I like to think as it as an interface between my event source (e.g. a queue, a bus or HTTP events) and the amount of pods you have running on your cluster. KEDA will act as the scale to 0 or the scale from 0->1, and KEDA will be the event source for the Horizontal Pod Autoscaler for the scaling beyond 1 pod.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From a Microsoft perspective, KEDA is integrated very well with Azure Functions. Azure Functions has the option to run on Kubernetes, but you&#8217;d then use the default Kubernetes scaling mechanisms. Combining KEDA with Azure Functions allows you to use different scaling metrics to scale in\/out your functions deployment. You don&#8217;t have to combine KEDA with Azure Functions, but you can.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So, why don&#8217;t we get started? I&#8217;ll all of the below on my Ubuntu 18.04 running on WSL.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">First, let&#8217;s go ahead and install .net core 3. We&#8217;ll use that later when building our testing application.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wget -q https:\/\/packages.microsoft.com\/config\/ubuntu\/18.04\/packages-microsoft-prod.deb -O packages-microsoft-prod.deb\nsudo dpkg -i packages-microsoft-prod.deb\nsudo add-apt-repository universe\nsudo apt-get update\nsudo apt-get install apt-transport-https -y\nsudo apt-get update\nsudo apt-get install dotnet-sdk-3.0 -y<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Although KEDA doesn&#8217;t require using Azure Functions, for our demo we&#8217;ll be using Azure functions to setup KEDA real easily (single line command). We&#8217;ll install the core runtime on our machine.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get install azure-functions-core-tools -y<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Another prereq, is to have helm on our kubernetes cluster. To keep things easy, I&#8217;ll assume you have a Kubernetes cluster on Azure running in AKS. Depending on how you want to run your demo, you can have a clsuter with or without virtual nodes. I&#8217;ll be running this with virtual nodes turned on.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First we&#8217;ll get access to our cluster and show our nodes to see if the connection actually works:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>az aks get-credentials -n nf-keda -g KEDA\nkubectl get nodes\nNAME                                STATUS   ROLES   AGE     VERSION\naks-agentpool-32106788-vmss000000   Ready    agent   2d21h   v1.14.6\naks-agentpool-32106788-vmss000001   Ready    agent   2d21h   v1.14.6\naks-agentpool-32106788-vmss000002   Ready    agent   2d21h   v1.14.6\nvirtual-node-aci-linux              Ready    agent   2d21h   v1.13.1-vk-v0.9.0-1-g7b92d1ee-dev<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, I have a virtual node. More on that later, but let&#8217;s go ahead and setup helm on our cluster:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I already had the helm binary on my machine, if you don&#8217;t already, go ahead and download the tarbal and move it to a directory that&#8217;s on your PATH.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wget https:\/\/get.helm.sh\/helm-v2.14.3-linux-amd64.tar.gz #look for the latest version here https:\/\/github.com\/helm\/helm\/releases\ntar -zxvf helm*.tar.gz\nsudo mv linux-amd64\/helm \/usr\/local\/bin\/helm<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, we need to setup Tiller (as long as we&#8217;re using Helm v2 that is. In v3 there&#8217;s no more requirement for Tiller, but that&#8217;s still in beta).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If your cluster is RBAC enabled, you&#8217;ll need to create a role and rolebinding to make Tiller work correctly. <a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/06\/24\/error-installing-helm-error-no-available-release-name-found\/\">I wrote about this a while ago.<\/a><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create serviceaccount --namespace kube-system tiller\nkubectl create clusterrolebinding tiller-cluster-rule --clusterrole=cluster-admin --serviceaccount=kube-system:tiller\nhelm init --service-account tiller<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">To verify Helm installed correctly, you can run <code>helm version<\/code>, which should return the following:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Client: &amp;version.Version{SemVer:\"v2.14.3\", GitCommit:\"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085\", GitTreeState:\"clean\"}\nServer: &amp;version.Version{SemVer:\"v2.14.3\", GitCommit:\"0e7f3b6637f7af8fcfddb3d2941fcc7cbebb0085\", GitTreeState:\"clean\"}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Setting up KEDA on an AKS cluster<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There are a couple of ways to setup Keda. The easiest way I found is to the Azure Functions tooling to install KEDA on our cluster.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func kubernetes install --namespace keda<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This will install KEDA in our cluster. The installation of KEDA will install KEDA and Osiris in our cluster. To see everything that got installed on our cluster, we can check via <code>kubernetes get all --namespace=keda<\/code>, which will return the following (screenshot in stead of text as the output is quiet lengthy).<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"653\" src=\"\/wp-content\/uploads\/2019\/10\/image-24-1024x653.png\" alt=\"\" class=\"wp-image-438\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-24-1024x653.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-24-300x191.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-24-768x490.png 768w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-24.png 1562w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>KEDA is now running in our cluster, and deployed a number of objects.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now go ahead and build a demo app.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Building a demo app<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">For the demo app, we&#8217;ll be using a pre-built C# application that reads from an Azure Service Bus. This demo is following the <a href=\"https:\/\/github.com\/kedacore\/sample-dotnet-worker-servicebus-queue\">following quickstart template<\/a> in the KEDA docs. We&#8217;ll do all of this in the keda-bus namespace:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create namespace keda-bus<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">First off, we&#8217;ll create a Service Bus namespace with a queue and get it&#8217;s connection string.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>BUSNAMESPACE=nfkedabus\nRG=KEDA\nQUEUE=orders\nAUTH=order-consumer\naz servicebus namespace create --name $BUSNAMESPACE --resource-group $RG --sku basic\naz servicebus queue create --namespace-name $BUSNAMESPACE --name $QUEUE --resource-group $RG\naz servicebus queue authorization-rule create --resource-group $RG --namespace-name $BUSNAMESPACE --queue-name $QUEUE --name $AUTH --rights Manage Send Listen\nconn=`az servicebus queue authorization-rule keys list --resource-group $RG --namespace-name $BUSNAMESPACE --queue-name $QUEUE --name $AUTH -o json`\nconnstring=`echo $conn | jq .primaryConnectionString`\nconnstring=`echo \"${connstring\/\/\\\"}\"`<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We&#8217;re going to use this connection string and store it as a kubernetes secret for use with our demo.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl create secret generic --from-literal=SERVICEBUS_QUEUE_CONNECTIONSTRING=$connstring --namespace=keda-bus order-secrets<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, we&#8217;ll create the actual application and scaled-object in KEDA. I&#8217;ll be pulling the deployment file straight from GitHub, but you can certainly download it and tweak it (which we&#8217;ll do later).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f https:\/\/raw.githubusercontent.com\/kedacore\/sample-dotnet-worker-servicebus-queue\/master\/deploy\/deploy-queue-processor.yaml --namespace keda-bus<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">If we then check everything on our cluster (<code>kubectl get all -n keda-bus<\/code>), we see 3 resources, but no pods).<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>A deployment, with 0\/0 ready pods<\/li><li>A replicaset, with 0 desired pods<\/li><li>A horizontal pod autoscaler, with 1 minimum and 10 maximum.<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Next up, is building some queue messages. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Testing things out<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">To test things out, <a href=\"https:\/\/twitter.com\/TomKerkhove\">Tom Kerkhove <\/a>built a Service Bus message generator. To get this working, first clone the Github repo and add your connection string to the code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>git clone https:\/\/github.com\/tomkerkhove\/sample-dotnet-worker-servicebus-queue\ncd sample-dotnet-worker-servicebus-queue\ncode .\\src\\Keda.Samples.Dotnet.OrderGenerator\\Program.cs #put your connection string in line 13<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"306\" src=\"\/wp-content\/uploads\/2019\/10\/image-25-1024x306.png\" alt=\"\" class=\"wp-image-440\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-25-1024x306.png 1024w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-25-300x90.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-25-768x230.png 768w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><figcaption>Fill in your connection string on line 13.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">After that, we can build the solution and push a number of messages to our cluster. To see KEDA work, let&#8217;s open a second window into our cluster, and setup a watch on pods:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#in second terminal\nkubectl get pods --namespace=keda-bus --watch\n#in primary terminal\nsudo dotnet run --project src\/Keda.Samples.Dotnet.OrderGenerator\/Keda.Samples.Dotnet.OrderGenerator.csproj<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">You&#8217;ll get a question about how many messages you want to push to ServiceBus. In our demo, let&#8217;s just push 1 message to start with. The message will reply with your order, and if you switch to the other terminal window, you should see that a pod was created! There is a default cooldown of 300 seconds, so it will take 300 seconds for that pod to go away.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That was cool, right. Let&#8217;s try this again with 10. <\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In my case, this caused a second pod to be created. Before I move to a larger test, I want to see my pods go away as well without waiting 300 seconds. Let&#8217;s change the cooldown time to 10 seconds:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl edit scaledobject order-processor-scaler -n keda-bus<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Look for the line cooldown, and add a small value (I chose 10). Then, let&#8217;s push 10 events again (some dotnet run command). In my case, this caused 2 pods to be created, and then after a couple of seconds to die as well. COOL!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>order-processor-775f5d5d59-b5h65\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-b5h65\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-b5h65\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-b5h65\t1-Jan\tRunning\t0\t5s\norder-processor-775f5d5d59-zcndt\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-zcndt\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-zcndt\t0\/1\tContainerCreating\t0\t0s\norder-processor-775f5d5d59-zcndt\t1-Jan\tRunning\t0\t4s\n]order-processor-775f5d5d59-zcndt\t1-Jan\tTerminating\t0\t24s\norder-processor-775f5d5d59-b5h65\t1-Jan\tTerminating\t0\t31s\norder-processor-775f5d5d59-b5h65\t0\/1\tTerminating\t0\t33s\norder-processor-775f5d5d59-zcndt\t0\/1\tTerminating\t0\t26s\norder-processor-775f5d5d59-zcndt\t0\/1\tTerminating\t0\t27s\norder-processor-775f5d5d59-zcndt\t0\/1\tTerminating\t0\t27s\norder-processor-775f5d5d59-b5h65\t0\/1\tTerminating\t0\t38s\norder-processor-775f5d5d59-b5h65\t0\/1\tTerminating\t0\t38s\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Now, let&#8217;s bring out the big guns and scale to a 1000 messages in the queue. This quickly makes KEDA scale our deployment to 10 pods (which is the maximum we defined). And before I even knew it, those 10 pods were also deleted. I&#8217;m having FUN right now!<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>order-processor-775f5d5d59-mhlm5\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-mhlm5\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-mhlm5\t0\/1\tContainerCreating\t0\t0s\norder-processor-775f5d5d59-mhlm5\t1-Jan\tRunning\t0\t3s\norder-processor-775f5d5d59-k6d6z\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-k6d6z\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-kx5s8\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-bhn52\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-kx5s8\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-bhn52\t0\/1\tPending\t0\t0s\norder-processor-775f5d5d59-k6d6z\t0\/1\tContainerCreating\t0\t0s\norder-processor-775f5d5d59-bhn52\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-kx5s8\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-kx5s8\t1-Jan\tRunning\t0\t4s\norder-processor-775f5d5d59-k6d6z\t1-Jan\tRunning\t0\t4s\norder-processor-775f5d5d59-bhn52\t1-Jan\tRunning\t0\t5s\norder-processor-775f5d5d59-wwcz6\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-wwcz6\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-klzfn\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-k9b2z\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-klzfn\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-k9b2z\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-wwcz6\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-xmxjj\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-xmxjj\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-klzfn\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-k9b2z\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-xmxjj\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-klzfn\t1-Jan\tRunning\t0\t4s\norder-processor-775f5d5d59-xmxjj\t1-Jan\tRunning\t0\t4s\norder-processor-775f5d5d59-k9b2z\t1-Jan\tRunning\t0\t5s\norder-processor-775f5d5d59-wwcz6\t1-Jan\tRunning\t0\t6s\norder-processor-775f5d5d59-84l5h\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-84l5h\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-rxwgs\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-84l5h\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-rxwgs\t0\/1\tPending\t0\t1s\norder-processor-775f5d5d59-rxwgs\t0\/1\tContainerCreating\t0\t1s\norder-processor-775f5d5d59-rxwgs\t1-Jan\tRunning\t0\t4s\norder-processor-775f5d5d59-84l5h\t1-Jan\tRunning\t0\t4s\norder-processor-775f5d5d59-k9b2z\t1-Jan\tTerminating\t0\t3m38s\norder-processor-775f5d5d59-klzfn\t1-Jan\tTerminating\t0\t3m38s\norder-processor-775f5d5d59-xmxjj\t1-Jan\tTerminating\t0\t3m38s\norder-processor-775f5d5d59-rxwgs\t1-Jan\tTerminating\t0\t3m22s\norder-processor-775f5d5d59-mhlm5\t1-Jan\tTerminating\t0\t4m1s\norder-processor-775f5d5d59-84l5h\t1-Jan\tTerminating\t0\t3m22s\norder-processor-775f5d5d59-bhn52\t1-Jan\tTerminating\t0\t3m52s\norder-processor-775f5d5d59-wwcz6\t1-Jan\tTerminating\t0\t3m38s\norder-processor-775f5d5d59-kx5s8\t1-Jan\tTerminating\t0\t3m52s\norder-processor-775f5d5d59-k6d6z\t1-Jan\tTerminating\t0\t3m52s\norder-processor-775f5d5d59-kx5s8\t0\/1\tTerminating\t0\t3m54s\norder-processor-775f5d5d59-84l5h\t0\/1\tTerminating\t0\t3m24s\norder-processor-775f5d5d59-klzfn\t0\/1\tTerminating\t0\t3m40s\norder-processor-775f5d5d59-rxwgs\t0\/1\tTerminating\t0\t3m24s\norder-processor-775f5d5d59-k6d6z\t0\/1\tTerminating\t0\t3m54s\norder-processor-775f5d5d59-bhn52\t0\/1\tTerminating\t0\t3m54s\norder-processor-775f5d5d59-xmxjj\t0\/1\tTerminating\t0\t3m40s\norder-processor-775f5d5d59-wwcz6\t0\/1\tTerminating\t0\t3m40s\norder-processor-775f5d5d59-wwcz6\t0\/1\tTerminating\t0\t3m40s\norder-processor-775f5d5d59-k9b2z\t0\/1\tTerminating\t0\t3m40s\norder-processor-775f5d5d59-k9b2z\t0\/1\tTerminating\t0\t3m40s\norder-processor-775f5d5d59-mhlm5\t0\/1\tTerminating\t0\t4m3s\norder-processor-775f5d5d59-mhlm5\t0\/1\tTerminating\t0\t4m3s\norder-processor-775f5d5d59-klzfn\t0\/1\tTerminating\t0\t3m41s\norder-processor-775f5d5d59-klzfn\t0\/1\tTerminating\t0\t3m41s\norder-processor-775f5d5d59-kx5s8\t0\/1\tTerminating\t0\t3m55s\norder-processor-775f5d5d59-kx5s8\t0\/1\tTerminating\t0\t3m55s\norder-processor-775f5d5d59-k6d6z\t0\/1\tTerminating\t0\t3m55s\norder-processor-775f5d5d59-84l5h\t0\/1\tTerminating\t0\t3m25s\norder-processor-775f5d5d59-84l5h\t0\/1\tTerminating\t0\t3m25s\norder-processor-775f5d5d59-xmxjj\t0\/1\tTerminating\t0\t3m44s\norder-processor-775f5d5d59-xmxjj\t0\/1\tTerminating\t0\t3m44s\norder-processor-775f5d5d59-k6d6z\t0\/1\tTerminating\t0\t3m58s\norder-processor-775f5d5d59-k6d6z\t0\/1\tTerminating\t0\t3m58s\norder-processor-775f5d5d59-rxwgs\t0\/1\tTerminating\t0\t3m28s\norder-processor-775f5d5d59-rxwgs\t0\/1\tTerminating\t0\t3m28s\norder-processor-775f5d5d59-bhn52\t0\/1\tTerminating\t0\t3m58s\norder-processor-775f5d5d59-bhn52\t0\/1\tTerminating\t0\t3m58s\norder-processor-775f5d5d59-mhlm5\t0\/1\tTerminating\t0\t4m13s\norder-processor-775f5d5d59-mhlm5\t0\/1\tTerminating\t0\t4m13s\norder-processor-775f5d5d59-wwcz6\t0\/1\tTerminating\t0\t3m50s\norder-processor-775f5d5d59-wwcz6\t0\/1\tTerminating\t0\t3m50s\norder-processor-775f5d5d59-k9b2z\t0\/1\tTerminating\t0\t3m50s\norder-processor-775f5d5d59-k9b2z\t0\/1\tTerminating\t0\t3m50s\n<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Watching the Azure Monitor graph for my Service Bus is also pretty fun:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"904\" height=\"529\" src=\"\/wp-content\/uploads\/2019\/10\/image-26.png\" alt=\"\" class=\"wp-image-442\" srcset=\"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-26.png 904w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-26-300x176.png 300w, https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/image-26-768x449.png 768w\" sizes=\"auto, (max-width: 904px) 100vw, 904px\" \/><figcaption>Azure service bus graph for pushing 1000 messages into the bus, and consuming with KEDA<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Scaling KEDA to the Virtual Kubelet<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">I was hoping to also see KEDA burst into the virtual kubelet. Right now, the default deployment doesn&#8217;t contain any resource constraints. If you do not know what those are, please <a href=\"https:\/\/blog.nillsf.com\/index.php\/2019\/07\/21\/ckad-series-part-3-configuration\/\">head on over to this blog post<\/a> where I explain them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let take the deployment file, and add some resource constraints. My cluster has a total of 6vCPUs, so I&#8217;ll reserve 500m CPU per pod &#8211; and up the amount of replica&#8217;s to 20 (making 8 pods (hopefully)) start as virtual nodes. Let&#8217;s do this with the following deployment and scaled object:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: order-processor\n  labels:\n    app: order-processor\nspec:\n  selector:\n    matchLabels:\n      app: order-processor\n  template:\n    metadata:\n      labels:\n        app: order-processor\n    spec:\n      containers:\n      - name: order-processor\n        image: tomkerkhove\/keda-sample-dotnet-worker-servicebus-queue\n        resources:\n          requests:\n            cpu: \"500m\"\n        env:\n        - name: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING\n          valueFrom:\n            secretKeyRef:\n             name: order-secrets\n             key: SERVICEBUS_QUEUE_CONNECTIONSTRING\n---\napiVersion: keda.k8s.io\/v1alpha1\nkind: ScaledObject\nmetadata:\n  name: order-processor-scaler\n  labels:\n    app: order-processor\n    deploymentName: order-processor\nspec:\n  scaleTargetRef:\n    deploymentName: order-processor\n  # minReplicaCount: 0 Change to define how many minimum replicas you want\n  cooldownPeriod: 10\n  maxReplicaCount: 10\n  triggers:\n  - type: azure-servicebus\n    metadata:\n      queueName: orders\n      connection: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING\n      queueLength: '5'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s update that with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f deployment.yaml -n keda-bus<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s now push another 1000 messages and see how the system behaves:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I can see the system scale again, but it doesn&#8217;t scale to my virtual node. Looking at a pending Pod, I got the following message:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>0\/4 nodes are available: 1 node(s) had taints that the pod didn't tolerate, 3 Insufficient cpu.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s two messages in that one message:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>1 node had taints that the pod didn&#8217;t tolerate<\/li><li>3 nodes had insufficient CPU (as expected).<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Let me first explain<a href=\"https:\/\/kubernetes.io\/docs\/concepts\/configuration\/taint-and-toleration\/\"> what a node taint is.<\/a> A taint is a mark you put on a node, and only pods that tolerate that taint should be scheduled on that node. Meaning, that in my case, because I don&#8217;t have a toleration for virtual kubelet, my pods wont get scheduled there.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s try to solve this node taint issue. The following deployment file should solve this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: order-processor\n  labels:\n    app: order-processor\nspec:\n  selector:\n    matchLabels:\n      app: order-processor\n  template:\n    metadata:\n      labels:\n        app: order-processor\n    spec:\n      containers:\n      - name: order-processor\n        image: tomkerkhove\/keda-sample-dotnet-worker-servicebus-queue\n        resources:\n          requests:\n            cpu: \"500m\"\n        env:\n        - name: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING\n          valueFrom:\n            secretKeyRef:\n             name: order-secrets\n             key: SERVICEBUS_QUEUE_CONNECTIONSTRING\n      tolerations:\n      - key: virtual-kubelet.io\/provider\n        operator: Equal\n        value: azure\n        effect: NoSchedule\n---\napiVersion: keda.k8s.io\/v1alpha1\nkind: ScaledObject\nmetadata:\n  name: order-processor-scaler\n  labels:\n    app: order-processor\n    deploymentName: order-processor\nspec:\n  scaleTargetRef:\n    deploymentName: order-processor\n  # minReplicaCount: 0 Change to define how many minimum replicas you want\n  cooldownPeriod: 10\n  maxReplicaCount: 10\n  triggers:\n  - type: azure-servicebus\n    metadata:\n      queueName: orders\n      connection: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING\n      queueLength: '5'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Again, we can apply this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f deployment.yaml -n keda-bus<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And let&#8217;s try again with 1000 objects. My observations:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Kubernetes immediately scheduled pods across &#8220;real&#8221; nodes and virtual nodes. Not prioritizing the &#8220;real nodes&#8221;. I ended up with 7 virtual pods and 3 &#8220;real&#8221; pods. I was actually expecting the real nodes to be prioritized<\/li><li>It took some time for the virtual nodes to come live. This makes a little sense, as the image needs to be downloaded. But I was still thinking it to scale a little faster. <\/li><\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">I believe we can solve number 1 by adding yet another scheduling mark to our pods. Let&#8217;s try adding a nodeAffinity preference to our deployment. And in the meanwhile, let&#8217;s also go wild in scaling and scale to 15 pods in total. <\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  apiVersion: apps\/v1\n  kind: Deployment\n  metadata:\n    name: order-processor\n    labels:\n      app: order-processor\n  spec:\n    selector:\n      matchLabels:\n        app: order-processor\n    template:\n      metadata:\n        labels:\n          app: order-processor\n      spec:\n        containers:\n        - name: order-processor\n          image: tomkerkhove\/keda-sample-dotnet-worker-servicebus-queue\n          resources:\n            requests:\n              cpu: \"500m\"\n          env:\n          - name: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING\n            valueFrom:\n              secretKeyRef:\n              name: order-secrets\n              key: SERVICEBUS_QUEUE_CONNECTIONSTRING\n        tolerations:\n        - key: virtual-kubelet.io\/provider\n          operator: Equal\n          value: azure\n          effect: NoSchedule\n        affinity:\n          nodeAffinity:\n            preferredDuringSchedulingIgnoredDuringExecution:\n            - weight: 100\n              preference:\n                matchExpressions:\n                - key: agentpool\n                  operator: In\n                  values:\n                  - agentpool\n  ---\n  apiVersion: keda.k8s.io\/v1alpha1\n  kind: ScaledObject\n  metadata:\n    name: order-processor-scaler\n    labels:\n      app: order-processor\n      deploymentName: order-processor\n  spec:\n    scaleTargetRef:\n      deploymentName: order-processor\n    # minReplicaCount: 0 Change to define how many minimum replicas you want\n    cooldownPeriod: 10\n    maxReplicaCount: 15\n    triggers:\n    - type: azure-servicebus\n      metadata:\n        queueName: orders\n        connection: KEDA_SERVICEBUS_QUEUE_CONNECTIONSTRING\n        queueLength: '5'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And again, this should do the job:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>kubectl apply -f deployment.yaml -n keda-bus<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And again, let&#8217;s send 1000 messages to our queue. This time, I still noticed pods getting scheduled on both AKS-nodes and the virtual kubelet, but there were more pods in the cluster than on the virtual nodes early on. So my affinity had some effect &#8211; not all that I was hoping for. But, I&#8217;m fairly happy with the result to be honest. <\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This was a cool demo to build, and the play around with. I liked that this demo focused on Keda, without needing me building a function in Kubernetes. I am not fully satisfied with the outcome of the nodeaffinity, but decided to call this done &#8211; and keep that topic for another day.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I&#8217;m presenting at a meetup tonight on the topic of Nodeless and Serverless on Kubernetes. My friend and colleague Richard Spitz is presenting on nodeless kubernetes and the virtual kubelet &#8211; while I&#8217;ll be taking on the topic of Serverless Kubernetes, with a focus on KEDA. KEDA (Kubernetes Event Driven Architecture) is a kubernetes component [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":444,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[2,5,1],"tags":[8,18,40,16],"class_list":["post-437","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure","category-open-source","category-uncategorized","tag-azure","tag-kubernetes","tag-meetup","tag-open-source"],"jetpack_featured_media_url":"https:\/\/nillsfblog.blob.core.windows.net\/media\/2019\/10\/keda-logo-transparent1.png","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/437","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/comments?post=437"}],"version-history":[{"count":4,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/437\/revisions"}],"predecessor-version":[{"id":445,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/posts\/437\/revisions\/445"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/media\/444"}],"wp:attachment":[{"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/media?parent=437"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/categories?post=437"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.nillsf.com\/index.php\/wp-json\/wp\/v2\/tags?post=437"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}