Usage: bindit

Bindit is a command-line application. The basic usage is to prepend bindit to the container runner command you would ordinarily use.

Motivating example

Let’s say you wanted to use a docker container to list the contents of your current home directory you might do something like this on a unix-based system (Linux, OS X):

$ docker run -v "$HOME":/data alpine:latest ls /data

With bindit, the bind mount is created automatically by detecting the file path reference in the container image arguments. You can basically pretend that the container has access to your host file system. So this works:

$ bindit docker run alpine:latest ls "$HOME"/data

Bindit mounts are mapped to a new /bindit path inside the container to avoid clobbering directories that might be necessary for the container to run (in particular, messing with the container home directory as singularity does by default often breaks containers). If we set the --dryrun flag the docker command prints to standard out instead of running, and we can see the mapping bindit created:

$ bindit --dryrun docker run alpine:latest ls "$HOME"
docker run -v /Users/jc01:/bindit/Users/jc01 alpine:latest ls /bindit/Users/jc01

Turning the above examples into a re-usable application that can be added to your path is easy - see Usage: bindit_partial.

Default and optional behavior

Bindit’s default behavior can be controlled with the following flags:

-a, –absonly

Only auto-bind absolute paths. Useful if you call bindit from a folder that has sub-folders which shadow binaries inside the container (python is a frequent offender for me).

-d, –dryrun

Return a formatted shell command without invoking container runner. Useful for debugging, and when you want to defer running the container to a different context (for instance, HPC job submission).

Note that bindit does require the container runner on path to parse container runner arguments correctly, so for instance, it’s not good practice to dryrun docker jobs on a machine that does not have docker available (or indeed a different docker version from what you use in production).

-i, –ignorepath

bindit won’t attempt to bind any path that is a sub-directory of the specified path(s). You can use this flag multiple times. The result is appended to a list of default unix root folders (see bindit.IGNORE_PATH).

-l, –loglevel

Set the verbosity of log messages printed to the shell standard out. Default level is INFO, try DEBUG for more detail.

Combining user-defined and automatic binds

We have seen that bindit defaults to creating new bind mounts in the /bindit directory inside the container. But bindit also detects user-specified binds, and will use them whenever possible (for instance, when the user-specified bind is a parent of the input path):

$ mkdir -p foo/bar
$ bindit --dryrun docker run -v $(PWD):/container alpine:latest ls foo
docker run -v /Users/jc01/temp:/container alpine:latest ls /container/foo

Limitations

There are limitations to what paths bindit can detect automatically. The workaround for these is typically to use absolute paths or to specify manual binds.

Specifying output paths

Bindit can only detect relative paths when they exist. Absolute paths work either way. This can trip you up when calling a container application with an argument that defines the output file path. These must always be absolute (unless it’s a folder, or you are otherwise modifying or overwriting an existing path).

This can lead to seemingly baffling behavior. For example, suppose we want to use ImageMagick to downsize an image and save it to a new name. We use --dryrun to preview what the final docker run command will look like. Note that the input image has been re-mapped correctly, but the output image hasn’t because it doesn’t exist yet:

$ touch in.jpg
$ rm -f out.jpg
$ bindit --dryrun docker run dpokidov/imagemagick in.jpg -resize 100x100 out.jpg
docker run -v /Users/jc01/temp:/bindit/Users/jc01/temp dpokidov/imagemagick \
   /bindit/Users/jc01/temp/in.jpg -resize 100x100 out.jpg

If we instead specify an absolute output path, both in.jpg and out.jpg get re-mapped to the correct locations:

$ bindit --dryrun docker run dpokidov/imagemagick in.jpg -resize 100x100 "$PWD"/out.jpg
docker run -v /Users/jc01/temp:/bindit/Users/jc01/temp dpokidov/imagemagick \
   /bindit/Users/jc01/temp/in.jpg -resize 100x100 /bindit/Users/jc01/temp/out.jpg

Handling implicit output paths

Bindit can only recognize paths that are explicitly provided when the container runner is called. If you are using bindit to wrap an application that generates new files without any API control over where they go (for instance by writing to cwd as in the default gzip -d behavior), this won’t work because bindit won’t be able to anticipate this output.