While Dockerfiles are executed in order from top to bottom, you can trigger an instruction to be executed at a later time when the image is used as the base for another image. The result is you can delay your execution to be dependent on the application which you're building
ONBUILD instruction works in a different way. This instruction will not run in this image but will set a trigger instruction to be executed at a later point. The Onbuild instruction in our example is “ONBUILD RUN mkdir /tmp/hello”. This instruction will not be executed in the current image but will be executed in the next image if the current build is used as Base image. Lets understand with an example,
In the First Dockerfile we have,
[root@ip-172-31-31-127 onbuild]# cat hello1/Dockerfile
FROM busybox
RUN echo "hello world" >> /tmp/hello
ONBUILD RUN mkdir /tmp/onbuild-hello
Now when i build the docker image hello1 with the above dockerfile as below,
[root@ip-172-31-31-127 hello1]# docker build -t hello1 .
Sending build context to Docker daemon 2.048kB
Step 1/3 : FROM busybox
latest: Pulling from library/busybox
53071b97a884: Already exists
Digest: sha256:4b6ad3a68d34da29bf7c8ccb5d355ba8b4babcad1f99798204e7abb43e54ee3d
Status: Downloaded newer image for busybox:latest
---> 64f5d945efcc
Step 2/3 : RUN echo "hello world" >> /tmp/hello
---> Running in cadcbc51c329
Removing intermediate container cadcbc51c329
---> c24d27271dee
Step 3/3 : ONBUILD RUN mkdir /tmp/onbuild-hello
---> Running in 827b86347475
Removing intermediate container 827b86347475
---> 1eae88c4a42d
Successfully built 1eae88c4a42d
Successfully tagged hello1:latest
Now if i run the image and see the contents of the container, we don’t see any directories created as defined in ONBUILD instruction
[root@ip-172-31-31-127 hello1]# docker run -it hello1 /bin/sh
/ # ll /tmp
/bin/sh: ll: not found
/ # ls /tmp
hello
/ # exit
Now lets see the second Dockerfile,
[root@ip-172-31-31-127 hello2]# cat Dockerfile
FROM hello1
RUN echo "second Build" >> /tmp/second
Now in the second dockerfile, iam using the hello1 ( created above ) as base image. Now if we create the image and run a container from it, we can see things as below,
[root@ip-172-31-31-127 hello2]# docker run -it hello2 /bin/sh
/ # ls /tmp
hello onbuild-hello second
We can see that the onbuild-hello directory is created in the container created from the image whose base image is hello1. Now in order for the ONBUILD instruction to execute, we need to create a container whose base image has this ONBUILD instruction.
In a production application, a nodejs application Dockerfile would look like below,
[root@ip-172-31-9-137 tmp]# cat Dockerfile
FROM node:7
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" ]
Build the image and name the image as node:7-build. The result is that we can build this image but the application specific commands won't be executed until the built image is used as a base image. They'll then be executed as part of the base image's build. In the above Dockerfile, the instructions that start with ONBUILD will not be executed in the node:7-build but when this image is used as base, they will be executed.
With all of the logic to copy the code, install our dependencies and launch our application the only aspect which needs to be defined on the application level is which port(s) to expose. The advantage of creating OnBuild images is that our Dockerfile is now much simpler and can be easily reused across multiple projects without having to re-run the same steps improving build times.
FROM node:7-onbuild EXPOSE 3000
Build the application as node-app and run the application,
docker run -d --name node-app -p 3000:3000 my-nodejs-app
The ONBUILD instruction is very useful for automating the build of your chosen software stack.
No comments :
Post a Comment