Skip to main content

Command Palette

Search for a command to run...

60 Days DevOps Challenge: Day 9

Published
•8 min read•View as Markdown

Docker Networking, Volumes, and Compose

Initial Tasks:

✅ Task 1: Inspect the default Docker network and list all networks:

docker network ls

✅ Task 2: Install Docker Compose (Guide).

✅ Challenge 1: Create a custom bridge network and connect two containers (alpine and nginx).

Step 1: Create a Custom Bridge Network

docker network create my-custom-network

Step 2: Run an Nginx Container on the Custom Network

docker run -d --name nginx-container --network my-custom-network nginx

Step 3: Run an Alpine Container on the Same Network

docker run -it --rm --name alpine-container --network my-custom-network alpine sh

Step 4: Test Connectivity from Alpine to Nginx

apk add --no-cache curl
curl nginx-container

Step 5: Verify Network Connection

docker network ls
docker network inspect my-custom-network

✅ Challenge 2: Run a MySQL container with a named volume and confirm data persistence

Step 1: Create a Named Volume

docker volume create mysql_data

Step 2: Run MySQL with the Named Volume

docker run -d --name mysql-container \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -e MYSQL_DATABASE=mydb \
  -v mysql_data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:latest

Step 3: Verify Data Persistence

docker exec -it mysql-container mysql -uroot -prootpass -e "CREATE TABLE mydb.users (id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(50));"
docker exec -it mysql-container mysql -uroot -prootpass -e "INSERT INTO mydb.users (name) VALUES ('Alice'), ('Bob');"
docker exec -it mysql-container mysql -uroot -prootpass -e "SELECT * FROM mydb.users;"

Step 4: Restart and Verify

docker stop mysql-container
docker rm mysql-container
docker run -d --name mysql-container \
  -e MYSQL_ROOT_PASSWORD=rootpass \
  -e MYSQL_DATABASE=mydb \
  -v mysql_data:/var/lib/mysql \
  -p 3306:3306 \
  mysql:latest
docker exec -it mysql-container mysql -uroot -prootpass -e "SELECT * FROM mydb.users;"

✅ Challenge 3: docker-compose.yml for Flask API and PostgreSQL

Step 1: app.py

from flask import Flask
import psycopg2

app = Flask(__name__)

@app.route("/")
def home():
    return "🚀 Flask API with PostgreSQL is running!"

@app.route("/db-status")
def db_status():
    try:
        conn = psycopg2.connect(dbname="mydb", user="postgres", password="postgres", host="db")
        conn.close()
        return "✅ PostgreSQL is connected!"
    except Exception as e:
        return f"❌ Database connection failed: {e}"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 2: requirements.txt

flask
psycopg2

Step 3: Dockerfile

FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

Step 4: docker-compose.yml

version: "3.8"
services:
  flask-app:
    build: .
    container_name: flask-container
    ports:
      - "5000:5000"
    depends_on:
      - db
    environment:
      DATABASE_URL: "postgresql://postgres:postgres@db:5432/mydb"

  db:
    image: postgres:latest
    container_name: postgres-container
    environment:
      POSTGRES_DB: mydb
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgres
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

Step 5: Run docker-compose

docker-compose up -d

Step 6: check the status and connection

curl http://localhost:5000/
curl http://localhost:5000/db-status

✅ Challenge 4: Secure credentials using environment variables

Step 1: .env

POSTGRES_DB=mydb
POSTGRES_USER=postgres
POSTGRES_PASSWORD=supersecurepassword

Step 2: Modified docker-compose.yml

version: "3.8"
services:
  flask-app:
    build: .
    container_name: flask-container
    ports:
      - "5000:5000"
    depends_on:
      - db
    env_file:
      - .env
    environment:
      DATABASE_URL: "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"

  db:
    image: postgres:latest
    container_name: postgres-container
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
    env_file:
      - .env

volumes:
  postgres_data:

Step 3: Updated app.py

import os
import psycopg2
from flask import Flask

app = Flask(__name__)

DB_NAME = os.getenv("POSTGRES_DB", "default_db")
DB_USER = os.getenv("POSTGRES_USER", "default_user")
DB_PASSWORD = os.getenv("POSTGRES_PASSWORD", "default_pass")
DB_HOST = "db"

@app.route("/")
def home():
    return "🚀 Flask API with PostgreSQL using .env is running!"

@app.route("/db-status")
def db_status():
    try:
        conn = psycopg2.connect(dbname=DB_NAME, user=DB_USER, password=DB_PASSWORD, host=DB_HOST)
        conn.close()
        return "✅ PostgreSQL is connected!"
    except Exception as e:
        return f"❌ Database connection failed: {e}"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 4:

docker compose up -d --build

Step 5:

curl http://localhost:5000/
curl http://localhost:5000/db-status
docker exec -it flask-container env | grep POSTGRES

Bonus: Add .env to .gitignore

echo ".env" >> .gitignore

✅ Challenge 5: Deploy a WordPress site using Docker Compose (WordPress + MySQL)

Step 1: Create a .env File

# MySQL Database Credentials
MYSQL_ROOT_PASSWORD=rootpassword
MYSQL_DATABASE=wordpress
MYSQL_USER=wordpressuser
MYSQL_PASSWORD=wordpresspass

# WordPress Configuration
WORDPRESS_DB_HOST=db
WORDPRESS_DB_USER=wordpressuser
WORDPRESS_DB_PASSWORD=wordpresspass
WORDPRESS_DB_NAME=wordpress

Step 2: Create the docker-compose.yml File

version: "3.8"
services:
  wordpress:
    image: wordpress:latest
    container_name: wordpress-container
    restart: always
    depends_on:
      - db
    ports:
      - "8080:80"
    environment:
      WORDPRESS_DB_HOST: ${WORDPRESS_DB_HOST}
      WORDPRESS_DB_USER: ${WORDPRESS_DB_USER}
      WORDPRESS_DB_PASSWORD: ${WORDPRESS_DB_PASSWORD}
      WORDPRESS_DB_NAME: ${WORDPRESS_DB_NAME}
    env_file:
      - .env
    volumes:
      - wordpress_data:/var/www/html

  db:
    image: mysql:latest
    container_name: mysql-container
    restart: always
    command: --default-authentication-plugin=mysql_native_password
    ports:
      - "3306:3306"
    env_file:
      - .env
    volumes:
      - mysql_data:/var/lib/mysql

volumes:
  wordpress_data:
  mysql_data:

Step 3: Start WordPress + MySQL

docker-compose up -d

Step 4: Access WordPress

Open http://localhost:8080 in your browser.

Step 5: Verify Containers

docker ps

✅ Challenge 6: Reverse Proxy with Nginx for Flask

Step 1: app.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def home():
    return "🚀 Flask API is running behind Nginx!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 2: requirements.txt

flask

Step 3: Dockerfile

FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

Step 4: nginx.conf

server {
    listen 80;
    location / {
        proxy_pass http://backend:5000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Step 5: docker-compose.yml

version: "3.8"
services:
  nginx:
    image: nginx:latest
    container_name: nginx-proxy
    ports:
      - "8080:80"
    depends_on:
      - backend
    volumes:
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    restart: always

  backend:
    build: .
    container_name: flask-backend
    expose:
      - "5000"
    restart: always

Step 6: Build and Run

docker-compose up -d --build

Step 7: Test

curl http://localhost:8080

✅ Challenge 7: Network Alias for DB Access

Step 1: app.py

import os
import psycopg2
from flask import Flask

app = Flask(__name__)

DB_HOST = os.getenv("DB_HOST", "db-alias")
DB_NAME = os.getenv("POSTGRES_DB", "mydb")
DB_USER = os.getenv("POSTGRES_USER", "postgres")
DB_PASSWORD = os.getenv("POSTGRES_PASSWORD", "postgres")

@app.route("/")
def home():
    return "🚀 Flask API connected to PostgreSQL using network alias!"

@app.route("/db-status")
def db_status():
    try:
        conn = psycopg2.connect(
            dbname=DB_NAME, user=DB_USER,
            password=DB_PASSWORD, host=DB_HOST
        )
        conn.close()
        return "✅ PostgreSQL is connected via alias!"
    except Exception as e:
        return f"❌ Database connection failed: {e}"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 2: requirements.txt

flask
psycopg2

Step 3: Dockerfile

FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

Step 4: .env

POSTGRES_DB=mydb
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
DB_HOST=db-alias

Step 5: docker-compose.yml

version: "3.8"
services:
  flask-app:
    build: .
    container_name: flask-container
    ports:
      - "5000:5000"
    depends_on:
      - db
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
      DB_HOST: db-alias
    networks:
      mynetwork:
        aliases:
          - app-alias

  db:
    image: postgres:latest
    container_name: postgres-container
    ports:
      - "5432:5432"
    environment:
      POSTGRES_DB: ${POSTGRES_DB}
      POSTGRES_USER: ${POSTGRES_USER}
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      mynetwork:
        aliases:
          - db-alias

volumes:
  postgres_data:

networks:
  mynetwork:

Step 6: Build and Run

docker-compose up -d --build

Step 7: Verify

curl http://localhost:5000/
curl http://localhost:5000/db-status
docker network inspect mynetwork

✅ Challenge 8: Inspect Container IP and Access via IP

Step 1: Run an Nginx Container

docker run -d --name my-nginx -p 8080:80 nginx

Step 2: Get Internal IP Address

docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' my-nginx

Step 3: Access via Internal IP

curl http://<container_ip>

Example Output:

<!DOCTYPE html>
<html>
<head><title>Welcome to nginx!</title></head>
<body>
<h1>Welcome to nginx!</h1>
</body>
</html>

Step 4: Validate Network Info

docker network inspect bridge

✅ Challenge 9: Redis-Based Caching with Python (Flask)

Step 1: app.py

import os
import redis
from flask import Flask

app = Flask(__name__)
redis_host = os.getenv("REDIS_HOST", "redis")
redis_client = redis.Redis(host=redis_host, port=6379, db=0)

@app.route("/")
def home():
    redis_client.incr("visits")
    visits = redis_client.get("visits").decode("utf-8")
    return f"🚀 Flask + Redis! Visit count: {visits}"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 2: requirements.txt

flask
redis

Step 3: Dockerfile

FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install -r requirements.txt
EXPOSE 5000
CMD ["python", "app.py"]

Step 4: docker-compose.yml

version: "3.8"
services:
  flask-app:
    build: .
    container_name: flask-container
    ports:
      - "5000:5000"
    depends_on:
      - redis
    environment:
      REDIS_HOST: redis
    restart: always

  redis:
    image: redis:latest
    container_name: redis-cache
    ports:
      - "6379:6379"
    restart: always

Step 5: Run the Setup

docker-compose up -d --build

Step 6: Test It

curl http://localhost:5000

Step 7: Tear Down

docker-compose down -v

✅ Challenge 10: Docker Compose Health Checks

Step 1: app.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def home():
    return "🚀 Flask API is running!"

@app.route("/health")
def health():
    return "✅ OK", 200

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 2: Dockerfile

FROM python:3.9
WORKDIR /app
COPY . .
RUN pip install flask
EXPOSE 5000
CMD ["python", "app.py"]

Step 3: docker-compose.yml with Health Checks

version: "3.8"
services:
  flask-app:
    build: .
    container_name: flask-container
    ports:
      - "5000:5000"
    restart: always
    depends_on:
      redis:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 5s

  redis:
    image: redis:latest
    container_name: redis-cache
    ports:
      - "6379:6379"
    restart: always
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s

Step 4: Run with Health Check

docker-compose up -d --build

Step 5: Check Health

docker ps

Step 6: Inspect Detailed Status

docker inspect --format='{{json .State.Health}}' flask-container | jq
Bonus 💡 Restart Unhealthy Containers Automatically

Modify docker-compose.yml to restart unhealthy containers:

services:
  flask-app:
    build: .
    container_name: flask-container
    ports:
      - "5000:5000"
    restart: on-failure
    depends_on:
      redis:
        condition: service_healthy # Wait for Redis to be healthy
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
      interval: 10s
      timeout: 5s
      retries: 3
      start_period: 5s

  redis:
    image: redis:latest
    container_name: redis-cache
    ports:
      - "6379:6379"
    restart: on-failure
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 5s

In conclusion, the 60 Days DevOps Challenge: Day 9 provided a comprehensive exploration of Docker networking, volumes, and Compose. Through a series of practical challenges, participants gained hands-on experience in creating custom networks, managing data persistence with volumes, and deploying applications using Docker Compose. The tasks also emphasized the importance of securing credentials, implementing reverse proxies, and utilizing health checks to ensure application reliability. By completing these challenges, participants have strengthened their understanding of Docker's capabilities and are better equipped to manage containerized applications in a DevOps environment.

More from this blog

DevOpsChallenge

18 posts

This publication is mainly focused on the 60 days DevOps challenge which covers various topics related to DevOps like Linux, Python, AWS etc