DEV Community

Atsushi Miyamoto
Atsushi Miyamoto

Posted on • Edited on

Kubernetes CI/CD With GHA and ArgoCD

Nowadays, most companies try to implement CI/CD pipelines to deploy applications to be easier. But how to implement it?
This article shows how to implement it to deploy your application on Kubernetes with GitHub Actions and ArgoCD.
I hope it can help your deployment process easier.

Before starting, Let me explain about what is GitOps. Because I will follow this methodology.

What is GitOps?

GitOps is an operational framework that takes DevOps best practices used for application development such as version control, collaboration, compliance, and CI/CD tooling, and applies them to infrastructure automation. GitOps was first coined by Weaveworks.

Based on that I will implement like below CI/CD.

Gitops

Let's start building the CI/CD!

There are 5 steps to deploy your application on Kubernetes with GitHub Actions and ArgoCD.

  1. Build CI
    1. Login to ECR
    2. Build docker image and push it to ECR
    3. Update manifest file in manifest file repository
    4. Create PR
  2. Prepare Manifest file
  3. Build CD
  4. Recap 5.

1. Build CI

The first step is building the CI by using Github Actions. yaml file should be in application source code repository.

During CI, we can do testing, building docker, push docker image to repository and update manifest file!
You must create ECR on AWS and IAM role to login and push docker image to ECR. I will omit these part in this time.

Complete GHA file is below. I will explain each step by step.

on: push: branches: [deploy/stg] permissions: id-token: write contents: read jobs: prepare: name: code-deploy runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v3 - name: configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: role-to-assume: ${{ secrets.IAM_ROLE }} aws-region: ${{ secrets.AWS_REGION }} - name: login to Amazon ECR id: ecr uses: aws-actions/amazon-ecr-login@v1 - name: docker build and push id: build-image env: ECR_REGISTRY: ${{ steps.ecr.outputs.registry }} ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }} IMAGE_TAG: ${{ github.sha }} run: | docker build -f ./Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG --target release . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" - name: fetch manifest repository uses: actions/checkout@v3 with: repository: github-organization/manifest-repository token: ${{ secrets.PAT }} path: manifest-repo fetch-depth: 1 - name: update manifest file image tag run: | wget -q https://github.com/mikefarah/yq/releases/download/v4.27.5/yq_linux_amd64 sudo mv yq_linux_amd64 /usr/local/bin/yq sudo chmod +x /usr/local/bin/yq yq e -i '.spec.template.spec.containers[0].image |= "${{ steps.build-image.outputs.image }}"' 'manifest-repo/k8s/sample/dev/deployment.yaml' - name: setup Repository working-directory: manifest-repo run: | git fetch origin main git config --global user.name "github-actions[bot]" git config --global user.email "github-actions[bot]@users.noreply.github.com" git remote set-url --push origin https://github.com/***/manifest-repo.git git checkout -b release-${{ github.sha }} - name: commit working-directory: manifest-repo run: | git add . git commit -m "Release bff-admin" git push origin HEAD - name: create PR working-directory: manifest-repo run: | gh pr create -B main -H release-${{ github.sha }} -t "deploy-${{ github.sha }}" -b "" 
Enter fullscreen mode Exit fullscreen mode
  1. Login to ECR The security reason, you must avoid to pass your access key and secret key. Instead pass credential, we can use GitHub's OIDC provider in conjunction with a configured AWS IAM Identity Provider endpoint. You must create Assume role and set it to role-to-assume
 - name: configure AWS credentials uses: aws-actions/configure-aws-credentials@v1 with: role-to-assume: ${{ secrets.IAM_ROLE }} aws-region: ${{ secrets.AWS_REGION }} - name: login to Amazon ECR id: ecr uses: aws-actions/amazon-ecr-login@v1 
Enter fullscreen mode Exit fullscreen mode

ref: aws-actions/configure-aws-credentials

  1. Build docker image and push it to ECR The next step, build a docker image and push it to ECR. I prefer to import the registry name and repository name from secret. After setting these variables, run the docker command to build and push to ECR.
 - name: docker build and push id: build-image env: ECR_REGISTRY: ${{ steps.ecr.outputs.registry }} ECR_REPOSITORY: ${{ secrets.ECR_REPOSITORY }} IMAGE_TAG: ${{ github.sha }} run: | docker build -f ./Dockerfile -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG --target target . docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG echo "::set-output name=image::$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" 
Enter fullscreen mode Exit fullscreen mode
  1. Update manifest file in manifest file repository After pushing the docker image, do update the manifest file to create PR to the manifest repository. Assume the manifest repository manages your k8s manifest files. At the first, fetch the latest code from the manifest repository, and after that update file using yq. We just need to update the container image to the newest image which we uploaded to ECR in the previous step.
 - name: fetch manifest repository uses: actions/checkout@v3 with: repository: github-organization/manifest-repository token: ${{ secrets.PAT }} path: manifest-repo fetch-depth: 1 - name: update manifest file image tag run: | wget -q https://github.com/mikefarah/yq/releases/download/v4.27.5/yq_linux_amd64 sudo mv yq_linux_amd64 /usr/local/bin/yq sudo chmod +x /usr/local/bin/yq yq e -i '.spec.template.spec.containers[0].image |= "${{ steps.build-image.outputs.image }}"' 'manifest-repo/k8s/sample/dev/deployment.yaml' 
Enter fullscreen mode Exit fullscreen mode
  1. Create PR After updating the manifest file, finally, create PR to trigger CD. Merging PR can be your deploy your latest code to the production environment. I use gh to create PR.
 - name: commit working-directory: manifest-repo run: | git add . git commit -m "Release bff-admin" git push origin HEAD - name: create PR working-directory: manifest-repo run: | gh pr create -B main -H release-${{ github.sha }} -t "deploy-${{ github.sha }}" -b "" 
Enter fullscreen mode Exit fullscreen mode

2. Prepare Manifest file

Prepare the manifest file in the manifest repository. It will update by CI.
Below is a sample manifest file.
You must create namespace first. This time I create deploy-test.

manifest-repository/k8s/deploy-test/deployment.yaml

apiVersion: apps/v1 kind: Deployment metadata: name: deploy-test namespace: deploy-test spec: selector: matchLabels: app: deploy-test template: metadata: labels: app: deploy-test spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: eks.amazonaws.com/nodegroup operator: In values: - sample-ng containers: - name: deploy-test image: yourAccountId.dkr.ecr.yourRegion.amazonaws.com/yourRepositoryName:yourImageTag 
Enter fullscreen mode Exit fullscreen mode

Remember, during CI, I use yq to update the image tag. the update part is yourImageTag.
So your pod will pull the latest image from ECR after PR is merged.

 containers: - name: deploy-test image: yourAccountId.dkr.ecr.yourRegion.amazonaws.com/yourRepositoryName:yourImageTag 
Enter fullscreen mode Exit fullscreen mode

3. Build CD

Finally, build a CD using ArgoCD!

*You must create namespace and cluster.

The first step is to set up a GitHub access token to access the cluster to your repository.

You can type the below cmd to set your GitHub token to your cluster.
This time namespace that I set is argocd.

export GITHUB_TOKEN=<github-token> kubectl create secret generic github-cred -n argocd \ --from-literal url=https://github.com/${GITHUB_USER}/manifest-repository \ --from-literal username=<github-user> \ --from-literal password=${GITHUB_TOKEN} \ --from-literal name=github-cred kubectl label secret task-tool-cluster-config-cred argocd.argoproj.io/secret-type=repository -n argoc 
Enter fullscreen mode Exit fullscreen mode

Then write argocd manifest file.
file path is manifest-repository/k8s/deploy-test/argocd/deploy.yaml

apiVersion: argoproj.io/v1alpha1 kind: Application metadata: name: deploy-test namespace: argocd spec: project: default source: repoURL: https://github.com/${GITHUB_USER}/manifest-repository targetRevision: main path: k8s/deploy-test syncPolicy: automated: selfHeal: true prune: true destination: server: https://kubernetes.default.svc namespace: deploy-test 
Enter fullscreen mode Exit fullscreen mode

The ArgoCD will sync with the repository that you set on spec.source part.

After all setup, finally, apply it.

kubectl apply -f k8s/deploy-test/argocd/deploy.yaml 
Enter fullscreen mode Exit fullscreen mode

That's it!!! you are all done setting CI/CD!

4. Recap

You created an application source code repository and manifest repository.
In the source code repo, we added CI to build and push the docker image to ECR, update the manifest file, and create PR.

You can manage your deployment whether you merge PR. After merging PR, ArgoCD will sync with your newest code.
The pod will replace to newest image from ECR!

Thank you for reading my article, Happy Coding!

Reference:

Guide To GitOps

What is GitOps? | GitLab

Top comments (0)