Important Docker Practices

A Dockerfile begins with a FROM field which defines it’s base from which the build process starts.
Docker runs instructions sequentially in the Dockerfile, hence the first instruction must be a FROM.

In order to build your own Docker Image, run docker image build command. This command will build an image from whatever source code is available in the PATH where you execute the command. The path defines the context of the build.
It is not the CLI which does the image build, it is the docker daemon which builds the image and hence the CLI has to transfer the context of the build to the docker daemon. Hence we see
"Sending build context to Docker daemon".

Ensure that the context is segregated properly and not set to a /. If set to / , then the cli will have to transfer everything that is there under / to the docker daemon which is definitely not a good idea and hence most of the times we see Dockerfiles sitting in a empty directory.

When you need another more additions, you can then add more files to that directory where the Dockerfile is so that when you run a new build, it creates a new image based on the dockerfile and the other files.

There is also an option to add .dockerignore to exclude files which we don’t want in the build.

Caching is an important concept which we need to keep in mind when working with Docker. If the instructions in our Dockerfile are same which were probably run earlier by the docker daemon, then docker uses the cache in order to speed up the process, you could see that by checking the Image ID values created everytime you run the docker build command and has run successfully.

So everytime docker build a new layer on top of the one which is already available.

When building container images, keep in mind that containers are designed to run a specific application; so when the container starts the app should also start, likewise when the app stops, the container should also stop.

Use small base images and use the builder pattern to reduce the image size of your docker containers.
Use images like alpine and then add package dependencies using COPY and RUN helps in reducing the image size.

Using the Builder pattern we remove th

e tools that are needed to run the compilation tasks which aren’t needed to run your application code. The way to achieve this is to build the compiled code in one container and package it into another container(without the additional compilation tools.)
So essentially our docker file would look like

FROM linux:alpine AS build-env
WORKDIR /app
ADD ./app
RUN cd /app && go build -o goapp


FROM alpine ## ——> Note that we are starting from a fresh alpine image
RUN
COPY —from=build-env /app/goapp/ /app ## —> This will just copy that one file from the first step.

Thereby reducing the overall image size.


Always prefer to have one application running in one container in order to take the advantages of the container orchestration tools like K8s. This helps to keep the lifecycle of the application at par with the lifecycle of the container.

So if the core component of your application is not healthy, using healthCheck probes like livenessProbe or Readinessprobes, k8s will be able to restart it to ensure availability.

The first process launched inside a container is PID1 and hence linux signals such as SIGINT and SIGTERM
Most programs gracefully shutdown when they receive a SIGTERM signal, however for those who don’t, you could use a preStop Hook to trigger a graceful shutdown of your application.
So when K8s sends a SIGTERM signal, the application should then start terminating any long lived connections like a DB connection etc. By default, K8s waits for 30 seconds for the application to terminate however this is configurable in the pod yaml by using terminationGracePeriodSeconds attribute and setting its value higher than 30 seconds.
You should test how your application behaves when a SIGTERM signal is sent to it.,
If containers are still running after the SIGTERM , it them sends a SIGKILL signal.