I’ve frequently had projects that have something like this…
- Dockerfile-dev - creates the base environment, but doesn’t copy in source, do a build, etc. Just gets the env setup.
- Dockerfile - creates the base environment AND copies source, does a build, etc. Builds the entire project.
What I often find is the step to “create the base environment” is the same between the two files. As we all know…
Why? Update the environment in one Dockerfile and oops! Forgot to update the other Dockerfile. Bad things happen!
Using Multi-Stage Builds
Multi-stage builds have been around for a while now. By default, the last image is the output of the build and is the tagged image. But, with the
--target flag, you can completely change that! So, imagine having a stage that creates the base environment and then another one that copies source, does the build, etc. For dev, you simply set the target to the base environment, while the prod build does the entire build. Cool, huh?!?
Show me an example!
For this example, I’m just going to do a very basic PHP app (source available here). Here’s what we’ll do:
- Create a single Dockerfile
- In the “base” stage (parent of
php:7.2-apache), we’ll setup the env that both dev and prod use
- Create a second stage, that builds from
baseand adds the production image build steps
- Update the
docker-compose.ymlfile to target the
basestage for local dev
FROM php:7.2-apache as base RUN docker-php-ext-install mysqli # Start from the base we just created FROM base COPY src/ /var/www/html/ HEALTHCHECK --interval=5s --timeout=5s CMD curl -f localhost/healthcheck.php || exit 1
And here is the updated
docker-compose.yml file, adding the
target flag for the build.
version: '3.6' services: app: build: context: . target: base ports: - 80:80 volumes: - ./src:/var/www/html db: build: ./docker/db/
Now, when I spin up the dev environment (using
docker-compose up), it’ll use the same Dockerfile as prod, but stop at the base stage. Then, when I want to build for prod, I simply exclude the target and the full production image will be used.
And there you go! Have any questions/thoughts? Let me know below!