Skip to content
adatum
  • Home
  •  About adatum
  •  Learn Azure Bicep
  •  SCOM Web API
Azure

Canary rollouts to Kubernetes without additional tools

  • 28/01/202531/03/2025
  • by Martin Ehrnst
I know you like my cheesy PowerPoint designer illustration

Whenever I search (google) for hot to do canary, A/B, progressive or whatever type of deployment to Kubernetes, the solutions are either Argo or Flagger. Both great tools. In fact I have used Flagger many times with my previous employer, and some day probably at my current as well. After all we already use Flux. But did you know you can do a controlled deployment to a subset of your users, with NGINX Ingress alone?

Progressive delivery

The concept of progressive delivery is already known to most, and you can find multiple definitions. But the whole point is to deploy your latest application version, and slowly shift users to the new version. Hopefully minimizing errors and happier customers. The default deployment strategy for Kubernetes is RollingUpdate and is kind of progressive in it self, but can only handle container errors, and not functional ones. You control your deployment rollout using maxUnavailable and maxSurge. The latter controls how many pods to scale out, and the first how many can be unavailable.

If your deployment have 3 replicas and you set maxSurge: 1 and maxUnavailable: 0 Kubernetes will create one additional pod before removing one existing. Keeping three pods available.

Canary with NGINX

Where I work, many of our applications are tightly coupled. Some because they need to, others because it just happened. While the older application stack is slowly being de-coupled by the introduction of newer services. The need to do a more controlled rollout is emerging. Switching K8S clusters is, in my opinion a bit drastic, but since our customers are in the more conservative end of the spectrum, we need to be in full control, and be able to move fast. And since only a subset of our applications are ready for a canary approach I do not see the need to introduce Flagger just yet.

Our setup is using Flux for GItOps, and NGINX ingress for the canary, and we control the traffic either through a header or a percentage of the traffic. A combination of header present and header value can also be used. In our case we know for example which country our customers comes from, so we can use nginx.ingress.kubernetes.io/canary-by-header: customer-region and nginx.ingress.kubernetes.io/canary-by-header-value norway to route all Norwegian customers to the latest version

With this our developers can monitor and validate new versions with minimal impact, and quickly revert (or promote) the canary by updating their deployment manifests.

Below is a simple setup with two deployments, two services, and two ingresses.

apiVersion: apps/v1
kind: Deployment
metadata:
name: echo-canary
spec:
replicas: 1
selector:
matchLabels:
app: echo
version: canary
template:
metadata:
labels:
app: echo
version: canary
spec:
containers:
– name: echo-container
image: hashicorp/http-echo:latest
args:
– -listen=:80
– –text="Hello, Canary"
ports:
– containerPort: 80
—
apiVersion: v1
kind: Service
metadata:
name: echo-canary-service
spec:
selector:
app: echo
version: canary
ports:
– protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
view raw canary-deployment.yaml hosted with ❤ by GitHub
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
spec:
replicas: 3
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
spec:
containers:
– name: echo-container
image: hashicorp/http-echo:latest
args:
– -listen=:80
– –text="Hello, World"
ports:
– containerPort: 80
—
apiVersion: v1
kind: Service
metadata:
name: echo-service
spec:
selector:
app: echo
ports:
– protocol: TCP
port: 80
targetPort: 80
type: ClusterIP
view raw deployment.yaml hosted with ❤ by GitHub
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-ingress
spec:
rules:
– host: echo.example.com
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: echo-service
port:
number: 80
—
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-canary-ingress
annotations:
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "20" # percentage of traffic. You can also chose to run other stragies like cookie based, header based etc.
spec:
rules:
– host: echo.example.com
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: echo-canary-service
port:
number: 80
view raw ingresses.yaml hosted with ❤ by GitHub

Share this:

  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on X (Opens in new window) X
  • Click to share on Reddit (Opens in new window) Reddit
Azure Monitor

Azure managed Prometheus for existing AKS clusters

  • 04/11/202407/01/2025
  • by Martin Ehrnst

In the company I work for, we have multiple AKS clusters. They have different purpose, and are split between environments, like dev and prod. Nothing special at all. We recently decided to enable the clusters for Prometheus, and build our application dashboards using Grafana. A lot has changed since my last look at Azure Managed Prometheus. This time around I encountered a few challenges, below you’ll find a summary.

Our clusters are deployed using Terraform, and the monitoring “stack” with Bicep. Apart from a small difference in language, we also have decided that Prometheus and Grafana should exist in one region only, and we would only split the data source between dev and production environments. The Grafana instance is the same for both.

Enable monitoring

Azure portal button to enable managed prometheus.

The button above makes it pretty simple to enable Azure Managed Prometheus for that specific cluster – but since we want to do this using code, we need to modify our modules. And what exactly does this Configure button do? It creates a deployment which consist of a data collection rule, data collection endpoint, and a few Prometheus recording rules. During the process it also allows you to specify an existing managed Prometheus (Azure monitor metrics workspace) and managed Grafana.

Deployments created by the automatic onboarding to prometheus

The data collection rule, and association is similar to what we already have with Log Analytics and container insights. That would mean a quick change to our existing Terraform code, adding a new collection rule. I thought…

All my issues is explained in various Microsoft Doc’s and GitHub repositories. However, piecing everything together together took a bit of time.

  • With clusters in multiple regions. The data collection rule need to exist in the same location as the Azure monitor workspace (Prometheus). Unless you want the collection endpoint to also be in the same region. You will need to create two. One in the cluster region, and one in the monitor workspace region. I used this example as an inspiration, and this doc as a deployment reference guide.
  • The automatic onboarding process deploy 1:1 relationship of the recording rules for the clusters. I did not want to manage the recording rules together with our clusters. And ended up creating them along-side Prometheus. By only specifying the prometheusWorkspaceId in the scope, these rules are applied to all clusters sending data to the specific workspace. An example Bicep module here. You will also find them here, but without the UX rules.
  • We did not want to keep performance metrics sent to Log Analytics. If you don’t want that either. You’ll need to modify the data collection rule by specifying the streams you want. Specifically, remove Microsoft-Perf and Microsoft-InsightsMetrics
Portal experience with UX rules.

Share this:

  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on X (Opens in new window) X
  • Click to share on Reddit (Opens in new window) Reddit
Migration to GitHub illustration Azure DevOps

Migrate from Azure DevOps to GitHub – what you…

  • 16/02/202407/01/2025
  • by Martin Ehrnst

Developers love GitHub, and I do to! However, migrating from one platform to another is akin to orchestrating a complex dance. The rhythm of code, the choreography of pipelines, and the harmony of collaboration—all must align seamlessly. But beneath the surface lies the intricate steps: data mapping, permissions, and legacy dependencies. As the curtain rises, let us delve into the intricacies of this migration journey, where every line of code carries the weight of history, and every commit echoes the promise of a new beginning.

I’ve been apart of this project before, and now I find my self in the same situation. The goal is the same, but not all solutions are identical. You will need to adjust your tasks to fit your company’s situation. But in this blog post, I will outline what you need to know when migrating from Azure DevOps to GitHub.

GitHub for organization and GitHub enterprise.

GitHub enterprise is probably what you’re looking for. You can get some things done with an Organization only, but if you want to use some security features, let’s say, branch policies for internal repositories, you’re forced to go the enterprise route.

GitHub Organizations are well-suited for open-source projects, small teams, or individual developers.

  • They provide a collaborative space for managing repositories, teams, and access permissions.
  • Features include team-based access control, issue tracking, and project management.
  • Ideal for community-driven development and public repositories.

GitHub Enterprise caters to larger organizations and enterprises.

  • It offers enhanced security, scalability, and administrative controls.
  • Features include advanced auditing, single sign-on (which you want), and enterprise-grade support.
  • Perfect for companies with (any) compliance requirements and a need for robust infrastructure.

GitHub Enterprise – account types

Time to chose how accounts are managed. In DevOps you probably used your company’s existing accounts synced with Entra ID. For GitHub you have two options.

Individual accounts using SAML: These are user accounts that are linked to an identity provider (IdP) using Security Assertion Markup Language (SAML). Users can sign in to GitHub Enterprise Cloud with their existing credentials with a linked account to your identity provider, most likely Azure Entra ID.

The other option is Enterprise managed users: Here the user accounts are provisioned an managed through the IdP. Of course this is what you want, right. Full control! However, in both projects we ended up with option 1, individual accounts with SAML. What you do comes down to whether you want to favor developer experience a bit more than full control and central management.

Enterprise managed users are totally blocked from any collaboration outside your enterprise. This means creating issues on a public repo, etc. I really hope GitHub will change this, because what we actually want is both!

Migrating repositories

Let’s delve in to the more technical side of things. You want your repositories moved from DevOps to GitHub. And that is pretty damn simple, possibly the easiest part of the whole project as both is using Git as the underlying technology.

If you only have a handful repo’s a simple git clone can do the job. But most likely, you want to do a bit more, and if you are like me, working as a platform engineer or similar, you probably would like to streamline the process, and have each repository set up with some baseline settings. All this will require some scripting.

Enter GitHub CLI and ADO2GH extension despite having a super annoying limitation requiring personal access tokens (PAT) for bot DevOps and GitHub, I still think this is you best option. I spent a few hours trying to find out how to use GH CLI with an application, but without luck. Considering this is a time limited project, using a PAT from a service account (will consume a license) is acceptable.

Our solution for migrating repositories is a workflow in GitHub developers can run. Below is an example on the workflow and the PowerShell migration script.

param(
[Parameter(HelpMessage="The Azure DevOps organization.")]
[string]$adoOrg = "Adatum",
[Parameter(Mandatory=$true, HelpMessage="The Azure DevOps team project.")]
[string]$adoTeamProject,
[Parameter(Mandatory=$true, HelpMessage="The Azure DevOps repository.")]
[string]$adoRepo,
[Parameter(HelpMessage="The GitHub organization.")]
[string]$githubOrg = "Adatum",
[Parameter(HelpMessage="The GitHub repository.")]
[bool]$lockAdoRepo = $false,
[Parameter(HelpMessage="Repository owner.", Mandatory=$true)]
[string]$repoOwner
)
# Use the Azure DevOps repository name as the GitHub repository name
[string]$githubRepo = $adoRepo
gh auth login –with-token $env:GH_TOKEN
$repoExists = $null
$repoExists = gh repo view $githubOrg/$githubRepo
if ($null -eq $repoExists) {
# Use the custom extension to migrate the repository
try {
gh ado2gh migrate-repo –ado-org $adoOrg –ado-team-project $adoTeamProject –ado-repo $adoRepo –github-org $githubOrg –github-repo $githubRepo –target-repo-visibility 'internal'
# get default branch and set branch protection
Write-Output "Setting branch protection…"
$defaultBranch = gh repo view "${githubOrg}/${githubRepo}" –json defaultBranchRef –jq '.defaultBranchRef.name'
gh api repos/$githubOrg/$githubRepo/branches/$defaultBranch/protection –method PUT `
-H "Accept: application/vnd.github+json" `
-F "required_pull_request_reviews[required_approving_review_count]=1" `
-F "required_status_checks=null" `
-F "restrictions=null" `
-F "enforce_admins=true" `
# setting the repo admin
gh api repos/$githubOrg/$githubRepo/collaborators/$repoOwner –method PUT -F permission=admin
Write-Output "creating environments…"
gh api repos/$githubOrg/$githubRepo/environments/production –method PUT `
-H "Accept: application/vnd.github+json" `
-F "deployment_branch_policy[protected_branches]=true" `
-F "deployment_branch_policy[custom_branch_policies]=false"
gh api repos/$githubOrg/$githubRepo/environments/dev –method PUT `
-H "Accept: application/vnd.github+json"
gh api repos/$githubOrg/$githubRepo/environments/qa –method PUT `
-H "Accept: application/vnd.github+json"
}
catch {
if ($LASTEXITCODE -eq 1) {
Write-Output "Migration failed. Aborting…"
gh ado2gh abort-migration –ado-org $adoOrg –ado-team-project $adoTeamProject –ado-repo $adoRepo –github-org $githubOrg –github-repo $githubRepo
break
}
}
if ($lockAdoRepo) {
Write-Output "Disabling Azure DevOps repository…"
gh ado2gh disable-ado-repo –ado-org $adoOrg –ado-team-project $adoTeamProject –ado-repo $adoRepo
}
} else {
Write-Output "Repository already exists. Migration skipped."
}
view raw migrate-devops-repo.ps1 hosted with ❤ by GitHub
name: Migrate DevOps Repo
on:
workflow_dispatch:
inputs:
adoTeamProject:
description: 'Azure DevOps team project'
required: true
adoRepo:
description: 'Azure DevOps repository'
required: true
lockAdoRepo:
description: 'Lock Azure DevOps repository'
required: false
type: boolean
default: false
jobs:
migrate:
name: Migrate repo, ${{ github.event.inputs.adoRepo }}
runs-on: ubuntu-latest
steps:
– name: Checkout code
uses: actions/checkout@v4
with:
persist-credentials: false
ref: ${{ github.head_ref }}
– name: Install GH CLI Extension
run: |
gh extension install github/gh-ado2gh
– name: Run PowerShell script
shell: pwsh
run: |
.\scripts\migrate-devops-repo.ps1 -adoTeamProject "${{ github.event.inputs.adoTeamProject }}" -adoRepo "${{ github.event.inputs.adoRepo }}" -repoOwner "${{ github.triggering_actor }}"
env:
GH_PAT: ${{ secrets.GH_PAT }}
ADO_PAT: ${{ secrets.ADO_PAT }}
view raw run-repo-migration.yaml hosted with ❤ by GitHub

From Azure Pipelines to GitHub workflows

Next up in your migration is CI/CD. What do you do? In our case, we have also discussed if it’s time to ditch our deployment pipelines in favor of GitOps, using Flux or ArgoCD. All our applications run on Kubernetes (AKS), which makes this a viable option. However, it is a broader discussion, and most likely some developers want to move, and some others will not. It’s reasonable to think deployment pipelines will be a part of our setup for a long time.

Question is, should you try using the GitHub actions importer, or is a refactor about time anyway? Given the fact that the importer tool has some limitation, and you probably have wanted to do some adjustment to your existing pipelines already, I believe this project will force some refactoring anyway.

As a platform engineer, I always strive to create self-service options. Now for pipelines, I can create custom starter workflows. I really like this approach, as it provides the DevOps/Platform team a way to streamline, and scaffold the bare minimum of what’s required, and developers can adjust to their application specific needs. The example in the image above is nothing but a slightly modified standard workflow. However, with the starter workflow we can add references to our container registries, use organization secrets, use and pre-populate connection to our Azure environment. As I mentioned above with the user accounts. We want both, freedom and control!

Azure Work Items and GitHub issues

Azure work items translate to GitHub issues (almost). Work items is a part of Azure Boards, and boards are almost similar to GitHub projects. With some careful consideration and new thinking, I believe it is possible to ditch Azure Boards and work items in favor of Issues with projects. If not, you can connect Azure Boards to GitHub. As you probably have noticed, I haven’t solved this yet, but I will do my best in making it happen.

The biggest difference between the work items and issues is that work items are linked to the board, where issues are tied to one repository. After making the repo migration, you will have to create a script to pull work items from Azure DevOps and create them as issues in the correct repository on GitHub. After that, we can re-create the boards in GitHub projects. There’s a few options/scripts for doing the first task, but I believe every organization use these features differently, so customization is needed. This solution by Josh Johanning is where I will start.

I will update this post when i hit a wall or find different solutions. Until then, happy migration!

Share this:

  • Click to share on LinkedIn (Opens in new window) LinkedIn
  • Click to share on X (Opens in new window) X
  • Click to share on Reddit (Opens in new window) Reddit

Posts pagination

1 2 3 … 37

Popular blog posts

  • Azure Application registrations, Enterprise Apps, and managed identities
  • Azure token from a custom app registration
  • Creating Azure AD Application using Powershell
  • Migrate from Azure DevOps to GitHub - what you need to know
  • Azure AD authentication in Azure Functions

Categories

Automation Azure Azure Active Directory Azure Bicep Azure DevOps Azure Functions Azure Lighthouse Azure Logic Apps Azure Monitor Azure Policy Community Conferences CSP Monitoring DevOps GitHub Guest blogs Infrastructure As Code Kubernetes Microsoft CSP MPAuthoring OMS Operations Manager Podcast Powershell Uncategorised Windows Admin Center Windows Server

Follow Martin Ehrnst

  • X
  • LinkedIn

RSS feed RSS - Posts

RSS feed RSS - Comments

Microsoft Azure MVP

Martin Ehrnst Microsoft Azure MVP
Adatum.no use cookies to ensure that we give you the best experience on our website. If you continue to use this site we will assume that you are happy with it. Cookie Policy
Theme by Colorlib Powered by WordPress