Home > OS >  How to separate environments of a project in docker and docker-compose
How to separate environments of a project in docker and docker-compose

Time:03-09

I have a couple of basic conceptual question regarding the environments of a webapp.

I am trying to build a dockerized Rails app, having: test, development, staging and production.

  1. My first question is, should the Dockerfile and docker-compose be the same for every environment?

The only thing that would change then would be that when I want to do the testing I pass the RAILS_ENV=test when creating a container, when I want to do the development I pass RAILS_ENV=development and so on.

Would this be the correct idea behind it? Or can they be different (in my case I am building nginx on production together with app and db but I have just a simple setup with only the app and db for testing and development)

  1. My second question is, when I pass the RAILS_ENV=test for example, should I do it on the Dockerfile (conditionally pass a different environment when building the image):
# Set environment
ARG BUILD_DEVELOPMENT
# if --build-arg BUILD_DEVELOPMENT=1, set RAILS_ENV to 'development' or set to null otherwise.
ENV RAILS_ENV=${BUILD_DEVELOPMENT: development}
# if RAILS_ENV is null, set it to 'production' (or leave as is otherwise).
ENV RAILS_ENV=${RAILS_ENV:-production}

Or keep the same image and pass the RAILS_ENV when doing the docker-compose ? :

docker-compose -f docker-compose.production.yml run rake db:create db:migrate  RAILS_ENV=production

Thank you!

CodePudding user response:

Should the Dockerfile be the same for every environment?

Yes. Build a single image and reuse it in all environments. Do not use Dockerfile ARG to pass in "is it production", or host names, or host-specific user IDs. Especially you should use an identical environment in your pre-production and production environments to avoid deploying an untested image.

Should docker-compose.yml be the same for every environment?

This is the main place you have to control deploy-time options like, for example, where your database is located, or what level of logs should be output. So it makes sense to have a separate Compose file per environment.

Compose supports multiple Compose files. You can use docker-compose -f ... multiple times to specify specific Compose files to use; or if you do not have that option then Compose will read both a docker-compose.yml and a docker-compose.override.yml. So you might have a base docker-compose.yml file that names the images to use

# docker-compose.yml
version: '3.8'
services:
  app:
    image: registry.example.com/app:${APP_TAG:-latest}

In the question you suggest a docker-compose.prod.yml. That can set $RAILS_ENV and point at your production database:

# docker-compose.prod.yml
services:
  app:
    environment:
      - RAILS_ENV=production
      - DB_HOST=db.example.com
      - DB_USERNAME=...
    # (but don't repeat image:)

You could separately have a docker-compose.dev.yml that launched a local database, and had instructions on how to build the image:

# docker-compose.dev.yaml
version: '3.8'
services:
  app:
    build: .
    environment:
      - RAILS_ENV=development
      - DB_HOST=db
      - DB_USERNAME=db
      - DB_PASSWORD=passw0rd
  db:
    image: postgres:14
    environment:
      - POSTGRES_USER=db
      - POSTGRES_PASSWORD=passw0rd
    volumes:
      - dbdata:/var/lib/postgresql/data
volumes:
  dbdata:

If you use the docker-compose -f option, you need to always mention both files you're using

docker-compose \
  -f docker-compose.yml \
  -f docker-compose.dev.yml \
  run app \
  rake db:migrate

You could also symlink docker-compose.override.yml to point at an environment-specific file, and then Compose would be able to find it by default.

ln -sf docker-compose.test.yml docker-compose.override.yml
docker-compose run app rspec
  • Related