DEV Community

Janak Shrestha
Janak Shrestha

Posted on

Day 64: Fix Python App Deployed on Kubernetes Cluster

There is an iron gallery app that the Nautilus DevOps team was developing. They have recently customized the app and are going to deploy the same on the Kubernetes cluster. Below you can find more details:

  1. Create a namespace iron-namespace-devops
  2. Create a deployment iron-gallery-deployment-devops for iron gallery under the same namespace you created.
    :- Labels run should be iron-gallery.
    :- Replicas count should be 1.
    :- Selector's matchLabels run should be iron-gallery.
    :- Template labels run should be iron-gallery under metadata.
    :- The container should be named as iron-gallery-container-devops, use kodekloud/irongallery:2.0 image ( use exact image name / tag ).
    :- Resources limits for memory should be 100Mi and for CPU should be 50m.
    :- First volumeMount name should be config, its mountPath should be /usr/share/nginx/html/data.
    :- Second volumeMount name should be images, its mountPath should be /usr/share/nginx/html/uploads.
    :- First volume name should be config and give it emptyDir and second volume name should be images, also give it emptyDir.

  3. Create a deployment iron-db-deployment-devops for iron db under the same namespace.
    :- Labels db should be mariadb.
    :- Replicas count should be 1.
    :- Selector's matchLabels db should be mariadb.
    :- Template labels db should be mariadb under metadata.
    :- The container name should be iron-db-container-devops, use kodekloud/irondb:2.0 image ( use exact image name / tag ).
    :- Define environment, set MYSQL_DATABASE its value should be database_host, set MYSQL_ROOT_PASSWORD and MYSQL_PASSWORD value should be with some complex passwords for DB connections, and MYSQL_USER value should be any custom user ( except root ).
    :- Volume mount name should be db and its mountPath should be /var/lib/mysql. Volume name should be db and give it an emptyDir.

  4. Create a service for iron db which should be named iron-db-service-devops under the same namespace. Configure spec as selector's db should be mariadb. Protocol should be TCP, port and targetPort should be 3306 and its type should be ClusterIP.

  5. Create a service for iron gallery which should be named iron-gallery-service-devops under the same namespace. Configure spec as selector's run should be iron-gallery. Protocol should be TCP, port and targetPort should be 80, nodePort should be 32678 and its type should be NodePort.

Note:

  1. We don't need to make connection b/w database and front-end now, if the installation page is coming up it should be enough for now.
  2. The kubectl utility on the jump-host has been configured to work with the Kubernetes cluster.

Understanding the Application Architecture

Application Components

The Iron Gallery application consists of two main components:

Component Description Image
Gallery Frontend Web server displaying the gallery kodekloud/irongallery:2.0
Database Backend MariaDB database for storing data kodekloud/irondb:2.0

Architecture Diagram

┌─────────────────────────────────────────────────────────────────────────────┐
│                  Kubernetes Cluster                                        │
│                                                                              │
│  ┌────────────────────────────────────────────────────────────────────────┐ │
│  │  Namespace: iron-namespace-devops                                    │ │
│  │                                                                       │ │
│  │  ┌──────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Gallery Pod                                                    │ │ │
│  │  │  ┌────────────────────────────────────────────────────────────┐ │ │ │
│  │  │  │  Container: iron-gallery-container-devops                 │ │ │ │
│  │  │  │  Image: kodekloud/irongallery:2.0                         │ │ │ │
│  │  │  │  Port: 80                                                  │ │ │ │
│  │  │  │  Resources: 100Mi memory, 50m CPU                         │ │ │ │
│  │  │  │  Volumes:                                                  │ │ │ │
│  │  │  │  - config → /usr/share/nginx/html/data                    │ │ │ │
│  │  │  │  - images → /usr/share/nginx/html/uploads                 │ │ │ │
│  │  │  └────────────────────────────────────────────────────────────┘ │ │ │
│  │  └──────────────────────────────────────────────────────────────────┘ │ │
│  │                                    │                                   │ │
│  │                                    ▼                                   │ │
│  │  ┌──────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Gallery Service                                                │ │ │
│  │  │  - Type: NodePort                                               │ │ │
│  │  │  - NodePort: 32678                                              │ │ │
│  │  │  - Target: Port 80                                              │ │ │
│  │  └──────────────────────────────────────────────────────────────────┘ │ │
│  │                                    │                                   │ │
│  │                                    ▼                                   │ │
│  │                    http://<node-ip>:32678                           │ │
│  │                                                                       │ │
│  │  ┌──────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Database Pod                                                   │ │ │
│  │  │  ┌────────────────────────────────────────────────────────────┐ │ │ │
│  │  │  │  Container: iron-db-container-devops                      │ │ │ │
│  │  │  │  Image: kodekloud/irondb:2.0                             │ │ │ │
│  │  │  │  Port: 3306                                               │ │ │ │
│  │  │  │  Environment:                                             │ │ │ │
│  │  │  │  - MYSQL_DATABASE: database_host                          │ │ │ │
│  │  │  │  - MYSQL_ROOT_PASSWORD: RootPass123!                      │ │ │ │
│  │  │  │  - MYSQL_PASSWORD: UserPass456!                           │ │ │ │
│  │  │  │  - MYSQL_USER: dbuser                                     │ │ │ │
│  │  │  │  Volume: db → /var/lib/mysql                             │ │ │ │
│  │  │  └────────────────────────────────────────────────────────────┘ │ │ │
│  │  └──────────────────────────────────────────────────────────────────┘ │ │
│  │                                    │                                   │ │
│  │                                    ▼                                   │ │
│  │  ┌──────────────────────────────────────────────────────────────────┐ │ │
│  │  │  Database Service                                               │ │ │
│  │  │  - Type: ClusterIP                                              │ │ │
│  │  │  - Port: 3306                                                   │ │ │
│  │  │  - Target: Port 3306                                            │ │ │
│  │  └──────────────────────────────────────────────────────────────────┘ │ │
│  └────────────────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────────┘
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Implementation

Step 1: Create the Namespace

Namespaces provide isolation for resources within a Kubernetes cluster.

kubectl create namespace iron-namespace-devops
Enter fullscreen mode Exit fullscreen mode

Output:

namespace/iron-namespace-devops created
Enter fullscreen mode Exit fullscreen mode

Step 2: Create the Gallery Deployment

Create the YAML file for the Gallery deployment:

cat > iron-gallery-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: iron-gallery-deployment-devops
  namespace: iron-namespace-devops
  labels:
    run: iron-gallery
spec:
  replicas: 1
  selector:
    matchLabels:
      run: iron-gallery
  template:
    metadata:
      labels:
        run: iron-gallery
    spec:
      containers:
      - name: iron-gallery-container-devops
        image: kodekloud/irongallery:2.0
        resources:
          limits:
            memory: 100Mi
            cpu: 50m
        volumeMounts:
        - name: config
          mountPath: /usr/share/nginx/html/data
        - name: images
          mountPath: /usr/share/nginx/html/uploads
      volumes:
      - name: config
        emptyDir: {}
      - name: images
        emptyDir: {}
EOF
Enter fullscreen mode Exit fullscreen mode

Key Configuration Points:

Element Value Purpose
namespace iron-namespace-devops Isolates resources
labels.run iron-gallery Identifies the deployment
replicas 1 Single instance for testing
image kodekloud/irongallery:2.0 Gallery application image
memory 100Mi Memory limit
cpu 50m CPU limit

Step 3: Create the Database Deployment

Create the YAML file for the Database deployment:

cat > iron-db-deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: iron-db-deployment-devops
  namespace: iron-namespace-devops
  labels:
    db: mariadb
spec:
  replicas: 1
  selector:
    matchLabels:
      db: mariadb
  template:
    metadata:
      labels:
        db: mariadb
    spec:
      containers:
      - name: iron-db-container-devops
        image: kodekloud/irondb:2.0
        env:
        - name: MYSQL_DATABASE
          value: database_host
        - name: MYSQL_ROOT_PASSWORD
          value: "RootPass123!"
        - name: MYSQL_PASSWORD
          value: "UserPass456!"
        - name: MYSQL_USER
          value: "dbuser"
        volumeMounts:
        - name: db
          mountPath: /var/lib/mysql
      volumes:
      - name: db
        emptyDir: {}
EOF
Enter fullscreen mode Exit fullscreen mode

Key Configuration Points:

Element Value Purpose
labels.db mariadb Identifies the database deployment
image kodekloud/irondb:2.0 Database application image
MYSQL_DATABASE database_host Database name
MYSQL_ROOT_PASSWORD RootPass123! Root password
MYSQL_PASSWORD UserPass456! User password
MYSQL_USER dbuser Database user

Step 4: Create the Database Service

Create the YAML file for the Database service:

cat > iron-db-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: iron-db-service-devops
  namespace: iron-namespace-devops
spec:
  type: ClusterIP
  selector:
    db: mariadb
  ports:
    - protocol: TCP
      port: 3306
      targetPort: 3306
EOF
Enter fullscreen mode Exit fullscreen mode

Key Configuration Points:

Element Value Purpose
type ClusterIP Internal service only
selector.db mariadb Routes to database pods
port 3306 Service port
targetPort 3306 Container port

Step 5: Create the Gallery Service

Create the YAML file for the Gallery service:

cat > iron-gallery-service.yaml << 'EOF'
apiVersion: v1
kind: Service
metadata:
  name: iron-gallery-service-devops
  namespace: iron-namespace-devops
spec:
  type: NodePort
  selector:
    run: iron-gallery
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
      nodePort: 32678
EOF
Enter fullscreen mode Exit fullscreen mode

Key Configuration Points:

Element Value Purpose
type NodePort External access
selector.run iron-gallery Routes to gallery pods
port 80 Service port
targetPort 80 Container port
nodePort 32678 Node port for external access

Step 6: Apply All Configurations

kubectl apply -f iron-gallery-deployment.yaml
kubectl apply -f iron-db-deployment.yaml
kubectl apply -f iron-db-service.yaml
kubectl apply -f iron-gallery-service.yaml
Enter fullscreen mode Exit fullscreen mode

Output:

deployment.apps/iron-gallery-deployment-devops created
deployment.apps/iron-db-deployment-devops created
service/iron-db-service-devops created
service/iron-gallery-service-devops created
Enter fullscreen mode Exit fullscreen mode

Step 7: Verify All Resources

kubectl get namespaces
Enter fullscreen mode Exit fullscreen mode

Output:

NAME                    STATUS   AGE
default                 Active   27m
iron-namespace-devops   Active   3m44s
kube-node-lease         Active   27m
kube-public             Active   27m
kube-system             Active   27m
Enter fullscreen mode Exit fullscreen mode
kubectl get all -n iron-namespace-devops
Enter fullscreen mode Exit fullscreen mode

Output:

NAME                                                  READY   STATUS    RESTARTS   AGE
pod/iron-db-deployment-devops-5dc5645559-nbv8x        1/1     Running   0          3m36s
pod/iron-gallery-deployment-devops-7b59878ffd-bt4mk   1/1     Running   0          3m36s

NAME                                  TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/iron-db-service-devops        ClusterIP   10.43.83.150   <none>        3306/TCP       3m36s
service/iron-gallery-service-devops   NodePort    10.43.14.63    <none>        80:32678/TCP   3m36s

NAME                                             READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/iron-db-deployment-devops        1/1     1            1           3m36s
deployment.apps/iron-gallery-deployment-devops   1/1     1            1           3m37s
Enter fullscreen mode Exit fullscreen mode

Accessing the Application

Via NodePort

The application can be accessed using the NodePort service:

http://<node-ip>:32678
Enter fullscreen mode Exit fullscreen mode

Via Port Forwarding

For local testing, you can use port forwarding:

kubectl port-forward -n iron-namespace-devops service/iron-gallery-service-devops 8080:80
Enter fullscreen mode Exit fullscreen mode

Then access: http://localhost:8080


Understanding the Configuration

Resource Management

Resource limits ensure the application does not consume excessive cluster resources:

resources:
  limits:
    memory: 100Mi
    cpu: 50m
Enter fullscreen mode Exit fullscreen mode

Volume Mounts

EmptyDir volumes provide temporary storage for the application:

volumeMounts:
- name: config
  mountPath: /usr/share/nginx/html/data
- name: images
  mountPath: /usr/share/nginx/html/uploads
Enter fullscreen mode Exit fullscreen mode

Environment Variables

Environment variables configure the database connection:

env:
- name: MYSQL_DATABASE
  value: database_host
- name: MYSQL_ROOT_PASSWORD
  value: "RootPass123!"
- name: MYSQL_PASSWORD
  value: "UserPass456!"
- name: MYSQL_USER
  value: "dbuser"
Enter fullscreen mode Exit fullscreen mode

Troubleshooting

Pod Not Starting

# Check pod status
kubectl get pods -n iron-namespace-devops

# Check pod details
kubectl describe pod <pod-name> -n iron-namespace-devops

# Check pod logs
kubectl logs <pod-name> -n iron-namespace-devops
Enter fullscreen mode Exit fullscreen mode

Service Not Accessible

# Check service status
kubectl get services -n iron-namespace-devops

# Check endpoints
kubectl get endpoints -n iron-namespace-devops

# Check service details
kubectl describe service <service-name> -n iron-namespace-devops
Enter fullscreen mode Exit fullscreen mode

Namespace Issues

# Verify namespace exists
kubectl get namespaces

# Check resources in namespace
kubectl get all -n iron-namespace-devops
Enter fullscreen mode Exit fullscreen mode

Key Concepts

Namespaces

Namespaces provide:

  • Resource isolation
  • Naming scope
  • Access control boundaries
  • Organizational separation

Deployments

Deployments provide:

  • Declarative updates
  • Rolling updates
  • Rollback capabilities
  • Self-healing

Services

Services provide:

  • Stable network endpoints
  • Load balancing
  • Service discovery
  • External access

Environment Variables

Environment variables provide:

  • Configuration management
  • Separation of config from code
  • Secure credential handling
  • Runtime flexibility

Summary

In this challenge, we successfully:

  1. Created a dedicated namespace for the application
  2. Deployed the Gallery front-end with resource limits and volume mounts
  3. Deployed the Database backend with environment variables
  4. Exposed the database internally using a ClusterIP service
  5. Exposed the front-end externally using a NodePort service

This deployment pattern demonstrates a complete multi-tier application setup in Kubernetes, following best practices for resource management, service exposure, and configuration management.


Useful Commands Reference

Command Purpose
kubectl create namespace <name> Create a namespace
kubectl get namespaces List namespaces
kubectl get all -n <namespace> List all resources in a namespace
kubectl get deployments -n <namespace> List deployments in a namespace
kubectl get pods -n <namespace> List pods in a namespace
kubectl get services -n <namespace> List services in a namespace
kubectl describe deployment <name> -n <namespace> Detailed deployment info
kubectl describe pod <name> -n <namespace> Detailed pod info
kubectl describe service <name> -n <namespace> Detailed service info
kubectl logs <pod-name> -n <namespace> View pod logs
kubectl apply -f <file> Apply configuration from file
kubectl delete namespace <name> Delete a namespace

Top comments (0)