I'm a newbie to docker, sorry if my question is too basic. I saw dockerfile like this:
FROM diamol/maven AS builder
WORKDIR /usr/src/iotd
COPY pom.xml .
RUN mvn -B dependency:go-offline
COPY . .
RUN mvn package
FROM diamol/openjdk
WORKDIR /app
COPY --from=builder /usr/src/iotd/target/iotd-service-0.1.0.jar .
EXPOSE 80
ENTRYPOINT ["java", "-jar", "/app/iotd-service-0.1.0.jar"]
I'm confused about COPY . . instruction, what does the first period and second period COPY . . mean?
Also, if I want to copy all files of the current working directory from my host machine into the image, then how can I modify COPY . . so that the first period means currenty directory of my machine?
In the Dockerfile COPY directive, the last argument is the path inside the container, relative to the current WORKDIR. All of the preceding arguments are paths inside the build context, the host directory passed as an argument to docker build.
I want to copy all files of the current working directory from my host machine into the image, then how can I modify COPY . . ...?
You probably don't need to. So long as you docker build . naming the current directory . as the last argument, that's exactly what COPY . . does. That instruction means to copy . – the entirety of the build context, from the original host system – to . – the current directory, inside the image.
WORKDIR /usr/src/iotd # `COPY anything .` will put it here inside the image
COPY pom.xml . # Copy a single file into that WORKDIR
COPY . . # Copy the entire build context into the WORKDIR
I've mentioned "build context" a couple of times. That is the directory argument to docker build
docker build \
-t myname/some-image: tag \
. # <--- the build context directory
or that you specify in a docker-compose.yml file
version: '3.8'
services:
one:
build: ./one # <-- this directory
two:
build:
context: ./two # <-- this directory
except that the files mentioned in a .dockerignore file are removed first.
In the question title you also ask
does dockerfile's instruction execute in order?
They do. The newer BuildKit backend has some capability to execute build stages not necessarily in the order they're written, but it ensures that you get the same results as if all of the COPY and RUN instructions from a previous stage had run before a COPY --from=... in a later stage happens.
From my perspective, one of the best ways to know all the details for COPY and WORKDIR docker commands is to go through following official documentation.
You can either search for COPY and WORKDIR keywords on the home page at below first link or please refer to last two links and find all the details including examples.
https://docs.docker.com/engine/reference/builder/
https://docs.docker.com/engine/reference/builder/#copy
https://docs.docker.com/engine/reference/builder/#workdir
I am currently trying to explore of a way on how to possibly make my dockerfile generic where it can be used by any .net projects.
My docker file looks like this. I have parameterized the base image to be used. And now I want to try to parameterized the EntryPoint instead of writing "mydotnet.dll" I want it to be passed as a variable/argument during docker build.
My docker file
ARG var1
FROM $var1
WORKDIR /app/
COPY . .
EXPOSE 80
ENTRYPOINT ["dotnet", "mydotnet.dll"]
How can we run mvn commands from a Dockerfile
Here is my Dockerfile:
FROM maven:3.3.9-jdk-8-alpine
WORKDIR /app
COPY code /app
WORKDIR /app
ENTRYPOINT ["mvn"]
CMD ["clean test -Dsurefire.suiteXmlFiles=/app/abc.xml"]
I tried to build and run the above image and it fails ( abc.xml is under the /app directory)
Is there a way to get this to work.
According to the documentation:
"If CMD is used to provide default arguments for the ENTRYPOINT instruction, both the CMD and ENTRYPOINT instructions should be specified with the JSON array format."
As such you should rewrite CMD as follow:
CMD ["clean","test","-Dsurefire.suiteXmlFiles=/app/abc.xml"]
You can also parameterize entry-point as a JSON array, as per documentation:
ENTRYPOINT["mvn","clean","test","-Dsurefire.suiteXmlFiles=/app/abc.
But, I suggest you use best-practice with an entrypoint ash-file. This ensures that changing these parameters does not require rewriting of the dockerfile:
create an entrypoint.sh file in the code directory. make it executable. It should read like this:
#!/bin/sh
if [ "$#" -ne 1 ]
then
FILE="abc.xml"
else
FILE=$1
fi
mvn clean test -Dsurefire.suiteXmlFiles="/app/$FILE"
replace your entrypoint with ENTRYPOINT["./entrypoint.sh"]
replace your command with CMD["abc.xml"]
PS
you have "WORKDIR /app" twice. this isn't what fails you, but it is redundant, you can get rid of it.
I'm not sure if this approach is the best so I'm open to suggestions.
My code is structured as follows:
src/
program1.py
util.py
jarfile.jar
Dockerfile
The Dockerfile looks like this:
WORKDIR /app
COPY src /app
COPY jarfile.jar /app
WORKDIR /app/src
RUN python program1.py
The main program being invoked is program1.py which generates a directory at the same level of src of the format data_{today-date}, so data_20200122 for example. The JAR file is to be invoked by passing this data directory as a command line argument, such as java -jar jarfile.jar -inputData /app/data/data_20200122
Since the data directory is dynamic, how can I have Docker generate the dir name? I thought about using a shell script instead but I'm new to Docker and wanted to know if there's a better approach.
I'm learning Docker. For many times I've seen that Dockerfile has WORKDIR command:
FROM node:latest
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
EXPOSE 3000
CMD [ “npm”, “start” ]
Can't I just omit WORKDIR and Copy and just have my Dockerfile at the root of my project? What are the downsides of using this approach?
According to the documentation:
The WORKDIR instruction sets the working directory for any RUN, CMD,
ENTRYPOINT, COPY and ADD instructions that follow it in the
Dockerfile. If the WORKDIR doesn’t exist, it will be created even if it’s not used in any subsequent Dockerfile instruction.
Also, in the Docker best practices it recommends you to use it:
... you should use WORKDIR instead of proliferating instructions like
RUN cd … && do-something, which are hard to read, troubleshoot, and
maintain.
I would suggest to keep it.
I think you can refactor your Dockerfile to something like:
FROM node:latest
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY . ./
EXPOSE 3000
CMD [ "npm", "start" ]
You dont have to
RUN mkdir -p /usr/src/app
This will be created automatically when you specifiy your WORKDIR
FROM node:latest
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY . ./
EXPOSE 3000
CMD [ “npm”, “start” ]
You can think of WORKDIR like a cd inside the container (it affects commands that come later in the Dockerfile, like the RUN command). If you removed WORKDIR in your example above, RUN npm install wouldn't work because you would not be in the /usr/src/app directory inside your container.
I don't see how this would be related to where you put your Dockerfile (since your Dockerfile location on the host machine has nothing to do with the pwd inside the container). You can put the Dockerfile wherever you'd like in your project. However, the first argument to COPY is a relative path, so if you move your Dockerfile you may need to update those COPY commands.
Before applying WORKDIR. Here the WORKDIR is at the wrong place and is not used wisely.
FROM microsoft/aspnetcore:2
COPY --from=build-env /publish /publish
WORKDIR /publish
ENTRYPOINT ["dotnet", "/publish/api.dll"]
We corrected the above code to put WORKDIR at the right location and optimised the following statements by removing /Publish
FROM microsoft/aspnetcore:2
WORKDIR /publish
COPY --from=build-env /publish .
ENTRYPOINT ["dotnet", "api.dll"]
So it acts like a cd and sets the tone for the upcoming statements.
The answer by #juanlumn is great, but I wanted to add one more (important) thing.
In regular command line, if you cd somewhere, it stays there until you change it. However, in a Dockerfile, each RUN command starts back at the root directory! That's a gotcha for docker newbies, and something to be aware of.
So not only does WORKDIR make a more obvious visual cue to someone reading your code, but it also keeps the working directory for more than just the one RUN command.
Beware of using vars as the target directory name for WORKDIR - doing that appears to result in a "cannot normalize nothing" fatal error. IMO, it's also worth pointing out that WORKDIR behaves in the same way as mkdir -p <path> i.e. all elements of the path are created if they don't exist already.
UPDATE:
I encountered the variable related problem (mentioned above) whilst running a multi-stage build - it now appears that using a variable is fine - if it (the variable) is "in scope" e.g. in the following, the 2nd WORKDIR reference fails ...
FROM <some image>
ENV varname varval
WORKDIR $varname
FROM <some other image>
WORKDIR $varname
whereas, it succeeds in this ...
FROM <some image>
ENV varname varval
WORKDIR $varname
FROM <some other image>
ENV varname varval
WORKDIR $varname
.oO(Maybe it's in the docs & I've missed it)
Be careful where you set WORKDIR because it can affect the continuous integration flow. For example, setting it to /home/circleci/project will cause error something like .ssh or whatever is the remote circleci is doing at setup time.