Reputation: 7502
I am trying to build a Dockerfile that builds my project for multiple base images (e.g., Ubuntu 17.10 for multiple architectures)
I want to be able to write something like
FROM {{ ARCH }}/ubuntu:17.10
...
And have it resolve at build time to ubuntu:17.10
or ppc64le/ubuntu:17.10
. Is there someway to do this?
Additionally a separate solution might be to have two docker files, but somehow include the common portions of the file into both the docker files.
Upvotes: 3
Views: 2872
Reputation: 4654
TL;DR - Unfortunately this is not supported by Docker out-of-the box. It means, you need to do the stuff by yourself.
There are several libraries written especially for this task:
dockerfile-template allows you to write stuff like that in Dockerfile
:
FROM %%BASE%%:%%TAG%%
...
And then process this docker-template file with the given values to produce the final Dockerfile. However it doesn't really support multiline replacement and it's written in Javascript (sic!).
Alternative solution could be to write your own simple templater like @Ricky Barillas suggested (also you could use existing template engines like jinja
if you want more power).
Mind the disadvantage of all such home-baked solutions is that your Dockertemplate
files will be going beyond the syntax of Dockerfile
, so you won't be able to use Dockerfile linters/validators any more (only on the final output). Also they will become less "portable" in terms that to build your image one will need not only base Docker, but your templating scripts as well.
Upvotes: 1
Reputation: 14666
Modern tags of the official docker library operating systems systems tend to be multiarch now. Just building against FROM ubuntu:17.10
on the architecture will fetch the right image.
e.g.:
docker run ubuntu:17.10 uname -a
Unable to find image 'ubuntu:17.10' locally
17.10: Pulling from library/ubuntu
c3cef7a7aa54: Pull complete
8cbf0d1d8127: Pull complete
1b5d7d7519b4: Pull complete
855c347561ac: Pull complete
ec1530a7444c: Pull complete
Digest: sha256:3b811ac794645dfaa47408f4333ac6e433858ff16908965c68f63d5d315acf94
Status: Downloaded newer image for ubuntu:17.10
Linux 93a4440468e9 4.4.0-116-generic #140-Ubuntu SMP Mon Feb 12 21:22:24 UTC 2018 ppc64le ppc64le ppc64le GNU/Linux
Upvotes: 0
Reputation: 41
My first thought is to write a templating script that will replace your variable values to whatever is needed at the time. This values could be passed into the script when called or sourced from environment variables.
I'm thinking in particular of using python's string.Template module and using the delimiter $
rather than {
since {
might be used in your Dockerfile else where. $
is the default delimiter for string.Template
. Also I would name this template file with a different extension since it Docker won't be able to build it with the templated variables inside.
Let's say your Dockerfile.template
looks like this:
FROM $ARCH/ubuntu:17.10
...
you could then set and environment variable ARCH=ppc64le
in your session and run a short script like this:
import os
import string
with open('Dockerfile.template', 'r') as f:
template = string.Template(f.read())
with open('Dockerfile', 'w') as f:
args = {key: value.strip() for (key, value) in os.environ.items()}
f.write(template.substitute(**args))
These two lines read your templated file and create a string.Template
object. The default delimiter for string.Template
is $
so it complies with your template.
with open('Dockerfile.template', 'r') as f:
template = string.Template(f.read())
This just sets us up to create and write to your actual Dockerfile
with open('Dockerfile', 'r') as f:
This fancy line is where the environment variables come in to play. os.environ is a mapping object of your os's environment variables. os.environ.items()
unpacks each pair into its respective key:value tuple. value.strip()
just cleans up the value to make sure there's no weird whitespacing. All of this is packed into dictionary comprehension, where the variable key
is the name of the environment variable, and in this case the templated variable, and it maps the the value of this variable
args = {key: value.strip() for (key, value) in os.environ.items()}
lastly we have this line. template.substitute()
takes in a mapping object and tries to substitute all of the keys with its values. **args
expands the dictionary.
f.write(template.substitute(**args))
Upvotes: 2
Reputation: 8819
You can use a build argument and use docker build --build-arg foo=bar
to build the image.
Upvotes: 3