# 60 Days DevOps Challenge: Day 9

## **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](https://docs.docker.com/compose/install/?_bhlid=5fca6ddc6dfc76419b66f71fa429c1c2496c764d&ref=learnxops.com)).

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

#### **Step 1: Create a Custom Bridge Network**

```bash
docker network create my-custom-network
```

#### **Step 2: Run an Nginx Container on the Custom Network**

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

#### **Step 3: Run an Alpine Container on the Same Network**

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

#### **Step 4: Test Connectivity from Alpine to Nginx**

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

#### **Step 5: Verify Network Connection**

```bash
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**

```bash
docker volume create mysql_data
```

#### **Step 2: Run MySQL with the Named Volume**

```bash
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**

```bash
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**

```bash
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**](http://app.py)

```python
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**

```python
flask
psycopg2
```

#### **Step 3: Dockerfile**

```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**

```yaml
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**

```bash
docker-compose up -d
```

#### **Step 6: check the status and connection**

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

### **✅ *Challenge 4: Secure credentials using environment variables***

#### **Step 1: .env**

```python
POSTGRES_DB=mydb
POSTGRES_USER=postgres
POSTGRES_PASSWORD=supersecurepassword
```

#### **Step 2: Modified docker-compose.yml**

```yaml
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**](http://app.py)

```python
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:**

```bash
docker compose up -d --build
```

#### **Step 5:**

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

#### **Bonus: Add .env to .gitignore**

```bash
echo ".env" >> .gitignore
```

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

### **Step 1: Create a** `.env` File

```python
# 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

```yaml
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**

```bash
docker-compose up -d
```

### **Step 4: Access WordPress**

Open [`http://localhost:8080`](http://localhost:8080) in your browser.

### **Step 5: Verify Containers**

```bash
docker ps
```

## **✅ *Challenge 6: Reverse Proxy with Nginx for Flask***

### **Step 1:** [`app.py`](http://app.py)

```python
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`

```txt
flask
```

### **Step 3:** `Dockerfile`

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

### **Step 4:** `nginx.conf`

```nginx
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`

```yaml
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**

```bash
docker-compose up -d --build
```

### **Step 7: Test**

```bash
curl http://localhost:8080
```

## **✅ *Challenge 7: Network Alias for DB Access***

### **Step 1:** [`app.py`](http://app.py)

```python
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`

```txt
flask
psycopg2
```

### **Step 3:** `Dockerfile`

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

### **Step 4:** `.env`

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

### **Step 5:** `docker-compose.yml`

```yaml
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**

```bash
docker-compose up -d --build
```

### **Step 7: Verify**

```bash
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**

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

### **Step 2: Get Internal IP Address**

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

### **Step 3: Access via Internal IP**

```bash
curl http://<container_ip>
```

Example Output:

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

### **Step 4: Validate Network Info**

```bash
docker network inspect bridge
```

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

### **Step 1:** [`app.py`](http://app.py)

```python
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`

```txt
flask
redis
```

### **Step 3:** `Dockerfile`

```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`

```yaml
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**

```bash
docker-compose up -d --build
```

### **Step 6: Test It**

```bash
curl http://localhost:5000
```

### **Step 7: Tear Down**

```bash
docker-compose down -v
```

## **✅ *Challenge 10: Docker Compose Health Checks***

### **Step 1:** [`app.py`](http://app.py)

```python
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`

```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

```yaml
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**

```bash
docker-compose up -d --build
```

### **Step 5: Check Health**

```bash
docker ps
```

### **Step 6: Inspect Detailed Status**

```bash
docker inspect --format='{{json .State.Health}}' flask-container | jq
```

##### **Bonus 💡 Restart Unhealthy Containers Automatically**

Modify docker-compose.yml to restart unhealthy containers:

```yaml
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.*
