As organizations scale their cloud-native infrastructure, the complexity of managing deployments grows exponentially. What works for a single developer testing a microservice on a local cluster—running a few manual commands—quickly becomes a nightmare when coordinating across multiple teams, environments, and clusters.
In this post, we’ll walk through the journey from fragile manual deployments to a robust, automated GitOps pipeline using Azure Kubernetes Service (AKS) and ArgoCD.
What a Manual AKS Deployment Looks Like
For many, the first interaction with Kubernetes involves the command line. You authenticate with Azure, get your credentials, and start flinging YAML manifests at the API server.
It usually starts with something like this:
# Authenticate with Azure
az login
# Get AKS credentials
az aks get-credentials --resource-group myResourceGroup --name myAKSCluster
# Deploy a manifest directly
kubectl apply -f deployment.yaml
Or, if you are using Helm charts:
# Install a chart manually
helm install my-app ./charts/my-app --set image.tag=v1.0.0
While this is fine for “Hello World” or rapid prototyping, it relies heavily on the operator’s machine and specific context.
Problems with Manual Deployments
This imperative approach—telling the cluster what to do right now—has several significant drawbacks in a production environment:
- Configuration Drift: Someone hot-fixes a production issue by editing a deployment directly (
kubectl edit). The Git repository now says one thing, but the cluster says another. The next time you deploy from Git, you might accidentally overwrite that critical fix or break the app again. - Human Error: It’s easy to deploy to the wrong namespace, use the wrong image tag, or forget a critical configuration flag when typing commands manually.
- Lack of Audit Trail: Who changed the replica count from 3 to 5? When was the memory limit increased? With manual
kubectlcommands, this history is often lost in terminal buffers rather than preserved in version control. - Rollback Difficulty: If a deployment fails, reverting often means finding the previous YAML file and applying it again—assuming you have it handy and know exactly which version was running before.
The GitOps Approach
GitOps flips the script. Instead of pushing changes to the cluster manually, you commit the desired state of your infrastructure and applications to a Git repository.
A software agent inside your cluster (the GitOps controller) constantly monitors that repository. When it sees a change in Git, it pulls the new configuration and applies it to the cluster, ensuring the actual state matches the desired state.
Key Principles:
- Git is the Source of Truth: The entire system is described declaratively in Git.
- Versioned & Immutable: Changes are committed, versioned, and audit-logged via standard Git history.
- Automated Pulling: The cluster pulls changes from Git, rather than CI pushing changes to the cluster.
ArgoCD in AKS
ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes.
Architecture
ArgoCD runs as a controller in your Kubernetes cluster. It comprises several components:
- API Server: Exposes the UI and API.
- Repository Server: Clones and caches Git repositories and generates manifests.
- Application Controller: The heart of the operation. It compares the live state (what’s in the cluster) with the target state (what’s in Git) and orchestrates the synchronization.
The Reconciliation Loop
ArgoCD continuously checks (reconciles) the cluster state against Git. If it detects a difference (e.g., you changed an image tag in Git, or someone manually deleted a service), it marks the application as “OutOfSync.” Depending on your settings, it can either alert you or automatically heal the drift.
Step-by-Step Guide
Let’s set up a basic pipeline.
1. Create an AKS Cluster
First, we need a Kubernetes cluster. We can create one quickly using the Azure CLI.
# Create a Resource Group
az group create --name argocd-demo-rg --location eastus
# Create the AKS cluster
az aks create \
--resource-group argocd-demo-rg \
--name argocd-cluster \
--node-count 2 \
--enable-addons monitoring \
--generate-ssh-keys
# Get the credentials
az aks get-credentials --resource-group argocd-demo-rg --name argocd-cluster
2. Install ArgoCD
We will install ArgoCD in its own namespace.
# Create namespace
kubectl create namespace argocd
# Apply the installation manifest
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Once the pods are running, you can access the ArgoCD UI. For development, we can port-forward the server:
kubectl port-forward svc/argocd-server -n argocd 8080:443
Now, visit https://localhost:8080. The default username is admin. To get the initial password:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
3. Connect a Git Repository
In the ArgoCD UI:
- Go to Settings > Repositories.
- Click Connect Repo Using HTTPS.
- Enter your repository URL (e.g.,
https://github.com/yisusvii/my-k8s-manifests.git).
4. Create an ArgoCD Application
You can create an app via the UI, but in the spirit of GitOps, let’s define it declaratively with YAML.
Create a file named application.yaml:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: guestbook
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/argoproj/argocd-example-apps.git
targetRevision: HEAD
path: guestbook
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
Apply this to your cluster:
kubectl apply -f application.yaml
5. Sync and Auto-Sync
Because we set syncPolicy.automated in the YAML above, ArgoCD will automatically detect the app and deploy it.
- Prune: Removes resources that are no longer in Git.
- SelfHeal: If you manually delete a deployment in the cluster, ArgoCD will immediately recreate it to match the Git state.
Integration with CI
GitOps handles the CD (Continuous Delivery). For CI (Continuous Integration), you still need a tool like GitHub Actions.
A typical workflow looks like this:
- Code Change: Developer pushes code to the application repo.
- CI Pipeline: GitHub Actions builds the Docker image and runs tests.
- Push Image: The new image is pushed to Azure Container Registry (ACR).
- Update Manifests: The CI pipeline commits a change to the infrastructure repository (e.g., updating the image tag in
values.yamlordeployment.yaml). - ArgoCD Sync: ArgoCD detects the change in the infrastructure repo and deploys the new image to AKS.
Recommended Best Practices
- Repo Structure: Keep application source code separate from Kubernetes manifests. This prevents infinite loops where a build triggers a deploy, which triggers a build.
- Namespaces: Deploy ArgoCD in a dedicated namespace (
argocd). Use separate namespaces for your applications (e.g.,dev,staging,prod). - RBAC: Integrate ArgoCD with Azure AD (Entra ID) for Single Sign-On. Don’t share the
adminpassword. - Secrets Management: Never commit raw secrets to Git. Use tools like Sealed Secrets or External Secrets Operator (fetching from Azure Key Vault) to manage sensitive data safely.
- App of Apps: For managing many applications, use the “App of Apps” pattern—a parent ArgoCD application that points to a folder containing other Application manifests.
Conclusion
Moving from manual kubectl commands to an automated ArgoCD pipeline transforms how you manage Kubernetes. You gain visibility, auditability, and stability. While the initial setup takes some effort, the peace of mind knowing your cluster always matches your Git repository is well worth it.
Next Steps
- Try setting up Argo Rollouts for Blue-Green or Canary deployments.
- Explore Argo Image Updater to automatically update image tags without a separate CI commit step.