Deploy Streamlit With Nginx Reverse Proxy Using Docker Compose

by Alex Johnson 63 views

Deploying Streamlit applications in a production environment often involves using a reverse proxy like Nginx to handle incoming traffic, manage SSL certificates, and improve security. Docker Compose simplifies the process of setting up multi-container applications, making it an ideal tool for deploying Streamlit with Nginx. This article guides you through creating a production-ready docker-compose.yml file for your Streamlit application, complete with Nginx as a reverse proxy.

Understanding the Basics: Docker, Docker Compose, Streamlit, and Nginx

Before diving into the specifics, let's clarify the core technologies involved.

  • Docker: Docker is a platform for developing, shipping, and running applications inside containers. Containers allow you to package an application with all of its dependencies, ensuring it runs consistently across different environments.
  • Docker Compose: Docker Compose is a tool for defining and managing multi-container Docker applications. It uses a YAML file (docker-compose.yml) to configure the application's services, networks, and volumes.
  • Streamlit: Streamlit is an open-source Python library that makes it easy to create and share custom web apps for machine learning and data science. It simplifies the process of turning data scripts into shareable web applications.
  • Nginx: Nginx is a high-performance web server and reverse proxy server. In this context, Nginx acts as an intermediary between the outside world and your Streamlit application, handling incoming HTTP requests and forwarding them to the Streamlit container. This setup allows you to manage SSL/TLS certificates, load balance traffic, and add other security features.

Why Use Nginx as a Reverse Proxy with Streamlit?

Using Nginx as a reverse proxy with Streamlit offers several significant advantages for production deployments. First and foremost, Nginx can handle SSL/TLS encryption, ensuring secure communication between clients and your application. This is crucial for protecting sensitive data transmitted over the internet. Secondly, Nginx can act as a load balancer, distributing traffic across multiple Streamlit instances if needed, which improves the application's availability and responsiveness under heavy load. Additionally, Nginx provides enhanced security features, such as protection against DDoS attacks and other web vulnerabilities. It also simplifies the management of static assets, allowing you to serve static files directly from Nginx without involving the Streamlit application server. By centralizing these functions in Nginx, you can optimize the performance and security of your Streamlit deployment, making it robust and scalable for production use.

Prerequisites

Before you start, make sure you have the following installed:

  • Docker: You can download and install Docker from the official Docker website.
  • Docker Compose: Docker Compose typically comes bundled with Docker Desktop. If you're using Docker Engine, you may need to install Docker Compose separately. Instructions can be found on the Docker documentation.
  • A Streamlit application: You should have a Streamlit application ready to deploy. If you don't have one, you can create a simple example for testing purposes.

Step-by-Step Guide: Creating the docker-compose.yml File

1. Project Structure

Start by creating a project directory for your Streamlit application and Docker Compose files. A typical project structure might look like this:

my-streamlit-app/
├── streamlit_app.py  # Your Streamlit application
├── requirements.txt  # Python dependencies
├── docker-compose.yml # Docker Compose configuration
└── nginx/          # Nginx configuration files
    └── nginx.conf    # Nginx configuration

2. Streamlit Application (streamlit_app.py)

If you don't already have a Streamlit application, create a simple one for testing:

# streamlit_app.py
import streamlit as st

st.title('Hello, Streamlit!')
st.write('This is a simple Streamlit application.')

3. Python Dependencies (requirements.txt)

List your Streamlit application's dependencies in a requirements.txt file:

streamlit

4. Nginx Configuration (nginx/nginx.conf)

Create an Nginx configuration file that acts as a reverse proxy for your Streamlit application:

# nginx/nginx.conf
upstream streamlit {
    server streamlit:8501; # Streamlit service name and port
}

server {
    listen 80;
    server_name yourdomain.com; # Replace with your domain

    location / {
        proxy_pass http://streamlit; # Proxy to Streamlit upstream
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

    # Add SSL configuration if needed
    # listen 443 ssl;
    # ssl_certificate /etc/nginx/ssl/yourdomain.crt;
    # ssl_certificate_key /etc/nginx/ssl/yourdomain.key;
}

The Nginx configuration file is crucial for setting up the reverse proxy. The upstream streamlit block defines the Streamlit service, which Nginx will forward requests to. The server block configures the Nginx server, listening on port 80 for HTTP traffic. The location / block specifies how Nginx should handle incoming requests. The proxy_pass directive tells Nginx to forward requests to the Streamlit upstream, while the proxy_set_header directives preserve the original client's IP address and host information. If you want to enable HTTPS, you would uncomment the SSL configuration lines and provide the paths to your SSL certificate and key files. Remember to replace yourdomain.com with your actual domain name and configure DNS settings accordingly.

5. Docker Compose Configuration (docker-compose.yml)

Now, create the docker-compose.yml file to define your application's services:

# docker-compose.yml
version: "3.8"

services:
  streamlit:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8501:8501" # Streamlit port
    volumes:
      - .:/app
    environment:
      - STREAMLIT_SERVER_PORT=8501
      - STREAMLIT_SERVER_ADDRESS=0.0.0.0
    networks:
      - app-network

  nginx:
    image: nginx:latest
    ports:
      - "80:80"    # HTTP port
      - "443:443"  # HTTPS port (if SSL configured)
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      # - ./nginx/ssl:/etc/nginx/ssl # Uncomment if using SSL
    depends_on:
      - streamlit
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

6. Dockerfile

Create a Dockerfile in the root of your project to define how the Streamlit application container should be built:

# Dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8501

CMD streamlit run streamlit_app.py

7. Building and Running the Application

Open a terminal in your project directory and run the following command to build and start the application:

docker-compose up --build

This command builds the Docker images and starts the containers defined in your docker-compose.yml file. The --build flag ensures that the images are rebuilt if there are any changes to the Dockerfile or other build context files.

Once the containers are running, you can access your Streamlit application through your browser by navigating to http://yourdomain.com (or http://localhost if you're testing locally). Nginx will forward the requests to the Streamlit container.

Key Components of the docker-compose.yml File

Let's break down the key components of the docker-compose.yml file:

  • Version: Specifies the version of the Docker Compose file format.
  • Services: Defines the different services that make up your application.
    • streamlit: This service defines the Streamlit application container.
      • build: Specifies the build context and Dockerfile to use for building the image.
      • ports: Maps the container port 8501 to the host port 8501, allowing external access to the Streamlit application.
      • volumes: Mounts the current directory (.) into the container's /app directory, enabling code changes to be reflected in the container without rebuilding the image.
      • environment: Sets environment variables for the Streamlit application, such as the server port and address.
      • networks: Attaches the container to the app-network network.
    • nginx: This service defines the Nginx reverse proxy container.
      • image: Specifies the Nginx image to use from Docker Hub.
      • ports: Maps the host ports 80 and 443 to the container ports 80 and 443, allowing HTTP and HTTPS traffic to be routed through Nginx.
      • volumes: Mounts the Nginx configuration file (nginx.conf) into the container and optionally mounts SSL certificate files if using HTTPS.
      • depends_on: Specifies that the Nginx service depends on the Streamlit service, ensuring that the Streamlit container is started before Nginx.
      • networks: Attaches the container to the app-network network.
  • Networks: Defines the networks that the services will use to communicate with each other.
    • app-network: A bridge network that allows the Streamlit and Nginx containers to communicate.

The Role of the Dockerfile

The Dockerfile is essential for building the Streamlit application container. It starts with a base Python image, sets the working directory inside the container, copies the requirements.txt file, and installs the Python dependencies. Then, it copies the Streamlit application code into the container, exposes port 8501, and specifies the command to run the Streamlit application. This ensures that the Streamlit application runs in a consistent and isolated environment.

Configuring SSL/TLS

To enable HTTPS, you need to configure SSL/TLS for Nginx. You can obtain SSL certificates from a Certificate Authority (CA) like Let's Encrypt. Here's how to configure SSL in your nginx.conf file:

  1. Obtain SSL Certificates:

    • Use Let's Encrypt or another CA to obtain SSL certificates for your domain.
  2. Update nginx.conf:

    • Uncomment the SSL configuration lines in your nginx.conf file.
    • Specify the paths to your SSL certificate and key files.
    # nginx/nginx.conf
    upstream streamlit {
        server streamlit:8501; # Streamlit service name and port
    }
    
    server {
        listen 80;
        server_name yourdomain.com; # Replace with your domain
        return 301 https://$host$request_uri; # Redirect HTTP to HTTPS
    }
    
    server {
        listen 443 ssl;
        server_name yourdomain.com; # Replace with your domain
    
        ssl_certificate /etc/nginx/ssl/yourdomain.crt; # Path to your SSL certificate
        ssl_certificate_key /etc/nginx/ssl/yourdomain.key; # Path to your SSL key
    
        location / {
            proxy_pass http://streamlit; # Proxy to Streamlit upstream
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $host;
            proxy_redirect off;
        }
    }
    
  3. Mount SSL Certificates:

    • Create an nginx/ssl directory in your project.
    • Place your SSL certificate and key files in this directory.
    • Uncomment the SSL volume mount in your docker-compose.yml file.
    # docker-compose.yml
    version: "3.8"
    
    services:
      streamlit:
        # ... (Streamlit service configuration)
    
      nginx:
        # ... (Nginx service configuration)
        volumes:
          - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
          - ./nginx/ssl:/etc/nginx/ssl # Uncomment if using SSL
        # ...
    

Additional Considerations for Production

1. Domain and DNS Configuration

Configuring your domain and DNS is a critical step in making your application accessible to users. You need to set up a domain name and configure DNS records to point to the IP address of your server. This involves purchasing a domain from a domain registrar and updating the DNS settings with your hosting provider. Specifically, you'll need to create an A record that maps your domain to the server's IP address. Additionally, you may want to set up a CNAME record for the www subdomain to point to your main domain. These configurations ensure that users can access your application by typing your domain name into their web browser, making it a user-friendly and professional experience.

2. Logging and Monitoring

Implement logging and monitoring to track your application's performance and identify potential issues. You can use tools like Prometheus and Grafana to monitor your application's metrics.

3. Security Best Practices

Follow security best practices to protect your application from vulnerabilities. This includes keeping your software up to date, using strong passwords, and implementing security policies.

4. Persistent Storage

If your application requires persistent storage, consider using Docker volumes or external storage solutions to store data outside of the container.

5. CI/CD Pipeline

Set up a CI/CD pipeline to automate the deployment process. This allows you to easily deploy updates to your application without manual intervention.

6. Load Balancing and Scalability

If your application experiences high traffic, consider implementing load balancing and scaling strategies to distribute traffic across multiple instances of your application.

Conclusion

Deploying Streamlit applications with Nginx as a reverse proxy using Docker Compose is an efficient and scalable way to create production-ready environments. By following this guide, you can set up a robust infrastructure that handles traffic, manages SSL certificates, and ensures the security of your Streamlit application. Remember to consider additional production considerations such as logging, monitoring, and security best practices to ensure the long-term stability and performance of your deployment.

For further information on Docker Compose and Nginx, you can refer to the official documentation: Docker Compose Documentation and Nginx Documentation.