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.
Let's start building the CI/CD!
There are 5 steps to deploy your application on Kubernetes with GitHub Actions and ArgoCD.
- Build CI
- Login to ECR
- Build docker image and push it to ECR
- Update manifest file in manifest file repository
- Create PR
- Prepare Manifest file
- Build CD
- 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 ""
- 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
ref: aws-actions/configure-aws-credentials
- 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"
- 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'
- 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 ""
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
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
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
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
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
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!
Top comments (0)