context or workdir for docker-compose - docker

I'm learning docker
I need to specify the working directory for a docker image, I think that'll be something like this:
version: '2'
services:
test:
build:
context: ./dir
Now I want to make the image python:onbuild to run on the ./dir, but I dont want to create any Dockerfile inside the ./dir.
The docker-compose manual says nothing about that.
Is it possible? How to do that?

I think you're looking for working_dir. Search for "working_dir" in the docker-compose reference.

You can specify the working directory as follows.
version: '2'
services:
test:
build:
context: /path/to/source/dir/
dockerfile: /path/to/custom-Dockerfile/relative/tocontextdir/Dockerfile
working_dir: /app

Possibly not exactly what you were looking for, but you can specify the "context" for the docker build to be a different directory to where the actual Dockerfile lives.
build:
context: ./folder/containing/files
dockerfile: path/to/dockerfile/relative/to/context/Dockerfile
Any ADD/COPY commands in your Dockerfile then act as if they are relative to the context regardless of where the Dockerfile actually is.

The build configuration in Docker Compose just ends up in a call to docker build, so you need to have a Dockerfile to use that workflow.
As the docs for python:onbuild say, you can start with a minimal Dockerfile that just contains FROM python:onbuild. But as they also say, :onbuild isn't a great option, you'll have much more control building your own Dockerfile FROM python.

Related

How to pass host computer working directory as a build argument to my Dockerfile using docker-compose?

I would like to pass the current working directory (in this case the directory on the host computer where docker-compose.yml is located) as a build argument to my Dockerfile, so I can use that host computer directory later inside the container for some specific things.
My approach was to first define an ARG and ENV variable in the Dockerfile like this:
FROM python:3.8
WORKDIR /usr/src/runner
ARG HOST_WORKING_DIRECTORY
ENV HOST_WORKING_DIRECTORY=$HOST_WORKING_DIRECTORY
and then define my build argument (which I would like to be the current working directory) in the docker-compose.yml like this:
version: "3.7"
services:
runner:
build:
context: "./runner"
dockerfile: "Dockerfile"
args:
HOST_WORKING_DIRECTORY: $PWD
However, this does not work. When I do print(os.environ["HOST_WORKING_DIRECTORY"]) inside my running container I get empty string in response.
Any ideas how can I achieve this?
Update:
Interestingly I can achieve this when I build my image directly with Docker command line like this:
docker build --build-arg HOST_WORKING_DIRECTORY="${PWD}" -t myimage .
However, this does not help because I need to build my image using docker-compose.
You need to use string interpolation ("${ENV_VAR}") to get the actual value of an environment variable (see documentation).
version: "3.7"
services:
runner:
build:
context: "./runner"
dockerfile: "Dockerfile"
args:
HOST_WORKING_DIRECTORY: "${PWD}"

Docker-compose for multi-stage build?

Let's say that we have docker-compose.yml which build few services with Dockerfile's in different locations like this:
version: '3.4'
services:
service1:
build: ./service1-dir
service2:
build: ./service2-dir
Let's say that in Dockerfile of service1 we have some folder that we already copy.
Can I pass this folder to docker file of service2?
With another words - can I use multi-stage build technique to pass layers between different Dockerfiles or multi-stage build should be only in one Dockerfile?
You can split things up the way you describe; it'll probably be more robust to do things in two stages in a single Dockerfile, or to do the primary part of your build using host tools.
There are two essential parts to this. The first is that, in your docker-compose.yml file, if you specify both a build: description and an image: name, then Docker Compose will tag the built image for you. The second is that the Dockerfile COPY directive can copy content --from a preceding build stage or from an arbitrary other image. So if your docker-compose.yml says
version: '3'
services:
service1:
build: ./service1-dir
image: me/service1
service2:
build: ./service2-dir
and service2-dir/Dockerfile says
COPY --from=me/service1 /app/somefile .
it will copy content from one image to the other.
The one challenge here is that docker-compose build doesn't specify the order the images are built in. If it builds service2 first, it will get old content from the previous build of service1 (or fail if it's the initial build). To do this reliably, you need to do something like
docker-compose build service1
docker-compose build
docker-compose up -d
If the build sequence isn't too complex, just including it in both Dockerfiles could make sense. It can also work to built whatever artifacts you need on the host, and have your Dockerfiles copy that in as-is instead of building it themselves; this works well especially if that content is platform-neutral (Java .jar files, HTML/Javascript/CSS files from a Webpack build for a browser application).

Docker compose with name other than dockerfile

I have used docker to create CLI interfaces where I test my code. These are named reasonably as:
proj_root/.../docks/foo.dockerfile
proj_root/.../docks/bar.dockerfile
Because there is more than one dock involved, the top level "Dockerfile" at the project root is unreasonable. Although I can't copy ancestor directories when building in docker, I can clone my entire repo.
So my project architecture works for me.
Next, I look up docker-compose because I need to match my docker cards up against a postgres db and expose some ports.
However, docker-compose seems to be anchored to the hard-coded '"Dockerfile" in the current working directory' user concept from the perspective of the command line interface.
But! I see the error message implies the tool is capable of looking for an arbitrarily named dockerfile:
ERROR: Cannot locate specified Dockerfile: Dockerfile
The question is: how do I set docker-compose off looking for foo.dockerfile rather than ./Dockerfile?
In your docker-compose, under the service:
services:
serviceA:
build:
context: <folder of your project>
dockerfile: <path and name to your Dockerfile>
As mentioned in the documentation of docker-compose.yml, you can overwrite the Dockerfile filename within the build properties of your docker-compose services.
For example:
version: 3
services:
foo:
image: user/foo
build:
context: .../docks
dockerfile: foo.Dockerfile
bar:
image: user/bar
build:
context: .../docks
dockerfile: bar.Dockerfile

Referencing files outside of build context

I would like to organize my docker image files based on the service i'm building in a .docker directory. To start, I'd like to build out the web docker container. Note how in the docker file, I am copying the webroot files to the docker container. The issue is, in my docker-compose.yml, when the image gets built, it references the root from the .docker/web directory instead of the actual webroot. I tried using a relative path in the docker file which didn't seem to work. Any ideas how I can accomplish this?
version: '3'
services:
web:
build:
context: .docker/web
dockerfile: Dockerfile
Dockerfile
FROM webdevops/php-apache-dev
ADD ./../../ /var/www/html
Error
ERROR: Service 'web' failed to build: ADD failed: Forbidden path outside the build context: ../../
It would be helpful to see more of the directory tree you're working with, but I'll make a guess at what you're trying to do.
The context setting in docker-compose.yml defines the starting point for relative paths passed as the first argument to ADD, and relative paths can't point to directories higher than that in the tree. If I understand your case correctly, you should be able to do:
version: '3'
services:
web:
build:
context: .
dockerfile: .docker/web/Dockerfile
And change your Dockerfile to have:
ADD . /var/www/html

What is the difference between `docker-compose build` and `docker build`?

What is the difference between docker-compose build and docker build?
Suppose in a dockerized project path there is a docker-compose.yml file:
docker-compose build
And
docker build
docker-compose can be considered a wrapper around the docker CLI (in fact it is another implementation in python as said in the comments) in order to gain time and avoid 500 characters-long lines (and also start multiple containers at the same time). It uses a file called docker-compose.yml in order to retrieve parameters.
You can find the reference for the docker-compose file format here.
So basically docker-compose build will read your docker-compose.yml, look for all services containing the build: statement and run a docker build for each one.
Each build: can specify a Dockerfile, a context and args to pass to docker.
To conclude with an example docker-compose.yml file :
version: '3.2'
services:
database:
image: mariadb
restart: always
volumes:
- ./.data/sql:/var/lib/mysql
web:
build:
dockerfile: Dockerfile-alpine
context: ./web
ports:
- 8099:80
depends_on:
- database
When calling docker-compose build, only the web target will need an image to be built. The docker build command would look like :
docker build -t web_myproject -f Dockerfile-alpine ./web
docker-compose build will build the services in the docker-compose.yml file.
https://docs.docker.com/compose/reference/build/
docker build will build the image defined by Dockerfile.
https://docs.docker.com/engine/reference/commandline/build/
Basically, docker-compose is a better way to use docker than just a docker command.
If the question here is if docker-compose build command, will build a zip kind of thing containing multiple images, which otherwise would have been built separately with usual Dockerfile, then the thinking is wrong.
Docker-compose build, will build individual images, by going into individual service entry in docker-compose.yml.
With docker images, command, we can see all the individual images being saved as well.
The real magic is docker-compose up.
This one will basically create a network of interconnected containers, that can talk to each other with name of container similar to a hostname.
Adding to the first answer...
You can give the image name and container name under the service definition.
e.g. for the service called 'web' in the below docker-compose example, you can give the image name and container name explicitly, so that docker does not have to use the defaults.
Otherwise the image name that docker will use will be the concatenation of the folder (Directory) and the service name. e.g. myprojectdir_web
So it is better to explicitly put the desired image name that will be generated when docker build command is executed.
e.g.
image: mywebserviceImage
container_name: my-webServiceImage-Container
example docker-compose.yml file :
version: '3.2'
services:
web:
build:
dockerfile: Dockerfile-alpine
context: ./web
ports:
- 8099:80
image: mywebserviceImage
container_name: my-webServiceImage-Container
depends_on:
- database
Few additional words about the difference between docker build and docker-compose build.
Both have an option for building images using an existing image as a cache of layers.
with docker build, the option is --cache-from <image>
with docker-composer, there is a tag cache_from in the build section.
Unfortunately, up until now, at this level, images made by one are not compatible with the other as a cache of layers (Ids are not compatible).
However, docker-compose v1.25.0 (2019-11-18), introduces an experimental feature COMPOSE_DOCKER_CLI_BUILD so that docker-compose uses native docker builder (therefore, images made by docker build can be used as a cache of layers for docker-compose build)

Resources