Ci/cd With Github Action And Argo Cd

Anish BistaAnish Bista
7 min read

Introduction

Continuous Integration and Continuous Deployment (CI/CD) are essential practices in modern software development, allowing teams to deliver high-quality software quickly and efficiently. In this blog, we'll explore how to set up a robust CI/CD pipeline using GitHub Actions and Argo CD.

Project link

Learn how to deploy same Application without CI/CD on kubernetes

Deploy direct on kubernetes without CI/CD

When you use ArgoCD, then you don't have to deploy it manually.

Prerequisites

Before we dive in, make sure you have the following:

  • A GitHub account

  • A Kubernetes cluster

  • Basic knowledge of Kubernetes and Docker

Setting Up GitHub Actions

Creating a Workflow File

GitHub Actions allows you to automate your development workflows. To set up a CI pipeline, create a new directory called .github/workflows in your repository. Inside this directory, create a file named githubAction.yaml.

name: build and push docker image
on:
  push

jobs:
  release-docker:
    name: Release docker image
    if: "!contains(github.event.head_commit.message, '[skip ci]')"
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2

      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

      - name: Get git SHA short
        id: vars
        run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_ENV

      - name: Build and push Docker image
        uses: docker/build-push-action@v4
        with:
          context: GoalApp-CICD/
          push: true
          tags: |
            anish60/app:latest
            anish60/app:sha-${{ env.sha_short }}
          labels: |
            org.opencontainers.image.source=${{ github.repository }}
            org.opencontainers.image.revision=${{ github.sha }}

      - name: Generate deploy manifest from Jinja template
        uses: cuchi/jinja2-action@v1.1.0
        with: 
          template: GoalApp-CICD/Jinja-Template/deploy.yaml
          output_file: GoalApp-CICD/Manifest/new-deploy.yaml
          strict: true
          variables: |
            image_deploy_tag=sha-${{ env.sha_short }}

      - name: Configure git for the action
        run: |
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"

      - name: Stash unstaged changes
        run: git stash --include-untracked

      - name: Pull latest changes from the remote branch
        run: git pull origin main --rebase

      - name: Apply stashed changes
        run: git stash pop || echo "No stashed changes to apply"

      - name: Commit deploy manifest on local repo
        run: |
          git add GoalApp-CICD/Manifest/new-deploy.yaml
          git commit -s -m "[skip ci] Generate deployment manifests"

      - name: Push deploy manifests to remote repo
        uses: ad-m/github-push-action@v0.6.0
        with: 
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: main

This YAML pipeline is a GitHub Actions workflow designed to build and push a Docker image to Docker Hub when there is a push to the repository. Here's a step-by-step explanation of the pipeline:

Explanation of Workflow

Name and Trigger

name: build and push docker image
on:
  push
  • name: The name of the workflow is "build and push docker image".

  • on: The workflow is triggered by a push event to the repository.

Jobs

jobs:
  release-docker:
    name: Release docker image
    if: "!contains(github.event.head_commit.message, '[skip ci]')"
    runs-on: ubuntu-20.04
  • release-docker: This defines a job named "Release docker image".

  • if: This condition ensures the job runs only if the latest commit message does not contain [skip ci].

  • runs-on: The job runs on an ubuntu-20.04 runner.

Steps

  1. Checkout the repository

     - name: Checkout
       uses: actions/checkout@v4
    
    • name: Checkout

    • uses: Uses the actions/checkout action to checkout the repository.

  2. Set up Docker Buildx

     - name: Set up Docker Buildx
       uses: docker/setup-buildx-action@v2
    
    • name: Set up Docker Buildx

    • uses: Uses the docker/setup-buildx-action action to set up Docker Buildx, which allows for advanced features like multi-platform builds.

  3. Log in to Docker Hub

     - name: Log in to Docker Hub
       uses: docker/login-action@v2
       with:
         username: ${{ secrets.DOCKER_USERNAME }}
         password: ${{ secrets.DOCKER_PASSWORD }}
    
    • name: Log in to Docker Hub

    • uses: Uses the docker/login-action to log in to Docker Hub with credentials stored in GitHub secrets.

  4. Get git SHA short

     - name: Get git SHA short
       id: vars
       run: echo "sha_short=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
    
    • name: Get git SHA short

    • id: vars

    • run: Runs a shell command to get the short SHA of the latest commit and sets it as an environment variable.

  5. Build and push Docker image

     - name: Build and push Docker image
       uses: docker/build-push-action@v4
       with:
         context: GoalApp-CICD/
         push: true
         tags: |
           anish60/app:latest
           anish60/app:sha-${{ env.sha_short }}
         labels: |
           org.opencontainers.image.source=${{ github.repository }}
           org.opencontainers.image.revision=${{ github.sha }}
    
    • name: Build and push Docker image

    • uses: Uses the docker/build-push-action to build and push the Docker image.

    • with: Specifies the context, push flag, tags, and labels for the Docker image.

  6. Generate deploy manifest from Jinja template

     - name: Generate deploy manifest from Jinja template
       uses: cuchi/jinja2-action@v1.1.0
       with: 
         template: GoalApp-CICD/Jinja-Template/deploy.yaml
         output_file: GoalApp-CICD/Manifest/new-deploy.yaml
         strict: true
         variables: |
           image_deploy_tag=sha-${{ env.sha_short }}
    
    • name: Generate deploy manifest from Jinja template

    • uses: Uses the cuchi/jinja2-action to generate a deployment manifest from a Jinja template.

    • with: Specifies the template file, output file, and variables to be used in the template.

  7. Configure git for the action

     - name: Configure git for the action
       run: |
         git config --local user.email "action@github.com"
         git config --local user.name "GitHub Action"
    
    • name: Configure git for the action

    • run: Configures git with a local user name and email for committing changes.

  8. Stash unstaged changes

     - name: Stash unstaged changes
       run: git stash --include-untracked
    
    • name: Stash unstaged changes

    • run: Stashes any unstaged changes including untracked files.

  9. Pull latest changes from the remote branch

     - name: Pull latest changes from the remote branch
       run: git pull origin main --rebase
    
    • name: Pull latest changes from the remote branch

    • run: Pulls the latest changes from the main branch with rebase.

  10. Apply stashed changes

    - name: Apply stashed changes
      run: git stash pop || echo "No stashed changes to apply"
    
    • name: Apply stashed changes

    • run: Applies the stashed changes or prints a message if there are no stashed changes to apply.

  11. Commit deploy manifest on local repo

    - name: Commit deploy manifest on local repo
      run: |
        git add GoalApp-CICD/Manifest/new-deploy.yaml
        git commit -s -m "[skip ci] Generate deployment manifests"
    
    • name: Commit deploy manifest on local repo

    • run: Adds and commits the newly generated deployment manifest with a commit message that includes [skip ci] to prevent triggering another CI run.

  12. Push deploy manifests to remote repo

    - name: Push deploy manifests to remote repo
      uses: ad-m/github-push-action@v0.6.0
      with: 
        github_token: ${{ secrets.GITHUB_TOKEN }}
        branch: main
    
    • name: Push deploy manifests to remote repo

    • uses: Uses the ad-m/github-push-action to push the changes to the remote main branch.

    • with: Specifies the GitHub token and branch to push to.

This workflow automates the process of building and pushing Docker images, generating deployment manifests, and pushing these manifests back to the repository, ensuring the deployment process is seamless and consistent.

Configuring the Docker username and password as secret in the Github

  • Go to the Setting in your Repo

  • Then , on the Left you have Secret and Variable section. Under that you have Action section. Click on that you will see

  • I have already configured here , In your case click on New repository secret and configure the username and password for docker hub.

Installing Argo CD

Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. To install Argo CD, run the following commands:

kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
kubectl get secret -n argocd argocd-initial-admin-secret -oyaml

You will get the LoadBalancer Ip of the ArgoServer . So , paste to browser . You will get admin page.

Username : admin
Password : <decoded secret>

Decode the secret using echo "<secret-key>" | base64 --decode.

Connecting Argo CD to Your GitHub Repository

To connect Argo CD to your GitHub repository, follow these steps:

  1. Access the Argo CD UI (port-forward or load balancer).

    After running command, you will get the LoadBalancer Ip and via which you can access UI.

     kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
    
  2. Log in using the default admin credentials.

     kubectl get secret -n argocd argocd-initial-admin-secret -oyaml
    

    Decode the secret using echo "<secret-key>" | base64 --decode

    Username : admin
    Password : <decoded secret>

  3. Create a new application in Argo CD, specifying your GitHub repository and the path to your Kubernetes manifests.

    In my case the the directory that ArgoCd is monitoring is GoalApp-CICD/Manifest

  4. Once you are done with creating the Application in ArgoCd then i will automatically fetch all the manifest file and deployed to Kubernetes cluster.

Conclusion

In this guide, we've set up a complete CI/CD pipeline using GitHub Actions and Argo CD. This setup allows you to automate your build, test, and deployment processes, ensuring that your applications are always up-to-date and running smoothly

0
Subscribe to my newsletter

Read articles from Anish Bista directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Anish Bista
Anish Bista

Anish Bista is a passionate student deeply involved in cloud-native, DevOps, and open-source software, particularly focusing on Kubernetes ☸. He actively contributes to CNCF projects, including KubeVirt and Kanisterio. Anish is also engaged in community work and is a member of DevOpsCloudJunction. He also serve as a Co-Organizer at CNCF Kathmandu. His interest and Expertise lies on K8s and Golang.