What is GitOps and Why Use It?
GitOps is a modern approach to Continuous Deployment (CD) that uses Git as a single source of truth for managing application deployments. Instead of manually applying Kubernetes configurations, GitOps tools like Argo CD automatically sync your cluster state with your Git repository.
What Are Argo CD and GitHub Actions?
Tools |
Description |
GitHub Action |
A declarative, GitOps-based Continuous Delivery tool for Kubernetes. It ensures your cluster matches the desired state in your Git repository. |
Argo CD |
A CI/CD automation tool that runs workflows for building, testing, and deploying applications automatically. |
What is Argo CD?
Argo CD is a declarative, GitOps-based continuous delivery tool designed for Kubernetes. It enables teams to manage application deployments using Git as the single source of truth. This ensures that deployments are automated, consistent, and easily auditable.
Why Use Argo CD?
- GitOps Workflow: Uses Git repositories as the source of truth for Kubernetes deployments.
- Automated Deployments: Ensures applications are always running the desired state.
- Self-Healing: Detects and corrects configuration drifts automatically.
- Rollback Capabilities: Easily roll back to previous versions in case of failure.
- Multi-Cluster Support: Deploy applications across multiple Kubernetes clusters.
- Declarative Configuration: Uses YAML manifests, making deployments predictable and maintainable.
Benefits of Argo CD
- Increased Deployment Speed: Automates the deployment pipeline, reducing manual intervention.
- Improved Security: Uses Git as the central source of truth, minimizing configuration drift.
- Enhanced Observability: Provides a UI and CLI to monitor deployments and sync status.
- Scalability: Supports complex, large-scale deployments with ease.
Real-World Use Cases of Argo CD
- Fintech Applications: Ensuring compliance-driven infrastructure management.
- E-commerce Platforms: Handling frequent updates and high traffic.
- AI/ML Models: Deploying and updating machine learning models in production environments.
What Will We Achieve in This Article?
Below are the things we will look into as part of this article.
- A .NET application deployed on Azure Kubernetes Service (AKS)
- Argo CD syncing the app with a Git repository
- GitHub Actions automating build and deployment
High-Level Architecture
- The developer pushes code to a GitHub repository.
- GitHub Actions builds the Docker image and pushes it to Azure Container Registry (ACR).
- Argo CD detects the new image and updates the Kubernetes deployment.
- AKS pulls the updated image and runs the application.
![ARGO]()
Fig Ref: URL
Prerequisites
Before diving in, ensure you have,
- An Azure Account with AKS (Azure Kubernetes Service) set up
- Azure Container Registry (ACR)
- Basic understanding of Docker, Kubernetes & GitHub Actions
Installation of Argo CD on AKS
Argo CD must be installed on AKS before using it. Here’s how,
Step 1. Install Argo CD in AKS.
//Creating namespace for argo cd
kubectl create namespace argocd
//Installation of argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
This creates an argocd namespace and installs all necessary Argo CD components.
Check if all the pods are running for argocd.
kubectl get all -n argocd
![CMD]()
Step 2. Expose the Argo CD UI.
Check if all the pods are running for argoCD
kubectl port-forward svc/argocd-server -n argocd 8080:443
Now, you can access Argo CD UI at: https://localhost:8080
Step 3. Log in to Argo CD CLI.
Get the default admin password.
kubectl get secrets -n argocd argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 --decode
Log in using CLI.
argocd login <ARGOCD_SERVER> --username admin --password <PASSWORD> --insecure
![CLI]()
Now, you can use Argo CD to manage Kubernetes deployments.
Setting Up the Application Codebase
Step 1. Setting Up the .NET Weather API App.
Our application follows a simple structure, with a Dockerfile used to containerize the app.
Dockerfile (ArgoWorkflowDemo/ArgoWorkflowDemo/Dockerfile)
# Use the official .NET Core SDK as a parent image
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /app
# Copy the project file and restore any dependencies (use .csproj for the project name)
COPY *.csproj ./
RUN dotnet restore
# Copy the rest of the application code
COPY . .
# Publish the application
RUN dotnet publish -c Release -o out
# Build the runtime image
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime
WORKDIR /app
COPY --from=build /app/out ./
# Expose the port your application will run on
EXPOSE 80
# Start the application
ENTRYPOINT ["dotnet", "ArgoWorkflowDemo.dll"]
- Base Image: Uses the official .NET 8 runtime image.
- Working Directory: Sets the working path inside the container.
- Copy Files: Copies the app files into the container.
- Entry Point: Runs the application inside the container.
Step 2. Writing Kubernetes Manifests.
We define two primary Kubernetes manifests.
- Application.yaml (Argo CD Application)
- deployment.yaml (Kubernetes Deployment and Service)
Argo CD Application Manifest (k8s/application.yaml)
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: weatherapi
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/Jaydeep-007/argo-cd-demo
path: k8s
targetRevision: main
destination:
server: https://kubernetes.default.svc
namespace: default
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
- Auto-sync & Self-heal: Ensures the app is always deployed correctly.
- Source Repository: Links to the GitHub repository.
- Namespace: Defines the target Kubernetes namespace.
Deployment YAML (k8s/deployment.yaml)
apiVersion: apps/v1
kind: Deployment
metadata:
name: weatherapi
namespace: argocd
spec:
replicas: 2
selector:
matchLabels:
app: weatherapi
template:
metadata:
labels:
app: weatherapi
spec:
containers:
- name: weatherapi
image: jddemoacr.azurecr.io/weatherapi:63c6e53528b0f817980212a121b7a73830c3664a
ports:
- containerPort: 80
env:
- name: ASPNETCORE_ENVIRONMENT
value: "Production"
---
apiVersion: v1
kind: Service
metadata:
name: weatherapi
namespace: argocd
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: weatherapi
Deployment Resource (Deployment - apps/v1)
- Name: weatherapi
- Namespace: argocd
- Replicas: 2 (Ensures high availability)
- Labels: app: weatherapi (Used for identifying resources)
- Container Image: jddemoacr.azurecr.io/weatherapi:63c6e53528b0f817980212a121b7a73830c3664a
- Port: Exposes the container on port 80
- Environment Variable:
- ASPNETCORE_ENVIRONMENT=Production (Sets production environment)
Service Resource (Service - v1)
- Type: LoadBalancer (Exposes service externally)
- Ports: Map port 80 on the cluster to 80 inside the container
- Selector: Matches app: weatherapi to link the service with the pods
Step 3. GitHub Actions Workflow for Deployment.
This workflow automates.
- Building and pushing the Docker image to ACR.
- Updating the Kubernetes manifest with the latest image.
- Triggering Argo CD to sync changes.
Workflow File: .github/workflows/deploy.yml
Note. Please configure all your secrets under github Actions from the repository settings.
name: Deploy to AKS via Argo CD
on:
push:
branches:
- main
env:
IMAGE_NAME: weatherapi
REGISTRY: jddemoacr.azurecr.io # Replace with your ACR or Docker Hub URL
TAG: ${{ github.sha }}
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Login to Azure Container Registry
run: echo "${{ secrets.ACR_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.ACR_USERNAME }} --password-stdin
- name: Build and Push Docker Image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} -f ArgoWorkflowDemo/ArgoWorkflowDemo/Dockerfile ArgoWorkflowDemo/ArgoWorkflowDemo
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
- name: Update Kubernetes Deployment YAML
run: |
sed -i "s|image: .*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}|" k8s/deployment.yaml
git config --global user.email "[email protected]"
git config --global user.name "GitHub Actions"
git add k8s/deployment.yaml
git diff --quiet && git diff --staged --quiet || git commit -m "Update image to ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}"
git push origin main || echo "No changes to push"
- name: Setup Kubeconfig
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG_DATA }}" | base64 --decode > $HOME/.kube/config
chmod 600 $HOME/.kube/config
export KUBECONFIG=$HOME/.kube/config
kubectl config view --minify
kubectl get nodes
- name: Check if Argo CD CLI is Installed
id: check-argocd
run: |
if command -v argocd &> /dev/null; then
echo "Argo CD is already installed"
echo "installed=true" >> $GITHUB_ENV
else
echo "Argo CD is not installed"
echo "installed=false" >> $GITHUB_ENV
fi
- name: Install Argo CD CLI
if: env.installed == 'false'
run: |
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
- name: Login to ArgoCD
run: |
argocd login ${{ secrets.ARGOCD_SERVER }} --username admin --password ${{ secrets.ARGOCD_PASSWORD }} --insecure
- name: Sync Argo CD Application
run: |
argocd app sync weatherapi
- name: Wait for Deployment to Complete
run: |
argocd app wait weatherapi --health --timeout 180
Overview of the Workflow
This GitHub Actions workflow automates.
- Building a Docker image for the .NET Weather API.
- Pushing the image to Azure Container Registry (ACR).
- Updating the Kubernetes deployment YAML to use the new image.
- Syncing with Argo CD to deploy the updated application to AKS.
Step-by-Step Breakdown
1. Workflow Trigger
on:
push:
branches:
- main
This workflow runs whenever code is pushed to the main branch.
2. Environment Variables
env:
IMAGE_NAME: weatherapi
REGISTRY: jddemoacr.azurecr.io
TAG: ${{ github.sha }}
Defines global variables.
- IMAGE_NAME: The Docker image name (weatherapi).
- REGISTRY: The container registry where images are stored.
- TAG: Uses Git commit SHA as the image version (ensures each image is unique).
3. Job: build-and-deploy
jobs:
build-and-deploy:
runs-on: ubuntu-latest
Runs the workflow on an Ubuntu-based GitHub Actions runner.
4. Checkout Repository
- name: Checkout Code
uses: actions/checkout@v3
Fetches the latest source code from the repository.
5. Log in to Azure Container Registry (ACR)
- name: Login to Azure Container Registry
run: echo "${{ secrets.ACR_PASSWORD }}" | docker login ${{ env.REGISTRY }} -u ${{ secrets.ACR_USERNAME }} --password-stdin
Authenticates with Azure Container Registry (ACR) using GitHub Secrets.
6. Build and Push Docker Image
- name: Build and Push Docker Image
run: |
docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }} -f ArgoWorkflowDemo/ArgoWorkflowDemo/Dockerfile ArgoWorkflowDemo/ArgoWorkflowDemo
docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}
- Builds a Docker image using the .NET Weather API’s Dockerfile.
- Tag the image with the commit SHA and push it to ACR.
7. Update Kubernetes Deployment YAML
- name: Update Kubernetes Deployment YAML
run: |
sed -i "s|image: .*|image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}|" k8s/deployment.yaml
git config --global user.email "[email protected]"
git config --global user.name "GitHub Actions"
git add k8s/deployment.yaml
git diff --quiet && git diff --staged --quiet || git commit -m "Update image to ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.TAG }}"
git push origin main || echo "No changes to push"
- Update k8s/deployment.yaml to use the newly built image.
- Commits and pushes the updated YAML file back to the repository.
8. Set up Kubeconfig (Access AKS Cluster)
Note: Here I used pre-configured kube config secrets, but in real-time, you should use Azure AD and managed identity for better security.
- name: Setup Kubeconfig
run: |
mkdir -p $HOME/.kube
echo "${{ secrets.KUBE_CONFIG_DATA }}" | base64 --decode > $HOME/.kube/config
chmod 600 $HOME/.kube/config
export KUBECONFIG=$HOME/.kube/config
kubectl config view --minify
kubectl get nodes
- Authenticates with the Kubernetes cluster (AKS) using a pre-configured kubeconfig file stored as a GitHub Secret.
- Runs kubectl get nodes to confirm connectivity.
9. Check if Argo CD CLI is Installed
- name: Check if Argo CD CLI is Installed
id: check-argocd
run: |
if command -v argocd &> /dev/null; then
echo "Argo CD is already installed"
echo "installed=true" >> $GITHUB_ENV
else
echo "Argo CD is not installed"
echo "installed=false" >> $GITHUB_ENV
fi
- Checks if Argo CD CLI is already installed.
- Saves the result (installed=true/false) as an environment variable.
10. Install Argo CD CLI (If Not Installed)
- name: Install Argo CD CLI
if: env.installed == 'false'
run: |
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
Downloads and installs the Argo CD CLI tool if it’s missing.
11. Log in to Argo CD
- name: Login to ArgoCD
run: |
argocd login ${{ secrets.ARGOCD_SERVER }} --username admin --password ${{ secrets.ARGOCD_PASSWORD }} --insecure
Logs in to Argo CD using GitHub Secrets.
12. Sync Application with Argo CD
- name: Sync Argo CD Application
run: |
argocd app sync weatherapi
Triggers Argo CD to deploy the updated Kubernetes manifests.
13. Wait for Deployment to Complete
- name: Wait for Deployment to Complete
run: |
argocd app wait weatherapi --health --timeout 180
- Waits for the deployment to reach a healthy state in Argo CD.
- The timeout is set to 180 seconds.
Testing and Verification
We'll add a new "Hello World" API endpoint in the .NET Weather API controller, push the changes, and verify if it gets deployed automatically.
Step 1. Modify the .NET Controller.
Open the existing WeatherForecastController.cs and add a new HelloWorld method.
Path: ArgoWorkflowDemo/ArgoWorkflowDemo/Controllers/WeatherForecastController.cs
[HttpGet("hello")]
public IActionResult HelloWorld()
{
return Ok("Hello, Argo CD Deployment is Successful!");
}
Step 2. Commit and Push Changes to GitHub.
- Save the file.
- Open Terminal / Command Prompt in the repository folder.
- Run the following commands to commit and push.
git add ArgoWorkflowDemo/ArgoWorkflowDemo/Controllers/WeatherForecastController.cs
git commit -m "Added Hello World endpoint for Argo CD test"
git push origin main
Step 3. Verify GitHub Actions Execution.
- Go to your GitHub repository.
- Navigate to Actions -> Deploy to AKS via Argo CD workflow
- Ensure all steps run successfully.
![CD workflow]()
![Build]()
Step 4. Verify the Build and Publish Image.
You can verify the updated weather api docker image, which was published with the latest updated change.
![Publish image]()
Step 5. Verify Argo CD Deployment.
- Open Argo CD UI
- Check if the weatherapi application is in a Synced & Healthy state.
![CD UI]()
![Sync]()
If you click on any of the above Kubernetes resources, you can check events, logs, and any configuration file.
![Configuration file]()
![Event]()
![Logs]()
Step 6. Test the Deployed API on AKS.
Get the external IP of the weatherapi service.
kubectl get services -n argocd
![External IP]()
Access the Hello World API in a browser.
![API]()
Conclusion
In this article, we’ve successfully built a GitOps-powered CI/CD pipeline for deploying a .NET application to AKS using Argo CD and GitHub Actions. By leveraging modern CI/CD practices, we automated the entire process from code changes to deployment, ensuring a seamless and scalable solution for any software engineer.
Key Takeaways
- GitOps enables automated, reliable, and consistent deployments to Kubernetes using Argo CD.
- GitHub Actions acts as the CI/CD tool, automating tasks like building, testing, and deploying our .NET application.
- With AKS, we set up a scalable Kubernetes cluster that runs the app and handles traffic efficiently.