Common docker compose setup

Usually when I start with development of new app, I first create docker-compose.yaml file to spin up services I need:

  • 2 instances of database; one for app and one for integration tests.
  • frontend - node
  • backend - e.g. python
  • redis, mailhog, … whatever I need

Usually I pick bullseye tag because there is no need to save on resources from the beginning if not needed. It might just slow down my development process.

docker-compose

Database

Notice the difference from db-test and db in tmpfs. Mysql dockerfile is designed to write data in /var/lib/mysql. If I set this folder as tmpfs it means it will live in memory which is faster and data is lost on app shutdown. Perfect for tests!

Sometimes I add a healthcheck to db so the backend app can be started only after db is ready with service_healthy predicate.

Setting logging driver to none helps reducing spamming the terminal, but only after database has fully started.

To provision database on startup you can map volume /docker-entrypoint-initdb.d/ to your folder where you hold some provision.sql like files

Backend

The important here is not to forget mapping volume for source files and also for installed packages. Otherwise you will need to install packages every time you restart your docker. For python you need /pip-lib and /root/.cache/pip. For GOlang you can define GOPATH and GOCACHE variable where modules will be saved, but for node you don’t need anything as dependencies are by default loaded alongside your app in node_modules.

Here is full docker-compose example.

version: '3'
services:
  db-test:
    image: "mysql:8.0.30"
    restart: always
    environment:
      - MYSQL_DATABASE=${DATABASE_NAME:-blog}
      - MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-root}
    tmpfs:
      - /var/lib/mysql:rw,noexec,nosuid,size=1000m
      - /tmp/:rw,noexec,nosuid,size=128m
    volumes:
      - ${APP_ROOT_DIR:-.}/db/conf.d/:/etc/mysql/conf.d/:cached
      - ${APP_ROOT_DIR:-.}/db/initdb.d/:/docker-entrypoint-initdb.d/:ro,cached
    ports:
      - "3307:3306"
    logging:
      driver: none
  db:
    image: "mysql:8.0.30"
    restart: always
    environment:
      - MYSQL_DATABASE=${DATABASE_NAME:-blog}
      - MYSQL_ROOT_PASSWORD=${DB_PASSWORD:-root}
    volumes:
      - ${APP_ROOT_DIR:-.}/db/conf.d/:/etc/mysql/conf.d/:cached
      - ${APP_ROOT_DIR:-.}/db/initdb.d/:/docker-entrypoint-initdb.d/:ro,cached
    logging:
      driver: none
    healthcheck:
      test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
      timeout: 20s
      retries: 10
    tmpfs:
      - /tmp/:rw,noexec,nosuid,size=128m
    ports:
      - "3308:3306"
  mailhog:
    image: mailhog/mailhog:v1.0.1
    environment:
      - MH_SMTP_BIND_ADDR=0.0.0.0:25
    ports:
      - '25800:8025'
      - '25000:25'
    expose:
      - '25'
  app:
    image: python:3.10.6-bullseye
    volumes:
      - ${APP_ROOT_DIR:-.}/:/blog:cached
      - ${APP_ROOT_DIR:-.}/lib:/pip-lib
      - ${APP_ROOT_DIR:-.}/pip-cache:/root/.cache/pip
    environment:
      - HOST=db
      - USER=root
      - PASSWORD=root
      - DATABASE=blog
      - PORT=3306
      - HOST_TEST=db-test
      - DATABASE_TEST=blog
      - USER_TEST=root
      - PASSWORD_TEST=root
      - PORT_TEST=3306
    working_dir: /blog/src
    entrypoint: [ "tail", "-f", "/dev/null" ]
    depends_on:
      - db:
          condition: service_healthy
      - db-test
    ports:
      - "8800:8800"
  frontend:
    image: node:lts-bullseye
    volumes:
      - ${APP_ROOT_DIR:-.}/frontend:/frontend
    working_dir: /frontend/blog
    entrypoint: [ "tail", "-f", "/dev/null" ]
    depends_on:
      - app
    ports:
      - "8880:8880"
comments powered by Disqus