318 lines
10 KiB
Markdown
318 lines
10 KiB
Markdown
|
|
# Docker Compose
|
||
|
|
|
||
|
|
Docker Compose is a powerful tool for defining and running multi-container Docker applications. With Compose, you use a YAML file to configure your application's services, networks, and volumes. Then, with a single command, you create and start all the services from your configuration.
|
||
|
|
|
||
|
|
> **Note**: Docker Compose is now integrated into Docker CLI. The new command is `docker compose` instead of `docker-compose`. We'll use the new command throughout this chapter.
|
||
|
|
|
||
|
|
## Key Benefits of Docker Compose
|
||
|
|
|
||
|
|
1. **Simplicity**: Define your entire application stack in a single file.
|
||
|
|
2. **Reproducibility**: Easily share and version control your application configuration.
|
||
|
|
3. **Scalability**: Simple commands to scale services up or down.
|
||
|
|
4. **Environment Consistency**: Ensure development, staging, and production environments are identical.
|
||
|
|
5. **Workflow Improvement**: Compose can be used throughout the development cycle for testing, staging, and production.
|
||
|
|
|
||
|
|
## The docker-compose.yml File
|
||
|
|
|
||
|
|
The `docker-compose.yml` file is the core of Docker Compose. It defines all the components and configurations of your application. Here's a basic example:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
web:
|
||
|
|
build: .
|
||
|
|
ports:
|
||
|
|
- "5000:5000"
|
||
|
|
volumes:
|
||
|
|
- .:/code
|
||
|
|
environment:
|
||
|
|
FLASK_ENV: development
|
||
|
|
redis:
|
||
|
|
image: "redis:alpine"
|
||
|
|
```
|
||
|
|
|
||
|
|
Let's break down this example:
|
||
|
|
|
||
|
|
- `version`: Specifies the Compose file format version.
|
||
|
|
- `services`: Defines the containers that make up your app.
|
||
|
|
- `web`: A service based on an image built from the Dockerfile in the current directory.
|
||
|
|
- `redis`: A service using the public Redis image.
|
||
|
|
|
||
|
|
## Key Concepts in Docker Compose
|
||
|
|
|
||
|
|
1. **Services**: Containers that make up your application.
|
||
|
|
2. **Networks**: How your services communicate with each other.
|
||
|
|
3. **Volumes**: Where your services store and access data.
|
||
|
|
|
||
|
|
## Basic Docker Compose Commands
|
||
|
|
|
||
|
|
- `docker compose up`: Create and start containers
|
||
|
|
```bash
|
||
|
|
docker compose up -d # Run in detached mode
|
||
|
|
```
|
||
|
|
|
||
|
|
- `docker compose down`: Stop and remove containers, networks, images, and volumes
|
||
|
|
```bash
|
||
|
|
docker compose down --volumes # Also remove volumes
|
||
|
|
```
|
||
|
|
|
||
|
|
- `docker compose ps`: List containers
|
||
|
|
- `docker compose logs`: View output from containers
|
||
|
|
```bash
|
||
|
|
docker compose logs -f web # Follow logs for the web service
|
||
|
|
```
|
||
|
|
|
||
|
|
## Advanced Docker Compose Features
|
||
|
|
|
||
|
|
### 1. Environment Variables
|
||
|
|
|
||
|
|
You can use .env files or set them directly in the compose file:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
web:
|
||
|
|
image: "webapp:${TAG}"
|
||
|
|
environment:
|
||
|
|
- DEBUG=1
|
||
|
|
```
|
||
|
|
|
||
|
|
### 2. Extending Services
|
||
|
|
|
||
|
|
Use `extends` to share common configurations:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
web:
|
||
|
|
extends:
|
||
|
|
file: common-services.yml
|
||
|
|
service: webapp
|
||
|
|
```
|
||
|
|
|
||
|
|
### 3. Healthchecks
|
||
|
|
|
||
|
|
Ensure services are ready before starting dependent services:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
web:
|
||
|
|
image: "webapp:latest"
|
||
|
|
healthcheck:
|
||
|
|
test: ["CMD", "curl", "-f", "http://localhost"]
|
||
|
|
interval: 1m30s
|
||
|
|
timeout: 10s
|
||
|
|
retries: 3
|
||
|
|
start_period: 40s
|
||
|
|
```
|
||
|
|
|
||
|
|
|
||
|
|
## Practical Examples
|
||
|
|
|
||
|
|
### Example 1: WordPress with MySQL
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
db:
|
||
|
|
image: mysql:5.7
|
||
|
|
volumes:
|
||
|
|
- db_data:/var/lib/mysql
|
||
|
|
restart: always
|
||
|
|
environment:
|
||
|
|
MYSQL_ROOT_PASSWORD: somewordpress
|
||
|
|
MYSQL_DATABASE: wordpress
|
||
|
|
MYSQL_USER: wordpress
|
||
|
|
MYSQL_PASSWORD: wordpress
|
||
|
|
|
||
|
|
wordpress:
|
||
|
|
depends_on:
|
||
|
|
- db
|
||
|
|
image: wordpress:latest
|
||
|
|
ports:
|
||
|
|
- "8000:80"
|
||
|
|
restart: always
|
||
|
|
environment:
|
||
|
|
WORDPRESS_DB_HOST: db:3306
|
||
|
|
WORDPRESS_DB_USER: wordpress
|
||
|
|
WORDPRESS_DB_PASSWORD: wordpress
|
||
|
|
WORDPRESS_DB_NAME: wordpress
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
db_data: {}
|
||
|
|
```
|
||
|
|
|
||
|
|
Let's break this down in detail:
|
||
|
|
|
||
|
|
1. **Version**:
|
||
|
|
`version: '3.8'` specifies the version of the Compose file format. Version 3.8 is compatible with Docker Engine 19.03.0+.
|
||
|
|
|
||
|
|
2. **Services**:
|
||
|
|
We define two services: `db` and `wordpress`.
|
||
|
|
|
||
|
|
a. **db service**:
|
||
|
|
- `image: mysql:5.7`: Uses the official MySQL 5.7 image.
|
||
|
|
- `volumes`: Creates a named volume `db_data` and mounts it to `/var/lib/mysql` in the container. This ensures that the database data persists even if the container is removed.
|
||
|
|
- `restart: always`: Ensures that the container always restarts if it stops.
|
||
|
|
- `environment`: Sets up the MySQL environment variables:
|
||
|
|
- `MYSQL_ROOT_PASSWORD`: Sets the root password for MySQL.
|
||
|
|
- `MYSQL_DATABASE`: Creates a database named "wordpress".
|
||
|
|
- `MYSQL_USER` and `MYSQL_PASSWORD`: Creates a new user with the specified password.
|
||
|
|
|
||
|
|
b. **wordpress service**:
|
||
|
|
- `depends_on`: Ensures that the `db` service is started before the `wordpress` service.
|
||
|
|
- `image: wordpress:latest`: Uses the latest official WordPress image.
|
||
|
|
- `ports`: Maps port 8000 on the host to port 80 in the container, where WordPress runs.
|
||
|
|
- `restart: always`: Ensures the container always restarts if it stops.
|
||
|
|
- `environment`: Sets up WordPress environment variables:
|
||
|
|
- `WORDPRESS_DB_HOST`: Specifies the database host. Note the use of `db:3306`, where `db` is the service name of our MySQL container.
|
||
|
|
- `WORDPRESS_DB_USER`, `WORDPRESS_DB_PASSWORD`, `WORDPRESS_DB_NAME`: These match the MySQL settings we defined in the `db` service.
|
||
|
|
|
||
|
|
3. **Volumes**:
|
||
|
|
`db_data: {}`: This creates a named volume that Docker manages. It's used to persist the MySQL data.
|
||
|
|
|
||
|
|
To run this setup:
|
||
|
|
|
||
|
|
1. Save the above YAML in a file named `docker-compose.yml`.
|
||
|
|
2. In the same directory, run `docker compose up -d`.
|
||
|
|
3. Once the containers are running, you can access WordPress by navigating to `http://localhost:8000` in your web browser.
|
||
|
|
|
||
|
|
This setup provides a complete WordPress environment with a MySQL database, all configured and ready to use. The use of environment variables and volumes ensures that the setup is both flexible and persistent.
|
||
|
|
|
||
|
|
### Example 2: Flask App with Redis and Nginx
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
flask:
|
||
|
|
build: ./flask
|
||
|
|
environment:
|
||
|
|
- FLASK_ENV=development
|
||
|
|
volumes:
|
||
|
|
- ./flask:/code
|
||
|
|
|
||
|
|
redis:
|
||
|
|
image: "redis:alpine"
|
||
|
|
|
||
|
|
nginx:
|
||
|
|
image: "nginx:alpine"
|
||
|
|
volumes:
|
||
|
|
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||
|
|
ports:
|
||
|
|
- "80:80"
|
||
|
|
depends_on:
|
||
|
|
- flask
|
||
|
|
|
||
|
|
networks:
|
||
|
|
frontend:
|
||
|
|
backend:
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
db-data:
|
||
|
|
```
|
||
|
|
|
||
|
|
Let's break this down:
|
||
|
|
|
||
|
|
1. **Version**:
|
||
|
|
As before, we're using version 3.8 of the Compose file format.
|
||
|
|
|
||
|
|
2. **Services**:
|
||
|
|
We define three services: `flask`, `redis`, and `nginx`.
|
||
|
|
|
||
|
|
a. **flask service**:
|
||
|
|
- `build: ./flask`: This tells Docker to build an image using the Dockerfile in the `./flask` directory.
|
||
|
|
- `environment`: Sets `FLASK_ENV=development`, which enables debug mode in Flask.
|
||
|
|
- `volumes`: Mounts the local `./flask` directory to `/code` in the container. This is useful for development as it allows you to make changes to your code without rebuilding the container.
|
||
|
|
|
||
|
|
b. **redis service**:
|
||
|
|
- `image: "redis:alpine"`: Uses the official Redis image based on Alpine Linux, which is lightweight.
|
||
|
|
|
||
|
|
c. **nginx service**:
|
||
|
|
- `image: "nginx:alpine"`: Uses the official Nginx image based on Alpine Linux.
|
||
|
|
- `volumes`: Mounts a local `nginx.conf` file to `/etc/nginx/nginx.conf` in the container. The `:ro` flag makes it read-only.
|
||
|
|
- `ports`: Maps port 80 on the host to port 80 in the container.
|
||
|
|
- `depends_on`: Ensures that the `flask` service is started before Nginx.
|
||
|
|
|
||
|
|
3. **Networks**:
|
||
|
|
We define two networks: `frontend` and `backend`. This allows us to isolate our services. For example, we could put Nginx and Flask on the frontend network, and Flask and Redis on the backend network.
|
||
|
|
|
||
|
|
4. **Volumes**:
|
||
|
|
`db-data`: This creates a named volume. Although it's not used in this configuration, it's available if we need persistent storage, perhaps for a database service we might add later.
|
||
|
|
|
||
|
|
To use this setup:
|
||
|
|
|
||
|
|
1. You need a Flask application in a directory named `flask`, with a Dockerfile to build it.
|
||
|
|
2. You need an `nginx.conf` file in the same directory as your `docker-compose.yml`.
|
||
|
|
3. Run `docker compose up -d` to start the services.
|
||
|
|
|
||
|
|
This configuration sets up a Flask application server, with Redis available for caching or as a message broker, and Nginx as a reverse proxy. The Flask code is mounted as a volume, allowing for easy development. Nginx handles incoming requests and forwards them to the Flask application.
|
||
|
|
|
||
|
|
The use of Alpine-based images for Redis and Nginx helps to keep the overall image size small, which is beneficial for deployment and scaling.
|
||
|
|
|
||
|
|
This setup is particularly useful for developing and testing a Flask application in an environment that closely mimics production, with a proper web server (Nginx) in front of the application server (Flask) and a caching/messaging system (Redis) available.
|
||
|
|
|
||
|
|
## Best Practices for Docker Compose
|
||
|
|
|
||
|
|
1. Use version control for your docker-compose.yml file.
|
||
|
|
2. Keep development, staging, and production environments as similar as possible.
|
||
|
|
3. Use build arguments and environment variables for flexibility.
|
||
|
|
4. Leverage healthchecks to ensure service dependencies are met.
|
||
|
|
5. Use `.env` files for environment-specific variables.
|
||
|
|
6. Optimize your images to keep them small and efficient.
|
||
|
|
7. Use docker-compose.override.yml for local development settings.
|
||
|
|
|
||
|
|
## Scaling Services
|
||
|
|
|
||
|
|
Docker Compose can scale services with a single command:
|
||
|
|
|
||
|
|
```bash
|
||
|
|
docker compose up -d --scale web=3
|
||
|
|
```
|
||
|
|
|
||
|
|
This command would start 3 instances of the `web` service.
|
||
|
|
|
||
|
|
## Networking in Docker Compose
|
||
|
|
|
||
|
|
By default, Compose sets up a single network for your app. Each container for a service joins the default network and is both reachable by other containers on that network, and discoverable by them at a hostname identical to the container name.
|
||
|
|
|
||
|
|
You can also specify custom networks:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
web:
|
||
|
|
networks:
|
||
|
|
- frontend
|
||
|
|
- backend
|
||
|
|
db:
|
||
|
|
networks:
|
||
|
|
- backend
|
||
|
|
|
||
|
|
networks:
|
||
|
|
frontend:
|
||
|
|
backend:
|
||
|
|
```
|
||
|
|
|
||
|
|
## Volumes in Docker Compose
|
||
|
|
|
||
|
|
Compose also lets you create named volumes that can be reused across multiple services:
|
||
|
|
|
||
|
|
```yaml
|
||
|
|
version: '3.8'
|
||
|
|
services:
|
||
|
|
db:
|
||
|
|
image: postgres
|
||
|
|
volumes:
|
||
|
|
- data:/var/lib/postgresql/data
|
||
|
|
|
||
|
|
volumes:
|
||
|
|
data:
|
||
|
|
```
|
||
|
|
|
||
|
|
## Conclusion
|
||
|
|
|
||
|
|
Docker Compose simplifies the process of managing multi-container applications, making it an essential tool for developers working with Docker. By mastering Docker Compose, you can streamline your development workflow, ensure consistency across different environments, and easily manage complex applications with multiple interconnected services.
|
||
|
|
|
||
|
|
Remember to always use the latest `docker compose` command instead of the older `docker-compose`, as it's now integrated directly into Docker CLI and offers improved functionality and performance.
|