DEV Community

Harish
Harish

Posted on • Originally published at harisheoran.hashnode.dev on

From Code to Cloud: A Hands-On DevOps Journey with Kubernetes Deployment

Welcome to a hands-on journey through the realms of DevOps! In this blog, we'll dive into the practical world of code deployment, exploring the seamless integration of development and operations that powers modern software delivery. Join me as we unravel the intricacies of a real-world DevOps assignment, focusing on Kubernetes deployment. From transforming code into containerized brilliance to orchestrating it in the cloud, this blog is your guide to mastering the practical aspects of DevOps. Ready to embark on a journey that bridges code, clouds, and containers? Let's dive in!

DevOps Workshop 2

You should try the workshop on your own first.

Workshop Requirements

  1. Source Code:

  2. Version Control:

  3. Continuous Integration (CI):

  4. Containerization:

  5. Continuous Deployment (CD):

  6. Monitoring (Optional):

Follow these steps, explaining your choices and demonstrating the flow. This assignment will showcase your skills in setting up a CI/CD pipeline and deploying applications to Kubernetes. If you have any questions along the way, feel free to ask!

First, build a simple Web server

You can choose the language for this task, I am using the Go language.

  • Create a main.go file.
touch main.go

go mod init webserver

package mainimport ("fmt""net/http")func main() { fs := http.FileServer(http.Dir("./static")) http.Handle("/", fs) port := 3000 fmt.Printf("Server is listening on 3000") err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil) if err != nil { fmt.Printf("Error: ", err) }}
Enter fullscreen mode Exit fullscreen mode

This server is just serving a index.html and main.js file.

  • Create a static directory for html and js files.
mkdir statictouch index.html main.js
Enter fullscreen mode Exit fullscreen mode
  • Run the website
go run main.go
Enter fullscreen mode Exit fullscreen mode
  • Go to 127.0.0.1:3000

Containerize your application using Docker

We use docker to containerize our application, let's write the Dockerfile.

We are going to use the Multistage Docker build or Distroless Images.

Must read about Multi-Stage Docker Build of GO web server

# Build stageFROM golang:latest as buildWORKDIR /appCOPY go.mod ./RUN go mod download# Copy the necessary files of applicationCOPY main.go .COPY static ./static# build the binary of app named "main"RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .# 2nd stage, run the binary of applicationFROM scratch# Copy "main" binary and static folder into current dirCOPY --from=build /app/main .COPY --from=build /app/static ./staticEXPOSE 3000# execute the binaryCMD ["./main"]
Enter fullscreen mode Exit fullscreen mode
  • Build the Docker Image
docker build -t memesoftheday-image .
Enter fullscreen mode Exit fullscreen mode
  • Build & run the container and check whether the application is running or not.
docker container run -d -p 4000:3000 --name memes-container memesoftheday-image
Enter fullscreen mode Exit fullscreen mode

Version Control

Use Git to control the version of source code and push it to GitHub.

Continuous Integration (CI)

If you are a beginner to CI, so understand the problem first

  • The Image of the application is present on your machine and let's say we are deploying our application on AWS or Azure -

We don't like manual things, we are Engineers, lets Automate it using CI pipeline.

What's the solution?

We build a Ci pipeline which uses our Dockerfile from the repository to build the image and push it to a registry.

  • so, you don't have to worry about building images each time you make changes.

  • and it uses CI server computing to build the image.

There are so many CI tools like Jenkins, Github Actions and many more. I think GitHub Actions is the right choice for our requirements.

Understand the workflow of the CI pipeline

  • Check out the Github repository.

  • Build the docker image using Dockerfile.

  • Log in to Docker Hub.

  • Push the image to Docker Hub.

Read about using Github Actions

mkdir -p .github/workflows && cd .github/workflows
Enter fullscreen mode Exit fullscreen mode

It triggers the build-on code push in the main branch.

name: Create and build docker image to Docker Hubon: push: branches: ["main"]env: REGISTRY: docker.io IMAGE_NAME: "memesoftheday-image"jobs: push_to_registry: name: Push Image to Docker Hub runs-on: ubuntu-latest steps: - name: checkout the repository uses: actions/checkout@v4 - name: Login to Docker Hub uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a with: username: ${{secrets.DOCKERHUB_USERNAME}} password: ${{secrets.DOCKERHUB_PASSWORD}} - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@9ec57ed1fcdbf14dcef7dfbe97b2010124a938b7 with: images: ${{secrets.DOCKERHUB_USERNAME}}/memesoftheday-image - name: Build & push the image uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671 with: context: . file: ./Dockerfile push: true tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }}
Enter fullscreen mode Exit fullscreen mode

Continuous Deployment (CD)

We want to reflect the changes we made in our code into the applications which we deploy on Kubernetes, which comes into the picture of the CD.

We are going to use the ArgoCD , so install the ArgoCD as an operator on our k8s cluster.

  • Create Kubernetes Cluster We use Minikuebe as local k8s cluster Install Minikube

  • Start Minikube cluster

minikube start --driver=docker
Enter fullscreen mode Exit fullscreen mode
mkdir k8stouch argocd.yml

apiVersion: argoproj.io/v1alpha1kind: ArgoCDmetadata: name: example-argocd labels: example: basicspec: {}
Enter fullscreen mode Exit fullscreen mode

ArgoCD is created as a service, you can verify using

kubectl get svc
Enter fullscreen mode Exit fullscreen mode

Now if you see that it is created as ClusterIP , which can access only within the cluster, so, to access ArgoCD in our browser we have to change its type to NodePort.

kubectl edit svc example-argocd-server
Enter fullscreen mode Exit fullscreen mode

change the type from Cluster IP to NodePort

  • Check the service again, and check its port - 31080
kubectl get svc

example-argocd-server NodePort 10.105.171.247 <none> 80:31080/TCP,443:32101/TCP 21h
Enter fullscreen mode Exit fullscreen mode
  • So, to access the argocd on the browser using node port, we need the IP of the cluster
minikube ip
Enter fullscreen mode Exit fullscreen mode

In my case, it is 192.168.49.2:31080

  • Username is admin.

  • Password is stored as a secret.

kubectl get secrets
Enter fullscreen mode Exit fullscreen mode

you'll get example-argocd-cluster, password is stored in this secret.

kubectl edit secrets example-argocd-cluster
Enter fullscreen mode Exit fullscreen mode

Copy the password.

  • Minikube uses a simple encryption algorithm base64.
echo dkxpeEEzeWVnYk53NEZrU1VtdW4xUlBhOHBoTWNyN0k= | base64 -d
Enter fullscreen mode Exit fullscreen mode
  • Login into ArgoCD.

Create Deployment and service for your application.

  • Create Deployment.yml in k8s directory.
# This file describes a Kubernetes Deployment, which manages a set of pods.apiVersion: apps/v1# Specifies the Kubernetes API version used for this resource.kind: Deployment# Indicates that this is a Deployment resource, used to manage application deployments.metadata: name: memesoftheday-deployment # Assigns a name to the Deployment, in this case, "memesoftheday-deployment." labels: app: memesoftheday # Labels are used to organize and select resources. This label is named "app" and set to "memesoftheday."spec: replicas: 3 # Specifies that the desired number of replicas (pods) is 3. selector: matchLabels: app: memesoftheday # Defines how the Deployment identifies which pods to manage based on labels. template: metadata: labels: app: memesoftheday # Labels assigned to the pods created by this Deployment. spec: containers: - name: memesostheday-container # Specifies the name of the container within the pod. image: harisheoran/memesoftheday-image:main # Specifies the Docker image to be used for the container, pulled from "harisheoran/memesoftheday-image" with the "main" tag. ports: - containerPort: 3000 # Specifies that the container within the pod will listen on port 3000. imagePullPolicy: If
Enter fullscreen mode Exit fullscreen mode
  • Create service.yml in the same directory.
# This file describes a Kubernetes Service, which exposes pods to the network.apiVersion: v1# Specifies the Kubernetes API version used for this resource.kind: Service# Indicates that this is a Service resource, used to expose pods.metadata: name: memesoftheday-service # Assigns a name to the Service, in this case, "memesoftheday-service."spec: type: NodePort # Specifies that the Service should be of type NodePort, making it accessible externally on each node. selector: app: memesoftheday # Specifies the labels used to select pods that this service will route traffic to. In this case, pods with the label "app: memesoftheday." ports: - port: 80 # Specifies that the Service should be accessible on port 80 externally. targetPort: 3000 # Specifies that incoming traffic on port 80 should be forwarded to the pods on port 3000. nodePort: 30007 # Specifies a static port on each node (accessible externally) where the Service will be available, set to 30007.
Enter fullscreen mode Exit fullscreen mode
  • and create a new app and fill in the details accordingly to your repo in the below format.

  • Please wait for it, it will create the pods of our application defined in our deployment.

We successfully deployed the application pods.

You can check the pods using command.

kubectl get pods -o wide
Enter fullscreen mode Exit fullscreen mode

  • Check the port of pods.
kubectl get svc
Enter fullscreen mode Exit fullscreen mode

visit your minikube ip with node port, in my case it is - http://192.168.49.2:30007/

Monitoring

To monitor our k8s cluster, we use Prometheus and Grafana.

  • Install Prometheus using Helm charts
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts

helm repo update

helm install prometheus prometheus-community/prometheus
Enter fullscreen mode Exit fullscreen mode

Now, it will create Prometheus pod and service but it is available as Cluster IP, to access the Prometheus, we need to change its service to NodePort.

kubectl expose service prometheus-server --type=NodePort --target-port=9090 --name=prometheus-server-ext
Enter fullscreen mode Exit fullscreen mode
  • Check it port using the command.
kubectl get svc
Enter fullscreen mode Exit fullscreen mode

Visit this port in browser with minikube IP.

To view these logs in graph form, we use Grafana.

  • Install Grafana using Helm Chart.
helm repo add grafana https://grafana.github.io/helm-charts

helm repo update

helm install grafana grafana/grafana
Enter fullscreen mode Exit fullscreen mode
  • Expose Grafana service as NodePort to access in browser.
kubectl expose service grafana --type=NodePort --target-port=3000 --name=grafana-ext
Enter fullscreen mode Exit fullscreen mode
  • Check the Grafana Nodeport using command and visit the port with minikube ip.
kubectl get svc
Enter fullscreen mode Exit fullscreen mode
  • Click on it to create your first Data source.

  • Add a Data Source to Prometheus.

  • Enter the Prometheus IP address.

  • Go back to Home and click on Create DashBoard > Import DashBoard > Enter 3662 ID to import the template of Dashboard.

  • Our Grafana Dashboard is ready.

That's it, our project is done, if you have any questions about this project or if stuck at any point, feel free to ping me.

Source Code

Top comments (0)