Ansible-Driven Container Deployment: Building, Pushing, and Deploying with Docker, Kubernetes, and Helm

Ansible-Driven Container Deployment: Building, Pushing, and Deploying with Docker, Kubernetes, and Helm

Embark on an Epic DevOps Voyage with Ansible-Driven Container Deployment

Are you ready to set sail on a DevOps adventure like no other? In this journey, you’ll harness the power of Ansible to orchestrate the deployment of your containerized applications with Docker, Kubernetes (K8s), and Helm. So, fasten your seatbelts and let’s get started!

Prerequisites:
Before we hoist the anchor, make sure you’ve got these tools ready to roll:

Ansible: Our trusty captain, Ansible, will be steering the ship. Make sure you have it installed and ready for action.

Docker: Ahoy there, containerization! We’ll be using Docker to package our applications. If you don’t have it, you can easily set sail by installing it from the official website.

Git: You’ll need Git to navigate the high seas of version control. Install it on your system if you haven’t already.

**Installation and Usage:
**We’ll explore how to create an Ansible script to automate the installation of Minikube, Docker, and Helm. With this Ansible playbook, we’ll streamline the process of building Docker images for your React and Node.js applications, pushing them to Docker Hub. Plus, we’ll create Helm charts in the helm-v1 directory, making it easy to deploy and manage your applications with Helm

So this is Directory Structure

Create playbook.yml

- name: Install and start Minikube
  hosts: localhost
  become: true
  gather_facts: false
  tasks:
  - name: Install dependencies
    apt:
      name:
      - curl
      - virtualbox
      - conntrack
      - libvirt-clients
      - libvirt-daemon-system

  - name: Download and install Minikube
    block:
    - name: Download Minikube
      get_url:
        url: "https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64"
        dest: "/usr/local/bin/minikube"
        mode: 0755
        validate_certs: no

    - name: Add Minikube to the PATH
      lineinfile:
        dest: "$HOME/.bashrc"
        line: 'export PATH=/usr/local/bin:$PATH'
        insertafter: EOF
      become: false

  - name: Start Minikube
    shell: minikube start
    args:
      executable: /bin/bash
      creates: "$HOME/.kube/config"

  handlers:
  - name: Restart shell
    shell: exec $SHELL


- name: Install and configure Kubernetes with Minikube
  hosts: localhost
  connection: local
  become: true
  tasks:
  - name: Install Docker
    snap:
      name: docker

  - name: Install Docker.io package
    apt:
      name: docker.io
      state: present

  - name: Start Docker service
    service:
      name: docker
      state: started

  - name: Download kubectl
    shell: |
      curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"

  - name: Verify kubectl integrity
    shell: |
      curl -LO "https://dl.k8s.io/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl.sha256"
      echo "$(cat kubectl.sha256)  kubectl" | sha256sum --check

  - name: Install kubectl
    shell: |
      sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl


- name: Login to Docker Hub & Build Docker Images
  hosts: localhost
  gather_facts: false
  vars:
    inventory_file: "inventory.yml"
  tasks:

  - name: Load inventory file
    include_vars:
      file: "{{ inventory_file }}"

  - name: Change Directory to Simple-crud-react-nodejs
    shell: cd /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/

  - name: Log in to Docker Hub
    docker_login:
      username: nacromancer858  #you should use your docker hub username
      password: your-docker_hub_password
      registry_url: https://index.docker.io/v2/

  - name: Print Docker Hub login status
    debug:
      msg: "Docker Hub login successful!"

  - name: Change Directory to Simple-crud-react-nodejs/frontend
    shell: cd /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/frontend/

  - name: Build Frontend Docker Image
    shell: docker build -t frontend-image-777  .

    args:
      chdir: /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/frontend/

  - name: Tag Frontend Docker Image
    shell: docker tag frontend-image-777 nacromancer858/frontend-image-777:"{{ image_tags.frontend_tag }}"
    args:
      chdir: /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/frontend/

  - name: Push Frontend Docker Image
    shell: docker push nacromancer858/frontend-image-777:"{{ image_tags.frontend_tag }}"
    args:
      chdir: /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/frontend/

  - name: Change Directory to Simple-crud-react-nodejs/backend
    shell: cd /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/backend/

  - name: Build Backend Docker Image
    shell: |
      docker build -t nacromancer858/backend-image -f Dockerfile .
      docker tag nacromancer858/backend-image nacromancer858/backend-image:"{{ image_tags.backend_tag }}"
      docker push nacromancer858/backend-image:"{{ image_tags.backend_tag }}"
    args:
      chdir: /home/saurabhadhau/Desktop/P-2/simple-crud-react-nodejs/backend/


- name: Install and configure Kubernetes with Minikube
  hosts: localhost
  connection: local
  tasks:
  - name: Start Minikube
    command: minikube start

  - name: Check Minikube status
    command: minikube status

  - name: Install helm
    shell: |
      curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
      chmod 700 get_helm.sh
      ./get_helm.sh

- name: Pulling the Images and Install Helm
  hosts: localhost
  connection: local
  tasks:
  - name: Pull frontend Docker image
    docker_image:
      name: nacromancer858/frontend-image-777
      tag: "{{ image_tags.frontend_tag }}"
      source: pull

  - name: Pull backend Docker image
    docker_image:
      name: nacromancer858/backend-image
      tag: "{{ image_tags.backend_tag }}"
      source: pull

  - name: Change Directory to Helm-charts
    shell: cd /home/saurabhadhau/Desktop/P-2/helm-v1/

- name: Update frontend values file
  hosts: localhost
  gather_facts: false

  tasks:
  - name: Read inventory file
    include_vars:
      file: inventory.yml
      name: inventory_data

  - name: Update frontend tag value
    lineinfile:
      path: helm-v1/values.frontend.yml
      regexp: '^frontend:\n[[:space:]]*tag:'
      line: 'frontend:\n  tag: "{{ inventory_data.image_tags.frontend_tag }}"'

  - name: Update Backend tag value
    lineinfile:
      path: helm-v1/values.backend.yml
      regexp: '^backend:\n[[:space:]]*tag:'
      line: 'backend:\n  tag: "{{ inventory_data.image_tags.backend_tag }}"'

  - name: Build Helm-Chart for Frontend
    shell: helm install frontend ./frontend-chart -f values.frontend.yml
    args:
      chdir: /home/saurabhadhau/Desktop/P-2/helm-v1/

  - name: Build Helm-Chart for Backend
    shell: helm install backend ./backend-chart -f values.backend.yml
    args:
      chdir: /home/saurabhadhau/Desktop/P-2/helm-v1/

Create helm-v1 directory and as per the directory structure create the other files as well!

frontend-chart/templates/frontend-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "frontend-chart.fullname" . }}
spec:
  replicas: {{ .Values.frontend.replicaCount }}
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
        - name: frontend
          image: {{ .Values.frontend.image.repository }}:{{ .Values.frontend.image.tag }}
          ports:
            - containerPort: {{ .Values.frontend.service.port }}
          resources:
            limits:
              cpu: {{ .Values.frontend.resources.limits.cpu }}
              memory: {{ .Values.frontend.resources.limits.memory }}
            requests:
              cpu: {{ .Values.frontend.resources.requests.cpu }}
              memory: {{ .Values.frontend.resources.requests.memory }}

frontend-chart/templates/frontend-service.yaml:

{{- if eq .Values.frontend.service.type "NodePort" }}
apiVersion: v1
kind: Service
metadata:
  name: {{ include "frontend-chart.fullname" . }}
spec:
  selector:
    app: frontend
  type: {{ .Values.frontend.service.type }}
  ports:
    - port: {{ .Values.frontend.service.port }}
      targetPort: {{ .Values.frontend.service.targetPort }}
      nodePort: {{ .Values.frontend.service.nodePort }}
{{- else }}
apiVersion: v1
kind: Service
metadata:
  name: {{ include "frontend-chart.fullname" . }}
spec:
  selector:
    app: frontend
  type: ClusterIP
  ports:
    - port: {{ .Values.frontend.service.port }}
      targetPort: {{ .Values.frontend.service.targetPort }}
{{- end }}

backend-chart/templates/backend-deployment.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: {{ include "backend.fullname" . }}
spec:
  replicas: {{ .Values.backend.replicas }}
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
        - name: backend
          image: {{ .Values.backend.image.repository }}:{{ .Values.backend.image.tag }}
          ports:
            - containerPort: {{ .Values.backend.port }}
          volumeMounts:
            - name: backend-storage
              mountPath: /data
          resources:
            limits:
              cpu: {{ .Values.backend.resources.limits.cpu }}
              memory: {{ .Values.backend.resources.limits.memory }}
            requests:
              cpu: {{ .Values.backend.resources.requests.cpu }}
              memory: {{ .Values.backend.resources.requests.memory }}
      volumes:
        - name: backend-storage
          persistentVolumeClaim:
            claimName: backend-pvc

backend-chart/templates/backend-service.yaml:

apiVersion: v1
kind: Service
metadata:
  name: {{ include "backend.fullname" . }}
spec:
  selector:
    app: backend
{{- if eq .Values.backend.serviceType "NodePort" }}
  type: NodePort
{{- else }}
  type: ClusterIP
{{- end }}
  ports:
    - port: {{ .Values.backend.port }}
      targetPort: {{ .Values.backend.port }}
      {{- if eq .Values.backend.serviceType "NodePort" }}
      nodePort: {{ .Values.backend.nodePort }}
      {{- end }}

backend-chart/templates/backend-pvc.yaml:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: backend-pvc
spec:
  accessModes:
    - ReadWriteOnce 
  resources:
    requests:
      storage: "1Gi"

You can dive into the complete code in all its glory right here at our repository : https://github.com/Saurabh-DevOpsVoyager77/ansible-driven-deployment-Docker_K8s_Helm

Now, let’s add some magic to your inventory.yaml file by setting those crucial tags:

image_tags:
  frontend_tag: 1.0
  backend_tag: 1.0

With the tags in place, you’re all set for the grand finale! Just execute this command:

sudo ansible-playbook playbook.yml -i inventory.yml

Celebrate Your Success 🎉

Congratulations! You’ve just embarked on a journey towards efficient container deployment with Ansible, Docker, Kubernetes, and Helm. This powerful combination allows you to take control of your applications and streamline your deployment process.

Now that you’ve completed the setup and executed your Ansible playbook, your React Node.js application is ready to shine ✨. Keep experimenting, exploring, and pushing your DevOps skills to new heights 🚀. There’s no limit to what you can achieve with this versatile toolkit.