Code Monkey home page Code Monkey logo

dockermake's Introduction

Docker-make

Codeship Status for avirshup/DockerMake PyPI version

Table of Contents

What is it?

A command line tool to build and manage stacks of docker images. You can mix and match different sets of build instructions as a dependency graph to create maintainable and extensible stacks of docker images.

Install it

Requires Docker and Python 2.7 or 3.5+.

pip install DockerMake 

This will install the command line tool, docker-make, and its supporting python package, which you can import as import dockermake.

Run it

To build some illustrative examples, try running the example in this repository:

git clone https://github.com/avirshup/DockerMake
cd DockerMake/example
docker-make --list
docker-make final

What you can do with it

The punchline: define small pieces of configuration or functionality, then mix them together into production container images.

Build steps

A DockerMake.yml file contains discrete build "steps". These steps can depend on each other, forming a dependency graph. DockerMake solves the dependency graph and drives building of each image as necessary. This makes it easy to keep your images up-to-date while still taking advantage of docker's shared fileystem layers and build cache.

Build automation

  • new: beta support for dockerfile build arguments
  • new: specify custom .dockerignore files for any given build step
  • Automated registry login and image pushes

Secrets and squashing

  • squash arbitrary parts of your build (using squash: true in a step definition) without busting the cache
  • Designate secret_files to erase them from intermediate layers (In the step definition, use secret_files: ['path1', 'path2', ...])

WARNING: these features are in alpha - use with extreme caution

File handling

  • Create builds that ADD or COPY files from anywhere on your file system
  • Build artifacts in one image, then copy them into smaller images for deployment

Cache control

  • Invalidate docker's build cache at a specific step in the build using --bust-cache [stepname]
  • new: Use specific images to resolve docker's build cache (using --cache-repo [repo] and/or --cache-tag [tag])
  • Force a clean rebuild without using the cache (using --no-cache)

How to write DockerMake.yml

DockerMake.yml lets you split your image build up into discrete, easy to manage steps that can be mixed together (as a dependency graph) to produce your desired container image.

Defining an image

The DockerMake.yml file is a YAML-formatted file that lists build steps. To create an extremely basic image, define a step with a base image and a series of dockerfile commands:

FIRST_STEP_NAME:
  FROM: BASE_IMAGE_NAME
  build: |
    RUN [something]
    ADD [something else]
    [Dockerfile commands go here]

Use the requires field to define additional steps that extend the first one:

FIRST_STEP_NAME:
   [...]

NEXT_STEP_NAME:
   requires:
     - FIRST_STEP_NAME
   build: |
     [additional Dockerfile instructions]

Image definition reference

Image definitions can include any of the following fields:

FROM/FROM_DOCKERFILE

The docker image to use as a base for this image (and those that require it). This can be either the name of an image (using FROM) or the path to a local Dockerfile (using FROM_DOCKERFILE).

Example:

baseimage:
   FROM: python:3.6-slim

or

baseimage:
   FROM_DOCKERFILE: ../myproject/Dockerfile

build

Multi-line string defining dockerfile commands to build this step. Note that these commands CANNOT contain 'FROM'. See also Notes on multi-line strings below.

Example:

build-image:
   requires:
     - baseimage
   build: |
     RUN apt-get update \
      && apt-get install -y gcc vi
     ENV editor=vi

requires

List of other image definitions to include in this one. docker-make will create a new image from a single DockerFile that includes an amalgamation of all image definitions.

Example:

my-tools:
  build: |
    RUN pip install numpy jupyter pandas
    [...]

data-sci-environment:
   requires:
     - baseimage
     - my-tools

build_directory

Path to a directory on your filesystem. This will be used to locate files for ADD and COPY commands in your dockerfile. See Notes on relative paths below.

Example:

data-image:
    build_directory: ./datafiles
    build: |
      COPY data /opt/data
    [...]

ignore/ignorefile

A custom .dockerignore for this step. This overrides any existing .dockerignore file in the build context. Only relevant for ADD or COPY commands when the build_directory is specified. This can either be a multi-line string (using the ignore field) or the path to a file (using the ignorefile field).

Example:

data-image:
    build_directory: ./datafiles
    build: |
      ADD [...]
    ignore: |
      *.pyc
      *~
      *.tmp

description

An arbitrary comment (ignored by docker-make)

copy_from

Used to copy files into this image from other images (to copy from your filesystem or a URL, use the standard ADD and COPY dockerfile commands). This is a mapping of mappings of the form:

[image-name]:
   [...]
   copy_from:
     [source_image1]:
        [source path 1]: [dest path 1]
        [source path 2]: [dest path 2]
     [source image2]:
        [...]

Note that, for historical reasons, these copies are performed after any build instructions are executed.

squash

NOTE: this feature requires that your docker daemon's experimental features be enabled.

Used to squash all layers produced in a given step. This can be helfpul both for keeping image sizes low, especially when it's necessary to add a lot of data via the ADD or COPY dockerfile commands.

Note that setting squash: True for a step only squashes the layers generated by that step. All layers in the base image are left intact.

Additionally, unlike the vanilla docker build --squash command, downstream image builds can use the squashed image in their cache, so that squashing doesn't force you to repeatedly re-run the same downstream build steps.

Example: In this example, we create a huge file in the image, do something with it, then erase it.

count-a-big-file:
    FROM: alpine
    build: |
        RUN dd if=/dev/zero of=/root/bigfile count=16384 bs=1024
        RUN wc /root/bigfile > /root/numbiglines
        RUN rm /root/bigfile

Let's build it and check the size:

$ docker-make count-a-big-file
[...]
docker-make finished.
Built: 
 * count-a-big-file
$ docker images count-a-big-file
REPOSITORY         ...   SIZE
count-a-big-file   ...   20.9MB

But, take them same definition and add a squash: true to it:

count-a-big-file:
    FROM: alpine
    squash: true
    build: |
        RUN dd if=/dev/zero of=/root/bigfile count=16384 bs=1024
        RUN wc /root/bigfile > /root/numbiglines
        RUN rm /root/bigfile

And we find that the deleted file is no longer taking up space:

$ docker-make count-a-big-file
[...]
docker-make finished.
Built: 
 * count-a-big-file
$ docker images count-a-big-file
REPOSITORY         ...   SIZE
count-a-big-file   ...   4.15MB

secret_files

Read these caveats first

  • This is an alpha-stage feature. DO NOT rely on it as a security tool. You must carefully verify that your final image, and all its layers AND its history, are free of sensitive information before deploying or publishing them.
  • It relies on experimental docker daemon features.
  • Although your final image won't contain your secrets, they will be present in intermediate images on your build machine. Your secrets will be exposed to all docker users on your build machine.
  • When you define secret_files for a step, it only erases files that are added in the build definition for that step. Files added in other steps will remain exposed in your image's layers.

Background

It's often necessary to perform some form of authentication during a build - for instance, you might need to clone a private git repository or download dependencies from a private server. However, it's quite challenging to do so without leaving your credentials inside a layer of the final docker image or its history.

Files added or created in a given step can be designated as secret_files in DockerMake.yml. These files will be automatically erased at the end of the step, and the step's layers will be squashed to keep the files out of the history.

Example

my-secret-steps:
    FROM: python:3.6
    build: |
        ADD my-credentials /opt/credentials
        RUN some-process --credentials /opt/credentials
    secret_files:
        - /opt/credentials

Special fields

_SOURCES_

You can include step definitions from other DockerMake.yml files by listing them in the _SOURCES_. For example:

_SOURCES_:
  - ~/mydefinitions/DockerMake.yml
  - ./other/file.yml
  [...]

Please note that relative file paths in each file are always interpreted relative to the directory containing that file.

_ALL_

By default, running docker-make --all will build all well-defined images defined in a file (and any files included via _SOURCES_). Images without a FROM or FROM_DOCKERFILE field in any of their requirements will be ignored.

Alternatively, you can use the _ALL_ field to designate specific images to build. For example, in the following definition, docker-make --all will only build imgone and imgtwo, not baseimage:

_ALL_:
 - imgone
 - imgtwo
 
baseimage:
  FROM: [...]
  [...]
 
imgone: [...]

imgtwo: [...]

Note that the _ALL_ fields from any files included via _SOURCES_ are ignored.

Notes on DockerMake.yml

Relative paths: Several of these fields include paths on your local filesystem. They may be absolute or relative; relative paths are resolved relative to the DockerMake.yml file they appear in. Use of ~ is allowed to denote the home directory.

Multiline strings: You'll usually want to express the build and ignore fields as multiline strings. To do so, use the following YML "literal block scalar" style, as in all examples above.

field-name: |
  [line 1]
  [line 2]
  [...]
next field: [...]

Example

(See also this production example)

This example builds a single docker image called data_science. It does this by mixing together three components: devbase (the base image), airline_data (a big CSV file), and python_image (a python installation). docker-make will create an image that combines all of these components.

Here's the DockerMake.yml file:

devbase:
 FROM: phusion/baseimage
 build: |
  RUN apt-get -y update && apt-get -y install 
      build-essential 
   && mkdir -p /opt

airline_data:
 build_directory: sample_data/airline_data
 build: |
  ADD AirPassengers.csv /data

plant_data:
 build_directory: sample_data/plant_growth
 build: |
  ADD Puromycin.csv /data

python_image:
 requires:
  - devbase
 build: |
  RUN apt-get install -y python python-pandas

data_science:
 requires:
  - python_image
  - airline_data
  - plant_data

To build an image called alice/data_science, you can run:

docker-make data_science --repository alice

which will create an image with all the commands in python_image and airline_data.

This works by dynamically generating a new Dockerfile every time you ask to build something. However, most of the commands will be cached, especially if you have a large hierarchy of base images. This actually leads to less rebuilding than if you had a series of Dockerfiles linked together with FROM commands.

Here's the dependency graph and generated Dockerfiles:

dependency graph dockerfiles

Command line usage

usage: docker-make [-h] [-f MAKEFILE] [-a] [-l] [--build-arg BUILD_ARG]
                   [--requires [REQUIRES [REQUIRES ...]]] [--name NAME] [-p]
                   [-n] [--dockerfile-dir DOCKERFILE_DIR] [--pull]
                   [--cache-repo CACHE_REPO] [--cache-tag CACHE_TAG]
                   [--no-cache] [--bust-cache BUST_CACHE] [--clear-copy-cache]
                   [--keep-build-tags] [--repository REPOSITORY] [--tag TAG]
                   [--push-to-registry] [--registry-user REGISTRY_USER]
                   [--registry-token REGISTRY_TOKEN] [--version] [--help-yaml]
                   [--debug]
                   [TARGETS [TARGETS ...]]

NOTE: Docker environmental variables must be set. For a docker-machine, run
`eval $(docker-machine env [machine-name])`

optional arguments:
  -h, --help            show this help message and exit

Choosing what to build:
  TARGETS               Docker images to build as specified in the YAML file
  -f MAKEFILE, --makefile MAKEFILE
                        YAML file containing build instructions
  -a, --all             Print or build all images (or those specified by
                        _ALL_)
  -l, --list            List all available targets in the file, then exit.
  --build-arg BUILD_ARG
                        Set build-time variables (used the same way as docker
                        build --build-arg), e.g., `... --build-arg VAR1=val1
                        --build-arg VAR2=val2`
  --requires [REQUIRES [REQUIRES ...]]
                        Build a special image from these requirements.
                        Requires --name
  --name NAME           Name for custom docker images (requires --requires)

Dockerfiles:
  -p, --print-dockerfiles, --print_dockerfiles
                        Print out the generated dockerfiles named
                        `Dockerfile.[image]`
  -n, --no_build        Only print Dockerfiles, don't build them. Implies
                        --print.
  --dockerfile-dir DOCKERFILE_DIR
                        Directory to save dockerfiles in (default:
                        ./docker_makefiles)

Image caching:
  --pull                Always try to pull updated FROM images
  --cache-repo CACHE_REPO
                        Repository to use for cached images. This allows you
                        to invoke the `docker build --build-from` option for
                        each image.For instance, running `docker-make foo bar
                        --cache-repo docker.io/cache` will use
                        docker.io/cache/foo as a cache for `foo` and
                        docker.io/cache/bar as a cachefor `bar`.
  --cache-tag CACHE_TAG
                        Tag to use for cached images; can be used with the
                        --cache-repo option (see above).
  --no-cache            Rebuild every layer
  --bust-cache BUST_CACHE
                        Force docker to rebuilt all layers in this image. You
                        can bust multiple image layers by passing --bust-cache
                        multiple times.
  --clear-copy-cache, --clear-cache
                        Remove docker-make's cache of files for `copy-from`.
  --keep-build-tags     Don't untag intermediate build containers when build
                        is complete

Repositories and tags:
  --repository REPOSITORY, -r REPOSITORY, -u REPOSITORY
                        Prepend this repository to all built images, e.g.
                        `docker-make hello-world -u quay.io/elvis` will tag
                        the image as `quay.io/elvis/hello-world`. You can add
                        a ':' to the end to image names into tags: `docker-
                        make -u quay.io/elvis/repo: hello-world` will create
                        the image in the elvis repository:
                        quay.io/elvis/repo:hello-world
  --tag TAG, -t TAG     Tag all built images with this tag. If image names are
                        ALREADY tags (i.e., your repo name ends in a ":"),
                        this will append the tag name with a dash. For
                        example: `docker-make hello-world -u elvis/repo: -t
                        1.0` will create the image "elvis/repo:hello-world-1.0
  --push-to-registry, -P
                        Push all built images to the repository specified
                        (only if image repository contains a URL) -- to push
                        to dockerhub.com, use index.docker.io as the registry)
  --registry-user REGISTRY_USER, --user REGISTRY_USER
                        For pushes: log into the registry using this username
  --registry-token REGISTRY_TOKEN, --token REGISTRY_TOKEN
                        Token or password to log into registry (optional; uses
                        $HOME/.dockercfg or $HOME/.docker/config.json if not
                        passed)

Help:
  --version             Print version and exit.
  --help-yaml           Print summary of YAML file format and exit.
  --debug

Copyright (c) 2015-2017, Autodesk Inc. Copyright (c) 2017-2018, Docker-Make contributors. Released under the Apache 2.0 License.

dockermake's People

Contributors

accwebs avatar attylionelign avatar avirshup avatar cleeland avatar hasnat avatar yoyonel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dockermake's Issues

Update readme how --requires and --name should be used

Hi, I have quite problem to simply figure out hou --requires and --name should be used on the CLI.
I want to add additional require to the target specified in DockerMake.yml. When --name is not specified it fails, if it's specified as any other target in DockerMake it fails and if it's not defined it fails as well.

docker-make -f DockerMake.yml --requires salt -t ubuntu-xenial-salt-stable debian-stretch                                  
Working directory: /home/pmichalec/hg2g/workspace-salt/docker-salt-formulas.develop
Copy cache directory: /tmp
READING DockerMake.yml
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 11, in <module>
    load_entry_point('DockerMake==0.6.0', 'console_scripts', 'docker-make')()
  File "/usr/local/lib/python3.6/dist-packages/dockermake/__main__.py", line 38, in main
    run(args)
  File "/usr/local/lib/python3.6/dist-packages/dockermake/__main__.py", line 81, in run
    targets = utils.get_build_targets(args, defs)
  File "/usr/local/lib/python3.6/dist-packages/dockermake/utils.py", line 75, in get_build_targets
    assert args.requires and args.name
AssertionError
➜  docker-salt-formulas.develop git:(develop) ✗ docker-make -f DockerMake.yml --requires salt --name zzz -t ubuntu-xenial-salt-stable debian-stretch
Working directory: /home/pmichalec/hg2g/workspace-salt/docker-salt-formulas.develop
Copy cache directory: /tmp
READING DockerMake.yml
FATAL ERROR: No base image found in zzz's dependencies
➜  docker-salt-formulas.develop git:(develop) ✗ docker-make -f DockerMake.yml --requires salt --name debian-stretch -t ubuntu-xenial-salt-stable debian-stretch
Working directory: /home/pmichalec/hg2g/workspace-salt/docker-salt-formulas.develop
Copy cache directory: /tmp
READING DockerMake.yml
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 11, in <module>
    load_entry_point('DockerMake==0.6.0', 'console_scripts', 'docker-make')()
  File "/usr/local/lib/python3.6/dist-packages/dockermake/__main__.py", line 38, in main
    run(args)
  File "/usr/local/lib/python3.6/dist-packages/dockermake/__main__.py", line 81, in run
    targets = utils.get_build_targets(args, defs)
  File "/usr/local/lib/python3.6/dist-packages/dockermake/utils.py", line 76, in get_build_targets
    assert args.name not in defs.ymldefs
AssertionError
➜  docker-salt-formulas.develop git:(develop) ✗ docker-make -f DockerMake.yml --requires salt --name debian -t ubuntu-xenial-salt-stable debian-stretch 
Working directory: /home/pmichalec/hg2g/workspace-salt/docker-salt-formulas.develop
Copy cache directory: /tmp
READING DockerMake.yml
FATAL ERROR: No base image found in debian's dependencies
➜  docker-salt-formulas.develop git:(develop) ✗ docker-make -f DockerMake.yml --requires salt --name wheelhouse -t ubuntu-xenial-salt-stable debian-stretch
Working directory: /home/pmichalec/hg2g/workspace-salt/docker-salt-formulas.develop
Copy cache directory: /tmp
READING DockerMake.yml
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 11, in <module>
    load_entry_point('DockerMake==0.6.0', 'console_scripts', 'docker-make')()
  File "/usr/local/lib/python3.6/dist-packages/dockermake/__main__.py", line 38, in main
    run(args)
  File "/usr/local/lib/python3.6/dist-packages/dockermake/__main__.py", line 81, in run
    targets = utils.get_build_targets(args, defs)
  File "/usr/local/lib/python3.6/dist-packages/dockermake/utils.py", line 76, in get_build_targets
    assert args.name not in defs.ymldefs
AssertionError

can't use tags in image names

Consider this DockerMake.yml:

"foo:bar":
    FROM: ubuntu:trusty

With DockerMake 0.8, it will build fine. However, with 0.9 it fails:

➜  docker-make --version
docker-make version 0.9.0
➜  docker-make "foo:bar"
Working directory: /home/apollo/Development/test
Copy cache directory: /tmp
READING DockerMake.yml

Requested images: foo:bar

==========================================================================================================================================================================================================================================
                                                                             STARTING BUILD for "foo:bar" (image definition "foo:bar" from ./DockerMake.yml)

* Step 1/1 for image foo:bar
  Building step foo:bar defined in ./DockerMake.yml
  --------------------------------------------------------------------------------   1.foo:bar.dmk:c6f3faab-43e9-476a-aa89-df4f6a22bd80: BUILD LOG   --------------------------------------------------------------------------------
FATAL ERROR: Docker build failure

   -------- Docker daemon output --------
'500 Server Error: Internal Server Error ("invalid reference format")'
   -------- Arguments to client.build --------
{   'buildargs': None,
    'decode': True,
    'dockerfile': None,
    'fileobj': <_io.BytesIO object at 0x7f1012e4b0a0>,
    'nocache': False,
    'path': None,
    'pull': False,
    'rm': True,
    'squash': False,
    'tag': '1.foo:bar.dmk:c6f3faab-43e9-476a-aa89-df4f6a22bd80'}
This dockerfile was written to dockerfile.fail

Is Python 2.7 Supported?

Per the README:

Requires Docker and Python 2.7 or 3.5+.

However, after a pip install on Python 2.7 (system Python via Ubuntu 16.04,):

$ docker-make --list
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 7, in <module>
    from dockermake.__main__ import main
  File "/usr/local/lib/python2.7/dist-packages/dockermake/__main__.py", line 23, in <module>
    from . import cli, utils, staging
  File "/usr/local/lib/python2.7/dist-packages/dockermake/utils.py", line 24, in <module>
    from . import errors
  File "/usr/local/lib/python2.7/dist-packages/dockermake/errors.py", line 90
    print(dockerfile, file=dff)
                          ^
SyntaxError: invalid syntax

I noticed that metafiles (setup.py, codeship-*.yml, etc) only reference Python 3.x, and I wanted to confirm that 2.7 is officially supported before digging deeper into this problem.

colons in image names cause trouble

Given this DockerMake.yml:

"foo:bar":
    FROM: ubuntu:trusty

"foo:bar:baz":
    FROM: ubuntu:trusty

Running docker-make 'foo:bar' will work.

However, running docker-make 'foo:bar:baz' produces

docker-make 'foo:bar:baz'
Working directory: /home/sensory/Development/test/docker_make
Copy cache directory: /tmp
READING DockerMake.yml

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
       STARTING BUILD for "foo:bar:baz" (image definition "foo:bar:baz" from ./DockerMake.yml)

 * Building foo:bar:baz, Step 1/1:
     Image definition "foo:bar:baz" from file ./DockerMake.yml
  ----------------------------------------------------------------------------------------------   dmkbuild_foo:bar:baz_1: BUILD LOG   ----------------------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 9, in <module>
    load_entry_point('DockerMake==0.5.6', 'console_scripts', 'docker-make')()
  File "/usr/local/lib/python2.7/dist-packages/dockermake/__main__.py", line 64, in main
    built, warnings = utils.build_targets(args, defs, targets)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/utils.py", line 116, in build_targets
    pull=args.pull)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/builds.py", line 98, in build
    step.build(client, usecache=usecache)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/step.py", line 93, in build
    utils.stream_docker_logs(stream, self.buildname)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/utils.py", line 186, in stream_docker_logs
    for item in stream:
  File "/usr/local/lib/python2.7/dist-packages/docker/api/client.py", line 311, in _stream_helper
    yield self._result(response, json=decode)
  File "/usr/local/lib/python2.7/dist-packages/docker/api/client.py", line 226, in _result
    self._raise_for_status(response)
  File "/usr/local/lib/python2.7/dist-packages/docker/api/client.py", line 222, in _raise_for_status
    raise create_api_error_from_http_exception(e)
  File "/usr/local/lib/python2.7/dist-packages/docker/errors.py", line 31, in create_api_error_from_http_exception
    raise cls(e, response=response, explanation=explanation)
docker.errors.APIError: 500 Server Error: Internal Server Error ("invalid reference format")

Reuse existing dockerfiles

It would be nice to be able to reuse Dockerfiles in the build tag of the DockerMake.yaml. Or use a new tag eg
build_script: path/to/Dockerfile

Eg for reusing existing files, and to get proper syntax highlighting in the editor

Build-time secrets: features and/or best practices with DockerMake (decision)

Implement (or just document, if already possible) a reasonable method for passing build-time secrets that aren't accessible after build-time. See also https://runnable.com/docker/rails/managing-secrets-during-docker-builds

E.g., a build where we install artifacts from internal enterprise github, which requires a token for auth.

Options:

  1. Pass one-time-use tokens to docker-make (e.g., with hashicorp vaults)
    Pro: already supported
    Cons: cache becomes very difficult to reason about; potentially painful to set up; build-args feel brittle

  2. Auto-squashing
    Pro: easy to reason about
    Cons: no caching, no shared layers

  3. Context set-up - download artifacts into build context before the build
    Pro: pretty easy to reason about; pretty easy to implement
    Con: lose the reproducibility of the docker builds; build context becomes brittle

  4. Multi-stage builds
    Pro: keeps the build docker-based
    Con: somewhat tricky to set up; caching will also cache the secrets; pushing build images accidentally will publish secrets

Let's say the right solution will need to have the following properties:

  1. Maintains layers - when pushed to a registry, images can still share layers.
  2. Caching as normal for normal build steps - even if a "normal" depends on something that came from "secrets" layer, caching should work normally
  3. Protection against sharing secrets

Build arg to use as an variable in makefile

Recently ARGs support was added and it's missing in README.

Could you also support that build ARGs may be used to pre-process the yaml? Example:

# --build-arg os_codename=stretch

debian-{{os_codename}:
  FROM: debian:{{os_codename}}

publish to docker hub

I'm planning on pushing to docker hub, pointing at your repo. Please let me know if you have any issues with this.

--all option stops when target has no FROM defined

When --all option is used, targes without FROM should be skipped (or we have to decorate what targets are assumed as "all"). Otherwise they fail and workaround is ugly:

IGNORE="(base|common)"
        docker-make --list |awk '/*/{print $2}'| grep -v "${IGNORE}" | xargs -n1 -I% docker-make % 

Reminds me that would be nice to have the option to convert severe errors to warnings. So one target fails but other are still building.

get_console_width() failure can result in 'Inappropriate ioctl for device'

If 'stty size' fails in get_console_width(), the following message may be written to stderr:
'stty: standard input: Inappropriate ioctl for device'

This seems to show up in build systems (in my case Jenkins) resulting in a lot of instances of this error showing up in the build output.

It looks like this is a common problem with other Python programs that check the console width in this way. Pull request coming soon.

port in docker registry url

Given a simple DockerMake.yml:

foobar:
    FROM: ubuntu:xenial

we can build prepend a Docker registry URL like this:

$ docker-make -r quay.io/elvis foobar
...
  docker-make built: quay.io/elvis/foobar

docker-make finished.
Built: 
 * quay.io/elvis/foobar

However, if the registry is running on a non-standard port it causes problems:

$ docker-make -r quay.io:5005/elvis foobar
...
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 11, in <module>
    sys.exit(main())
  File "/usr/local/lib/python2.7/site-packages/dockermake/__main__.py", line 38, in main
    run(args)
  File "/usr/local/lib/python2.7/site-packages/dockermake/__main__.py", line 88, in run
    built, warnings = utils.build_targets(args, defs, targets)
  File "/usr/local/lib/python2.7/site-packages/dockermake/utils.py", line 144, in build_targets
    pull=args.pull)
  File "/usr/local/lib/python2.7/site-packages/dockermake/builds.py", line 114, in build
    self.finalizenames(client, finalimage)
  File "/usr/local/lib/python2.7/site-packages/dockermake/builds.py", line 145, in finalizenames
    client.api.tag(finalimage, *self.targetname.split(':'))
  File "/usr/local/lib/python2.7/site-packages/docker/utils/decorators.py", line 19, in wrapped
    return f(self, resource_id, *args, **kwargs)
  File "/usr/local/lib/python2.7/site-packages/docker/api/image.py", line 532, in tag
    self._raise_for_status(res)
  File "/usr/local/lib/python2.7/site-packages/docker/api/client.py", line 231, in _raise_for_status
    raise create_api_error_from_http_exception(e)
  File "/usr/local/lib/python2.7/site-packages/docker/errors.py", line 31, in create_api_error_from_http_exception
    raise cls(e, response=response, explanation=explanation)
docker.errors.APIError: 500 Server Error: Internal Server Error ("invalid tag format")

But this is a valid registry URL as far as Docker is concerned:

$ docker tag quay.io/elvis/foobar quay.io:5005/elvis/foobar
$ docker images | grep elvis 
quay.io/elvis/foobar                latest              0b1edfbffd27        4 months ago        113MB
quay.io:5005/elvis/foobar           latest              0b1edfbffd27        4 months ago        113MB

pre-build step?

Currently, build runs after all the requires run. Could we have a pre_build that runs before the requires? ie,

foobar:
    FROM: foobase
    pre_build: |
       RUN do stuff before foo and bar run
    requires:
       - foo
       - bar
    build: |
       RUN do stuff after foo and bar run

foo:
    build: |
       RUN do foo stuff

bar:
    build: |
       RUN do bar stuff

Since 0.5.7: Modules/images with no base image no longer allowed, breaks inclusion to multiple base images

On DockerMake 0.5.6, modules/image definitions with no base image defined were allowed and could be included into other (complete) images. 0.5.7 appears to have changed that behavior, outputting the error "FATAL ERROR: No base image found in dependencies"

Looking at the DockerMake code now, I think maybe allowing image definitions without a base image was accidental, but I found it really helpful for use in code re-usability. Allow me to explain via an example:

Suppose I want to create six docker images, all that are 'variants' of each other:

  • base centos images (centos 6 and 7)
  • base images with Java installed (centos 6 and 7)
  • base images with JDK & maven installed (centos 6 and 7)

I could start out by defining the base images:

"base_centos:6":
  FROM: centos:6
  build_directory: .
  build: |
    RUN ...
    RUN ...
    COPY ...
    ...

"base_centos:7":
  FROM: centos:7
  build_directory: .
  build: |
    RUN ...
    RUN ...
    COPY ...
    ...

Then, I could create re-usable 'module' images with no base. Examples below:

For Oracle JRE:

"java_oracle:jre_8_svr":
  build_directory: .
  build: |
    RUN mkdir -p /opt/java
    COPY cache/java-1.8.0.141-1.tgz /opt/java/java-1.8.0.141-1.tgz
    RUN cd /opt/java && tar xvf java-1.8.0.141-1.tgz && rm -f java-1.8.0.141-1.tgz
    RUN chown -R root:root /opt/java/java-1.8.0.141-1
    ...

and for Oracle JDK:

"java_oracle:jdk_8":
  build_directory: .
  build: |
    RUN mkdir -p /opt/java
    COPY cache/jdk-1.8.0.141-1.tgz /opt/java/jdk-1.8.0.141-1.tgz
    ...

and for maven:

"maven:3.3.9":
  build_directory: .
  build: |
    RUN mkdir -p /opt/maven
    COPY cache/apache-maven-3.3.9-bin.tar.gz /opt/maven/apache-maven-3.3.9-bin.tar.gz
    ...

Finally, I can now mix-and-match on assembling the base images together with optional 'modules' to create a set of variants:

"base_centos:6_java_8":
  requires:
    - base_centos:6
    - java_oracle:jre_8_svr
  build_directory: .

"base_centos:7_java_8":
  requires:
    - base_centos:7
    - java_oracle:jre_8_svr
  build_directory: .

"dev_centos:6_java_8":
  requires:
    - base_centos:6
    - java_oracle:jdk_8
    - maven:3.3.9
  build_directory: .
  build:
    RUN yum install -y git subversion

"dev_centos:7_java_8":
  requires:
    - base_centos:7
    - java_oracle:jdk_8
    - maven:3.3.9
  build_directory: .
  build:
    RUN yum install -y git subversion

See how I've basically re-used the same installation instructions for Java and Maven (without having to copy-paste)? This no longer works in 0.5.7.

Is there some way we can tweak the code to get this type of functionality back, or better yet, officially support this type of inclusion using a different syntax? What do you think?

Issue with PyYAML 6

Current setup.py from master https://github.com/avirshup/DockerMake/blob/master/setup.py#L19 defines dependency: pyyaml >= 5. Which is now PyYAML 6.

Version 6 made breaking change in load function (added mandatory Loader argument), so this package does not work if you just install pip install dockermake.

Proposal to fix is to freeze versions in setup.py, and in requirements.txt to use .. Then, if you do pip install -r requirements.txt it will go to setup.py and will grab deps from there.
Or, simply upgrade to pyyaml 6.

Template pre-processing of the makefile

If I am aware there is not better Makefile/build a framework to compose images as in this tool. I have some suggestions/enhancements:

Add "Jinja2" processing of the makefile before yaml is parsed, to allow things like:
% for os_codename in [xenial, bionic]
debian-{{os_codename}:
FROM: debian:{{os_codename}}
% endfor

Evaluate selective squashing with secrets

Task for me - I need to make sure that selective squashing would work the way I imagine, where

  1. layers (possibly just at the top) can be selectively squashed, and
  2. squashing erases any trace of the build arguments from history or docker inspect

Answer: It does not work as I assumed. Build arguments are preserved in a squashed image.

Since 0.5.7, relative paths are computed differently

Between release 0.5.6 and 0.5.7, relative paths are computed differently. Specifically, the relative paths appear to now be based off of the 'root' DockerMake.yml file rather than the included .yml file.

Example: I have the following base_centos.yml inside src/images/base/ (relative to DockerMake.yml):

"base_centos:6":
  FROM: centos:6
  build_directory: .
  build: |
    RUN mkdir -p /etc/yum.repos.d/old
    RUN mv /etc/yum.repos.d/*.repo /etc/yum.repos.d/old/
    COPY conf/internal_repo.repo /etc/yum.repos.d/internal_repo.repo

And in DockerMake.yml, I include it using:

_SOURCES_:
  - ./src/images/base/base_centos.yml

The conf/internal_repo.repo file is at the path: src/images/base/conf/internal_repo.repo

In 0.5.6, this resolved. But now, it appears it's resolving that file at conf/internal_repo.repo (relative to DockerMake.yml).

The change in behavior was made by commit a58e941

Specifically, the ymlfilepath used here:

-    def _fix_file_paths(ymlfilepath, yamldefs):
+    def _fix_file_paths(pathroot, ymlfilepath, yamldefs):
         """ Interpret all paths relative the the current yaml file
         """
-        pathroot = os.path.dirname(ymlfilepath)
-

Is the inner yml file. Now, we do the following:

def __init__(self, makefile_path):
         self._sources = set()
         self.makefile_path = makefile_path
+        self.pathroot = os.path.abspath(os.path.dirname(makefile_path))
         print('Working directory: %s' % os.path.abspath(os.curdir))
         print('Copy cache directory: %s' % staging.TMPDIR)
         self.ymldefs = self.parse_yaml(self.makefile_path)

And pass that value in. This is the path to the outer DockerMake.yml file instead.

Was this change intentional?

BuildError on termcolor - TypeError: unicode argument expected, got 'str'

Got an issue on ubuntu bionic - not yet investigated...

It's just related to printing an red colored "error" output, E: Unable to locate package vi . When I fixed the name of the package to vim - it finished the build without any issues.

docker-make -f test-dockermake2.yaml build-image
Working directory: /home/pmichalec/hg2g/workspace-docker
Copy cache directory: /tmp
READING test-dockermake2.yaml

=============================================================================================================================================================================
                                       STARTING BUILD for "build-image" (image definition "build-image" from ./test-dockermake2.yaml)

* Step 1/2 for image build-image
  Building step baseimage defined in ./test-dockermake2.yaml
  ----------------------------------------------------------------   dmkbuild_build-image_1: BUILD LOG   ----------------------------------------------------------------
  Step 1/1 : FROM python:3.6-slim
  3.6-slim: Pulling from library/python
  c4bb02b17bb4: Pulling fs layer
  6d9a50844d9d: Pulling fs layer
  fd9974f38d04: Pulling fs layer
  4b23af621dd8: Pulling fs layer
  767ba56e1368: Pulling fs layer
  4b23af621dd8: Waiting
  767ba56e1368: Waiting
  6d9a50844d9d: Downloading (2.7MiB)
  c4bb02b17bb4: Downloading (28.7MiB)
  fd9974f38d04: Downloading (18.9MiB)
  6d9a50844d9d: Verifying Checksum
  6d9a50844d9d: Download complete
  4b23af621dd8: Downloading (240.0B)
  4b23af621dd8: Verifying Checksum
  4b23af621dd8: Download complete
  767ba56e1368: Downloading (1.9MiB)
  767ba56e1368: Verifying Checksum
  767ba56e1368: Download complete
  fd9974f38d04: Verifying Checksum
  fd9974f38d04: Download complete
  c4bb02b17bb4: Verifying Checksum
  c4bb02b17bb4: Download complete
  c4bb02b17bb4: Extracting
  c4bb02b17bb4: Pull complete
  6d9a50844d9d: Extracting
  6d9a50844d9d: Pull complete
  fd9974f38d04: Extracting
  fd9974f38d04: Pull complete
  4b23af621dd8: Extracting
  4b23af621dd8: Pull complete
  767ba56e1368: Extracting
  767ba56e1368: Pull complete
  {u'status': u'Digest: sha256:33a5a60a9a155f05ce10ad4a35fc4628a5209200cf024d040de02acb0b14360b'}
  {u'status': u'Status: Downloaded newer image for python:3.6-slim'}
  ---> dc41c0491c65
  Successfully built dc41c0491c65
  Successfully tagged dmkbuild_build-image_1:latest
  -------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* Created intermediate image dmkbuild_build-image_1

* Step 2/2 for image build-image
  Building step build-image defined in ./test-dockermake2.yaml
  ----------------------------------------------------------------   dmkbuild_build-image_2: BUILD LOG   ----------------------------------------------------------------
  Step 1/3 : FROM dmkbuild_build-image_1
  ---> dc41c0491c65
  Step 2/3 : RUN apt-get update  && apt-get install -y gcc vi
  ---> Running in 5f3b270d9a12
  Get:1 http://security.debian.org jessie/updates InRelease [63.1 kB]
  Get:2 http://security.debian.org jessie/updates/main amd64 Packages [608 kB]
  Ign http://deb.debian.org jessie InRelease
  Get:3 http://deb.debian.org jessie-updates InRelease [145 kB]
  Get:4 http://deb.debian.org jessie Release.gpg [2434 B]
  Get:5 http://deb.debian.org jessie Release [148 kB]
  Get:6 http://deb.debian.org jessie-updates/main amd64 Packages [23.1 kB]
  Get:7 http://deb.debian.org jessie/main amd64 Packages [9064 kB]
  Fetched 10.1 MB in 8s (1185 kB/s) Reading package lists...
  Reading package lists...
  Building dependency tree...
  Reading state information...
  E: Unable to locate package vi 
Traceback (most recent call last):
  File "/usr/local/bin/docker-make", line 11, in <module>
    load_entry_point('DockerMake==0.6.0', 'console_scripts', 'docker-make')()
  File "/usr/local/lib/python2.7/dist-packages/dockermake/__main__.py", line 38, in main
    run(args)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/__main__.py", line 88, in run
    built, warnings = utils.build_targets(args, defs, targets)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/utils.py", line 128, in build_targets
    pull=args.pull)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/builds.py", line 103, in build
    step.build(client, usecache=usecache)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/step.py", line 151, in build
    raise errors.BuildError(dockerfile, str(e), kwargs)
  File "/usr/local/lib/python2.7/dist-packages/dockermake/errors.py", line 88, in __init__
    cprint('Docker build failure', 'red', attrs=['bold'], file=stream)
  File "/usr/local/lib/python2.7/dist-packages/termcolor.py", line 124, in cprint
    print((colored(text, color, on_color, attrs)), **kwargs)
TypeError: unicode argument expected, got 'str'


➜  workspace-docker python --version 
Python 2.7.14+


➜  workspace-docker docker-make --version
docker-make version 0.6.1a3


➜  workspace-docker cat test-dockermake2.yaml 
baseimage:
   FROM: python:3.6-slim

build-image:
   requires:
     - baseimage
   build: |
     RUN apt-get update \
      && apt-get install -y gcc vi
     ENV editor=vi

--pull does not propagate to docker build command

Hi @avirshup!

I'm using DockerMake with the --pull option thinking that this would make it pull the base image manifest every time.
After looking at the code, I realize that the --pull option is not sent to the docker build command, it only affects sourceimages.
I.e. --pull option does not pull new FROM images if they have changed, this make it hard for us to have updated base images.

Changing line 113 in builds.py to step.build(client, usecache=usecache, pull=True) fixes this for me.

Is this is bug or is there a reason pull not being propagated?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.