The main goal of this project is to implement DevOps practices in a Go web application. The project is a simple website written in Golang, using the net/http
package to serve HTTP requests. ππ»
What We Will Implement:
Containerizing the Project:
- Multi-stage Dockerfile
Kubernetes:
- Deployments, services, and ingress
Continuous Integration (CI):
- Using GitHub Actions
Continuous Deployment (CD):
- Using GitOps with ArgoCD
Kubernetes Cluster Setup
Helm Chart for the Application
Ingress Controller
steps:
Test the project locally.
(you can clone the repo from GitHub using
git clone
)Build project:
go build -o main .
To run binary:
./main
Now that the application is running, let's see it in the browser by navigating to
localhost:8080
.It throws a
404 Page Not Found
error because the development team has indicated that the application is accessible at, not athttp://localhost:8080
. Please check the GitHub repository for more details.Here, the application is working locally.
Creating Dockerfile (Multi-stage build)
The Dockerfile is used to build a Docker image. The Docker image contains the Go web application and its dependencies. The Docker image is then used to create a Docker container. π³
We will use a multi-stage build to create the Docker image. Multi-stage builds are a feature of Docker that allows you to use multiple build stages in a single Dockerfile. This reduces the size of the final Docker image and secures the image by removing unnecessary files and packages. ππ
Our Dockerfile is ready. Let's test it by building the image. (Note: you must have docker on your local machine)
docker build -t rohitd4/go-web-app:v1 .
*rohitd4 is my docker username.
The error message indicates that the version of Go installed on the system is lower than the version required by the
go.mod
file. Specifically, thego.mod
file requires Go version 1.22.5, but the system is currently running Go version 1.21.12.we will update the version in the dockerfile.
No try to build the image again
docker build -t rohitd4/go-web-app:v1 .
The image has been built. Now, run the container using the image.
Containerization
Containerization is the process of packaging an application and its dependencies into a container. The container is then run on a container platform such as Docker. Containerization allows you to run the application in a consistent environment, regardless of the underlying infrastructure.
We will use Docker to containerize the Go web application. Docker is a container platform that allows you to build, ship, and run containers.
Command to run the Docker container:
docker run -it -p 8080:8080 rohitd4/go-web-app:v1
You can use your Docker username and whatever image name you have used.
Before running the container, check the
go-web-app
in the browser. You will see "This site canβt be reached."Now use the docker run command and run the container.
Now the container is running, and you can check the
go-web-app
in the browser. It should be in a working state.to stop container use Ctrl c
Push the Docker image to Docker Hub
docker push rohitd4/go-web-app:v1
We have completed containerization by writing a Dockerfile that uses a multi-stage Docker build. Next, we will write Kubernetes manifests.
K8s Manifests
Create a folder
k8s
and inside it createmanifests
folder.Inside the
manifest
folder, we will start by writingdeployment.yaml
. Create a file nameddeployment.yaml
.Create file
service.yaml
Create file:
ingress.yaml
Now that we have the deployment, service, and ingress resources, it's time to validate the Kubernetes manifests that we have written.
For that we need to have k8s cluster and we are going to use AKS.AKS Cluster
Command:
az login
Create a resource group:
az group create --name gowebrg --location eastus
This command creates a resource group named
gowebrg
in theeastus
region.Create an AKS cluster:
az aks create --resource-group gowebrg --name gowebcluster --node-count 1 --enable-addons monitoring
This command creates an AKS cluster named
gowebcluster
in thegowebrg
resource group with one node and enables monitoring. The--generate-ssh-keys
option generates SSH keys if you don't already have them.Get Kubernetes Credentials:
To interact with your AKS cluster usingkubectl
, you need to get the Kubernetes credentials.az aks get-credentials --resource-group gowebrg --name gowebcluster
Verify Access to the Cluster:
Usekubectl
to verify access to your cluster and list the nodes.kubectl get nodes
The cluster is ready for deployment.
Applying YAML Files to AKS
We already have our YAML files ready, we can apply them to
our AKS cluster using the
kubectl apply
command.kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
kubectl apply -f ingress.yaml
As we don't have an ingress controller you can see there is no ADDRESS assigned for the ingress resource.
Install the Nginx Ingress Controller on Azure
and deploy the below manifest.
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.11.1/deploy/static/provider/cloud/deploy.yaml
Run this command and it will create an ingress controller resource.
use the command to see the ingress controller pod:
kubectl get pods -n ingress-nginx
Let's see if the ingress controller is able to watch our ingress resource.
command:
kubectl get ingress
We got an IP address, so we can say that the ingress resource is being watched by the ingress controller, which provided us with the IP address.
Now map the IP address with
go-web-app.local
Command :
sudo vim /etc/hosts
Now using the host
go-web-app.local
try to access the application.Helm Configuration
Install and check the Helm version.
create a helm folder.
inside the helm folder run this command:
helm create go-web-app-chart
from
go-web-app-chart
folder removecharts
folder andto remove use
rm -rf charts
command.Now go to the templates folder and remove everything.
Inside the templates folder copy and paste the manifests.
We have deployment.yml, ingress.yml, and service.yml in the templates folder.
The advantage of Helm is that we can variablize our YAML files.
In
deployment.yaml
change the image tag with this{{ .Values.image.tag }}
{{ .Values.image.tag }}
is accessing a value from the Helm chart'svalues.yaml
file, whereimage.tag
represents the tag of a container image.Go to values.yaml and remove everything from the file.
remove all
You can update the values.yaml something as below.
# Default values for go-web-app-chart. # This is a YAML-formatted file. # Declare variables to be passed into your templates. replicaCount: 1 image: repository: abhishekf5/go-web-app pullPolicy: IfNotPresent # Overrides the image tag whose default is the chart appVersion. tag: "v1" ingress: enabled: false className: "" annotations: {} # kubernetes.io/ingress.class: nginx # kubernetes.io/tls-acme: "true" hosts: - host: chart-example.local paths: - path: / pathType: ImplementationSpecific
Verify if Helm is working as expected or not.
Delete everything from the namespace.
kubectl get all
kubectl delete deploy go-web-app
kubectl delete svc go-web-app
kubectl delete ing go-web-app
use
kubectl get all
and you will find nothing related to our go-web-app.Go to the helm folder and run the command
helm install go-web-app ./go-web-app-chart
Verify all the resources are deployed or not
kubectl get all
We are done with Helm. Just uninstall everything.
helm uninstall go-web-app
Continuous Integration (CI)
Continuous Integration (CI) is the practice of automating the integration of code changes into a shared repository. CI helps to catch bugs early in the development process and ensures that the code is always in a deployable state.
We will use GitHub Actions to implement CI for the Go web application. GitHub Actions is a feature of GitHub that allows you to automate workflows, such as building, testing, and deploying code.
The GitHub Actions workflow will run the following steps:
Checkout the code from the repository
Build the Docker image
Run the Docker container
Run tests
Create a folder and name it .github
. Inside this folder, create another folder named workflows
.
Inside the workflows
folder create a file name as cicd.yaml
or you can give whatever name you want and paste the below code into your cicd.yaml
.
# CICD using GitHub actions
name: CI/CD
# Exclude the workflow to run on changes to the helm chart
on:
push:
branches:
- main
paths-ignore:
- 'helm/**'
- 'k8s/**'
- 'README.md'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go 1.22
uses: actions/setup-go@v2
with:
go-version: 1.22
- name: Build
run: go build -o go-web-app
- name: Test
run: go test ./...
code-quality:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.56.2
push:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push action
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/go-web-app:${{github.run_id}}
update-newtag-in-helm-chart:
runs-on: ubuntu-latest
needs: push
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}
- name: Update tag in Helm chart
run: |
sed -i 's/tag: .*/tag: "${{github.run_id}}"/' helm/go-web-app-chart/values.yaml
- name: Commit and push changes
run: |
git config --global user.email "rjd3955@gmail.com"
git config --global user.name "Rohit Deore"
git add helm/go-web-app-chart/values.yaml
git commit -m "Update tag in Helm chart"
git push
Now we have to store our secrets in "GitHub Actions secrets and variables"
Go to settings in the GitHub repo, under the security section select the secrets and variables -> select Actions.
Click on the repository secrets.
provide secrets and click on add secret.
do the same steps for the docker hub token. (follow below screenshots)
Sign in to the docker hub and then go to account settings.
Go to Personal Access tokens and generate new token.
Click on Generate.
Copy the token
and paste it into the secret section.
Again create a new secret for the GitHub token within the same repository.
Generate the GitHub personal access token.
Let us check whether CI is working or not.
you will see a new commit has to be pushed.
Go to actions and see implemented CI.
Now let's make some changes, push the code to GitHub again and it will trigger the CI pipeline.
Remove this - 'k8s/**'
# CICD using GitHub actions
name: CI/CD
# Exclude the workflow to run on changes to the helm chart
on:
push:
branches:
- main
paths-ignore:
- 'helm/**'
- 'README.md'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go 1.22
uses: actions/setup-go@v2
with:
go-version: 1.22
- name: Build
run: go build -o go-web-app
- name: Test
run: go test ./...
code-quality:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.56.2
push:
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and Push action
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/go-web-app:${{github.run_id}}
update-newtag-in-helm-chart:
runs-on: ubuntu-latest
needs: push
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
token: ${{ secrets.TOKEN }}
- name: Update tag in Helm chart
run: |
sed -i 's/tag: .*/tag: "${{github.run_id}}"/' helm/go-web-app-chart/values.yaml
- name: Commit and push changes
run: |
git config --global user.email "rjd3955@gmail.com"
git config --global user.name "Rohit Deore"
git add helm/go-web-app-chart/values.yaml
git commit -m "Update tag in Helm chart"
git push
Save and commit the changes.
CI is successful, now go to values.yaml and check for the image tag.
The CI part is completed.
Continuous Deployment (CD)
Continuous Deployment (CD) is the practice of automatically deploying code changes to a production environment. CD helps to reduce the time between code changes and deployment, allowing you to deliver new features and fixes to users faster.
We will use Argo CD to implement CD for the Go web application. Argo CD is a declarative, GitOps continuous delivery tool for Kubernetes. It allows you to deploy applications to Kubernetes clusters using Git as the source of truth.
The Argo CD application will deploy the Go web application to a Kubernetes cluster. The application will be automatically synced with the Git repository, ensuring that the application is always up to date.
Install Argo CD
Install argocd using manifests:
kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
Access the Argo CD UI (Loadbalancer service):
kubectl patch svc argocd-server -n argocd -p '{"spec": {"type": "LoadBalancer"}}'
Get the Loadbalancer service IP:
kubectl get svc argocd-server -n argocd
To access argocd UI use the external IP and and port 80.
(In my case :57.152.10.206:80
)You need to provide a username and password, the username is admin and for the password run the command
kubectl get secrets -n argocd
use command :
kubectl edit secret argocd-initial-admin-secret -n argocd
The password will be base64 encoded just copy and decode it.base64 encoded:
UFdROVBtYXdOVWUxRGZiUA==
to decode the password use the command:
echo UFdROVBtYXdOVWUxRGZiUA== | base64 --decode
decoded:
PWQ9PmawNUe1DfbP
Paste the decoded password in argocd login.
Click on the new app.
Now click on Create.
Now argocd will look for all the files in your repository, within the helm chart it will update the values.yaml.
click here and you will see argocd start deploying everything; Pod, svc, and ingress are created.
Check for the deployment, svc and ingress on the cluster.
kubectl get all
Now try to access the application using
go-web-app.local/courses
Here we have implemented our CICD and deployed the application successfully.
Verifying End-To-End CICD
Let's make a simple change in static content.
In home.html we will add a background color.
CICD will be triggered.
The image is also pushed in the docker hub a minute ago.
Now argocd will pickup this new change.
ArgoCD has updated the cluster with new changes and lets go and check the application.
You can see there is a new background color.
Congratulations, we've successfully DevOpsified the project! ππ₯³
Here's what we've achieved:
Containerization completed
Kubernetes (K8s) manifests created
CI/CD pipelines implemented
Kubernetes cluster (AKS) set up
Helm charts developed
Ingress controller configured
Argo CD configured
All these in one comprehensive project!
Github repo: https://github.com/DeoreRohit4/Go-Web-App-CICD
If you have any issues while performing the project practically please reach out to me on Linkedin - rohit deore
Keep Exploring...