skip to Main Content

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


  1. 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:

    ...
    COPY docker-entrypoint.sh /usr/local/bin/
    {{ if .major >= 11 then "" else ( -}}
    RUN ln -s usr/local/bin/docker-entrypoint.sh / # backwards compat
    {{ ) end -}}
    ENTRYPOINT ["docker-entrypoint.sh"]
    ...
    
    Login or Signup to reply.
  2. 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 the FROM image; then you could have a Dockerfile fragment that installed each of these pieces individually, and have a series of docker 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 functions

    install_opencv() {
      apt-get update
      apt-get install ...
    }
    
    install_aws_streams() {
      ...
    }
    

    You could then bring this library of installation commands into your image and RUN them.

    FROM ubuntu:22.04
    COPY installers.sh /usr/local/bin
    RUN . installers.sh && install_opencv
    RUN . installers.sh && install_aws_streams
    

    (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-standard source. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search