preloader

Docker Compose: Portability From The Start

Build powerful multi-container apps with ease using Docker Compose

/images/blog/cover-images/docker-compose-portability.png
docker-compose-portability-from-the-start

by on

Docker has quickly become an essential platform for application containerization. By empowering developers to rapidly deploy apps and host them in the cloud, Docker has simplified the dev cycle by expediting the process of building scalable, modern applications.

Docker Compose

Docker Compose is a powerful tool for “code-to-cloud” development. It allows developers to define how to retrieve, build and run multiple containers simultaneously, all defined within a single YAML file (docker-compose.yaml). Let’s check out some cases where Compose can simplify app development.

Compose for local development

Containers accelerate development by eliminating the need to install and manage dependencies locally. This allows for a “plug and play” approach to the dev cycle — applications can run on any major OS (including cloud hosts), as they come prepackaged with everything they need to run independently. All developers need to install is Docker.

Docker Compose takes the convenience of containers one step further, by streamlining each service’s building and runtime config into a single process. With Compose, it’s as simple as:

  • Define how to build your app’s services with a Dockerfile
  • Define how to run your app’s services in the docker-compose.yaml
  • Build and run your app with docker compose up

Compose also allows devs to configure mount volumes (basically directories where data persists) and port mappings (to forward local traffic to the containers).

Here’s an example Compose file for a full-stack React, Flask, and Postgres app (along with a few Shipyard metadata labels)!

version: '3'

services:

  frontend:
    labels:
      shipyard.route: '/'
    build: 'frontend'
    environment:
      CI: 'true'
      DANGEROUSLY_DISABLE_HOST_CHECK: 'true'
    env_file:
      - frontend/frontend.env
    volumes:
      - './frontend/src:/app/src'
      - './frontend/public:/app/public'
    ports:
      - '3000:3000'

  backend:
    labels:
      shipyard.route: '/api'
    build: 'backend'
    environment:
      DATABASE_URL: 'postgres://obscure-user:obscure-password@postgres/app'
      DEV: ${DEV}
      FLASK_DEBUG: '1'
    volumes:
      - './backend/filesystem/entrypoints:/entrypoints:ro'
      - './backend/migrations:/srv/migrations'
      - './backend/src:/srv/src:ro'
    ports:
      - '8080:8080'

  worker:
    labels:
      shipyard.init: 'poetry run flask db upgrade'
    build: 'backend'
    environment:
      DATABASE_URL: 'postgres://obscure-user:obscure-password@postgres/app'
      DEV: ${DEV}
      FLASK_DEBUG: '1'
      LOCALSTACK_HOST: 'localstack'
    command: '/entrypoints/worker.sh'
    volumes:
      - './backend/filesystem/entrypoints:/entrypoints:ro'
      - './backend/migrations:/srv/migrations'
      - './backend/src:/srv/src:ro'

  postgres:
    image: 'postgres:9.6-alpine'
    environment:
      POSTGRES_USER: 'obscure-user'
      POSTGRES_PASSWORD: 'obscure-password'
      POSTGRES_DB: 'app'
      PGDATA: '/var/lib/postgresql/data/pgdata'
    volumes:
      - 'postgres:/var/lib/postgresql/data'
    ports:
      - '5432'

  redis:
    image: 'redis:5.0-alpine'
    ports:
      - '6379'

volumes:
  postgres:

Compose for automated testing

Most modern software development uses the trunk-based model, involving small, frequent changes to a codebase and automated post-commit tests.

As microservices become more common, applications consist of more integrations than ever before. This calls for continuous testing with every new commit, which can become time and resource intensive.

Unit-testing with Compose is pretty straightforward, while integration and end-to-end (E2E) testing tend to be more complex. These types of automated testing require a number of services, which often need to be modified in order to replicate a production environment.

Many of the features that make Compose stand out for local development are also useful for automated testing. Compose can quickly and efficiently spin up and configure full-stack environments for automated testing (which your DevOps engineers appreciate). This allows for executing tests in a reliable and repeatable manner.

Compose is also valuable for testing database integrations. Because containers are ephemeral by nature, we can choose to start with a fresh full database each test, with the option to easily seed it with the same data. This eliminates the possibility of remnant or corrupt data causing false positives/negatives.

In most cases, the same Compose file can be used for both local development and remote testing environments. But if there are differences in how the environments run, you can put the few changes you need in a second Compose file and override the general Compose file like so:

docker-compose -f docker-compose.yml -f docker-compose.test.yml up -d

Compose for cloud deployments

Compose is a format that is being adopted natively by clouds as a valid format to define your application. In particular, Shipyard supports Compose as a first-class citizen. With just the single Compose file, users get ephemeral environments for all their pull requests, and one-click deploys to more long-lived QA, staging, and production environments!

Conclusion

Docker Compose is an essential tool for container development and deployment. Check out the Compose file reference and guide for a full tour of features. And check out our starter repos at github.com/shipyard for examples of containerizing modern frameworks.

Thanks for reading, and good luck with your deployments!

Try Shipyard today

Get isolated, full-stack ephemeral environments on every PR.

What is Shipyard?

Shipyard is the Ephemeral Environment Self-Service Platform.

Automated review environments on every pull request for Developers, Product, and QA teams.

Stay connected

Latest Articles