Kubernetes CI/CD Pipelines: GitHub Actions with Devtron

Within the previous blog, we had set up a complete CI/CD pipeline using GitHub Actions to build, test, perform security scans on our application, build the container image, and push it to DockerHub. We also created a custom script that would create a deployment to our Kubernetes cluster. To address the security concerns of having the KubeConfig credentials leaked, we created a custom GitHub runner within our target cluster, eliminating the problem of accidentally leaking credentials.

While GitHub Actions are great for creating pipelines and can be run as soon as you push a commit or raise a PR, they might lack certain capabilities you would require to orchestrate the deployment and the Day-2 operations. These challenges include handling drifts, rolling back to a previous deployment, monitoring application health, using deployment strategies, etc. While there are hacks that can solve these challenges, GitHub Action lacks when it comes to a continuous deployment platform. We discussed some of these challenges in the previous blog. Along with these, GitHub Actions are great for CI, but when it comes to continuous deployment, it might not be the best solution available.

Within this blog, we will build up on what we did previously. Instead of using GitHub Actions to handle the CD pipeline, we will use GitHub Actions for CI, and we will then use Devtron to create the CD pipeline and deploy the application on the Kubernetes cluster. We'll also take a look at how GitHub Actions integrates with Devtron for continuous deployment and can provide advanced capabilities for orchestrating the deployments.

Enhancing Kubernetes Deployments with Devtron

Devtron is a software delivery platform for Kubernetes, that helps you seamlessly create and manage Kubernetes deployments. It can help you manage the entire application lifecycle, integrate with different tooling from the Kubernetes ecosystem, help you troubleshoot and debug your applications, real-time status of workloads, SLO-based rollbacks, etc. You can create an end-to-end CI and CD pipeline within Devtron, and integrate various functionalities such as image scanning with Trivy.

Apart from several Devtron-Native integrations, you have the option to use several different plugins for defining some pre and post-CI actions. These plugins include dependency trackers for various tech stacks, integrating Sonarqube, k6 load testing, and many more.

For our purposes, we will be using the same application that we used in the previous blog. We already had set up a CI and CD pipeline. We are going to be using the same pipeline, and send the information of the newly created image to Devtron. Further, Devtron will then use the image to create the CD pipeline.

Creating a Continous Deployment Pipeline with Devtron

Within Devtron, you have the option to create a CI and CD pipeline for your application. However, since we already have the CI pipeline set up with GitHub Actions, we can use an external webhook which can be used for triggering the deployment within Devtron. Let’s walk through the entire process of setting up an external webhook for the GitHub CI pipeline, and see what are the additional capabilities that Devtron enables us with.

Before proceeding please make sure that you have Devtron setup and is accessible through an external webhook API. The installation is pretty much straightforward with just helm commands. For more details, you can refer to the documentation.

helm repo add devtron https://helm.devtron.ai

helm repo update devtron

helm install devtron devtron/devtron-operator \
--create-namespace --namespace devtroncd \
--set installer.modules={cicd} \
--set argo-cd.enabled=true

Also, make sure that you have the CI/CD and the GitOps integrations enabled if you want to have GitOps-based deployments. You can enable them from the Devtron Stack Manager.

On the Devtron Dashboard, we have to first create an application. Click on Create and create a Custom app from the Applications Dashboard.

[Fig. 1] Create an application

We have to set a few configurations for this application. Give your application a name, and select which environment you want to deploy it to. Then click on Create. For this application, we will call it gin-app

[Fig. 2] Application settings

After creating the application, there are certain application-specific configurations that we need to configure. The first thing to configure is to set up a Git Repository. We will point it to our gin-kv repository.

[Fig. 3] Add a Git Repository

The next step is to set up a container registry. To set the container registry. You need to first add and authenticate to the Registry from the Global Configuration. Please check this doc to learn how to add a container registry to your Devtron Platform.

After adding the registry in the Global configurations, we can add the registry for the application.

[Fig. 4] Add a container registry

If you have read the previous blog, we had to create our Kubernetes manifest files, i.e. Deployment,  Service, and Ingress YAML files. Within Devtron, we do not need to create our own YAML files. It comes with a deployment template, which satisfies most of the use cases.

Within this Deployment template, you can easily configure ingress, PDB, HPA, KEDA, Service, etc, and the best part is, that you don't need to write and manage the Kubernetes manifests. Additionally, you don't even need to worry about API deprecations, or config changes as Devtron makes sure it's forward and backward-compatible with Kubernetes clusters which can also help you in cluster upgrades if the configurations are being managed through Devtron.

[Fig. 5] Configure Deployment Template

Now, let’s create the Deployment Pipeline. Within Devtron, you have the option to create the CI pipeline within Devtron itself. However, since we already have a pipeline created and configured, we will select the external pipeline option. Click on Deploy Image from External Service

[Fig. 6] Create a webhook for the external CI pipeline

After selecting the destination, you will have to set the deployment pipeline options. Within this pipeline, you have to configure the environment where you wish to deploy the application and the deployment strategy. You can also configure if you want to trigger the deployment manually, or automatically.

If you select the Automatic deployment, Devtron will trigger the deployment as soon as it receives the image from the external pipeline.

[Fig. 7] Configure the CD pipeline

As we have chosen an external pipeline, Devtron is not aware of which pipeline we are using for the build. All Devtron cares about is the correct image. We will be providing this information to the Devtron pipeline using a webhook.

After clicking on the external pipeline from the workflow editor, you will see the configurations of the webhook. From the below image [Fig. 8], you can see that we already have a template for the curl request that needs to be sent by the GitHub pipeline.

[Fig. 8] Create a Token for the Webhook request

Before the request can be made, we need to generate an Authentication token. If you have previously generated an API token in Devtron, you can use an existing token. If not, Devtron can auto-generate a token for you with the required permissions.

We are auto-generating a token named github-ci-token. To maintain good security practices, we will reference this token within the pipeline using a Secret variable.

Let’s create a new secret variable within the GitHub repository and its value will be the token generated by Devtron. We will call this token as DEVTRON_TOKEN and it will be referenced in the curl command which we run in the CI pipeline.

To create the token, head to the repository settings, and from Secrets and Variables, select Actions. Here you can create a secret variable.

[Fig. 9] Adding Action Secrets

Next, we want to put the curl command within the build and push action. As a quick recap, the Build and Push action builds the docker image and pushes it to DockerHub. We want to add a step within this job i,e sending a curl request to Devtron.

We can use the following YAML to create the new job.

      - name: Ping devtron server
        run: |
              curl --location --request POST \
              'http://35.184.124.194:31515/orchestrator/webhook/ext-ci/3' \
              --header 'Content-Type: application/json' \
              --header 'api-token: ${{ secrets.DEVTRON_TOKEN }}' \
              --data-raw '{
              "dockerImage": "siddhantkhisty/gin-kv:latest"
              }'

The above step sends the correct POST request to the Devtron CD pipeline. Notice that in the api-token section, we have referenced the secret variable that we created earlier. This ensures that the token is not leaked.

For the dockerImage field, we have defined the image name as well as the tag. If you want to have custom tags, you can use a tool such as Jinja to dynamically tag the image, and use the same tag within the curl command.

With this, we have created the entire CI and CD pipeline. When we trigger the GitHub Actions, the GitHub CI will build and test the application, run security scans using CodeQL, build the docker image, and push it to DockerHub. As an additional step, the pipeline will send a curl request to the Devtron dashboard with the correct Docker Image.

What we have created right now, is a simple pipeline. What if we wanted to deploy this application to multiple different environments, or we wanted to pass it through a QA environment before releasing it to production? Within Devtron, we can build any kind of workflow that we want. Be it a parallel pipeline or a sequential pipeline, there are different steps involved before deploying it on production.

[Fig. 10] Deployment pipelines for multiple environments

When you’re deploying your application to a production environment, you would not want to directly set up a deployment. You want to ensure that the production environment gets proper approval before the deployment gets triggered.

Within the deployment pipeline for the production environment, we can set the number of approvals that are required to trigger the deployment. Check out this blog to learn how to configure and use approval workflows with Devtron.

[Fig. 11] Require Approval for Triggering Deployment

For many applications, you also have a config map to define configurations or secrets that contain sensitive data such as database passwords or security tokens. You want to ensure that these tokens do not get leaked no matter what.

By using GitHub Actions, it would be tricky to create these tokens. You might need to have a separate private repository that listens for a webhook from the main application repository to trigger the deployments of the config maps and secrets. This process becomes very complex very quickly.

Within Devtron, you can create these configmaps and secrets with ease, without having to worry about the keys getting into the wrong hands. Unless the user has the correct permissions, they will not be able to view or change the secrets.

[Fig. 12] Set configmap and secret values

When you’re deploying applications to multiple different environments, you will want to change certain configurations within those environments. Devtron will let you configure files for all the different environments.

In our case, since we have 5 different environments, we can set environment-specific config maps and secrets along with the deployment configurations for each. We can even change the deployment template as needed. For example, you might want the ingress host for your QA application to be a host defined for the QA environment. Similarly, your dev and production environments would have different ingress hosts.

[Fig. 13] Multi-Environment Overrides

Within Kubernetes, it becomes a challenge to monitor and configure various configuration drifts. Many times, you want to ensure that the configurations of a particular environment cannot be changed unless the user has the correct permissions.

Within Devtron, you can protect the configuration of environments using the Protect Configuration capability. This aids developers and the operations team to minimize drifts caused by human intervention.

[Fig. 14] Protect configuration

Within the above image [Fig. 14], we have protected the configuration for the production environment. Unless the user has the correct permissions, they will not be able to change any of the configurations that have been set for the production environment.

Before deploying your application to production, you will run the application within a QA environment to ensure that all the configurations are working properly and ensure that nothing is breaking within your deployments. Ideally, you would want to carry the same configurations to your production environment after the tests have passed. You can ensure that the configurations are carried over using the Environment Overrides.

To check for the configuration drift between the QA and production environments, you can use the dropdown on the top right to compare the configuration of the prod environment, with any environment you wish. In this case, we are comparing it with the QA environment. You can see the complete configuration drift and carry the same configurations in the production environment and save it. For critical environments, it is also recommended to have a configuration lock, so that if any changes are made, they first need to be approved by the respective approver then only those configurations will be deployed on prod.

[Fig. 15] Configurations Drift

Deploying the application to Kubernetes

After you’ve built the application, and the curl command has successfully run, you are ready to deploy the image. In the Build and Deploy tab, you can see that the deployment stage has only the images that were sent via the curl request. Here, we can see that all the images have the latest tag, as we build the images with only one specific tag. If we used a tool such as Jinja to template the tags, each image would have a unique tag.

From here, we can go ahead and trigger a build. When creating the deployment pipeline, if you select the automatic deployment mode, then the deployment will be triggered as soon as the curl request succeeds.

[Fig. 16] Select image to deploy

After you’ve triggered the deployment, you will be able to see the real-time status of the entire deployment. You can also see the health of each individual resource.

The different pods, deployments, networking resources, and all other resources will be grouped into their logical sections.

[Fig. 17] Application Status

As you can see above [Fig. 17], we have successfully deployed the application. But that’s not where it ends. There are still a lot more things that we will need to do after the initial deployment is completed. Let’s explore what are some of the Day-2 operations that Devtron can help us out with.

Handling post-deployment operations

Now that the application is successfully deployed, there are some Day 2 operations that we need to take care of. Within a DevOps pipeline, the Day 2 operations include a lot of maintenance tasks. These are important to ensure that your application is healthy and runs well. Day 2 operations include tasks such as monitoring the application's health, performance, resource utilization, upgrading clusters and applications, managing configuration drifts, easy rollback, fined-grained RBAC, application logs, easy troubleshooting and debugging, etc.

As you might have already speculated, it’s almost impossible to handle every single day 2 operation task using GitHub Actions. It’s possible to handle some of these things using GitHub Actions. For example, you can run a cronjob at a set interval to perform certain routine maintenance tasks. But if you have any kind of downtime or your applications experience a configuration drift, GitHub falls short in monitoring, and showing the config differences. GitOps can be the way to ensure no human intervention is involved and the configs are in the desired state but how do you provide SLO-based rollbacks, or how will you ensure the protection of configurations, DORA metrics, and much more.

Conclusion

While GitHub Actions are amazing for creating CI pipelines for applications, they lack certain critical functionality when it comes to creating a CD pipeline. There is a lack of many essential functionalities for Kubernetes deployments such as multi-cluster deployments, configuring a rollout strategy, or specifying environment-specific configurations. Moreover, once the application is deployed, there is a lack of visibility on the health of the application. The GitHub action will appear as successful once the action is complete regardless of whether the deployment is healthy or not.

Devtron provides a complete view of your application and its deployment status within Kubernetes. It is a Kubernetes native dashboard, which lets you easily configure the secrets and config maps securely, and prevent any data leaks. Moreover, since Devtron allows you to deploy to multiple different clusters, you can also set the environment-specific configurations. To simplify your deployments for Kubernetes, install Devtron and integrate it with your existing GitHub Actions CI pipeline.