how ONBUILD docker instruction works - docker

I have two Dockerfile,
Dockerfile1
FROM centos:centos7
WORKDIR /root
ONBUILD COPY ./onbuilddemo.txt /tmp/onbuilddemo.txt
Dockerfile2
FROM onbuilddemo:latest
FROM adoptopenjdk/openjdk8:jre8u352-b05-ea-ubuntu-nightly
EXPOSE 8080
WORKDIR /root
CMD ["npm", "start"]
The image created out of dockerfile1 is onbuilddemo:latest
Now, when Im running the container built out of the image created from Dockerfile2 , then Im not seeing the file (onbuilddemo.txt) created/available in /tmp folder
Can someone please help , what Im missing . Thanks

You never used the onbuilddemo:latest image for anything, and if built with buildkit, this first step would be completely skipped:
FROM onbuilddemo:latest
FROM adoptopenjdk/openjdk8:jre8u352-b05-ea-ubuntu-nightly
A multi-stage build is used to split build dependencies from the runtime image. It does not merge multiple images together (there's no way to universally do this with arbitrary Linux filesystems that would result in a lot of broken use cases).
You need to remove the second from step, or copy the file from the first to second stage (using copy --from), or add the onbuild definition to the other base image.
Note that onbuild tends to be a bad idea, it's hard to debug and is rarely documented in places that someone is looking to explain the behavior of their build. If you can't run the steps in an entrypoint, consider templating the Dockerfile so that it's clear exactly what's being performed in the build.

Related

Why should I use docker ONBUILD?

1. Scenario with ONBUILD
Base Dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install python3
ONBUILD COPY test.py test.py
Obliviously, when we build above Dockerfile(test-image:latest), the COPY wont affected.(The test.py not copied)
Now onbuild Dockerfile
FROM test-image:latest
Now, when we build above Dockerfile, the COPY will affect, copies test.py
2. Scenario without ONBUILD
I achieve same thing without use ONBUILD
Base Dockerfile
FROM ubuntu:latest
RUN apt-get update && apt-get install python3
Above Dockerfile build docker image which has python3 (test-image2:latest)
Now child docker image Dockerfile
FROM test-image2:latest
COPY test.py /test.py
So, my question is, why should I use ONBUILD or when should use? is there any performance difference
I think that the answer is simple: you want to use ONBUILD when your parent image has to be used in various children images, so you
avoid repetitions
contstrain the user of the image to have test.py copied
In general you shouldn't use ONBUILD at all. Having a later Dockerfile FROM line do something other than simply incorporate its contents violates the principle of least surprise.
If the thing you're trying to do ONBUILD is something like a RUN or ENV instruction, semantically it makes no difference whether you do it in the base image or the derived image. It will be more efficient if you do it in the base image (once ever, as opposed to once each time a derived image is built).
If you're trying to ONBUILD COPY ... then you're trying to force a specific file to be on the host system at the point you run docker build, which is a little strange as a consumer. Docker's Best practices for writing Dockerfiles notes
Be careful when putting ADD or COPY in ONBUILD. The “onbuild” image fails catastrophically if the new build’s context is missing the resource being added. Adding a separate tag, as recommended above, helps mitigate this by allowing the Dockerfile author to make a choice.
As that page notes, if you must use ONBUILD, you should call it out in the image tag so it's clear when you build a Dockerfile FROM that image, something strange is going on. Most current Docker Hub images don't have -onbuild variants at all, even for things like tomcat that generally have extremely formulaic uses.

Dockerfile with entrypoint only from base image

I have a very simple Dockerfile like the below :-
FROM my-base-image
COPY abc.properties /opt/conf/
Now my base-image has a docker entrypoint (at the end of its Dockerfile) but this resulting image as you can see has none. Does this work or do we need to have a docker entrypoint/CMD in any given Dockerfile. Also what would be the order of execution for the COPY instruction in the resulting image. With this i mean since this Dockerfile has no entrypoint it would execute one from the base image but will that be executed after the COPY instruction or will the base image entrypoint be executed first and then execute this COPY instruction when the container starts
Just looking for concepts in docker.
Several of the Dockerfile directives (notably ENTRYPOINT and CMD, but also EXPOSE, LABEL, and MAINTAINER) just set metadata in the image; they don't really do anything themselves. Within a single Dockerfile this will work just fine:
FROM ubuntu:18.04
WORKDIR /app
# Just remembers this in the image metadata; doesn't actually run it
CMD ["/app/main.sh"]
# ...we should actually copy the file in too
COPY main.sh /app
When you have one Dockerfile built FROM another image it acts almost entirely like you ran all of the commands in the first Dockerfile, then all of the commands in the second Dockerfile. Since CMD and ENTRYPOINT just set metadata, the second image inherits this metadata.
Building and running an image are two separate steps. In the example you show, the COPY directive happens during the docker build step, and the base image's command doesn't take effect until the later docker run step. (This is also true in Docker Compose; a common question is about why Dockerfile steps can't connect to other containers declared in the Compose YAML file.)
There is one exception, and it's around ENTRYPOINT. If you have a base image that declares an ENTRYPOINT and a CMD both, and you redeclare the ENTRYPOINT in a derived image, it resets the CMD as well (very last paragraph in this section). This usually isn't a practical problem.
When you build an image, the Dockerfiles are merged as per their instructions. Building an image doesnt mean it runs. So ideally your instructions from your Base Dockerfile and your current Dockerfile will be packed. Since you have mentioned CMD entrypoint in the Base Dockerfile, that will be used for execution of your image inside the container, when you use docker run.
So when you build your image, the COPY statement from your child Dockerfile will also get set. and your image must be built fine.
Execute your docker build and docker run and let us know

Can we have a docker container without a shell?

Can we build a docker container that doesn't have any shell in that container ? Is it possible to create a container without a shell ?
Yes, you can create a container from scratch, which does not contain anything even bash, it will only contain binaries that you copy during build time, otherwise, it will be empty.
FROM scratch
COPY hello /
CMD ["/hello"]
You can use Docker’s reserved, minimal image, scratch, as a starting
point for building containers. Using the scratch “image” signals to
the build process that you want the next command in the Dockerfile to
be the first filesystem layer in your image.
While scratch appears in Docker’s repository on the hub, you can’t
pull it, run it, or tag any image with the name scratch. Instead, you
can refer to it in your Dockerfile. For example, to create a minimal
container using scratch:
scratch-docker-image
Using this as a base image, you can create your custom image, for example you only node runtime not thing more, then you try form scratch-node.
FROM node as builder
WORKDIR /app
COPY package.json package-lock.json index.js ./
RUN npm install --prod
FROM astefanutti/scratch-node
COPY --from=builder /app /
ENTRYPOINT ["node", "index.js"]

Install Bash on scratch Docker image

I am presently working with a third party Docker image whose Dockerfile is based on the empty image, starting with the FROM scratch directive.
How can Bash be installed on such image? I tried adding some extra commands to the Dockerfile, but apparently the RUN directive itself requires Bash.
When you start a Docker image FROM scratch you get absolutely nothing. Usually the way you work with one of these is by building a static binary on your host (or these days in an earlier Dockerfile build stage) and then COPY it into the image.
FROM scratch
COPY mybinary /
ENTRYPOINT ["/mybinary"]
Nothing would stop you from creating a derived image and COPYing additional binaries into it. Either you'd have to specifically build a static binary or install a full dynamic library environment.
If you're doing this to try to debug the container, there is probably nothing else in the image. One thing this means is that the set of things you can do with a shell is pretty boring. The other is that you're not going to have the standard tool set you're used to (there is not an ls or a cp). If you can live without bash's various extensions, BusyBox is a small tool designed to be statically built and installed in limited environments that provides minimal versions of most of these standard tools.
The question is old but I see a similar question and came here, SO posting to deal with such case below.
I am presently working with a third party Docker image whose
Dockerfile is based on the empty image, starting with the FROM scratch
directive.
As mentioned by #David there is nothing in such image that is based on scratch, If the image is based on the scratch image they just copy the binaries to image and that's it.
So the hack around with such image is to copy the binaries into your extend image and use them in your desired docker image.
For example postgres_exporter
FROM scratch
ARG binary
COPY $binary /postgres_exporter
EXPOSE 9187
ENTRYPOINT [ "/postgres_exporter" ]
So this is based on scratch and I can not install bash or anything else I can just copy binaries to run.
So here is the work, use them as the multi-stage base image, copy the binaries and installed packages in your docker images.
Below we need to add wait-for-it
FROM wrouesnel/postgres_exporter
# use the above base image
FROM debian:7.11-slim
RUN useradd -u 20001 postgres_exporter
USER postgres_exporter
#copy binires
COPY --from=0 /postgres_exporter /postgres_exporter
EXPOSE 9187
COPY wait-for-it.sh wait-for-it.sh
USER root
RUN chmod +x wait-for-it.sh
USER postgres_exporter
RUN pwd
ENTRYPOINT ["./wait-for-it.sh", "db:5432", "--", "./postgres_exporter"]

Dockerfile ONBUILD instruction

I read on the docker documentation how ONBUILD instruction can be used, but it is not clear at all.
Can someone please explain it to me?
The ONBUILD instruction is very useful for automating the build of your chosen software stack.
Example
The Maven container is designed to compile java programs. Magically all your project's Dockerfile needs to do is reference the base container containing the ONBUILD intructions:
FROM maven:3.3-jdk-8-onbuild
CMD ["java","-jar","/usr/src/app/target/demo-1.0-SNAPSHOT-jar-with-dependencies.jar"]
The base image's Dockerfile tells all
FROM maven:3-jdk-8
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ONBUILD ADD . /usr/src/app
ONBUILD RUN mvn install
There's a base image that has both Java and Maven installed and a series of instructions to copy files and run Maven.
The following answer gives a Java example
How to build a docker container for a java app
As stated by the docker docs:
The ONBUILD instruction adds to the image a trigger instruction to be executed at a later time, when the image is used as the base for another build. The trigger will be executed in the context of the downstream build, as if it had been inserted immediately after the FROM instruction in the downstream Dockerfile.
So what does that mean? Let's take this Nodejs Dockerfile:
FROM node:0.12.6
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
ONBUILD COPY package.json /usr/src/app/
ONBUILD RUN npm install
ONBUILD COPY . /usr/src/app
CMD [ "npm", "start" ]
In your own Dockerfile, when you do FROM node:0.12.6-onbuild you're getting an image, which means the build command has already been run, so the instructions have ALREADY been executed as well, however all but those starting with ONBUILD. These have been deferred to another time, when the downstream build (when your image is getting built from your own Dockerfile) uses this image as the base (FROM node:0.12.6-onbuild).
You can’t just call ADD and RUN now, because you don’t yet have access to the application source code, and it will be different for each application build.
That's right! The image containing onbuild instructions wasn't built on your machine, so it doesn't yet have access to package.json.
Then when you build your own Dockerfile, before executing any instruction in your file, the builder will look for ONBUILD triggers, which were added to the metadata of the parent image when it was built.
That spares you the hassle of executing these commands yourself, it really is as though these commands were written in your own Dockerfile.
Finally, they add:
You could simply provide application developers with a boilerplate Dockerfile to copy-paste into their application, but that is inefficient, error-prone and difficult to update because it mixes with application-specific code.
The thing is that if these instructions are modified in the boilerplate Dockerfile, you will have to modify them as well in your Dockerfile. But thanks to the ONBUILD instruction, we don't have to worry about it.

Resources