Docker-Based Python Service Deployment with Nginx Load Balancing

Project Overview

This guide demonstrates how to deploy a Python web service using Docker with Nginx for load balencing. The example includes MySQL and Redis dependencies, along with instructions for disabling load balancing if not needed.

Project Structure

1. Dockerfile

# Base image for the application
FROM python:3.7

# Set working directory inside container
WORKDIR /app

# Copy application files to container
COPY . /app

# Install Python dependencies from requirements file
RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple -r requirements.txt

# Expose the application port
EXPOSE 1000

# Startup command
CMD ["sh", "entrypoint.sh"]

2. docker-compose.yml

version: '3.3'

networks:
  app_network:
    driver: bridge

services:

  db_service:
    image: mysql:5.7
    container_name: db_service
    environment:
      MYSQL_DATABASE: 'appdb'
      MYSQL_USER: 'dbuser'
      MYSQL_PASSWORD: 'password123'
      MYSQL_ROOT_PASSWORD: 'rootpass123'
    volumes:
      - ./data/mysql:/var/lib/mysql
    ports:
      - "3306:3306"
    networks:
      - app_network

  cache_service:
    image: redis:latest
    container_name: cache_service
    ports:
      - "6379:6379"
    volumes:
      - ./data/redis:/data
    networks:
      - app_network

  web_service:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: web_service
    image: web_image:v1.0
    volumes:
      - ./app:/app
    depends_on:
      - cache_service
      - db_service
    networks:
      - app_network

  web_service_secondary:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: web_service_secondary
    image: web_image:v1.0
    volumes:
      - ./app:/app
    depends_on:
      - cache_service
      - db_service
    networks:
      - app_network

  proxy_server:
    image: nginx:latest
    container_name: proxy_server
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
    depends_on:
      - web_service
    networks:
      - app_network

3. Application Code (app.py)

import os
import socket
import logging
import redis
import pymysql
from flask import Flask, jsonify
from flask_cors import CORS

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

app = Flask(__name__)
app.json.ensure_ascii = False
CORS(app, supports_credentials=True)


@app.route('/health', methods=['GET'])
def health_check():
    """Health check endpoint for load balancer."""
    return jsonify({'status': 'healthy', 'service': 'web_app'})


@app.route('/api/data', methods=['GET'])
def get_data():
    """Main application endpoint demonstrating database and cache connectivity."""
    
    # Initialize Redis connection using container hostname
    try:
        redis_client = redis.Redis(
            host='cache_service',
            port=6379,
            db=0,
            decode_responses=True
        )
        counter = redis_client.get('request_counter') or 0
        redis_client.set('request_counter', int(counter) + 1)
        cache_status = f"Request #{int(counter) + 1}"
    except Exception as e:
        logger.error(f"Redis connection failed: {e}")
        cache_status = "Cache unavailable"

    # Initialize MySQL connection using container hostname
    try:
        db_connection = pymysql.connect(
            host='db_service',
            user='root',
            password='rootpass123',
            database='appdb',
            port=3306
        )
        with db_connection.cursor() as cursor:
            cursor.execute('SELECT DATABASE()')
            db_name = cursor.fetchone()[0]
            db_status = f"Connected to {db_name}"
        db_connection.close()
    except Exception as e:
        logger.error(f"Database connection failed: {e}")
        db_status = f"Database error: {str(e)}"

    # Get container metadata
    container_name = socket.gethostname()
    container_ip = socket.gethostbyname(container_name)
    process_id = os.getpid()

    return jsonify({
        'code': 200,
        'message': 'Service operational',
        'cache': cache_status,
        'database': db_status,
        'metadata': {
            'hostname': container_name,
            'ip_address': container_ip,
            'pid': process_id
        }
    })


if __name__ == '__main__':
    app.run(debug=False, host='0.0.0.0', port=1000)

4. Requirements File

flask==3.0.3
gunicorn==22.0.0
flask_cors==5.0.0
redis==5.2.0
pymysql==1.1.1

5. Startup Script (entrypoint.sh)

#!/bin/bash

# Start Gunicorn with multiple workers for handling concurrent requests
# --reload enables automatic reloading when code changes (useful in development)
# -w 4 spawns 4 worker processes
# --bind 0.0.0.0:1000 makes the service accessible from other containers
gunicorn --reload -w 4 --bind 0.0.0.0:1000 app:app

6. Nginx Configuration

worker_processes auto;

events {
    worker_connections 1024;
}

http {
    upstream backend {
        least_conn;
        server web_service:1000 weight=3;
        server web_service_secondary:1000 weight=2;
    }

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

        location /health {
            return 200 'OK';
            add_header Content-Type text/plain;
        }
    }
}

Deployment Instructions

Starting the Stack

# Start all containers in detached mode (background)
docker-compose up -d

# View container logs
docker-compose logs -f

# View status of all services
docker-compose ps

Troubleshooting

If image pull fails during startup, manually pull required images first:

docker pull mysql:5.7
docker pull redis:latest
docker pull nginx:latest
docker pull python:3.7

# Then start the stack
docker-compose up -d

To disable load balancing and run a single instance, comment out the web_service_secondary section and update the nginx upstream configuration to reference only one backend server.

Tags: docker python nginx load-balancing docker-compose

Posted on Thu, 22 Oct 2026 16:12:34 +0000 by brianbehrens