I have a docker compose file that builds and runs a couple of dockerfiles locally. I am using multi-stage builds and I pass an argument into my docker build commands. The following works perfectly:
docker build -t local/admin-api --build-arg SONAR_TOKEN=XXXXXXXXXXXXXXX .
I am trying to replicate that in my docker compose file with:
services:
comply:
build:
context: ./websites/comply/
dockerfile: Dockerfile
args:
SONAR_TOKEN: xxxxxxxxxxx
ports:
- "8080:80"
In my dockerfile I use:
FROM kinectify.azurecr.io/buildbase:ci-631 as build
ARG PAT
ARG SONAR_PROJECT_KEY=xxxxxxx
ARG SONAR_ORGANIZATION_KEY=xxxxxxx
ARG SONAR_HOST_URL=https://sonarcloud.io
ARG SONAR_TOKEN
RUN echo "Token: ${SONAR_TOKEN} | hostURL: ${SONAR_HOST_URL}"
I am aware of the scoping of build args, and as you can see above, the arg is defined after my FROM statement. I have also tried setting the arg to an environment variable, but that doesn't seem to matter. Additionally, the SONAR_HOST_URL is being output correctly in echo statement (and in the place where I am actually using the argument). I am running the build with:
docker compose build comply
** UPDATE **
Strangely, when I run docker compose up -d --build it does pass variable. I am not sure what the difference is though.
This is a known issue that's been fixed in docker compose. You are likely waiting for the next release still. Using docker-compose instead of docker compose should also work while you wait for that release.
Related
Docker build tricky behavior:
Dockerfile
ARG php_version="7.2"
ARG flavor="stretch"
FROM php:${php_version}-fpm-${flavor}
#ARG php_version="7.2"
ENV php_v $php_version
CMD echo $php_v
If you build it and run:
docker build -t args:1.0 .
docker run -it --name testargs args:1.0
Output is empty string instead of the expected "7.2"
But if the third ARG line is uncommented we get "7.2"
Why does it behave this way?
Each FROM in a Dockerfile represents a new build stage and an ARG declared before the FROM is not available for use in the newer build stages starting with another FROM.
To quote the relevant section of the doc:
An ARG declared before a FROM is outside of a build stage, so it can’t
be used in any instruction after a FROM.
I am trying to build an image/container with docker compose. The container builds/runs successfully, but the image REPOSITORY and TAG both appear as <none> in the output for docker images and the container gets an auto-generated name (e.g. eloquent_wiles). I would for it to tag the image/container with the names specified in my config files (in this case I would like them to be named 'myservice' and the image to be tagged 'v2').
I have the following docker-compose.yml:
version: '3'
services:
myservice:
build: .
image: myservice:v2
container_name: myservice
ports:
- "1337:1337"
This is my Dockerfile:
FROM node:10
WORKDIR /usr/src/myservice
COPY . /usr/src/myservice
EXPOSE 1337/tcp
RUN yarn \
&& yarn transpile \
&& node ./build/grpc-server.js
docker -v gives Docker version 18.09.2, build 6247962
docker-compose -v gives docker-compose version 1.22.0, build f46880fe
And I am running docker-compose build. I get the same results using docker-compose version 2.
I don't suppose anyone can spot what I'm doing wrong?
Build a named image: docker build -t <repo>:<tag> . in the directory where the Dockerfile is.
Deploy a named service: docker stack deploy -c <your_yaml_file> <your_stack> --with-registry-auth in the directory where your YAML is.
That was very silly of me. The issue was with the last line of the Dockerfile where you can see I start the server as part of the build process instead of as an entrypoint, blocking the build from reaching the step where it tags the images.
Thanks to #Mihai for pointing out that the <none>:<none> image is an intermediate and not the result of my build.
Scripts or executables can run on a Docker container automatically when running docker-compose up --build with a configured Dockerfile containing syntax RUN, through which a script or executables etc. can run automatically during build.
Question: But is it possible to achieve the same goal, say run executables or scripts, with docker-compose only without a Dockerfile? In this case there are probably the similar command in docker-compose.yml like the RUN in Dockerfile ?
What you can do in docker-compose is overriding the default command that is executed after the build, by setting "command".
See here: https://docs.docker.com/compose/compose-file/#command
I don't think there is a RUN-like thing for docker-compose.yml.
if I understand your problem, then the answer is yes: on your container configuration in your docker-compose.yml file use:
entrypoint: ["/bin/sh","-c"]
command:
- |
ls -la
echo 'hello'
or whatever is your commands you want to run.
How can I pass a host environment variable (like user and hostname) to a dockerfile?
For example, if my username is taha:
echo $USER
taha
How do I write my Docker file to get the same output?
FROM centos:centos7
ARG myuser=$USER
CMD echo $myuser
I was experiencing the same issue. My solution was to provide the variable inside of a docker-compose.yml because yml supports the use of environment variables.
In my opinion this is the most efficient way for me because I didn't like typing it over and over again in the command line using something like docker run -e myuser=$USER . . .
Declaring ENV myuser=$USER will NOT work, in the container, $myuser will be set to null.
So your docker-compose.yml could look something like this:
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
environment:
- "myuser=${USER}"
and can be run with the short command docker-compose up
To check that the variable has been applied, run docker exec -it container-name printenv to list all variables in the container.
When you start your docker container you can pass environment variables using the -e option like so:
docker run -it <image> -e USER=$USER /bin/bash
I had a similar use-case where I wanted to be able to specify a version for the image. This has a slight extra requirement that you must specify the ARG before the FROM.
First we specify IMAGE_VERSION in the Dockerfile, as per the question I'm also including a USER arg that we can pass in too:
# give it a default of latest
# declare the ARG before FROM
ARG IMAGE_VERSION=latest
FROM centos:${IMAGE_VERSION}
ARG myuser
CMD echo $myuser
Then, as from the Docker compose docs on args:
Add build arguments, which are environment variables accessible only during the build process.
You can add these in your docker-compose.yml for example:
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
"myuser=${USER}"
IMAGE_VERSION
Then as long as you have IMAGE_VERSION set in your environment it will be passed through.
you need to use the ENV setting in your dockerfile
ENV myuser=$USER
https://docs.docker.com/engine/reference/builder/#env
You can set image name when building a custom image, like this:
docker build -t dude/man:v2 . # Will be named dude/man:v2
Is there a way to define the name of the image in Dockerfile, so I don't have to mention it in the docker build command?
Using -t on invocation
How to build an image with custom name without using yml file:
docker build -t image_name .
How to run a container with custom name:
docker run -d --name container_name image_name
Workaround using docker-compose
Tagging of the image isn't supported inside the Dockerfile. This needs to be done in your build command. As a workaround, you can do the build with a docker-compose.yml that identifies the target image name and then run a docker-compose build. A sample docker-compose.yml would look like
version: '2'
services:
man:
build: .
image: dude/man:v2
That said, there's a push against doing the build with compose since that doesn't work with swarm mode deploys. So you're back to running the command as you've given in your question:
docker build -t dude/man:v2 .
Personally, I tend to build with a small shell script in my folder (build.sh) which passes any args and includes the name of the image there to save typing. And for production, the build is handled by a ci/cd server that has the image name inside the pipeline script.
Workaround using docker-compose
Here is another version if you have to reference a specific docker file:
version: "3"
services:
nginx:
container_name: nginx
build:
context: ../..
dockerfile: ./docker/nginx/Dockerfile
image: my_nginx:latest
Then you just run
docker-compose build
My Dockerfile alone solution is adding a shebang line:
#!/usr/bin/env -S docker build . --tag=dude/man:v2 --network=host --file
FROM ubuntu:22.04
# ...
Then chmod +x Dockerfile and ./Dockerfile is to go.
I even add more docker build command line arguments like specifying a host network.
NOTE: env with -S/--split-string support is only available for newer coreutils versions.
With a specific Dockerfile you could try:
docker build --tag <Docker Image name> --file <specific Dockerfile> .
for example
docker build --tag second --file Dockerfile_Second .
Workaround using Docker (and a Makefile)
Generally in Docker you can't say what you want the image to be tagged as in the Dockerfile. So what you do is
Create a Dockerfile
Create a Makefile
.PHONY: all
all: docker build -t image_name .
Use make instead of invoking docker build directly
Or, use buildah
But here is a better idea... Don't build images with Docker! Instead build them with buildah, the new build tool provided by the podman crew which uses shell (or any language), allows building in the cloud easily (without using a different project like kaniko), and allows rootless building of images! At the end of the build script just save the image inside with buildah commit. Here is what it looks like.
#!/bin/sh
# Create a new offline container from the `alpine:3` image, return the id.
ctr=$(buildah from "alpine:3")
# Create a new mount, return the path on the host.
mnt=$(buildah mount "$ctr")
# Copy files to the mount
cp -Rv files/* "$mnt/"
# Do some things or whatever
buildah config --author "Evan Carroll" --env "FOO=bar" -- "$ctr"
# Run a script inside the container
buildah run "$ctr" -- /bin/sh <<EOF
echo "This is just a regular shell script"
echo "Do all the things."
EOF
# Another one, same layer though
buildah run "$ctr" -- /bin/sh <<EOF
echo "Another one!"
echo "No excess layers created as with RUN."
EOF
# Commit this container as "myImageName"
buildah commit -- "$ctr" "myImageName"
Now you don't have to hack around with a Makefile. You have one shell script that does everything, and is far more powerful than a Dockerfile.
Side note, buildah can also build from Dockerfiles (using buildah bud), but this short coming is with the Dockerfile. So that won't help.