Exploring Multi-Container Architecture with Docker and FastAPI
Introduction
In modern application development, microservices and multi-container architectures have become increasingly popular. Separating different services into individual containers gives you flexibility, scalability, and easier maintenance. In this blog, we’ll explore a multi-container architecture using Docker, specifically focusing on a practical example involving a FastAPI-based OAuth2 authentication service and a MySQL database.
Why Multi-Container Architecture?
Isolation: Each service runs in its container, reducing conflicts.
Scalability: Scale specific components without affecting the entire application.
Maintainability: Easier to update and manage individual services.
Resilience: If one container fails, it does not bring down the entire system.
Project Structure
The folder structure of our multi-container project looks like this:
multi_container
├── database-container
│ ├── compose.yaml
│ ├── Dockerfile
│ └── README.Docker.md
└── Oauth2-fastapi
├── compose.yaml
├── Dockerfile
├── main.py
├── README.Docker.md
├── requirements.txt
├── routers
│ ├── auth.py
│ ├── google_auth.py
│ └── users.py
└── utils
├── config.py
├── db_helper.py
└── schema.py
Components Explained
1.
database-container:
Contains the Docker setup for the MySQL database.
compose.yamlconfigures the service with volume and network settings.
2.
Oauth2-fastapi:
Implements an authentication service using FastAPI and OAuth2.
- Includes route handlers (
auth.py,google_auth.py,users.py) and utility scripts (config.py,db_helper.py,schema.py).
Code for database-container/compose.yaml
version: '3.8'
services:
server:
build:
context: .
ports:
- "8000:8000"
depends_on:
db:
condition: service_healthy # Ensures MySQL is ready before FastAPI starts
environment:
- DATABASE_URL=mysql+mysqlconnector://levelup:levelup2025@db:3306/levelup
networks:
- app-network
db:
image: mysql:latest
restart: always
env_file:
- .env
ports:
- "3306:3306"
volumes:
- db-data:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
networks:
- app-network
volumes:
db-data:
networks:
app-network:
driver: bridge
Code for database-container/Dockerfile
# syntax=docker/dockerfile:1
FROM alpine:latest as base
FROM base as build
RUN echo -e '#!/bin/sh\necho Hello world from $(whoami)! In order to get your application running in a container, take a look at the comments in the Dockerfile to get started.' > /bin/hello.sh
RUN chmod +x /bin/hello.sh
FROM base AS final
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser
USER appuser
COPY --from=build /bin/hello.sh /bin/
ENTRYPOINT [ "/bin/hello.sh" ]
Code for oauth2-fastapi/compose.yaml
networks:
database-container_app-network:
external: true
services:
server:
build:
context: .
ports:
- 8060:8060
networks:
- database-container_app-network
Explanation:
- Configures the FastAPI service in a separate container.
- Connects to the shared network (
database-container_app-network) for inter-container communication. - Exposes port
8060for accessing the FastAPI service.
Code for oauth2-fastapi/dockerfile
# Use a lightweight Python image
FROM python:3.10-slim as base
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1
# Set the working directory inside the container
WORKDIR /app
# Create a non-root user
ARG UID=10001
RUN adduser --disabled-password --gecos "" --uid "${UID}" appuser
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY .env .env
# Copy application code
COPY . .
# Change ownership to the non-root user
RUN chown -R appuser:appuser /app
# Switch to non-root user
USER appuser
# Expose the port FastAPI will run on
EXPOSE 8060
# Start FastAPI application
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8060"]
How Docker Networks Work
Bridge Network:
- By default, Docker creates a bridge network for containers.
- Containers on the same bridge network can communicate using their container names as hostnames.
External Network:
- The
oauth2-fastapicontainer uses an external network (database-container_app-network) to connect with the MySQL container. - This allows services in different Docker Compose files to communicate with each other.
Benefits of Networking:
Isolated environments for services.
Flexibility to connect and disconnect containers as needed.
Ensures secure communication between services.
Conclusion
By setting up a multi-container architecture with Docker and FastAPI, you achieve a modular, maintainable, and scalable application. Using external networks and dependency management ensures robust communication between services, enhancing the overall resilience of the system.
If you want code on routers and utils, check my Medium post: Google Authentication in FastAPI using OAuth2
Hope this is useful. Happy coding! ![]()
