GitOps is a new approach for continuous deployment of your workload on Kubernetes. Extension of the DevOps workflow pattern, it uses any source code/version control repository as the single source of truth for all the infrastructure to be deployed on Kubernetes.
In simple aspects of CI/CD, continuous integration is merging of the developed code into master branch and continuous deployment is releasing the latest version of code from master branch to a runtime. This is exactly what GitOps does it automates your infra updates, where you as a developer don’t have to bother about any CI/CD files and other setups, and can solely focus on your code with a one time configuration for YAML files in your repository.
A traditional deployment pipeline involves two paradigms, one being continuous integration meaning the source code changes extracted from multiple contributors is integrated into one git repository and is passed through build/test stages, and continuous deployment which is taking the integrated code and reflecting the change on a running software.
Also read: What is CI/CD in DevOps
This kind of approach of deployment of code is known as push based deployments wherein whenever a new piece of code is pushed to a repository a new build cycle is triggered which creates a docker image pushes it to a repository and the YAML files residing in the the code repository is applied to deploy the change.
GitOps uses a more modern approach bringing pull based deployments into picture. In this scenario no external monitoring is required, instead a tool, operator comes into play which directly compares the states of deployed infrastructure with that of code residing in the repository and basis this manages the state of the infrastructure. Not only code based changes can be reflected to infra, in fact operator can compare the changes in image registry as well to keep the infra up to date with latest image changes.
Now we take a look at how GitOps works in detail by taking a test repository into consideration.
We will host a sample application running a http server using NodeJS and create a container for the application using Dockerfile.
const http = require('http');
const hostname = '127.0.0.1';
const port = 3000;
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Hello World');
});
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});
FROM node:11-alpine
COPY . .
CMD ["node","node.js"]
Now, since we have our code and dockerfile ready we need to create a docker image and push it to a registry. For this example we consider you have an account on https://hub.docker.com/ with proper read/write access in place. You can manually build an image and push it to registry but since we are following GitOps practice we will automate our build using GitHub actions.
name: CI
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
-
name: Generate build number
id: buildnumber
uses: einaregilsson/build-number@v3
with:
token: ${{secrets.github_token}}
-
name: Build and push
uses: docker/build-push-action@v2
with:
context: .
push: true
tags: <Your_username>/helloworld:${{ steps.buildnumber.outputs.build_number }}
After commit you should see .github/workflows/main.yml present in your code repo. It’ll trigger a new pipeline everytime a commit is observed in master branch, but since we haven’t configured DockerHub credentials as of yet your push will fail.
So to configure secrets, go to settings -> secrets and add these two secrets,
DOCKERHUB_USERNAME
value : <Your Username>
DOCKERHUB_TOKEN
value: <Your Password>
Now next time the pipeline runs you’ll see a successful CI build.
apiVersion: apps/v1
kind: Deployment
metadata:
name: nodejs-helloworld
annotations:
flux.weave.works/automated: "true"
labels:
app: helloworld
spec:
replicas: 1
selector:
matchLabels:
app: helloworld
template:
metadata:
labels:
app: helloworld
spec:
containers:
- name: helloworld
image: <your user name>/helloworld:latest
ports:
- containerPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: helloworld-svc
spec:
selector:
app: helloworld
ports:
- protocol: TCP
port: 3000
targetPort: 3000
nodePort: 30001
type: NodePort
You can apply the following file and check on your browser hitting, http://<node ip>:30001/
Make sure you have a Kubernetes cluster deployed with Kubectl configured from your local machine. The GitOps operator we will be installing is Flux. Follow the steps to install the same using helm,
git clone https://github.com/fluxcd/flux && cd flux
kubectl apply -f deploy.yml
After setting up our repository and operator on Kubernetes, you can commit your new code and change your image tag in k8s/deployment_svc.yml and you should see a new build automatically triggered through GitHub actions which is basically you CI, and a deployment created in Kubernetes by flux comparing k8s/deployment_svc.yml with the infra.
This way we have achieved a complete CI/CD pipeline configuration using GitHub actions and GitOps.
After understanding differences between traditional CI/CD vs. GitOps CD and configuring a test CI/CD using GitOps operator we can conclude that GitOps is a modern DevOps paradigm which is meant to ease your deployments with providing utmost security, with better management of code and infra since there exists only a single source of truth for syncing and operability.
Have a product idea?
Talk to our experts to see how you can turn it
into an engaging, sustainable digital product.