I have some really large docker files that I would like to refactor and code share.
A lot of them share a lot of build steps (i.e. installing specific dependencies from github), but some of them have different base images.
Is there a good way to create functions to abstract away and share this logic. Ideally I would create a Docker library with functions like:
def install_opencv():
... DO stuff ...
def install_aws_streams():
... DO stuff ...
... other helpful install commands ...
And then I could import them in the relevant docker files:
DockerFileA
import my_custom_docker_lib
From baseX
install_opencv()
install_aws_streams()
DockerFileB
import my_custom_docker_lib
From baseY
install_opencv()
install_aws_streams()
2
Answers
Each docker image consists of layers, and you are supposed to build the effective layer model. This approach differs from writing a program in some procedural language where you want to decouple the spaghetti code in logical modules.
If you are confident about your existing layers, then you can check out the templates approach, like in many popular images like Postgres:
A Dockerfile has no notion of "functions" or "subroutines". It executes the
RUN
instructions and other commands in linear order; there aren’t even loops or conditionals.In principle it could be possible to build up something like this with a series of Dockerfiles, but you’d have an extremely manual build system. The trick you could use here is using a Dockerfile
ARG
to dynamically provide theFROM
image; then you could have a Dockerfile fragment that installed each of these pieces individually, and have a series ofdocker build --build-arg base_image=...
commands to stitch them together.If it’s possible to do each of these installations in a single
RUN
command then you could build a shell script of installation functionsYou could then bring this library of installation commands into your image and
RUN
them.(Remember to use standard Bourne shell syntax and not bash extensions, especially if you’ll be targeting Alpine images: shell functions do not begin with the word
function
, and prefer.
to non-standardsource
. Also note that the entire build sequence will be repeated if the installer function script changes at all.)This setup does conflict a little bit with how I’d expect a typical image to work. An image would usually package only a single application. That image would also work outside a container, and a language-specific dependency file would list out the things it needs. I’d then expect the Dockerfile to have at most one distribution-package-manager "install" command, that installed things needed to build C extensions, and then one laiguage-package-manager "install" command to install the actual application dependencies. A containerized application wouldn’t necessarily expect a large library of dependencies to be available in the runtime environment.