Skip to content

Task 7: Docker Compose with To Do App

In this section, we will learn how to use Docker Compose to manage multi-container applications (frontend, backend, database).

Project Challange

Deskripsi: Sebuah startup ingin membangun aplikasi berbasis web dengan arsitektur berikut:

  1. Frontend: Menggunakan Nginx.
  2. Backend: API sederhana berbasis Python.
  3. Database: PostgreSQL untuk menyimpan data aplikasi.
  4. Networking: Backend dan database harus saling terhubung dengan jaringan.
  5. Volume: Database PostgreSQL memerlukan penyimpanan data yang persisten.

Tugas Peserta:

  1. Buat Docker Compose file untuk menjalankan semua service.
  2. Pastikan Nginx dapat diakses melalui browser.
  3. Gunakan Docker Volume untuk menyimpan data PostgreSQL.
  4. Pastikan semua container saling terhubung melalui jaringan.

Notes

Ada beberapa hal yang saya ganti dari project challange ini namun tanpa mengurangi esensi dari project ini. Yaitu:

  1. Frontend: Saya menggunakan html+css+js sederhana untuk frontend yang akan berinteraksi dengan RestAPI.
  2. Backend: Saya menggunakan Rust sebagai backendnya. Saya menggunakan actix-web sebagai frameworknya dan projectnya mengenai todo app sederhana.

Source Code

Full source code bisa dilihat di Github

0. Architecture

architecture

architecture of the project. source: my own

1. Docker Compose Explanation

Here, i will just explain the docker-compose.yml file that i created for this project. We have 3 services in this docker-compose file:

  1. PostgreSQL: The database service.
  2. App: The backend service.
  3. Frontend: The frontend service.
service: postgres
postgres:
    image: postgres:16-alpine
    container_name: todo_app_postgres
    environment:
        POSTGRES_USER: postgres
        POSTGRES_PASSWORD: postgres
        POSTGRES_DB: todo_db
    ports:
        - "5432:5432" # (1)
    volumes:
        - ./tmp/postgres:/var/lib/postgresql/data # (2)
    restart: unless-stopped # (3)
    networks:
        - app-network # (4)
  1. Expose PostgreSQL Port: Port 5432 of the PostgreSQL container is exposed to the host machine, allowing external access to the database.
  2. Persistent Storage: The PostgreSQL data is stored in a local directory (./tmp/postgres) to ensure data persistence even if the container is removed. It means also, if we made some changes it will be saved in the local directory, also if we do some changes in the local directory, it will be reflected in the container. Be careful!
  3. Restart Policy: The container will restart-unless it is explicitly stopped, ensuring high availability.
  4. Network Configuration: The PostgreSQL container is connected to a custom bridge network (app-network), allowing communication with other containers.
service: app
app:
    image: todo-app:latest
    build:
        context: ./todo-app
        dockerfile: Dockerfile
    ports:
        - "8080:8080"
    environment:
        - DATABASE_URL=postgres://postgres:postgres@postgres:5432/todo_db # (1)
    depends_on:
        - postgres # (2)
    networks:
        - app-network
  1. Environment Variables: The database connection string is passed as an environment variable to the application container. The url like this: postgres://postgres:postgres@postgres:5432/todo_db is the connection string to connect to the PostgreSQL database. The format is like this: postgres://<username>:<password>@<hostname>:<port>/<database_name>. The hostname is the name of the service in the docker-compose file, in this case, it is postgres.
  2. Service Dependency: The application container depends on the PostgreSQL container, ensuring that the database is started before the application.
service: frontend
frontend:
    image: node:18
    working_dir: /app
    volumes:
        - ./frontend:/app 
    command: npx http-server -p 8081  # (1)
    ports:
        - "8081:8081"
    depends_on:
        - app # (2)
    networks:
        - app-network
  1. Frontend Service: A simple Node.js server is used to serve the frontend files, allowing access to the web application.
  2. Service Dependency: The frontend container depends on the application container, ensuring that the backend is started before the frontend.
networks
networks:
    app-network:
        driver: bridge

A custom bridge network is created with name app-network to allow communication between containers. Bridge networks are the default network type in Docker, allowing containers to communicate with each other. So we can use the service name as the hostname to connect to other services.

Here is the full docker-compose.yml file:

docker-compose.yml
version: '3.8' # version of docker-compose file format

services:
    postgres:
        image: postgres:16-alpine
        container_name: todo_app_postgres
        environment:
            POSTGRES_USER: postgres
            POSTGRES_PASSWORD: postgres
            POSTGRES_DB: todo_db
        ports:
            - "5432:5432"
        volumes:
            - ./tmp/postgres:/var/lib/postgresql/data
        restart: unless-stopped
        networks:
            - app-network

    app:
        image: todo-app:latest
        build:
            context: ./todo-app
            dockerfile: Dockerfile
        ports:
            - "8080:8080"
        environment:
            - DATABASE_URL=postgres://postgres:postgres@postgres:5432/todo_db
        depends_on:
            - postgres
        networks:
            - app-network

    frontend:
        image: node:18
        working_dir: /app
        volumes:
            - ./frontend:/app 
        command: npx http-server -p 8081
        ports:
            - "8081:8081"
        depends_on:
            - app
        networks:
            - app-network

networks:
    app-network:
        driver: bridge

2. Run the Docker Compose

docker-compose really makes our life easier. to run this docker-compose file, just run this command:

# running project
docker-compose up -d --build

The container will be build by pull each image from the docker hub and build the image from the Dockerfile in the todo-app directory.

  • -d flag is used to run the containers in detached mode, allowing them to run in the background.
  • --build flag is used to build the images before starting the containers.
# to stop the docker-compose
docker-compose down

3. Result

3.1 Frontend

frontend

logs of the frontend container. source: my own

frontend

3.2 Backend

frontend

logs of the backend container. source: my own

frontend

3.3 Database

frontend

logs of the database container. source: my own

frontend

4. You can do it too!

git clone https://github.com/agfianf/todo-app-rust
cd todo-app-rust
docker-compose up -d --build

Notes

if you find this useful, please give me a ⭐ on my repo

Thanks!

Comments