Referencing files outside of build context - docker

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

Related

Why does building and image with docker compose fail but succeeds without it?

I am trying to build an image with docker compose and it fails, however it works with just docker. I have read some SO posts saying that the error thrown when failing happens when a file/folder cannot be found in the Dockerfile. The build works when building with docker so I dont know why it wouldn't work with docker-compose. Why is this happening?
The structure for this project is this:
parent_proj
|_proj
|_Dockerfile
|_docker-compose.yml
Here is my docker-compose file:
version: '3.4'
services:
integrations:
build:
context: .
dockerfile: proj/Dockerfile
network: host
image: int
ports:
- "5000:5000"
Here is the Dockerfile inside proj/
FROM openjdk:11
USER root
#RUN apt-get bash
ARG JAR_FILE=target/proj-0.0.1-SNAPSHOT.jar
COPY ${JAR_FILE} /app2.jar
ENTRYPOINT ["java","-jar", "/app2.jar"]
When I'm inside the proj folder. I can run
docker build . -t proj
The above succeeds and I can subsequently run the container. However when I am in parent_proj and run docker compose build it fails with the error message
failed to compute cache key: failed to walk
/var/lib/docker/tmp/buildkit-mount316454722/target: lstat
/var/lib/docker/tmp/buildkit-mount316454722/target: no such file or
directory
Why does this happen? How can I build successfully with docker-compose without restructuring the project?
thanks
Your Compose build options and the docker build options you show are different. The successful command is (where -f Dockerfile is the default):
docker build ./proj -t proj # -f Dockerfile
# context: image: dockerfile:
But your Compose setup is running
docker build . -t img -f proj/Dockerfile
# context: image: dockerfile:
Which one is right? In the Dockerfile, you
COPY target/proj-0.0.1-SNAPSHOT.jar /some/container/path
That target/... source path is always relative to the build-context directory (Compose context: option, the directory parameter to docker build), even if it looks like an absolute path and even if the Dockerfile is in a different directory. If that target directory is a subdirectory of proj then you need the first form.
There's a shorthand Compose build: syntax if the only thing you need to specify is the context directory, and I'd use that here. If you don't specifically care what the image name is (you're not pushing it to a registry) then Compose can pick a reasonable name on its own; you don't need to specify image:.
version: '3.8'
services:
integrations:
build: ./proj
ports:
- "5000:5000"

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

What is happening when using ../ with docker-compose volume

I am having problems with writing files out from inside a docker container to my host computer. I believe this is a privilege issue and prefer not to set privileged: True. A work around for writing out files is by pre-pending ../ to a volume in my docker-compose.yml file. For example,
version: '3'
services:
example:
volumes:
- ../:/example
What exactly is ../ doing here? Is it taking from the container's privileges and "going up" a directory to the host machine? Without ../, I am unable to write out files to my host machine.
Specifying a path as the source, as opposed to a volume name, bind mounts a host path to a path inside the container. In your example, ../ will be visible inside the container at /example on a recent version of docker.
Older versions of docker can only access the directory it is in and lower, not higher, unless you specify the higher directory as the context.
To run the docker build from the parent directory:
docker build -f /home/me myapp/Dockerfile
As opposed to
docker build -f /home/me/myapp Dockerfile
Doing the same in composer:
#docker-compose.yml
version: '3.3'
services:
yourservice:
build:
context: /home/me
dockerfile: myapp/Dockerfile
Or with your example:
version: '3'
services:
build:
context: /home/me/app
dockerfile: docker/Dockerfile
example:
volumes:
- /home/me/app:/example
Additionally you have to supply full paths, not relative paths. Ie.
- /home/me/myapp/files/example:/example
If you have a script that is generating the Dockerfile from an unknown path, you can use:
CWD=`pwd`; echo $CWD
To refer to the current working directory. From there you can append /..
Alternately you can build the image from a directory one up, or use a volume which you can share with an image that is run from a higher directory, or you need to output your file to stdout and redirect the output of the command to the file you need from the script that runs it.
See also: Docker: adding a file from a parent directory
The statement volumes: ['../:/example'] makes the parent directory of the directory containing docker-compose.yml on the host (../) visible inside the container at /example. Host directory bind-mounts like this, plus some equivalent constructs using a named volume attached to a specific host directory, are the only way a container can write out to the host filesystem.

context or workdir for docker-compose

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.

How to set the "current" directory of a Dockerfile with Docker Compose?

I have the following project structure:
.
..
project/
docker/cli/Dockerfile
docker-compose.yml
In docker-compose.yml I have the following configuration:
cli:
build: docker/cli
And somewhere in my Dockerfile:
COPY . /app
Now the problem is that when I do docker-compose build cli docker copies the contents of docker/cli/ in /app in my image. Which makes sense because that is the relative path to my docker/cli/Dockerfile. Is there a way however to tell in my docker-compose.yml config that the path should be different (namely the root dir of my project where the actual project files are)?
You can use the context attribute of a build instruction inside the docker-compose file.
version: '2'
services:
cli:
build:
context: .
dockerfile: docker/cli/Dockerfile
This should work as desired. The context is what is sent to the docker daemon for build. You will also want a .dockerignore file in your project directory though, to ensure only what you need is sent.

Resources