Docker Compose | Portability from the Start
Tags: programming, devops, docker, tutorial
Published: 2021-11-19 19:46:55

**Original Source:** https://shipyard.build/blog/docker-compose-portability-from-the-start/

---

[Docker](https://www.docker.com/) 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.

## What is 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](https://github.com/shipyard/react-flask-starter) app (along with a few Shipyard metadata labels)!

```yaml
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](https://shipyard.build/) 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](https://docs.docker.com/compose/compose-file/) and [guide](https://docs.docker.com/compose/) for a full tour of features. And check out our starter repos at [github.com/shipyard](https://github.com/shipyard) for examples of containerizing modern frameworks.

{% tag productivity %} {% tag devops %} {% tag docker %}