In today's world of containerized applications, managing multiple containers efficiently is crucial. Docker Compose is a powerful tool that addresses this need by simplifying the deployment and management of multi-container Docker applications. In this article, we'll explore what Docker Compose is, how it works, and provide a step-by-step guide on deploying a Flask application using Docker Compose on an Amazon EC2 instance.
What is Docker Compose?
Docker Compose is a tool that allows developers to define and manage multi-container Docker applications using a single configuration file. It is especially useful when your application requires multiple services (e.g., a web server, a database, a message broker) that need to work together. Instead of managing each container separately, Docker Compose allows you to define all the services your application needs in a docker-compose.yml
file, making it easier to build, run, and manage complex applications.
Key Features:
Multi-Container Management: Define and manage multiple containers as a single application.
Declarative Configuration: Use a YAML file to define the services, networks, and volumes your application needs.
Portability: The same configuration can be used across different environments, ensuring consistency.
Orchestration: Start, stop, and rebuild services with a single command.
How Does Docker Compose Work?
Docker Compose works by reading the docker-compose.yml
file, which contains the configuration for your services. The file describes the services that make up your application, including their images, networks, volumes, and other configurations.
Here’s a breakdown of how Docker Compose works:
Define Services: Each service in your application is defined in the
docker-compose.yml
file. A service is typically one container, such as a web server, database, or worker process.Configure Networks and Volumes: Docker Compose allows you to define custom networks and volumes for your services, ensuring they can communicate and share data effectively.
Orchestrate the Application: With a single command, Docker Compose can build, start, or stop all the services defined in the configuration file. It can also handle service dependencies, ensuring that services start in the correct order.
Scaling: Docker Compose allows you to scale your services up or down with a single command, making it easy to manage applications that need to handle varying loads.
Step-by-Step Guide: Deploying a Flask Application Using Docker Compose on EC2
Now that we understand what Docker Compose is and how it works, let’s dive into a practical example: deploying a Flask application using Docker Compose on an Amazon EC2 instance.
Step 1: Launch an EC2 Instance and Connect
We will set up an AWS EC2 instance where the Docker container will be running.
Step 2: Install Docker and Docker Compose
Install Docker:
Follow these instructions to install Docker on your operating system ->
Install Docker Compose:
sudo curl -L "https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep -Po '"tag_name": "\K.*\d')/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose sudo chmod +x /usr/local/bin/docker-compose
Breaking Down the bash script:
Line 1:
sudo curl -L "https://github.com/docker/compose/releases/download/$(curl -s https://api.github.com/repos/docker/compose/releases/latest | grep -Po '"tag_name": "\K.*\d')/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo
: runs the command with superuser privileges.curl
: downloads a file from the specified URL.-L
: follows redirects (in case the URL redirects to another location).The URL is constructed dynamically using several commands:
curl -s
https://api.github.com/repos/docker/compose/releases/latest
: fetches the latest release information from the Docker Compose GitHub repository. The-s
flag silences the output.grep -Po '"tag_name": "\K.*\d'
: extracts the latest version number (e.g., "v2.3.4") from the JSON response using a Perl-compatible regular expression. The\K
syntax discards the matched text before the\K
, and.*\d
matches any characters followed by a digit.The resulting version number is used to construct the download URL.
docker-compose-$(uname -s)-$(uname -m)
: appends the platform-specific suffix to the download URL.uname -s
returns the operating system name (e.g., "Linux"), anduname -m
returns the machine architecture (e.g., "x86_64").
-o /usr/local/bin/docker-compose
: saves the downloaded file to/usr/local/bin/docker-compose
.
Line 2:
sudo chmod +x /usr/local/bin/docker-compose
sudo
: runs the command with superuser privileges.chmod
: changes the file permissions.+x
: adds execute permissions to the file./usr/local/bin/docker-compose
: specifies the file to modify.
In summary, this script downloads the latest version of Docker Compose from GitHub, saves it to /usr/local/bin/docker-compose
, and makes the file executable.
Step 3: Prepare Your Flask Application
Already prepared "A Simple Weather Application" Flask app - Check here for detailed blog.
We will deploy this application using docker-compose.
Step 4: Create a Dockerfile
Add a Dockerfile
to define how the Flask app should be containerized:
# Use an official Python image as a base
FROM python:3.9-slim
# Set the working directory in the container
WORKDIR /app
# Copy the requirements file
COPY requirements.txt .
# Install the dependencies
RUN pip install -r requirements.txt
# Copy the application code
COPY . .
# Load environment variables from .env file
RUN pip install python-dotenv
# Expose the port the app will run on
EXPOSE 5000
# Run the command to start the development server when the container launches
CMD ["flask", "run", "--host=0.0.0.0", "--port=5000"]
This Dockerfile builds a Python 3.9 image for a Flask app:
Uses
python:3.9-slim
as the base image.Sets the working directory to
/app
.Copies the
requirements.txt
file from the current directory into the container's working directory (/app
).Installs dependencies from
requirements.txt
.Copies the app code into the container.
Installs
python-dotenv
to load environment variables.Exposes port 5000 for the app.
Runs the Flask development server with
flask run
when the container launches.
Step 5: Create a Docker Compose File
Add a docker-compose.yml
file:
services:
web:
build: .
ports:
- "80:5000"
environment:
- API_KEY=${API_KEY}
This Docker Compose file defines and runs multi-container Docker applications. Here's a breakdown:
services: Top-level key defining a list of services.
web: Name of the web application service.
build:
Context:
.
(current directory).Uses the Dockerfile in the current directory.
ports:
"80:5000"
: Maps port 80 on the host to port 5000 in the container.Requests to
http://localhost:80
are forwarded to port 5000 in the container.
environment:
API_KEY=${API_KEY}
: Sets theAPI_KEY
environment variable inside the container.Uses the value of
API_KEY
from the host machine.
Summary:
Builds a Docker image using the Dockerfile in the current directory.
Maps port 80 on the host to port 5000 in the container.
Sets the
API_KEY
environment variable inside the container using the host'sAPI_KEY
value.
Step 6: Deploy the Flask Application Using Docker Compose
Transfer Files to EC2:
Use
scp
to transfer your files to the EC2 instance:scp -i "your-key-file.pem" -r /path/to/your/flask-app ec2-user@your-ec2-public-ip:/home/ec2-user/
You can use
git
to clone your repository:git clone https://github.com/your-username/your-repo.git
You can also use WinSCP to upload your project files:
Navigate to the Project Directory:
cd /home/ec2-user/your-flask-app-name
Build and Run the Application:
docker-compose up -d
Step 7: Access Your Flask Application
Open your browser and navigate to
http://your-ec2-public-ip
.
Common Web Services Defined in Docker Compose: Examples and Configurations
In Docker Compose, you can define various types of web services, each tailored to specific needs. Here are some common types and examples of web services you might define:
Web Application Servers:
These services run your web application code. Examples include:
Node.js/Express Server:
version: '3' services: web: image: node:14 working_dir: /app volumes: - .:/app command: node server.js ports: - "3000:3000"
Python/Flask Application:
version: '3' services: web: image: python:3.8 working_dir: /app volumes: - .:/app command: flask run --host=0.0.0.0 ports: - "5000:5000"
Reverse Proxies:
These services route requests to other services, often used for load balancing and SSL termination.
Nginx:
version: '3' services: web: image: nginx:latest ports: - "80:80" volumes: - ./nginx.conf:/etc/nginx/nginx.conf
Databases:
Services that provide persistent storage for your application data.
MySQL:
version: '3' services: db: image: mysql:5.7 environment: MYSQL_ROOT_PASSWORD: rootpassword MYSQL_DATABASE: mydatabase ports: - "3306:3306"
PostgreSQL:
version: '3' services: db: image: postgres:13 environment: POSTGRES_USER: user POSTGRES_PASSWORD: password POSTGRES_DB: mydatabase ports: - "5432:5432"
Cache Systems:
Services that cache data to improve performance.
Redis:
version: '3' services: cache: image: redis:latest ports: - "6379:6379"
Message Brokers:
Services that handle messaging and queuing for asynchronous communication.
RabbitMQ:
version: '3' services: message_broker: image: rabbitmq:3-management ports: - "5672:5672" - "15672:15672"
Background Workers:
Services that perform tasks in the background, often used for processing jobs or handling asynchronous tasks.
Celery (with Redis as a broker):
version: '3' services: worker: image: python:3.8 working_dir: /app volumes: - .:/app command: celery -A myapp worker --loglevel=info
Development Tools:
Services used for development and testing purposes.
Development Environment with Docker:
version: '3' services: dev: image: node:14 working_dir: /app volumes: - .:/app command: npm start ports: - "3000:3000"
Each service can be customized with different configurations such as volumes, networks, environment variables, and more, depending on the requirements of your application.
Conclusion
Docker Compose is a game-changer when it comes to managing multi-container Docker applications. By defining your services in a single docker-compose.yml
file, you can easily deploy, manage, and scale your applications. This guide walked you through the basics of Docker Compose and demonstrated how to deploy a Flask application on an EC2 instance. With these skills, you can now take on more complex applications and orchestrations with confidence. Happy deploying!