<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>The Ops Community ⚙️: Massimiliano Donini</title>
    <description>The latest articles on The Ops Community ⚙️ by Massimiliano Donini (@maxx_don).</description>
    <link>https://community.ops.io/maxx_don</link>
    <image>
      <url>https://community.ops.io/images/X-c7HmGMohsNBvduP6CUAAYcqRYIJklJImrzeFEqkOM/rs:fill:90:90/g:sm/mb:500000/ar:1/aHR0cHM6Ly9jb21t/dW5pdHkub3BzLmlv/L3JlbW90ZWltYWdl/cy91cGxvYWRzL3Vz/ZXIvcHJvZmlsZV9p/bWFnZS84NzAvNmNm/MTEwZWUtMmRhZi00/ZTQ1LTg0ZjktYmI0/MDFjM2Y1Y2FmLmpw/Zw</url>
      <title>The Ops Community ⚙️: Massimiliano Donini</title>
      <link>https://community.ops.io/maxx_don</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://community.ops.io/feed/maxx_don"/>
    <language>en</language>
    <item>
      <title>Azure WebJobs, Service Bus and Managed Identity: Lesson learned</title>
      <dc:creator>Massimiliano Donini</dc:creator>
      <pubDate>Tue, 09 Aug 2022 13:24:58 +0000</pubDate>
      <link>https://community.ops.io/maxx_don/azure-webjobs-service-bus-and-managed-identity-lesson-learned-3p40</link>
      <guid>https://community.ops.io/maxx_don/azure-webjobs-service-bus-and-managed-identity-lesson-learned-3p40</guid>
      <description>&lt;p&gt;Today I was converting some Azure webjobs to connect to Azure Service Bus using managed service identity (MSI).&lt;/p&gt;

&lt;p&gt;The application is a simple C# Azure WebJob built using the Azure WebJob SDK that subscribe to a topic and process incoming message writing to a database.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;These are the nuget packages used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Microsoft.Azure.WebJobs v 3.0.33&lt;/li&gt;
&lt;li&gt;Microsoft.Azure.WebJobs.Extensions.ServiceBus v 5.6.0
Please note that since Azure Functions are built on top of the WebJobs SDK, you may encounter the same issue there, I haven't verified though.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to grant the required permission, I created a security group and added the managed identity of the app service to the group, then I proceeded to grant this service group the &lt;a href="https://docs.microsoft.com/en-us/azure/role-based-access-control/built-in-roles#azure-service-bus-data-owner"&gt;Azure Service Bus Data Owner&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;According to the description, this role should have full access to the whole Service Bus namespace, so imagine my surprise when I tried to run the application and got an error that looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Unauthorized access. 'Listen' claim(s) are required to perform this operation. 
Resource: 'sb://{namespace-name}.servicebus.windows.net/{topic-name}/subscriptions/{service-name}'.
TrackingId:4e956a067b044b1089b5e327c0d08fd0_G9, SystemTracker:gateway7, Timestamp:2022-08-05T15:32:58 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After several trial and error, this is what I found:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;If you assign the permission &lt;strong&gt;Azure Service Bus Data Owner&lt;/strong&gt; to a group and the managed identity of the application is part of the group, it won't work, no matter which permission you grant ❌&lt;/li&gt;
&lt;li&gt;If you assign the managed service identity the permission &lt;strong&gt;Azure Service Bus Data Owner&lt;/strong&gt; it won't work ❌&lt;/li&gt;
&lt;li&gt;If you assign the permission &lt;strong&gt;Azure Service Bus Data Receiver&lt;/strong&gt; to a group and the managed identity of the application is part of the group, it will work ✅&lt;/li&gt;
&lt;li&gt;If you assign the managed service identity the permission &lt;strong&gt;Azure Service Bus Data Receiver&lt;/strong&gt;, it will work ✅&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note it may take up to 5 minutes for the permission changes to be applied, so you may still experience failures after applying them.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The only way to get it working was to assign the managed identity the &lt;strong&gt;Azure Service Bus Data Receiver&lt;/strong&gt; role to either the service identity or the security group.&lt;/p&gt;

&lt;p&gt;This seems either a bug in the Azure SDK or in the Service Bus itself, I'm not the only one that ran into this &lt;a href="https://github.com/Azure/azure-sdk-for-net/issues/24289"&gt;issue&lt;/a&gt; and here you can find additional information.&lt;/p&gt;

&lt;p&gt;Till the next time.&lt;/p&gt;

</description>
      <category>azure</category>
      <category>csharp</category>
    </item>
    <item>
      <title>Dynamically scale down AppService outside business hours to save 💰💰</title>
      <dc:creator>Massimiliano Donini</dc:creator>
      <pubDate>Tue, 26 Jul 2022 16:26:56 +0000</pubDate>
      <link>https://community.ops.io/maxx_don/dynamically-scale-down-appservice-outside-business-hours-to-save-48ol</link>
      <guid>https://community.ops.io/maxx_don/dynamically-scale-down-appservice-outside-business-hours-to-save-48ol</guid>
      <description>&lt;p&gt;The other day I was on a quest to lower a bit our Azure spending.&lt;/p&gt;

&lt;p&gt;Im my current company we have several environment that we use for different purposes, Development, Test, Acceptance and so on.&lt;/p&gt;

&lt;p&gt;All these environments have slightly different tiers for various services and I was wondering how to lower App Service Plan tier outside business hours.&lt;/p&gt;

&lt;p&gt;App Services have some built-in, albeit limited, capabilities to scale but this only involves scaling out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scaling out&lt;/strong&gt; is the process of adding additional instances of our application to adapt to an increasing load.&lt;br&gt;
&lt;strong&gt;Scaling up&lt;/strong&gt; is the process of running the application on a more performant hardware. &lt;/p&gt;

&lt;p&gt;Since there's no built-in support to scale up &amp;amp; down in App Services, I had to come up with a custom solution.&lt;/p&gt;

&lt;p&gt;After a bit of research, I ended up creating an Azure automation account, two runbooks that execute on a schedule the scale down and scale up of our App Service Plan.&lt;/p&gt;

&lt;p&gt;It turned out to be extremely simple to implement yet effective.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: This is just one of the possible way to scale services up &amp;amp; down outside business hours, you can achieve the same with a scheduled github action or Azure DevOps pipeline than runs your IaC code with different Sku parameter values for example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here's what I've created:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Azure automation account&lt;/li&gt;
&lt;li&gt;Azure Runbooks&lt;/li&gt;
&lt;li&gt;Azure automation schedule&lt;/li&gt;
&lt;li&gt;Azure automation account variables&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Azure automation account - &lt;a href="https://docs.microsoft.com/en-us/azure/automation/overview"&gt;Docs&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is the go to resource to automate processes in Azure, where you define the runbooks, the schedule and the variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure Runbooks - &lt;a href="https://docs.microsoft.com/en-us/azure/automation/overview"&gt;Docs&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is where I defined what needs to happen when the schedule triggers the runbook and starts a job.&lt;br&gt;
There are several &lt;a href="https://docs.microsoft.com/en-us/azure/automation/automation-runbook-types"&gt;different types&lt;/a&gt; of runbooks, here I chose the powershell one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure automation schedule - &lt;a href="https://docs.microsoft.com/en-us/azure/automation/shared-resources/schedules"&gt;Docs&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is where I defined when to execute our runbooks, I went with a weekly schedule to scale down resource in the evening and scale them back up early in the morning. &lt;/p&gt;

&lt;h2&gt;
  
  
  Azure automation account variables - &lt;a href="https://docs.microsoft.com/en-us/azure/automation/shared-resources/variables?tabs=azure-powershell"&gt;Docs&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This is where I defined few variables used by the runbook.&lt;br&gt;
This step is optional since you can potentially hardcode everything in the runbook itself, but if you want to use the same runbook across different environment, you can define variables and read them in the runbook.&lt;br&gt;
I defined few variables, one for the resource group name, one for the app service plan name and the desired scale down sku and the one the needs to be used during business hours.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In order for the runbook to successfully change the App Service Plan, we also need to grant the identity - either managed identity or user assigned one - of the automation account enough grant on the App Service Plan. I went with managed identity.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;

&lt;p&gt;After creating the variables, the schedules and the runbooks I linked the schedule to the runbook.&lt;br&gt;
I created two schedules called scale-down and scale-up, two runbooks named the same way and linked the schedule to the runbook. You link a runbook to a schedule in the overview page of the runbook itself.&lt;/p&gt;

&lt;p&gt;Last missing part is the code of the runbook itself, so here's the shortest possible version of it (of course you can make it smarter based on your needs) used to scale down, the scale up version is exactly the same but read a different variable for the sku.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Till the next one!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>devops</category>
      <category>tutorials</category>
    </item>
    <item>
      <title>Zero downtime deployment with Azure Container Apps and Github Actions - Part 1</title>
      <dc:creator>Massimiliano Donini</dc:creator>
      <pubDate>Fri, 24 Jun 2022 16:29:35 +0000</pubDate>
      <link>https://community.ops.io/maxx_don/zero-downtime-deployment-with-azure-container-apps-and-github-actions-part-1-mao</link>
      <guid>https://community.ops.io/maxx_don/zero-downtime-deployment-with-azure-container-apps-and-github-actions-part-1-mao</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;As you may know, Azure Container Apps went out of preview during Microsoft Build in late May this year.&lt;br&gt;
Azure Container Apps is a very interesting service that runs on top of Kubernetes adding some additional powerful capabilities in a simple and covinient way.&lt;br&gt;
Some of these capabilities are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Built in support for Keda autoscalers&lt;/li&gt;
&lt;li&gt;Built in support for Dapr components&lt;/li&gt;
&lt;li&gt;Ability to scale to zero&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There's a lot more to it, you can dig deeper on the official Microsoft documentation &lt;a href="https://docs.microsoft.com/en-us/azure/container-apps/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;TL;DR; All the code described in this article is available on Github &lt;a href="https://github.com/ilmax/container-apps-sample/releases/tag/v0.3"&gt;here&lt;/a&gt;&lt;br&gt;
I'm still working on improvements so main branch may be updated by the time you read this.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;I would like to migrate several Azure App Services to Azure Container Apps, I have two different type of services, http api that write to a Service Bus queue/topic and web jobs that consume and process the messages, a pretty common setup these days.&lt;/p&gt;

&lt;p&gt;Azure Container Apps allows me to easily scale the web jobs based on the amount of messages present in the queue/topic and also makes it easy to scale to zero outside business hours.&lt;/p&gt;

&lt;p&gt;In order to migrate from Azure App Service to Azure Container Apps I want to implement a zero downtime deployment for the http api services.&lt;/p&gt;
&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Azure Container Apps has built in support for &lt;a href="https://docs.microsoft.com/en-us/azure/container-apps/health-probes"&gt;health probes&lt;/a&gt;, there are 3 types of health probes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Liveness&lt;/li&gt;
&lt;li&gt;Readiness&lt;/li&gt;
&lt;li&gt;Startup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Given the built-in health probes support, I went under the assumption that, while using single revision mode, we could just deploy another revision and the control plane could take care of warming up and then swapping traffic to the new revision without downtime.&lt;br&gt;
To my surprise I figured out that's not the case and if you're reading this blog post, you might have noticed that too.&lt;/p&gt;

&lt;p&gt;I also double checked it on &lt;a href="https://aka.ms/containerapps-discord"&gt;Discord&lt;/a&gt; with the Azure Container Apps team if I was missing something on my end, but they confirmed my findings.&lt;/p&gt;



&lt;p&gt;After doing a bit of research, I figured it out that I can implement my own workflow to implement zero downtime deployment. This is far from ideal but still better than a deployment process that causes downtime.&lt;br&gt;
Hopefully Azure Container Apps will implement built support for zero downtime deployment, but in the meantime the following approach is an acceptable workaround.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;In order to implement zero downtime deployment in (pseudo) single revisions mode (meaning using multiple revision mode with a single active revision at a time, serving all the traffic), we need to do the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Redirect all the traffic to the latest revision by name (more on that later)&lt;/li&gt;
&lt;li&gt;Deploy a new revision&lt;/li&gt;
&lt;li&gt;Warm up the new revision&lt;/li&gt;
&lt;li&gt;Redirect the traffic to the newly deployed revision&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;In this blog post I am using multiple revision mode because it happened to me that moving from single revision to multiple revision mode caused downtime - I'm currently investigating it and will update this post as soon as I found.&lt;br&gt;
The idea is to have the container app configured in multiple revision mode but only have one active revision at a time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Regarding step 1, Redirect all the traffic to the latest revision by name, has to do with the &lt;strong&gt;latest&lt;/strong&gt; revision alias.&lt;br&gt;
I found that if the traffic is set to the latest revision, when deploying a new revision, the traffic get redirected to the new revision even while it's still in the provisioning state, leading to possible timeouts and failures on the caller side.&lt;/p&gt;

&lt;p&gt;The workflow above is a bit long but not particularly difficult, and we can easily implement it with the help of the Azure Cli &lt;strong&gt;containerapp&lt;/strong&gt; extension.&lt;/p&gt;

&lt;p&gt;Out of all the steps above, point 3 (Warm up the new revision) is the most tricky since different services may have different health probe configurations. I really didn't want to duplicate health probe configurations in the infrastructure and in the deployment pipeline but rather reuse what has been configured in the Container App Container instead.&lt;/p&gt;

&lt;p&gt;Dynamically discovering and calling the health probe in bash is doable but I am a bit more proficient writing that code in a high level language so I decided to write a small C# application to do that.&lt;/p&gt;

&lt;p&gt;In order to manage Azure resources, we can use the Azure Management SDK, this is a set of packages that allows you to manage Azure resources in your language of choice.&lt;br&gt;
You can find all the supported resources &lt;a href="https://azure.github.io/azure-sdk-for-net/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Luckily, Azure Container Apps already have an SDK available, although in beta at the moment of writing.&lt;/p&gt;

&lt;p&gt;Since I should be able to invoke this application from a  Github Action, I decided to implementing a web application that exposes an api to make my life easy.&lt;/p&gt;

&lt;p&gt;Essentially all the steps described above but steps 4 are executed in a github action, while step 4 is executed by a web application invoked by cURL in the Github Actions.&lt;/p&gt;

&lt;p&gt;The next point to solve is where should be this web application deployed, we have few options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Have it always available&lt;/li&gt;
&lt;li&gt;Make it available on demand&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;If you deploy an internal Azure Container App Environment and the application is not exposed to the internet, point two may be a bit more complicated since you need to make sure the github action runner can reach the Azure Container App you want to warm up.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I initially went with the first approach, having the application always available, but since I didn't want to also add authentication to the mix (to make sure only verified clients can call the warmup endpoint), I later decided against it.&lt;/p&gt;

&lt;p&gt;In order to make this application available on demand, I decided to use Github Actions service container.&lt;br&gt;
This post is getting already a bit too long so I won't go into detail of what service container is, let's just say that it allows you to run a container and made it available on the runner. If you wanna dig deeper, you can check the documentation &lt;a href="https://docs.github.com/en/actions/using-containerized-services/about-service-containers"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After sorting the Github Actions service container, last problem that I had to tackle was how to authenticate the application against Azure.&lt;br&gt;
Thanks to &lt;strong&gt;Azure.Identity&lt;/strong&gt; package and the &lt;strong&gt;DefaultAzureCredential&lt;/strong&gt; class, we can just set some environment variables and authenticate with a previously defined service principal.&lt;/p&gt;
&lt;h3&gt;
  
  
  Azure management SDK authentication
&lt;/h3&gt;

&lt;p&gt;In order to get the required credentials to authenticate, we need to create a service principal with the &lt;strong&gt;Reader&lt;/strong&gt; role on the resource group that contains the Azure Container Apps. &lt;br&gt;
We can quickly create this with the az cli and the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az ad sp create-for-rbac &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"HealthProbeSp"&lt;/span&gt; &lt;span class="nt"&gt;--role&lt;/span&gt; Contributor &lt;span class="nt"&gt;--scopes&lt;/span&gt; /subscriptions/&lt;span class="o"&gt;{&lt;/span&gt;subscriptionId&lt;span class="o"&gt;}&lt;/span&gt;/resourceGroups/&lt;span class="o"&gt;{&lt;/span&gt;resourceGroupId&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Remember to replace the subscription and resource group names&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After setting all this up, I was able to implement zero downtime deployment.&lt;/p&gt;

&lt;p&gt;Here's an extract of the Github Action:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build ${{ matrix.services.appName }}&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
      &lt;span class="na"&gt;packages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt;
    &lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;health-invoker&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ghcr.io/${{ github.repository }}/health-invoker:main&lt;/span&gt;
        &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5000:80&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;AZURE_TENANT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_TENANT_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;AZURE_CLIENT_ID&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;  &lt;span class="s"&gt;${{ secrets.AZURE_CLIENT_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;AZURE_CLIENT_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_CLIENT_SECRET }}&lt;/span&gt;
          &lt;span class="na"&gt;Azure__SubscriptionId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AZURE_SUBSCRIPTION_ID }}&lt;/span&gt;
          &lt;span class="na"&gt;Azure__ResourceGroupName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.RESOURCE_GROUP_NAME }}&lt;/span&gt;

&lt;span class="c1"&gt;# Clone repo, Build and push omitted for brevity &lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy azure container app without downtime&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;github.event_name != 'pull_request' &amp;amp;&amp;amp; matrix.services.zeroDowntime == &lt;/span&gt;&lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;echo "Installing containerapp extension"&lt;/span&gt;
          &lt;span class="s"&gt;az extension add --name containerapp --upgrade &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
          &lt;span class="s"&gt;echo "Get latest active revision name"&lt;/span&gt;
          &lt;span class="s"&gt;latest_revision=$(az containerapp show -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --query properties.latestRevisionName -o tsv)&lt;/span&gt;
          &lt;span class="s"&gt;echo "Redirect traffic to active revision $latest_revision"&lt;/span&gt;
          &lt;span class="s"&gt;az containerapp ingress traffic set -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --revision-weight $latest_revision=100 &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
          &lt;span class="s"&gt;echo "Create new revision"&lt;/span&gt;
          &lt;span class="s"&gt;az containerapp update -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} -i ${{ steps.image-tag.outputs.tag }} &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
          &lt;span class="s"&gt;new_revision=$(az containerapp show -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --query properties.latestRevisionName -o tsv)&lt;/span&gt;
          &lt;span class="s"&gt;echo "Warmup new revision at ${{ env.WARMUP_APP }}/warmup/${{ matrix.services.appName }}"&lt;/span&gt;
          &lt;span class="s"&gt;health_response_status=$(curl -m 180 --write-out "%{http_code}\n" -s ${{ env.WARMUP_APP }}/warmup/${{ matrix.services.appName }} --output backend.txt)&lt;/span&gt;
          &lt;span class="s"&gt;if [ $health_response_status = "200" ]; then&lt;/span&gt;
            &lt;span class="s"&gt;echo "Redirect traffic to new revision $new_revision"&lt;/span&gt;
            &lt;span class="s"&gt;az containerapp ingress traffic set -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --revision-weight $new_revision=100 $latest_revision=0 &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
            &lt;span class="s"&gt;echo "Deactivate revision $latest_revision"&lt;/span&gt;
            &lt;span class="s"&gt;az containerapp revision deactivate -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --revision $latest_revision &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
          &lt;span class="s"&gt;else&lt;/span&gt;
            &lt;span class="s"&gt;echo "Warmup failed with status code $health_response_status"&lt;/span&gt;
            &lt;span class="s"&gt;cat ./backend.txt&lt;/span&gt;
            &lt;span class="s"&gt;echo "Redirect traffic to active revision $latest_revision"&lt;/span&gt;
            &lt;span class="s"&gt;az containerapp ingress traffic set -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --revision-weight $latest_revision=100 &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
            &lt;span class="s"&gt;if [ ! -z "$new_revision" ]; then&lt;/span&gt;
              &lt;span class="s"&gt;echo "Deactivate revision $new_revision"&lt;/span&gt;
              &lt;span class="s"&gt;az containerapp revision deactivate -n ${{ matrix.services.appName }} -g ${{ secrets.RESOURCE_GROUP_NAME }} --revision $new_revision &amp;amp;&amp;gt; /dev/null&lt;/span&gt;
            &lt;span class="s"&gt;fi&lt;/span&gt;
            &lt;span class="s"&gt;exit 1&lt;/span&gt;
          &lt;span class="s"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the output of &lt;a href="https://github.com/wg/wrk"&gt;wrk&lt;/a&gt; while deploying a new revision:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wrk -t12 -c400 -d30s https://xxxxxxxx.azurecontainerapps.io/api/echo/ping
Running 30s test @ https://xxxxxxxx.azurecontainerapps.io/api/echo/ping
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   268.53ms  123.73ms   1.02s    68.90%
    Req/Sec   137.15    103.17     1.15k    66.59%
  41604 requests in 30.10s, 8.89MB read
Requests/sec:   1382.32
Transfer/sec:    302.38KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After deployment has been completed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;wrk -t12 -c400 -d30s https://xxxxxxxx.azurecontainerapps.io/api/echo/ping
Running 30s test @ https://xxxxxxxx.azurecontainerapps.io/api/echo/ping
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   251.00ms  135.47ms   1.24s    77.22%
    Req/Sec   148.38    104.52   434.00     63.10%
  44970 requests in 30.09s, 9.61MB read
Requests/sec:   1494.36
Transfer/sec:    326.89KB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see there's almost no difference and, most importantly, wrk doesn't indicate any non 2XX or 3XX response meaning that we were able to serve all requests while deploying a new revision.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The numbers are quite low because I'm using a very small configuration for testing purposes (0.25 Cores and 0.5 Gi of memory)&lt;/p&gt;
&lt;/blockquote&gt;




&lt;p&gt;I hope you find this helpful and if you have suggestions , don’t hesitate to comment or reach me out via twitter at &lt;a href="//twitter.com/maxx_don"&gt;twitter.com/maxx_don&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Stay tuned for part 2 that will be out very soon!&lt;/p&gt;

</description>
      <category>azure</category>
      <category>github</category>
      <category>devops</category>
      <category>tutorials</category>
    </item>
  </channel>
</rss>
