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.
- 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)
- 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