WARNING: I haven't had time to maintain this project. Use them at your own risk. Thank you for your understanding.
- Description
- Getting started
- Install and Usage
- Syntax of Bockerfile
- Dockerfile vs. Bockerfile
- Bocker.sh script
- Important notes
- History
- License. Author
Bocker
makes your Dockerfile
reusable.
The name is combined from B(ash)
and (D)ocker
.
Dockerfile
is a mix of shell commands, run-time settings and stuff.
It looks simple at first, but in a long run, you'll have some problems:
- Have to
copy
andpaste
common codes betweenDockerfile
s; (Examples:FROM
,MAINTAINER
,ENV
,...); - No way to do some basic checks. For example, the
COPY /foo /bar
will raise an error if/foo
doesn't exist; if that's the case, you have no way to tellCOPY
to continue; - It's hard to define and call a
sub-routine
inRUN
statement, becauseRUN
is one-line statement. Yes, you can try to do that with a mess of unreadable and un-maintainable codes; - No way to include some useful parts from other
Dockerfile
; - No way to create the full
Dockerfile
of an image and its ancestors.
This project is to solve these problems. It will read some Bash
source files, and write new Dockerfile
to STDOUT
. The output
is cacheable, and can be executed by docker build
command.
Take a look at a quite minimal example in examples/Bockerfile.alpine
.
#!/usr/bin/env bash
# The default Alpine shell /bin/sh
ed_shell /bin/sh
ed_from alpine:3.8
ed_env --later Hello World
ed_bocker() {
:
}
To use this file, type the following commands
$ cd examples/
$ ../bocker.sh Bockerfile.alpine > Dockerfile.alpine
New contents are exactly a Dockerfile
for your build.
Overloading? Improve caching with --later
?
Use ship
instead of ADD / COPY
commands?
See more from examples/README.md
at
https://github.com/icy/bocker/blob/master/examples/README.md
or a collection of Bockerfile
at
https://github.com/icy/docker/tree/master/bocker.
Bocker
requires the popular tools:
- On local machine where you run
bocker
script:Bash
,base64
,grep
,sed
,gawk
; - On base image:
bash
orsh
,base64
.
base64
is a basic tool from coreutils
package.
There is only one Bash
script bocker.sh
. Put this script in
one of your binary directories, and start it with Bockerfile
$ bocker.sh MyBockerfile >/dev/null # to see if there is any error
$ bocker.sh MyBockerfile # to see Dockerfile output
The output is written to STDOUT
. You should check if there is anything
wrong from STDERR
, because Bocker
is unable to check if your source
file has problem.
-v
(--version
): Print the script version;-t
(--test
): Check if there is any problem with input.
All Bockerfile
s are Bash
source files. That means you can write
your source in some small files, and include them in other files.
The first rule is that every method is started with ed_
.
There are some basic methods to define your meta information. For your overview, let's see:
ed_from YOUR_BASE_IMAGE
ed_maintainer "YOUR INFORMATION"
ed_env FOO /bar/
ed_expose 80
ed_volume /example.net/
ed_cmd '["/supervisor.sh", "foo", "bar"]'
ed_ship foobar
....
Think of FROM
, MAINTAINER
, EXPOSE
. They are:
ed_from
: Define yourFROM
information;ed_maintainer
: Define yourMAINTAINER
information;ed_env
: Define newENV
instruction; Use--later
option if your environment is only needed at the run-time;ed_expose
: Specify a list of exposed ports;ed_volume
: Specify a list of volumes;ed_onbuild
: Specify trigger on the descendant image build;ed_cmd
: Define yourCMD
statement;ed_user
: Define yourUSER
statement; Must use with--later
option;ed_copy
: Define yourCOPY
statement; If you want to haveADD
, use--add
option. Must use with the option--later
;ed_entrypoint
: Define yourENTRYPOINT
statement;ed_ship
: Define a list of methods to be shipped to the image; That means, you can define a functioned_foobar
, and called_ship ed_foobar
to make this function available toDocker
at build time and run time. Actually, functions' definitions are written to the file/bocker.sh
in the result image, and that will be included at everyRUN
;ed_ship --later
: Likeed_ship
, but the contents are shipped at the very end ofDockerfile
. This is very useful when the functions are only needed at the run-time, because that really speeds up your build process. See example inexamples/lib/debian.sh
.)ed_reuse
: Load theBockerfile
(s) specified in argument, and re-useed_docker
from that source if any. Alled_docker
definitions are additive in order provided.
All these commands can be used multiple times, and/or be put in
your base libraries. (See examples/lib/core.sh
.)
The last statement of ed_from
(ed_maintainer
, ed_cmd
, ed_entrypoint
)
will win; other functions have additive effect.
You can define your set of methods as Bash
functions, each of them
has a name started by ed_
. For example, in examples/lib/debian.sh
,
you will see ed_apt_clean
that removes unused apt-get
variable data
to minimize the size of your result image.
This is a must-have function. Bocker
will raise error if you
don't define it.
This function should not contain any function from PREAMBLE
section.
It can have some special functions
ed_copy
: Define yourCOPY
statement;ed_add
: Define yourADD
statement;ed_user
: Define yourUSER
statement;ed_workdir
: Define yourWORKDIR
statement;ed_run
: Define yourRUN
statement;ed_group
: Group multiple methods into a singleRUN
statement.
Bocker
will read the contents of this ed_bocker
function,
replace every appearance of ed_*
by __ed_ship_method ed_*
.
That means, if you type ed_apt_clean
, Bocker
will invoke
__ed_ship_method ed_apt_clean
for you.
Because this is actually a replace-execute trick,
it's your duty to make your definition of ed_bocker
as simple
as possible. Don't use complex stuff like expansion and (WHO KNOWS
?)
If you have to do that, put your stuff under some functions,
ship them to the image with ed_ship
, and that's just enough.
Facts
Dockerfile
statements are ordered. First declared first run. InBockerfile
, most stuff inPREAMBLE
are un-ordered;Dockerfile
supports array form ofENV
,EXPOSE
,VOLUME
; butBockerfile
doesn't. This way helpsBockerfile
to glue declarations from multiple library files into a single statement;- To group
RUN
commands inDockerfile
, you have to use&&
and removeRUN
from the later statements. InBockerfile
, you simply useed_group
. See this example; - To declare a
Bash
function and use them in everyRUN
statement, you may put that definition in a file, useCOPY
to transfer the file to the container and load it, e.g,RUN source /mylib.sh; ...
; You can love this way or not. InBockerfile
, you simply useed_ship
for build-time methods, anded_ship --later
for run-time methods with a minimum number of layers.
Here is a table for quick reference.
Purpose | Dockerfile | Bockerfile (Preamble) | ed_bocker |
---|---|---|---|
Base image | FROM | ed_from | |
Base script | ed_reuse | ||
Base script | ed_source, source | ||
Maintainer | MAINTAINER | ed_maintainer | |
Volume expose | VOLUME | ed_volume | |
Port expose | EXPOSE | ed_expose | |
Init script | ENTRYPOINT | ed_entrypoint | |
Init command | CMD | ed_cmd | |
Int command | ONBUILD | ed_onbuild | |
Variable | ENV | ed_env [--later] | |
Build command | RUN | ed_bocker |
ed_foo , ed_run |
Build command | ADD | ed_copy --add --later | ed_add |
Build command | COPY | ed_copy --later | ed_copy |
Build command | USER | ed_user --later | ed_user |
Build command | WORKDIR | TODO | ed_workdir |
Declare method | N/A | ed_ship [--later] | |
Grouping | && | ed_group | |
Label | LABEL | ed_label | echo "LABEL foo=bar" |
Raw statement | echo "# Something" |
The result image has /bocker.sh
script that contains (almost) all
your functions.
When you use ed_ship
or invoke some command inside your ed_bocker
,
your function definitions are saved originally (except the comments,
of course) to the /bocker.sh
script in the result image.
This script only contains functions, and if you provide any arguments to it, they are considered as command in the environment where your functions are defined. For example
# ed_ship --later my_method
/bocker.sh ed_my_method
# /bocker.sh find
will invoke ed_my_method
(or find
command) that you have shipped.
Because of this, you can simply define a start-up
function, and
use /bocker.sh
to call them. That exactly means, bocker.sh
can
be used as your ENTRYPOINT
.
ed_bocker
is executed locally at run time, on your local machine. This is dangerous. Please don't add too much codes insideed_bocker
. That function should only contained_*
methods.- Any
RUN
generated byBocker
has option-xeu
set by default; That means any error will stop. If you want to have something else, you can always do that in youed_*
definition.
When the project is started, its name is EDocker
, that's why you see
ed_
prefixes. EDocker
isn't a good name, hence you see Bocker
now.
This work is released the terms of MIT
license.
The author is Anh K. Huynh.