Kubernetes

Embracing GitOps: Setting Up a GitOps-Ready Cluster on Azure with AKS, ArgoCD, and Terraform

A Step-by-step guide to setup a robust and well-supported GitOps stack: Kubernetes on Azure Cloud, ArgoCD, and using Infrastructure as Code with Terraform.

Amine Raji
6 min readDec 18, 2023

--

In the landscape of cloud-native applications, the concept of GitOps has emerged as a powerful practice for managing and deploying software in a consistent and reliable manner.

GitOps advocates for treating infrastructure and applications as code, ensuring that the desired state of the system is reflected in the versioned Git repository.

This approach brings transparency, reproducibility, and automation to the software delivery process.

To effectively implement GitOps on Azure, a combination of tools and technologies is commonly employed:

  • Azure Kubernetes Service (AKS) serves as the managed Kubernetes platform, providing a robust foundation for running containerized applications.
  • ArgoCD, an open-source GitOps operator, acts as the orchestrator, syncing the desired state declared in Git with the actual state of the applications deployed on the AKS cluster.
  • Terraform an Infrastructure as Code (IaC) tool, automates the creation and management of the underlying infrastructure components, ensuring consistency and repeatability.
Architecture overview

Infrastructure Provision with Terraform

Terraform plays a crucial role in provisioning the necessary infrastructure components, including virtual machines, networking resources, and storage.

It automates the creation of these components, ensuring that the underlying infrastructure matches the desired state defined in the Terraform configuration files.

Infrastructure as Code eliminates manual configurations and reduces the risk of human error, promoting a consistent and stable infrastructure environment.

Here is the Terraform script to deploy a Kubernetes cluster on Azure:

provider "azurerm" {
features {}
}

locals {
infra_rg_name = "aks-poc"
infra_nodes_rg_name = "aks-poc-nodes"
}

# The main resource group
resource "azurerm_resource_group" "main" {
name = local.infra_rg_name
location = var.location
}

# The AKS nodes resource group (auto generated by AKS cluster)
data "azurerm_resource_group" "nodes" {
name = azurerm_kubernetes_cluster.main.node_resource_group
depends_on = [azurerm_kubernetes_cluster.main]
}

# AKS cluster
resource "azurerm_kubernetes_cluster" "main" {
name = var.kubernetes_cluster_name
location = var.location
resource_group_name = azurerm_resource_group.main.name
dns_prefix = var.kubernetes_cluster_name
node_resource_group = local.infra_nodes_rg_name
kubernetes_version = "1.28"

default_node_pool {
name = "default"
node_count = 2
os_disk_size_gb = 30
vm_size = "Standard_D2_v2"
temporary_name_for_rotation = "tmpdefault"
}

linux_profile {
admin_username = "azureuser"
ssh_key {
key_data = file("~/.ssh/id_rsa_azure.pub")
}
}

identity {
type = "SystemAssigned"
}

azure_active_directory_role_based_access_control {
managed = true
azure_rbac_enabled = true
}
}

Kubernetes Cluster Deployment with AKS

Once the infrastructure is provisioned, AKS steps in to deploy the Kubernetes cluster.

AKS provides a managed Kubernetes environment that handles the complexities of cluster management, including node provisioning, configuration, and updates.

This allows developers and operators to focus on deploying and managing applications rather than infrastructure concerns.

GitOps Orchestration with ArgoCD

ArgoCD takes over the responsibility of ensuring that the desired state of the applications aligns with the actual state of the cluster.

ArgoCD acts as a centralized controller, continuously watching the Git repository for updates to application manifests. When changes are detected, ArgoCD triggers the necessary actions to synchronize the cluster with the desired state, ensuring that applications are always deployed in the intended configuration.

Here is the Terraform script to deploy ArgoCD on the AKS cluster and run the bootstrap application:

locals {
infra_rg_name = "aks-poc"
infra_nodes_rg_name = "aks-poc-nodes"
infra_kubernetes_cluster_name = "aks-poc-lb"
}

provider "azurerm" {
features {}
}

terraform {
required_version = ">= 0.13"

required_providers {
kubectl = {
source = "gavinbunney/kubectl"
version = ">= 1.7.0"
}
}
}

data "azurerm_kubernetes_cluster" "main" {
name = var.kubernetes_cluster_name
resource_group_name = local.infra_rg_name
}

provider "kubernetes" {
host = data.azurerm_kubernetes_cluster.main.kube_admin_config.0.host
username = data.azurerm_kubernetes_cluster.main.kube_admin_config.0.username
password = data.azurerm_kubernetes_cluster.main.kube_admin_config.0.password
client_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_admin_config.0.client_certificate)
client_key = base64decode(data.azurerm_kubernetes_cluster.main.kube_admin_config.0.client_key)
cluster_ca_certificate = base64decode(data.azurerm_kubernetes_cluster.main.kube_admin_config.0.cluster_ca_certificate)
}

provider "helm" {
kubernetes {
config_path = "~/.kube/config"
}
}

locals {
argocd_resources_labels = {
"app.kubernetes.io/instance" = "argocd"
"argocd.argoproj.io/instance" = "argocd"
}

argocd_resources_annotations = {
"argocd.argoproj.io/compare-options" = "IgnoreExtraneous"
"argocd.argoproj.io/sync-options" = "Prune=false,Delete=false"
}
}

# Declare some resources, and the git-ops tool:
resource "kubernetes_namespace" "argocd" {
depends_on = [data.azurerm_kubernetes_cluster.main]

metadata {
name = "argocd"
}
}

# Auth to fetch git-ops code
resource "kubernetes_secret" "argocd_repo_credentials" {
depends_on = [kubernetes_namespace.argocd]
metadata {
name = "argocd-repo-credentials"
namespace = "argocd"
labels = merge(local.argocd_resources_labels, {
"argocd.argoproj.io/secret-type" = "repo-creds"
})
annotations = local.argocd_resources_annotations
}
type = "Opaque"
data = {
url = "git@github.com:ORG"
sshPrivateKey = file("./files/githubSSHPrivateKey.key")
}
}

resource "helm_release" "argocd" {
name = "argocd"
namespace = "argocd"
repository = "https://argoproj.github.io/argo-helm"
chart = "argo-cd"
version = "5.46.7"
skip_crds = true
depends_on = [
kubernetes_secret.argocd_repo_credentials,
]
values = [
file("./files/argocd-bootstrap-values.yaml"),
]
}

# The bootstrap application
resource "kubectl_manifest" "argocd_bootstrap" {
depends_on = [helm_release.argocd]

yaml_body = yamlencode({
apiVersion = "argoproj.io/v1alpha1"
kind = "Application"

metadata = {
name = "bootstrap-${var.kubernetes_cluster_name}"
namespace = "argocd"
}

spec = {
project = "default"
destination = {
namespace = "argocd"
name = "in-cluster"
}
source = {
repoURL = "git@github.com:ORG.git"
path: "apps"
revision: "HEAD"
}
}
})
}

Deploying Applications and Manifests

Application manifests, which define the desired state of the applications, are stored in a Git repository.

Developers can commit changes to the manifests, triggering ArgoCD to synchronize the changes with the cluster.

This practice promotes a smooth and collaborative workflow for deploying applications, enabling developers to focus on code changes without worrying about infrastructure management.

ArgoCD bootstrap App

Key benefits of this setup

Simplifying Infrastructure Management

With Terraform, infrastructure management becomes a streamlined process.

Changes to infrastructure configurations are reflected in the Terraform configuration files, and Terraform automatically applies these changes, ensuring that the infrastructure reflects the desired state.

Enhancing Application Consistency

GitOps ensures that the desired state of the applications is always aligned with the actual state of the cluster.

This consistency is paramount for maintaining the integrity and stability of applications. By treating applications as code, GitOps ensures that deployments are reproducible, consistent, and auditable.

Promoting Collaboration and Automation

GitOps fosters a collaborative environment by enabling developers to manage application configurations directly from the Git repository.

This approach promotes transparency and reduces the reliance on manual infrastructure management tasks.

Additionally, ArgoCD’s automation capabilities streamline the deployment process, reducing the time and effort required for application updates.

Hands-on 🙌

If you want to replicate what I just demonstrated, here is the source code used in this article:

Video tutorial 📺

Want to follow along instead, no problem, here is me demonstrating the lab on video:

The takeaway

The combination of AKS, ArgoCD, and Terraform has emerged as a popular and well-supported GitOps stack on Azure.

This approach provides a robust and reliable foundation for managing cloud-native applications, ensuring consistency, repeatability, and automation throughout the software delivery process.

By embracing GitOps, organizations can streamline their development workflows, enhance application stability, and promote collaboration among teams.

Before you go!

If you liked this article and you want to encourage me publishing more:

  1. Throw some Medium love 💕(claps, comments and highlights), your support means a lot to me.👏
  2. Follow me on Medium and subscribe to get my latest article🫶

--

--

Amine Raji

Security expert 🔒 | Empowering organizations 🌐 to safeguard their assets with future-proof architectures & security solutions💥