Docker build time arguments - docker

I have a bunch of Dockerfiles that are build from a common automated place using the same build command:
docker build -t $name:$tag --build-arg BRANCH=$branch .
Some of the Dockerfiles contain this:
ARG BRANCH=master
And that argument is used for some steps of the image build.
But for some Dockerfiles which doesn't need that argument I get this error at the end:
One or more build-args [BRANCH] were not consumed, failing build.
How can I overcome this problem without including the argument to all the Dockerfiles?

Have you considered grepping your Dockerfile for BRANCH and using it result to decide if you should supply your ARG or not?
You could replace your automation build trigger with something like:
if grep BRANCH Dockerfile; then docker build -t $name:$tag --build-arg BRANCH=$branch .; else docker build -t $name:$tag . ; fi

I don't see any documented way to avoid this error without changing your input or your Dockerfile. robertobado already covers changing your input. As a second option, you can include an effectively unused build arg at the end of your Dockerfile which would have a very minor impact on your build.
ARG BRANCH=undefined
RUN echo "Built from branch ${BRANCH}"
Since this doesn't modify the filesystem, I believe the image checksum will be identical.

Related

Bust cache bust within Dockerfile without providing external build args

We have a Dockerfile, where at a certain point would like no caching to happen.
Currently we are using
ENV CACHE_BUST=$($RANDOM)
Upon further inspection, funny enough that gets cached:
Step 1/1 : ENV CACHE_BUST=$($RANDOM)
---> Using cache
Is there any way from inside the Dockerfile to bust the cache without passing in a unique build-arg (Like docker build . --build-arg CACHE_BUST=$(date +%s)) in the build step?
Update: Reviewing this one, it looks like you injected the cache busting option incorrectly in two ways:
ENV is not an ARG
The $(x) syntax is not a variable expansion, you need curly brackets (${}), not parenthesis ($()).
To break the cache on the next run line, the syntax is:
ARG CACHE_BUST
RUN echo "command with external dependencies"
And then build with:
docker build --build-arg CACHE_BUST=$(date +%s) .
Why does that work? Because during the build, the values for ARG are injected into RUN commands as environment variables. Changing an environment variable results in a cache miss on the new build.
To bust the cache, one of the inputs needs to change. If the command being run is the same, the cache will be reused even if the command has external dependencies that have changed, since docker cannot see those external dependencies.
Options to work around this include:
Passing a build arg that changes (e.g. setting it to a date stamp).
Changing a file that gets included into the image with COPY or ADD.
Running your build with the --no-cache option.
Since you do not want to do option 1, there is a way to do option 3 on a specific line, but only if you can split up your Dockerfile into 2 parts. The first Dockerfile has all the lines as you have today up to the point you want to break the cache. Then the second Dockerfile has a FROM line to depend on the first Dockerfile, and you build that with the --no-cache option. E.g.
Dockerfile1:
FROM base
RUN normal steps
Dockerfile2
FROM intermediate
RUN curl external.jar>file.jar
RUN other lines that cannot be cached
CMD your cmd
Then build with:
docker build -f Dockerfile1 -t intermediate .
docker build -f Dockerfile2 -t final --no-cache .
The only other option I can think of is to make a new frontend with BuildKit that allows you to inject an explicit cache break, or unique variable that results in a cache break.
You can add ADD layer with the downloading of some dynamic page from stable source in the beginning of Dockerfile. Image will be always re-built without using a cache.
Just an example of Dockerfile:
FROM alpine:3.9
ADD https://google.com cache_bust
RUN apk add --no-cache wget
p.s. I believe you are aware of docker build --no-cache option.

docker build - exactly 1 argument

I've been trying to build syntaxnet on my ubuntu setup and bumped into a problem (simple as it may be) that I had hard time finding the solution to.
Whenever I try to build using the command:
docker build -t dragnn-oss:latest-minimal -f docker-devel/Dockerile.min
I get the error message:
"docker build" requires exactly 1 argument(s).
Now, my docker version is 17.06 and according to this page,
[docker: "build" requires 1 argument. See 'docker build --help', I should be able to specify a Dockerfile that is located in a different directory, so I don't see what the problem is.
Edit: I created a symlink by doing:
ln -s docker-devel/Dockerfile.min link1
Then I just went through with the command:
docker build -t dragnn-oss:latest-minimal -f link1 .
and it worked.
I thought I did not need to put the . at the end since I specified the Dockerfile with -f but learned the mistake.
You need to add a dot at the end, either
docker build -t mytag .
or
docker build -t dragnn-oss:latest-minimal -f docker-devel/Dockerile.min .
see also
docker: "build" requires 1 argument. See 'docker build --help'
While the other answers are all correct and you've found the solution, I want to point you to the documentation for docker build for anyone stumbling upon this. This is what the command is supposed to look like:
$ docker build [OPTIONS] PATH
In your example, you specified two options (-f and -t). But you didn't specify the PATH argument. Citing the documentation:
The PATH specifies where to find the files for the “context” of the build on the Docker daemon.
The context is sent to the Docker daemon when building an image. You can just use . if you want to use the correct directory as Docker's build context, but as others have mentioned, you can also specify a specific directory, e.g. docker-devel.
Specify a directory and in this directory, a Dockerfile :
docker build -t dragnn-oss:latest-minimal -f Dockerile.min docker-devel
You need to add an argument to the end with just the path.
If it's the current directory then . (simple dot) will suffice.
The -f param is just for the filename (Dockerfile being the default)
docker build -t dragnn-oss:latest-minimal -f Dockerile.min docker-devel

force a docker build to 'rebuild' a single step

I know docker has a --no-cache=true option to force a clean build of a docker image. For me however, all I'd really like to do is force the last step to run in my dockerfile, which is a CMD command that runs a shell script.
For whatever reason, when I modify that script and save it, a typical docker build will reuse the cached version of that step. Is there a way to force docker not to do so, just on that one portion?
Note that this would invalidate the cache for all Dockerfile directives after that line. This is requested in Issue 1996 (not yet implemented, and now (2021) closed), and issue 42799 (mentioned by ub-marco in the comments).
The current workaround is:
FROM foo
ARG CACHE_DATE=2016-01-01
<your command without cache>
docker build --build-arg CACHE_DATE=$(date) ....
That would invalidate cache after the ARG CACHE_DATE line for every build.
acdcjunior reports in the comments having to use:
docker build --build-arg CACHE_DATE=$(date +%Y-%m-%d_%H:%M:%S)
Another workaround from azul:
Here's what I am using to rebuild in CI if changes in git happened:
export LAST_SERVER_COMMIT=`git ls-remote $REPO "refs/heads/$BRANCH" | grep -o "^\S\+"`
docker build --build-arg LAST_SERVER_COMMIT="$LAST_SERVER_COMMIT"
And then in the Dockerfile:
ARG LAST_SERVER_COMMIT
RUN git clone ...
This will only rebuild the following layers if the git repo actually changed.

docker: "build" requires 1 argument. See 'docker build --help'

Trying to follow the instructions for building a docker image from the docker website.
https://docs.docker.com/examples/running_redis_service/
this is the error I get will following the instructions on the doc and using this Dockerfile
FROM ubuntu:14.04
RUN apt-get update && apt-get install -y redis-server
EXPOSE 6379
ENTRYPOINT ["/usr/bin/redis-server"]
sudo docker build -t myrepo/redis
docker: "build" requires 1 argument. See 'docker build --help'.
How do resolve?
You need to add a dot, which means to use the Dockerfile in the local directory.
For example:
docker build -t mytag .
It means you use the Dockerfile in the local directory, and if you use docker 1.5 you can specify a Dockerfile elsewhere. Extract from the help output from docker build:
-f, --file="" Name of the Dockerfile(Default is 'Dockerfile' at context root)
In my case this error was happening in a Gitlab CI pipeline when I was passing multiple Gitlab env variables to docker build with --build-arg flags.
Turns out that one of the variables had a space in it which was causing the error. It was difficult to find since the pipeline logs just showed the $VARIABLE_NAME.
Make sure to quote the environment variables so that spaces get handled correctly.
Change from:
--build-arg VARIABLE_NAME=$VARIABLE_NAME
to:
--build-arg VARIABLE_NAME="$VARIABLE_NAME"
Did you copy the build command from somewhere else (webpage or some other file)? Try typing it in from scratch.
I copied a build command from an AWS tutorial and pasted it into my terminal and was getting this error. It was driving me crazy. After typing it in by hand, it worked! Looking closer and my previous failed commands, I noticed the "dash" character was different, it was a thinner, longer dash character than I got if I typed it myself using the "minus/dash" key.
Bad:
sudo docker build –t foo .
Good:
sudo docker build -t foo .
Can you see the difference?.. Cut and paste is hard.
In case anyone is running into this problem when trying to tag -t the image and also build it from a file that is NOT named Dockerfile (i.e. not using simply the . path), you can do it like this:
docker build -t my_image -f my_dockerfile .
Notice that docker expects a directory as the parameter and the filename as an option.
Use the following command
docker build -t mytag .
Note that mytag and dot has a space between them . This dot represents the present working directory .
Just provide dot (.) at the end of command including one space.
example:
command: docker build -t "blink:v1" .
Here you can see "blink:v1" then a space then dot(.)
Thats it.
You Need a DOT at the end...
So for example:
$ docker build -t <your username>/node-web-app .
It's a bit hidden, but if you pay attention to the . at the end...
From the command run:
sudo docker build -t myrepo/redis
there are no "arguments" passed to the docker build command, only a single flag -t and a value for that flag. After docker parses all of the flags for the command, there should be one argument left when you're running a build.
That argument is the build context. The standard command includes a trailing dot for the context:
sudo docker build -t myrepo/redis .
What's the build context?
Every docker build sends a directory to the build server. Docker is a client/server application, and the build runs on the server which isn't necessarily where the docker command is run. Docker uses the build context as the source for files used in COPY and ADD steps. When you are in the current directory to run the build, you would pass a . for the context, aka the current directory. You could pass a completely different directory, even a git repo, and docker will perform the build using that as the context, e.g.:
docker build -t sudobmitch/base:alpine --target alpine-base \
'https://github.com/sudo-bmitch/docker-base.git#main'
For more details on these options to the build command, see the docker build documentation.
What if you included an argument?
If you are including the value for the build context (typically the .) and still see this error message, you have likely passed more than one argument. Typically this is from failing to parse a flag, or passing a string with spaces without quotes. Possible causes for docker to see more than one argument include:
Missing quotes around a path or argument with spaces (take note using variables that may have spaces in them)
Incorrect dashes in the command: make sure to type these manually rather than copy and pasting
Incorrect quotes: smart quotes don't work on the command line, type them manually rather than copy and pasting.
Whitespace that isn't white space, or that doesn't appear to be a space.
Most all of these come from either a typo or copy and pasting from a source that modified the text to look pretty, breaking it for using as a command.
How do you figure out where the CLI error is?
The easiest way I have to debug this, run the command without any other flags:
docker build .
Once that works, add flags back in until you get the error, and then you'll know what flag is broken and needs the quotes to be fixed/added or dashes corrected, etc.
On older versions of Docker it seems you need to use this order:
docker build -t tag .
and not
docker build . -t tag
You can build docker image from a file called docker file and named Dockerfile by default. It has set of command/instruction that you need in your docker container.
Below command creates image with tag latest, Dockerfile should present on that location (. means present direcotry)
docker build . -t <image_name>:latest
You can specify the Dockerfile via -f if the file name in not default (Dockerfile)
Sameple Docker file contents.
FROM busybox
RUN echo "hello world"
Open PowerShelland and follow these istruction.
This type of error is tipically in Windows S.O.
When you use command build need an option and a path.
There is this type of error becouse you have not specified a path whit your Dockerfile.
Try this:
C:\Users\Daniele\app> docker build -t friendlyhello C:\Users\Daniele\app\
friendlyhello is the name who you assign to your conteiner
C:\Users\Daniele\app\ is the path who conteins your Dockerfile
if you want to add a tag
C:\Users\Daniele\app> docker build -t friendlyhello:3.0 C:\Users\Daniele\app\
The following command worked for me. Docker file was placed in my-app-master folder.
docker build -f my-app-master/Dockerfile -t my-app-master .
My problem was the Dockerfile.txt needed to be converted to a Unix executable file. Once I did that that error went away.
You may need to remove the .txt portion before doing this, but on a mac go to terminal and cd into the directory where your Dockerfile is and the type
chmod +x "Dockerfile"
And then it will convert your file to a Unix executable file which can then be executed by the Docker build command.
#Using a file other than Dockerfile instead.
#Supose my file is `Dockerfile-dev`
docker build -t mytag - < Dockerfile-dev
In my case I was using a dash (slightly longer hyphen) symbol – before the t option was the problem.
docker build –t simple-node .
Replace with a hyphen/ minus symbol.
docker build -t simple-node .
I got this error when using Docker with Jenkins pipeline within a pipeline script. The solution was to use this syntax in the pipeline script:
docker.build("[my_docker_image_tag]", "-f ./path/to/my/Dockerfile.jvm .")
Docker Build Command Format
In your powershell :
There is this type of error because you have not specified a path whith your Dockerfile.
Try this:
$ docker build -t friendlyhello:latest -f C:\\TestDockerApp\\Dockerfile.txt
friendlyhello is the name you assign to your container and add the version , just use the :latest
-f C:\TestDockerApp\Dockerfile.txt
- you want to add a tag because the build command needs a parameter or tag
- The DockerFile is a text document so explicitly add the extension .txt
**Try this format :
$ docker build -t friendlyhello:latest -f C:\\TestDockerApp\\Dockerfile.txt .**

Create dynamic environment variables at build time in Docker

My specific use case is that I want to organize some data about the EC2 instance a container is running on and make i available as an environment variable. I'd like to do this when the container is built.
I was hoping to be able to do something like ENV VAR_NAME $(./script/that/gets/var) in my Dockerfile, but unsurprisingly that does not work (you just get the string $(./script...).
I should mention that I know the docker run --env... will do this, but I specifically want it to be built into the container.
Am I missing something obvious? Is this even possible?
Docker v1.9 or newer
If you are using Docker v1.9 or newer, this is possible via support for build time arguments. Arguments are declared in the Dockerfile by using the ARG statement.
ARG REQUIRED_ARGUMENT
ARG OPTIONAL_ARGUMENT=default_value
When you later actually build your image using docker build you can pass arguments via the flag --build-arg as described in the docker docs.
$ docker build --build-arg REQUIRED_ARGUMENT=this-is-required .
Please note that it is not recommended to use build-time variables for passwords or secrets such as keys or credentials.
Furthermore, build-time variables may have great impact on caching. Therefore the Dockerfile should be constructed with great care to be able to utilize caching as much as possible and therein speed up the building process.
Edit: the "docker newer than v1.9"-part was added after input from leedm777:s answer.
Docker before v1.9
If you are using a Docker-version before 1.9, the ARG/--build-arg approach was not possible. You couldn't resolve this kind of info during the build so you had to pass them as parameters to the docker run command.
Docker images are to be consistent over time whereas containers can be tweaked and considered as "throw away processes".
More info about ENV
A docker discussion about dynamic builds
The old solution to this problem was to use templating. This is not a neat solution but was one of very few viable options at the time. (Inspiration from this discussion).
save all your dynamic data in a json or yaml file
create a docker file "template" where the dynamic can later be expanded
write a script that creates a Dockerfile from the config data using some templating library that you are familiar with
Docker 1.9 has added support for build time arguments.
In your Dockerfile, you add an ARG statement, which has a similar syntax to ENV.
ARG FOO_REQUIRED
ARG BAR_OPTIONAL=something
At build time, you can pass pass a --build-arg argument to set the argument for that build. Any ARG that was not given a default value in the Dockerfile must be specified.
$ docker build --build-arg FOO_REQUIRED=best-foo-ever .
To build ENV VAR_NAME $(./script/that/gets/var) into the container, create a dynamic Dockerfile at build time:
$ docker build -t awesome -f Dockerfile .
$ # get VAR_NAME value:
$ VAR_VALUE=`docker run --rm awesome \
bash -c 'echo $(./script/that/gets/var)'`
$ # use dynamic Dockerfile:
$ {
echo "FROM awesome"
echo "ENV VAR_NAME $VAR_VALUE"
} | docker build -t awesome -
https://github.com/42ua/docker-autobuild/blob/master/emscripten-sdk/README.md#build-docker-image

Resources