Retro on "Docker Compose for Developers"
Tags: docker, compose, devops
Published: 2024-06-23 07:38:47

The "Docker Compose for Developers" course provided a comprehensive exploration of advanced Docker tools. It focused on simplifying workflows with Docker-Compose and scaling clusters with Docker Swarm. Here, we provide a summary of key concepts and an example exercise.

### Course Overview

**Docker-Compose Explanation**:

- Docker-compose allows combining and running multiple related containers with a single command.
- All application dependencies are defined in a single `docker-compose.yml` file, executed with `docker-compose up`.

**Working with Multiple Dockerfiles**:

- **Use Cases**:
    - Microservices applications.
    - Different environments (development, production).
- **Default Behavior**:
    - Docker-compose looks for a file named `Dockerfile`.
- **Override Default**:
    - Use `dockerfile: 'custom-name'` in the build section.
- **Example**:
    - Place `Dockerfile-db` in the `db` folder.
    - Modify `docker-compose.yml`:
        
        ```yaml
        build:
          context: ./db
          dockerfile: Dockerfile-db
        ```
        

**Directives**:

- `context`: Directory of the Dockerfile relative to `docker-compose.yml`.
- `dockerfile`: Name of the alternate Dockerfile.

**Environment Variables with Docker-compose**:

- Avoid hardcoding credentials in the code.
- Use environment variables for managing credentials.
- **Accessing Environment Variables**:
    - **Using** `.env` file:
        - Store variables in a hidden `.env` file (not pushed to code).
        - Use `${}` syntax in `docker-compose.yml`.
        - `.env` file should be in the same folder as `docker-compose.yml`.
    - **Using** `env_file`:
        - Use `env_file` keyword in `docker-compose.yml` instead of `environment`.
        - Specify `.env` file location with `env_file: - ./.env`.
        - `.env` file does not need to be in the same directory as `docker-compose.yml`.

### Example Exercise

**Problem Statement**: In this exercise, write a `docker-compose.yml` file to automate the deployment of two services: a web application and a MySQL database.

<img width="100%" style="width:100%" src="https://i.giphy.com/media/v1.Y2lkPTc5MGI3NjExdjM1YjZzZXY2ODZybGoycmwyd29zMW5ocHZyZ2wzcTc0eGRyeHh2aiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/26n7bfS3Awhpe9CrC/giphy.gif">

**Solution**:

**docker-compose.yml**:

```bash
version: '3.8'  # Specifies the version of Docker Compose

services:
  web:
    build:  # Instructions for building the web service container
      context: .  # Use the current directory as the build context
      dockerfile: Dockerfile  # Specify the Dockerfile to use for the build
    ports:
      - "5000:5000"  # Map port 5000 on the host to port 5000 on the container
    environment:  # Environment variables for the web service
      - MYSQL_HOST=db  # Hostname for the MySQL service
      - MYSQL_USER=root  # MySQL username
      - MYSQL_PASSWORD=example  # MySQL password
      - MYSQL_DB=testdb  # MySQL database name

  db:
    image: mysql:5.7  # Use the MySQL 5.7 image from Docker Hub
    environment:  # Environment variables for the MySQL service
      MYSQL_ROOT_PASSWORD: example  # Root password for MySQL
      MYSQL_DATABASE: testdb  # Database name to create in MySQL

```

[**app.py**](http://app.py/):

```bash
from flask import Flask
import MySQLdb  # Import the MySQLdb module to connect to MySQL

app = Flask(__name__)  # Initialize the Flask application

@app.route('/')  # Define a route for the root URL
def hello():
    # Connect to the MySQL database
    db = MySQLdb.connect(
        host="db",  # Hostname of the MySQL service as defined in docker-compose.yml
        user="root",  # MySQL username
        passwd="example",  # MySQL password
        db="testdb"  # Name of the MySQL database
    )
    cursor = db.cursor()  # Create a cursor object to interact with the database
    cursor.execute("SELECT VERSION()")  # Execute a query to get the MySQL version
    data = cursor.fetchone()  # Fetch the result of the query
    return f"MySQL version: {data}"  # Return the MySQL version as a response

if __name__ == "__main__":
    app.run(host='0.0.0.0')  # Run the Flask app, making it accessible from outside the container

```

**Dockerfile**:

```bash
FROM python:3.8-slim  # Use the slim version of Python 3.8 as the base image

WORKDIR /app  # Set the working directory in the container to /app

COPY app.py /app  # Copy the app.py file from the current directory to /app in the container

RUN pip install flask mysqlclient  # Install Flask and mysqlclient using pip

CMD ["python", "app.py"]  # Specify the command to run the Flask app
```

### Conclusion

The "Docker Compose for Developers" course has provided a thorough understanding of how to effectively use Docker Compose to manage multi-container applications. By mastering the use of `docker-compose.yml` files, handling multiple Dockerfiles, and securely managing environment variables, developers can streamline their workflows and enhance the scalability of their applications. The example exercise included in this retrospective serves as a practical application of these concepts, reinforcing the knowledge gained throughout the course. As you continue to work with Docker Compose, these skills will be invaluable in creating efficient, scalable, and maintainable containerized applications.