Building Dockerfile fails when touching a file after a mkdir - docker

I'm new to Docker and try to build an image with a simple Dockerfile:
FROM jenkins
USER root
RUN mkdir -pv /home/a/b
RUN touch /home/a/b/test.txt
RUN mkdir -pv /var/jenkins_home/a/b
RUN touch /var/jenkins_home/a/b/test.txt
USER jenkins
When I build it, it fails with the following output:
Step 0 : FROM jenkins
Step 1 : USER root
Step 2 : RUN mkdir -pv /home/a/b
mkdir: created directory '/home/a'
mkdir: created directory '/home/a/b'
Step 3 : RUN touch /home/a/b/test.txt
Step 4 : RUN mkdir -pv /var/jenkins_home/a/b
mkdir: created directory '/var/jenkins_home/a'
mkdir: created directory '/var/jenkins_home/a/b'
Step 5 : RUN touch /var/jenkins_home/a/b/test.txt
touch: cannot touch '/var/jenkins_home/a/b/test.txt': No such file or directory
Can anyone tell me, what I am missing here? Why does the first mkdir & touch combination work and the second does not?

Looking at https://registry.hub.docker.com/u/library/jenkins/, it seems that /var/jenkins_home is a volume. You can only create files there while the container is running, presumably with a volume mapping like
docker run ... -v /your/jenkins/home:/var/jenkins_home ...
The docker build process knows nothing about shared volumes.

This is currently investigated in docker/docker/issues/3639, and summarized in this comment:
Okay, I did little research and it seems that volume is non-mutable between Dockerfile instruction.
Here even smaller Dockerfile for testing:
FROM busybox
RUN mkdir /tmp/volume
RUN echo "hello" > /tmp/volume/hello
VOLUME ["/tmp/volume/"]
RUN [[ -f /tmp/volume/hello ]]
RUN rm /tmp/volume/hello
RUN [[ ! -e /tmp/volume/hello ]]
On each instruction we create new volume and copy content from original volume.
Update April 2019:
Use DOCKER_BUILDKIT=1
The new builder does not exhibit this behavior.
Example from dominikzalewski:
That's a very simple Dockerfile that I'm using:
FROM wordpress:latest
ARG UPLOAD_DIR=/var/www/html/wp-content/uploads
RUN mkdir -p $UPLOAD_DIR
RUN ls -lhd $UPLOAD_DIR
Cf. Build Enhancements for Docker
Docker Build enhancements for 18.09 release introduces a much-needed overhaul of the build architecture.
By integrating BuildKit, users should see an improvement on performance, storage management, feature functionality, and security.
Docker images created with buildkit can be pushed to Docker Hub and DTR just like Docker images created with legacy build
The Dockerfile format that works on legacy build will also work with buildkit builds
The new --secret command line option allows the user to pass secret information for building new images with a specified Dockerfile

Related

LUIS mount points

I am trying to use a custom Dockerfile to build the LUIS container and copy the app file (app exported from the Luis portal) into the container. For this reason, I really don't need the mount points, since the .gz file will already live in the container. Is this possible? It seems that the mount points are always required...
I have to copy the files into the container and the move them to the input location at runtime (using an init.sh script). But, even then the container seemed to not load the app correctly. It behaves differently from that scenario compared to just putting the file in the host folder and mounting that to the container.
UPDATE: When I try to move the files around internally (at the start of the container), LUIS gives this output:
Using '/input' for reading models and other read-only data.
Using '/output/luis/fbfb798892fd' for writing logs and other output data.
Logging to console.
Submitting metering to 'https://southcentralus.api.cognitive.microsoft.com/'.
warn: Microsoft.AspNetCore.Server.Kestrel[0]
Overriding address(es) 'http://+:80'. Binding to endpoints defined in UseKestrel() instead.
Hosting environment: Production
Content root path: /app
Now listening on: http://0.0.0.0:5000
Application started. Press Ctrl+C to shut down.
fail: Luis[0]
Failed while prefetching App: AppId: d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee - Slot: PRODUCTION Could not find file '/input/d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee_PRODUCTION.gz'.
fail: Luis[0]
Failed while getting response for AppId: d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee - Slot: PRODUCTION. Error: Could not find file '/input/d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee_PRODUCTION.gz'.
warn: Microsoft.CloudAI.Containers.Controllers.LuisControllerV3[0]
Response status code: 404
Exception: Could not find file '/input/d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee_PRODUCTION.gz'. SubscriptionId='' RequestId='d7dfee25-05d9-4af6-804d-58558f55465e' Timestamp=''
^C
nuc#nuc-NUC8i7BEK:/tmp/input$ sudo docker exec -it luis bash
root#fbfb798892fd:/app# cd /input
root#fbfb798892fd:/input# ls
d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee_production.gz
root#fbfb798892fd:/input# ls -l
total 8
-rwxrwxrwx 1 root root 4960 Dec 2 17:35 d6fa5fd3-c32a-44d5-bb7f-d563775cf6ee_production.gz
root#fbfb798892fd:/input#
Notice that even though I can log into the container and browse the location of the model files and they are present, LUIS cannot load/find them.
UPDATE #2 - posting my Dockerfile:
FROM mcr.microsoft.com/azure-cognitive-services/luis:latest
ENV Eula=accept
ENV Billing=https://southcentralus.api.cognitive.microsoft.com/
ENV ApiKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
ENV Logging:Console:LogLevel:Default=Debug
RUN mkdir /app/inputfiles/
RUN chmod 777 /app/inputfiles/
COPY *.gz /app/inputfiles/
WORKDIR /app
COPY init.sh .
RUN chmod 777 /app/init.sh
ENTRYPOINT /app/init.sh && dotnet Microsoft.CloudAI.Containers.Luis.dll
Option 1
The models can be COPY'd directly into /input/.
e.g.
FROM mcr.microsoft.com/azure-cognitive-services/luis:latest
COPY *.gz /input/
This will work, but requires that you don't mount to /input at runtime as it will squash the COPY'd files. The message "A folder must be mounted" is only logged if the /input directory does not exist.
> docker build . -t luis --no-cache
Sending build context to Docker daemon 40.43MB
Step 1/2 : FROM aicpppe.azurecr.io/microsoft/cognitive-services-luis
---> df4e32e45b1e
Step 2/2 : COPY ./*.gz /input/
---> c5f41a9d8522
Successfully built c5f41a9d8522
Successfully tagged luis:latest
> docker run --rm -it -p 5000:5000 luis eula=accept billing=*** apikey=***
...
Using '/input' for reading models and other read-only data.
...
Application started. Press Ctrl+C to shut down.
Option 2
The configuration value Mounts:Input can be set to configure the input location.
This might be useful if you need your models to live in /app/inputfiles or if you need to mount to /input for another reason at runtime.
e.g.
FROM aicpppe.azurecr.io/microsoft/cognitive-services-luis
ENV Mounts:Input=/app/inputfiles
COPY ./*.gz /app/inputfiles/
This results in:
> docker build . -t luis --no-cache
Sending build context to Docker daemon 40.43MB
Step 1/3 : FROM aicpppe.azurecr.io/microsoft/cognitive-services-luis
---> df4e32e45b1e
Step 2/3 : ENV Mounts:Input=/app/inputfiles
---> Running in b6029a2b54d0
Removing intermediate container b6029a2b54d0
---> cb9a4e06463b
Step 3/3 : COPY ./*.gz /app/inputfiles/
---> 9ab1dfaa36e7
Successfully built 9ab1dfaa36e7
Successfully tagged luis:latest
> docker run --rm -it -p 5000:5000 luis eula=accept billing=*** apikey=***
...
Using '/app/inputfiles' for reading models and other read-only data.
...
Application started. Press Ctrl+C to shut down.
It's true that the input mount won't be necessary if your .gz file is already in the image, but the output mount is used for logging and you may still want that for active learning purposes.
To build your desired image, create a text file named Dockerfile (no extension) and populate it with the following lines:
FROM mcr.microsoft.com/azure-cognitive-services/luis:latest
ENV Eula=accept
ENV Billing={ENDPOINT_URI}
ENV ApiKey={API_KEY}
COPY ./{luisAppId}_PRODUCTION.gz /input/{luisAppId}_PRODUCTION.gz
You can find your {ENDPOINT_URI} and your {API_KEY} using the normal LUIS container instructions, and {luisAppId} will be found in the name of your .gz file of course. Once your Dockerfile is ready, run it from the same folder that contains your .gz file with this command:
docker build -t luis .
Your image will now be ready. All your teammate has to do is run this command:
docker run --rm -it -p 5000:5000
--memory 4g
--cpus 2
--mount type=bind,src={OUTPUT_FOLDER},target=/output luis
{OUTPUT_FOLDER} can be any local absolute path you want as long as it exists. You may also omit the output mount if you don't want any logging:
docker run --rm -it -p 5000:5000 --memory 4g --cpus 2 luis

Create multistage docker file

I have 2 different docker files (production and test environment). I want to build a single multistage docker file with these 2 dockerfiles.
First dockerfile as below:
FROM wildfly:17.0.0
USER jboss
RUN mkdir /opt/wildfly/install && mkdir /opt/wildfly/install/config
COPY --chown=jboss:jboss install /opt/wildfly/install
COPY --chown=jboss:jboss install.sh /opt/wildfly/bin
RUN mkdir -p $JBOSS_HOME/standalone/data/datastorage
CMD ["/opt/wildfly/bin/install.sh"]
Second dockerfile as below:
FROM wildfly:17.0.0
USER jboss
RUN mkdir /opt/wildfly/install && mkdir /opt/wildfly/install/config
COPY --chown=jboss:jboss ./install /opt/wildfly/install
COPY --chown=jboss:jboss install.sh /opt/wildfly/bin
RUN rm /opt/wildfly/install/wildfly-scripts/Setup.cli
RUN mv /opt/wildfly/install/wildfly-scripts/SetupforTest.cli /opt/wildfly/install/wildfly-scripts/Setup.cli
RUN rm /opt/wildfly/install/wildfly-scripts/Properties.properties
RUN mv /opt/wildfly/install/wildfly-scripts/Properties-test.properties /opt/wildfly/install/wildfly-scripts/Properties.properties
RUN mkdir -p $JBOSS_HOME/standalone/data/datastorage
CMD ["/opt/wildfly/bin/install.sh"]
Question How to create a multistage docker file for these 2 docker files?
Thanks in advance.
I'm not sure what you are trying to do requires a multi-stage build. Instead, what you may want to do is use a custom base image (for dev) which would be your first code block and use that base image for production.
If the first image was tagged as shanmukh/wildfly:dev:
FROM shanmukh/wildfly:dev
USER jboss
RUN rm /opt/wildfly/install/wildfly-scripts/Setup.cli
RUN mv /opt/wildfly/install/wildfly-scripts/SetupforTest.cli /opt/wildfly/install/wildfly-scripts/Setup.cli
RUN rm /opt/wildfly/install/wildfly-scripts/Properties.properties
RUN mv /opt/wildfly/install/wildfly-scripts/Properties-test.properties /opt/wildfly/install/wildfly-scripts/Properties.properties
This could be tagged as shanmukh/wildfly:prod.
The reason why I don't think you want a multi-stage build is because you mentioned you are trying to handle two environments (test and production).
Even if you did want to say use a multi-stage build for production, from what I can see, there is no reason to. If your initial build stage included installing build dependencies such as a compiler, copying code and building it, then it'd be efficient to use a multi-stage build as the final image would not include the initial dependencies (such as the compiler and other potential dangerous dev tools) or any unneeded build artifacts that were produced.
Hope this helps.

Docker file: I want to invoke one script from docker file

I am creating one docker image name with soaphonda.
the code of docker file is below
FROM centos:7
FROM python:2.7
FROM java:openjdk-7-jdk
MAINTAINER Daniel Davison <sircapsalot#gmail.com>
# Version
ENV SOAPUI_VERSION 5.3.0
COPY entry_point.sh /opt/bin/entry_point.sh
COPY server.py /opt/bin/server.py
COPY server_index.html /opt/bin/server_index.html
COPY SoapUI-5.3.0.tar.gz /opt/SoapUI-5.3.0.tar.gz
COPY exit.sh /opt/bin/exit.sh
RUN chmod +x /opt/bin/entry_point.sh
RUN chmod +x /opt/bin/server.py
# Download and unarchive SoapUI
RUN mkdir -p /opt
WORKDIR /opt
RUN tar -xvf SoapUI-5.3.0.tar.gz .
# Set working directory
WORKDIR /opt/bin
# Set environment
ENV PATH ${PATH}:/opt/SoapUI-5.3.0/bin
EXPOSE 3000
RUN chmod +x /opt/SoapUI-5.3.0/bin/mockservicerunner.sh
CMD ["/opt/bin/entry_point.sh","exit","pwd", "sh", "/Users/ankitsrivastava/Documents/SametimeFileTransfers/Honda/files/hondascript.sh"]
My image creation is getiing successfull. I want that once the image creation is done it should retag and push in the docker hub. For that i have created the script which is below;
docker tag soaphonda ankiksri/soaphonda
docker push ankiksri/soaphonda
docker login
docker run -d -p 8089:8089 --name demo ankiksri/soaphonda
containerid=`docker ps -aqf "name=demo"`
echo $containerid
docker exec -it $containerid bash -c 'cd ../SoapUI-5.3.0;sh /opt/SoapUI-5.3.0/bin/mockservicerunner.sh "/opt/SoapUI-5.3.0/Honda-soapui-project.xml"'
Please help me how i can call the second script from docker file and the exit command is not working in docker file.
What you have to understand here is what you are specifying within the Dockerfile are the commands that gets executed when you build and run a Docker container from the image you have created using your Dockerfile.
So Docker image tag, push running should all done after you have built the Docker image from the Dockerfile. It cannot be done within the Dockerfile itself.
To achieve this kind of a thing you would have to use a build tool like Maven (an example) and automate the process of tagging, pushing the image. Also by looking at your image, I don't see any nessactiy to keep on tagging and pushing the image unless you are continuously updating the image. Also there is no point of using three FROM commands as it will unnecessarily make your Docker image size huge.

Why is git clone failing when I build an image from a dockerfile?

FROM ansible/ansible:ubuntu1604
MAINTAINER myname
RUN git clone http://github.com/ansible/ansible.git /tmp/ansible
RUN git clone http://github.com/othertest.git /tmp/othertest
WORKDIR /tmp/ansible
ENV PATH /tmp/ansible/bin:/sbin:/usr/sbin:/usr/bin:bin
ENV PYTHONPATH /tmp/ansible/lib:$PYTHON_PATH
ADD inventory /etc/ansible/hosts
WORKDIR /tmp/
EXPOSE 8888
When I build from this dockerfile, I get Cloning into /tmp/ansible and othertest in red text (I assume is an error). When I then run the container and peruse around, I see that all my steps from the dockerfile built correctly other than the git repositories which are missing.
I can't figure out what I'm doing wrong, I'm assuming its a simple mistake.
Building dockerfile:
sudo docker build --no-cache -f Dockerfile .
Running dockerfile:
sudo docker run -I -t de32490234 /bin/bash
The short answer:
Put your files anywhere other than in /tmp and things should work fine.
The longer answer:
You're basing your image on the ansible/ansible:ubuntu1604 image. If you inspect this image via docker inspect ansible/ansible:ubuntu1604 or look at the Dockerfile from which it was built, you will find that it contains a number of volume mounts. The relevant line from the Dockerfile is:
VOLUME /sys/fs/cgroup /run/lock /run /tmp
That means that all of those directories are volume mount points, which means any data placed into them will not be committed as part of the image build process.
Looking at your Dockerfile, I have two comments unrelated to the above:
You're explicitly setting the PATH environment variable, but you're neglecting to include /bin, which will cause all kinds of problems, such as:
$ docker run -it --rm bash
docker: Error response from daemon: oci runtime error: exec: "bash": executable file not found in $PATH.
You're using WORKDIR twice, but the first time (WORKDIR /tmp/ansible) you're not actually doing anything that cares what directory you're in (you're just setting some environment variables and copying a file into /etc/ansible).

Docker container with build output and no source

I have a build process that converts typescript into javascript, minifies and concatenates css files, etc.
I would like to put those files into an nginx docker container, but I don't want the original javascript / css source to be included, nor the tools that I use to build them. Is there a good way to do this, or do I have to run the build outside docker (or in a separately defined container), then COPY the relevant files in?
This page talks about doing something similar in a manual way, but doesn't explain how to automate the process e.g. with docker-compose or something.
Create a docker images with all required tools to build your code also that can clone code and build it. After build it have to copy
into docker volume for example volume name is /opt/webapp.
Launch build docker container using build image in step 1
docker run -d -P --name BuildContainer -v /opt/webapp:/opt/webapp build_image_name
Launch nginx docker container that will use shared volume of build docker in which your build code resides.
docker run -d -P --name Appserver -v /opt/webapp:/usr/local/nginx/html nginx_image_name
After building and shipping your build code to Appserver . you can delete BuildContainer because that is no more required.
Advantage of above steps:
your build code will in host machine so if one Appserver docker fail or stop then your build code will be safe in host machine and you can launch new docker using that build code.
if you create docker image for building code then every time no need to install required tool while launching docker.
you can build your code in host machine also but if you want your code should be build in fresh environment every time then this will be good. or if you use same host machine to build/compile code every time then some older source code may create problem or git clone error etc.
EDIT:
you can append :ro (Read only) to volume by which one container will not affect another. you can Read more about docker volume Here . Thanks #BMitch for suggestion.
The latest version of docker supports multi-stage builds where build products can be copied from on container to another.
https://docs.docker.com/engine/userguide/eng-image/multistage-build/
This is an ideal scenario for a multi-stage build. You perform the compiling in the first stage, copy the output of that compile to the second stage, and only ship that second stage. Each stage is an independent image that begins with a FROM line. And to transfer files between stages, there's now a COPY --from syntax. The result looks roughly like:
# first stage with your full compile environment, e.g. maven/jdk
FROM maven as build
WORKDIR /src
COPY src /src
RUN mvn install
# second stage starts below with just a jre base image
FROM openjdk:jre
# copy the jar from the first stage here
COPY --from=build /src/result.jar /app
CMD java -jar /app/result.jar
Original answer:
Two common options:
As mentioned, you can build outside and copy the compiled result into the container.
You merge your download, build, and cleanup step into a single RUN command. This is a common best practice to minimize the size of each layer.
An example Dockerfile for the second option would look like:
FROM mybase:latest
RUN apt-get update && apt-get install tools \
&& git clone https://github.com/myproj \
&& cd myproj \
&& make \
&& make install
&& cd .. \
&& apt-get rm tools && apt-get clean \
&& rm -rf myproj
The lines would be a little more complicated than that, but that's the gist.
As #dnephin suggested in his comments on the question and on #pl_rock's answer, the standard docker tools are not designed to do this, but you can use a third party tool like one of the following:
dobi (48 GitHub stars)
packer (6210 GitHub stars)
rocker (759 GitHub stars)
conveyor (152 GitHub stars)
(GitHub stars correct when I wrote the answer)
We went with dobi as it was the first one we heard of (because of this question), but it looks like packer is the most popular.
Create a docker file to run your build process, then run cleanup code
Example:
FROM node:latest
# Provides cached layer for node_modules
ADD package.json /tmp/package.json
RUN cd /tmp && npm install
RUN mkdir -p /dist && cp -a /tmp/node_modules /dist/
RUN cp /tmp/package.json /dist
ADD . /tmp
RUN cd /tmp && npm run build
RUN mkdir -p /dist && cp -a /tmp/. /dist
#run some clean up code here
RUN npm run cleanup
# Define working directory
WORKDIR /dist
# Expose port
EXPOSE 4000
# Run app
CMD ["npm", "run", "start"]
In your docker compose file
web:
build: ../project_path
environment:
- NODE_ENV=production
restart: always
ports:
- "4000"

Resources