Docker build and docker-compose build with user namespace mapping - docker

I have enabled user namespace mapping in docker and building an image using docker build works but when I use docker-compose for the image it fails with below message. What can be the reason for it?
db#vagrant:~/docker$ docker-compose up --build
Building db
Step 1/3 : FROM alpine:latest
---> e7d92cdc71fe
Step 2/3 : WORKDIR /app
---> Using cache
---> 1491149423a1
Step 3/3 : COPY 1.txt .
ERROR: Service 'db' failed to build: failed to copy files: failed to copy file: Container ID 65536 cannot be mapped to a host ID
My user id is generated by some setup scripts which results in UID with larger than 65535 value.
db#vagrant:~/docker$ id
uid=65536(db) gid=1000(db) groups=1000(db),27(sudo),998(docker)
Docker configuration for namespace mapping
db#vagrant:~/docker$ cat /etc/docker/daemon.json
{
"userns-remap": "db"
}
db#vagrant:~/docker$ cat /etc/subuid /etc/subgid
db:100000:65536
db:100000:65536
Dockerfile contents(1.txt is empty file)
db#vagrant:~/docker$ cat Dockerfile
FROM alpine:latest
WORKDIR /app
COPY 1.txt .
docker-compose.yml file contents
db#vagrant:~/docker$ cat docker-compose.yml
version: "2"
services:
db:
build:
context: .
dockerfile: Dockerfile
image: sirishkumar/test
Output of docker build command
db#vagrant:~/docker$ docker build -t sirishkumar/test .
Sending build context to Docker daemon 3.584kB
Step 1/3 : FROM alpine:latest
latest: Pulling from library/alpine
c9b1b535fdd9: Pull complete
Digest: sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
Status: Downloaded newer image for alpine:latest
---> e7d92cdc71fe
Step 2/3 : WORKDIR /app
---> Running in 55f092b96268
Removing intermediate container 55f092b96268
---> 8af079e6a478
Step 3/3 : COPY 1.txt .
---> b3c14a691102
Successfully built b3c14a691102
Successfully tagged sirishkumar/test:latest
Output of docker-compose
db#vagrant:~/docker$ docker-compose up --build
Creating network "docker_default" with the default driver
Building db
Step 1/3 : FROM alpine:latest
latest: Pulling from library/alpine
c9b1b535fdd9: Pull complete
Digest: sha256:ab00606a42621fb68f2ed6ad3c88be54397f981a7b70a79db3d1172b11c4367d
Status: Downloaded newer image for alpine:latest
---> e7d92cdc71fe
Step 2/3 : WORKDIR /app
---> Running in fe39955aed1a
Removing intermediate container fe39955aed1a
---> fb23b8888f4a
Step 3/3 : COPY 1.txt .
ERROR: Service 'db' failed to build: failed to copy files: failed to copy file: Container ID 65536 cannot be mapped to a host ID

You have a range of 65,536 user id's to map into your docker user namespace:
db#vagrant:~/docker$ cat /etc/subuid /etc/subgid
db:100000:65536
db:100000:65536
And then you're telling docker to copy a file into the container owned by an ID outside of that range (user ID's start at 0):
db#vagrant:~/docker$ id
uid=65536(db) gid=1000(db) groups=1000(db),27(sudo),998(docker)
You need to set your user id on the host to be within the host user id range (less than 65536).

Related

How do I copy files from from the current docker build stage from one directory to another?

How is one meant to copy files that are part of the base image of the current stage in a multistage docker build?
E.g. if I want to start with a base image of alpine 3.7 how would I copy the file /etc/resolv.conf to somewhere I wanted it?
First version of my Dockerfile:
# Dockerfile
FROM alpine:3.7 as dev
WORKDIR /test
COPY /etc/resolv.conf /test
$ docker build -t foo:bar .
Sending build context to Docker daemon 14.85kB
Step 1/3 : FROM alpine:3.7 as dev
---> 6d1ef012b567
Step 2/3 : WORKDIR /test
---> Using cache
---> a82a71a856b0
Step 3/3 : COPY /etc/resolv.conf /test
COPY failed: stat /var/lib/docker/tmp/docker-builder820934750/etc/resolv.conf: no such file or directory
Second version of my Dockerfile:
# Dockerfile
FROM alpine:3.7 as dev
WORKDIR /test
COPY --from=dev /etc/resolv.conf /test
$ docker build -t foo:bar .
Sending build context to Docker daemon 14.85kB
Step 1/3 : FROM alpine:3.7 as dev
---> 6d1ef012b567
Step 2/3 : WORKDIR /test
---> Using cache
---> a82a71a856b0
Step 3/3 : COPY --from=dev /etc/resolv.conf /test
invalid from flag value dev: pull access denied for dev, repository does not exist or may require 'docker login': denied: requested access to the resource is denied
Creating a multi-stage build can be tricky to get right, but the basics look like this:
FROM alpine:3.7 as dev
WORKDIR /test
# ... Do additional stuff in the "dev" stage
# Define a new stage called whatever you want, here called "final"
FROM alpine:3.7 as final
# Copy from an earlier stage
COPY --from=dev /etc/resolv.conf /test
Doing --from only makes sense if you are referencing a previous stage. It doesn't do anything useful if you have only one stage. It cannot reference other Dockerfile stages directly, you must have a corresponding FROM ... AS ... directive.

docker - unable to change directory of volume

I have had no luck with searching for a solution to this.
I have 2 docker containers. frontend, and api. Both need a folder and its contents models to build.
All of these files live in a folder named Website, so the tree would look like:
Website:
-models
-api
-dockerfile
-frontend
-dockerfile
-docker-compose.yml
I am very new to docker, and believe this is how this should be setup, my dockerfile inside of the api directory is like so:
FROM node:latest
RUN mkdir -p /usr/src/app/models
WORKDIR /usr/src/app
COPY ./api/package*.json ./
RUN npm install
COPY ./api/ /usr/src/app
EXPOSE 3000
EXPOSE 3001
CMD [ "npm", "start" ]
This results in 2 models folders being created in side of the docker container, one containing no files located at path (this is where I want my contents):
root#0aa4496c9077:/usr/src/app/models#
and another folder that contains the content that I want it to have, however it is located at:
root#0aa4496c9077:/usr/src/app/src/models#
My question is: how do I map the folder and contents from the Website directory, to the docker container directory /usr/src/app/models?
my actual docker-compose.yml file is as so:
version: "3"
services:
api:
container_name: api
restart: always
build:
context: ./
dockerfile: ./api/dockerfile
ports:
- "3000:3000"
- "3001:3001"
volumes:
- ./models:./models
links:
- mongo
I have attempted changing the volumes path to be absolute, such as:
volumes:
- $PWD/models:/usr/src/app/models
with no luck :(
The build output of running docker-compose build is:
mongo uses an image, skipping
Building api
Step 1/8 : FROM node:latest
latest: Pulling from library/node
Digest: sha256:521df806339e2e60dfdee6e00e75656e69798c141bd2cff88c0c9a9c50ad4de5
Status: Downloaded newer image for node:latest
---> 4495f296c63b
Step 2/8 : WORKDIR /usr/src/app
---> Using cache
---> 94f87a509559
Step 3/8 : COPY ./api/package*.json ./
---> Using cache
---> 3c2e8c17ebf5
Step 4/8 : RUN npm install
---> Using cache
---> 90dc7d21af18
Step 5/8 : COPY ./api/ /usr/src/app
---> 5fa3a2b219ef
Step 6/8 : EXPOSE 3000
---> Running in 6ad5fbea1ed8
Removing intermediate container 6ad5fbea1ed8
---> b2f8f2f9129c
Step 7/8 : EXPOSE 3001
---> Running in 2342665c8da3
Removing intermediate container 2342665c8da3
---> 9f1162670b55
Step 8/8 : CMD [ "npm", "start" ]
---> Running in fc9d766bd5c1
Removing intermediate container fc9d766bd5c1
---> a7267b99b3c2
Successfully built a7267b99b3c2
Successfully tagged website_api:latest
Thank you very much for any help
Use ./models:./models without $PWD , and it's supposed to work.
./modelsis interpreted as relative to the location of the Compose file.
No need to supply absolute path, just relative path and docker-compose will figure it out.

Why isn't docker reusing docker-compose's cache layers?

This is a cut-down example of a problem I'm having with a bigger Dockerfile.
Here's a Dockerfile:
FROM alpine:latest AS base
COPY docker-compose.yml /tmp/docker-compose.yml
RUN touch /tmp/foo
Here's a docker-compose.yml:
version: '3.5'
services:
web:
build:
context: .
What I expect is that docker build will be able to reuse the cached layers that docker-compose builds. What I see when I run docker-compose build web is:
$ docker-compose build web
Building web
Step 1/3 : FROM alpine:latest AS base
---> f70734b6a266
Step 2/3 : COPY docker-compose.yml /tmp/docker-compose.yml
---> 764c54eb3dd4
Step 3/3 : RUN touch /tmp/foo
---> Running in 77bdf96af899
Removing intermediate container 77bdf96af899
---> 7d8197f7004f
Successfully built 7d8197f7004f
Successfully tagged docker-compose-caching_web:latest
If I re-run docker-compose build web, I get:
...
Step 2/3 : COPY docker-compose.yml /tmp/docker-compose.yml
---> Using cache
---> 764c54eb3dd4
...
So it's clearly able to cache the layer with the file in it. However, when I run docker build ., here's the output I see:
$ docker build .
Sending build context to Docker daemon 3.072kB
Step 1/3 : FROM alpine:latest AS base
---> f70734b6a266
Step 2/3 : COPY docker-compose.yml /tmp/docker-compose.yml
---> e8679333ba0d
Step 3/3 : RUN touch /tmp/foo
---> Running in af26cc65312d
Removing intermediate container af26cc65312d
---> 186c8341ee96
Successfully built 186c8341ee96
Note step 2 didn't come from the cache. Why not? Or, more importantly, how can I ensure that it does without using --cache-from?
The problem this causes is that after this step in my bigger Dockerfile that I'm not showing, there's a honking great RUN command that takes an age to run. How can I get docker build and docker-compose build to share cache layers?
(Docker Desktop v 2.3.0.2 (45183) on OS X 10.14.6 for those playing along at home)
With Docker-compose 1.25+ (Dec. 2019), try and use:
COMPOSE_DOCKER_CLI_BUILD=1 docker-compose build
That is what is needed to enable the docker-cli, instead of the own internal docker-compose build.
See also "Faster builds in Docker Compose 1.25.1 thanks to BuildKit Support".
But be aware of docker-compose issue 7336, when using it with DOCKER_BUILDKIT=1 (in addition of COMPOSE_DOCKER_CLI_BUILD=1)
Looks like a known issue. For reasons I don't entirely understand, hashes generated by docker compose build are different from those generated by docker build.
https://github.com/docker/compose/issues/883

Passing environment variable to docker image at build time with docker-compose

Everything I tried following Dockerfile and docker compose references to pass an environment variable to the Docker image did not work.
I want to make this env var available during docker build when using docker-compose.
On the Docker host I have:
export BUILD_VERSION=1.0
app.js
console.log('BUILD_VERSION: ' + process.env.BUILD_VERSION);
Dockerfile:
FROM node
ADD app.js /
ARG BUILD_VERSION
ENV BUILD_VERSION=$BUILD_VERSION
RUN echo Build Time: $BUILD_VERSION
RUN node /app.js
CMD echo Run Time: $BUILD_VERSION
docker-compose.yml:
version: '3'
services:
app:
build:
context: .
args:
- BUILD_VERSION
If I build the image directly, the env var is passed fine:
docker build -t test --no-cache --build-arg BUILD_VERSION .
and is also available at run-time:
$ docker run --rm test
Run Time: 1.0
$ docker run --rm test node /app
BUILD_VERSION: 1.0
but not with docker compose.
docker-compose up --build
...
Step 5/7 : RUN echo Build Time: $BUILD_VERSION
---> Running in 6115161f33bf
Build Time:
---> c691c619018a
Removing intermediate container 6115161f33bf
Step 6/7 : RUN node /app.js
---> Running in f51831cc5e1e
BUILD_VERSION:
It's only available at run-time:
$ docker run --rm test
Run Time: 1.0
$ docker run --rm test node /app
BUILD_VERSION: 1.0
I also tried using environment in docker-compose.yml like below which again only makes it available at run-time but not build-time:
version: '3'
services:
app:
build:
context: .
environment:
- BUILD_VERSION
Please advise, how can I make it work in the least convoluted way?
Your example is working for me.
Have you tried deleting the images and building again? Docker won't re-build your image despite environment variables changed if the image is in cache.
You can delete them with:
docker-compose down --rmi all
Edit, I show here how it is working for me at build time:
$ cat Dockerfile
FROM alpine
ARG BUILD_VERSION
ENV BUILD_VERSION=$BUILD_VERSION
RUN echo Build Time: $BUILD_VERSION
$ cat docker-compose.yml
version: '3'
services:
app:
build:
context: .
args:
- BUILD_VERSION
Build:
$ export BUILD_VERSION=122221
$ docker-compose up --build
Creating network "a_default" with the default driver
Building app
Step 1/4 : FROM alpine
latest: Pulling from library/alpine
8e3ba11ec2a2: Pull complete
Digest: sha256:7043076348bf5040220df6ad703798fd8593a0918d06d3ce30c6c93be117e430
Status: Downloaded newer image for alpine:latest
---> 11cd0b38bc3c
Step 2/4 : ARG BUILD_VERSION
---> Running in b0a1a79967a0
Removing intermediate container b0a1a79967a0
---> 9fa331d63f6d
Step 3/4 : ENV BUILD_VERSION=$BUILD_VERSION
---> Running in a602c27689a5
Removing intermediate container a602c27689a5
---> bf2181423c93
Step 4/4 : RUN echo Build Time: $BUILD_VERSION <<<<<< (*)
---> Running in 9d828cefcfab
Build Time: 122221
Removing intermediate container 9d828cefcfab
---> 2b3afa3d348c
Successfully built 2b3afa3d348c
Successfully tagged a_app:latest
Creating a_app_1 ... done
Attaching to a_app_1
a_app_1 exited with code 0
As the other answer mentioned, you can use docker-compose build --no-cache, and you can avoid mentioning "app" if you have multiple services, so docker-compose will build all the services. What you can do to handle different build versions in the same docker-compose build is to use different env vars, like:
$ cat docker-compose
version: '3'
services:
app1:
build:
context: .
args:
- BUILD_VERSION=$APP1_BUILD_VERSION
app2:
build:
context: .
args:
- BUILD_VERSION=$APP2_BUILD_VERSION
Export:
$ export APP1_BUILD_VERSION=1.1.1
$ export APP2_BUILD_VERSION=2.2.2
Build:
$ docker-compose build
Building app1
Step 1/4 : FROM alpine
latest: Pulling from library/alpine
8e3ba11ec2a2: Pull complete
Digest: sha256:7043076348bf5040220df6ad703798fd8593a0918d06d3ce30c6c93be117e430
Status: Downloaded newer image for alpine:latest
---> 11cd0b38bc3c
Step 2/4 : ARG BUILD_VERSION
---> Running in 0b66093bc2ef
Removing intermediate container 0b66093bc2ef
---> 906130ee5da8
Step 3/4 : ENV BUILD_VERSION=$BUILD_VERSION
---> Running in 9d89b48c875d
Removing intermediate container 9d89b48c875d
---> ca2480695149
Step 4/4 : RUN echo Build Time: $BUILD_VERSION
---> Running in 52dec27874ec
Build Time: 1.1.1
Removing intermediate container 52dec27874ec
---> 1b3654924297
Successfully built 1b3654924297
Successfully tagged a_app1:latest
Building app2
Step 1/4 : FROM alpine
---> 11cd0b38bc3c
Step 2/4 : ARG BUILD_VERSION
---> Using cache
---> 906130ee5da8
Step 3/4 : ENV BUILD_VERSION=$BUILD_VERSION
---> Running in d29442339459
Removing intermediate container d29442339459
---> 8b26def5ef3a
Step 4/4 : RUN echo Build Time: $BUILD_VERSION
---> Running in 4b3de2d223e5
Build Time: 2.2.2
Removing intermediate container 4b3de2d223e5
---> 89033b10b61e
Successfully built 89033b10b61e
Successfully tagged a_app2:latest
You need to set argument in docker-compose.yml as shown which will then be overriden to passed env variable -
version: '3'
services:
app:
build:
context: .
args:
- BUILD_VERSION
Next export environment variable you need to pass.
$ export BUILD_VERSION=1.0
Now build the image using command
$ docker-compose build --no-cache --build-arg BUILD_VERSION=$BUILD_VERSION app
You can pass in args to build, from the docker-compose file to the docker build. It is surprising the env vars aren't used for run and build.
// docker-compose.yml
version: '3'
services:
app:
build:
context: .
environment:
- BUILD_VERSION
args:
- BUILD_VERSION=${BUILD_VERSION}
volumes:
...
// Dockerfile
FROM node
ADD app.js /
ARG BUILD_VERSION
ENV BUILD_VERSION=$BUILD_VERSION
RUN echo Build Time: $BUILD_VERSION
RUN node /app.js
CMD echo Run Time: $BUILD_VERSION

docker-compose volume not appearing in container

I'm trying to build a stack with one docker-compose that should contain another containers inside. This is to run a development environment with all my projects inside.
So the problem is the volume with application source isn't appearing on built image.
MacOS Sierra
Docker version 17.03.0-ce, build 60ccb22
Boot2Docker-cli version: v1.8.0
my directory tree
/dockers <======= one directory with all docker files for each project
docker-compose.yml <======= The main image
/project1 <======= dockerfile for each project
Dockerfile
/project2
Dockerfile
/project3
Dockerfile
/project1 <======= project1 source folder
test.txt
/project2
/project3
my docker-compose.yml
project1:
build: ./project1
volumes:
- ../project1/:/src
my dockerfile for project1
FROM python:2.7
RUN mkdir -p /src
WORKDIR /src
RUN echo "---------------------"
RUN ls -la
RUN echo "---------------------"
So I try to build the docker-compose file
$ sudo docker-compose build --no-cache
And then it shows an empty folder when I expect test.txt file
Building express
ERROR: Cannot locate specified Dockerfile: Dockerfile
➜ docker git:(master) ✗ sudo docker-compose build --no-cache
Building project1
Step 1/7 : FROM python:2.7
---> ca388cdb5ac1
Step 2/7 : RUN mkdir -p /src
---> Running in 393a462f7a44
---> 4fbeb32d88b3
Removing intermediate container 393a462f7a44
Step 3/7 : WORKDIR /src
---> 03ce193577ab
Removing intermediate container b1cd746b699a
Step 4/7 : RUN echo "--------------------------"
---> Running in 82df8a512c90
----------------------------
---> 6dea58ba5051
Removing intermediate container 82df8a512c90
Step 5/7 : RUN ls -la
---> Running in 905417d0cd19
total 8
drwxr-xr-x 2 root root 4096 Mar 23 17:12 . <====== EMPTY :(
drwxr-xr-x 1 root root 4096 Mar 23 17:12 .. <====== EMPTY :(
---> 53764caffb1a
Removing intermediate container 905417d0cd19
Step 6/7 : RUN echo "-----------------------------"
---> Running in 110e765d102a
----------------------------
---> b752230fd6dc
Removing intermediate container 110e765d102a
Step 7/7 : EXPOSE 3000
---> Running in 1cfe2e80d282
---> 5e3e740d5a9a
Removing intermediate container 1cfe2e80d282
Successfully built 5e3e740d5a9a
Volumes are runtime configurations in Docker. Because they are configurable, if you were to reference volumes during the build phase you would essentially be creating a potentially uncheckable broken dependency.
I'm sure there is a more technical reason - but it really shouldn't be done. Move all that stuff to the runtime setup command and you should be OK.

Resources