Docker Implementation

Ritwik MathRitwik Math
6 min read

Docker is the most popular technology for implementing containers. It can be found everywhere, from local development machines to production servers. Knowing how to implement Docker containers is now more important than ever. A poorly constructed Docker container is not only a security threat, but also can slow down production servers and the deployment process. In this article, I have discussed several Docker commands you can use to implement your containers in local and production environments.

Using No-Cached Layer

When debugging your application, you want a clean installation. This eliminates any unexpected behaviour raised due to the cached layer. However, for security reasons, you should occasionally create an image without the cached layer. Sometimes, Docker build caches can be corrupted and generate inconsistent results.

For all the above scenarios, you can build a Docker image without using a cache layer.

docker build --no-cache -t dummy:latest .

Running Container with Non-Root User

By default, Docker runs a container with the root user. This directly affects the container security, because anyone who has access to the container has access to the root user. There are two ways to handle this.

  1. Create a non-root user and use it in the Dockerfile

  2. Use -u argument in docker run

Option 1: Dockerfile

FROM python:3.11

RUN useradd ritwik -ms /bin/bash ritwik

WORKDIR /app

COPY ./requirements.txt ./

RUN pip install -r requirements.txt

COPY ./src ./src

USER ritwik

ENTRYPOINT ["python"]
CMD ["/app/src/server.py"]

When you run the container, it uses the user ritwik to run the container.

Option 2: -u Argument

FROM alpine

CMD ["whoami"]
docker run -u 1001:1001 testuser

Even if user 1001 does not exist, the container does not use the root user.

Creating Image Tags

Creating tags for your image is very crucial. There are two popular ways of naming your tags.

  1. Use the latest commit hash

  2. Use the release date

Option 1: Commit Hash

docker build -t myserver:$(git rev-parse --short HEAD) .

Option 2: Release Date

Windows Powershell

docker build -t myserver:$(Get-Date -Format "yyyy-MM-dd") .

Linux Bash

docker build -t myserver:$(date +%Y-%m-%d) .

Restart Policy

Docker comes with four restart policies.

  1. no

  2. on failure with max retries

  3. always

  4. unless-stopped

no policy dictates, the container will not restart under any circumstances.

docker run -d --restart no myserver

On-failure policy dictates, the container restarts if the exit code is non-zero. You can also mention the number of retries.

docker run -d --restart on-failure:3 redis
FROM alpine

# Will exit with non-zero code
CMD ["sleep", "1", "&&", "exit", "1"]

always policy dictates, the container will restart without any condition. If manually stopped, the container will restart on Docker daemon restart.

# Mention at container run
docker run -d --restart always redis
# Update existing container policy
docker update --restart always eb223

When Stopped Manually

Once the Docker daemon is restarted, the container starts restarting.

unless-stopped policy dictates, the container does not restart automatically when the Docker daemon is restarted.

docker update --restart unless-stopped eb223

In case you want to apply the same policy on every container, you can run the following command.

docker update --restart unless-stopped $(docker ps -q)

Remove Container Automatically

When trying out different commands or configuring a Dockerfile, you will create the container from the same image. It is a good idea to give the container a name. But Docker does not allow more than one container to have the same name. That is why deleting the previous container is essential. Docker allows you to delete a container when it is stopped automatically using the —rm argument.

# --rm argument makes sure that container is deleted when it is stopped
docker run --rm -d -p 8088:8088 --name server myserver:latest

You cannot use the —rm with the —restart argument.

Container Logs

Logs are a crucial part of any application. Finding the source of error, tracing warnings, and checking the flow of requests are just a few examples of the benefits of Logs.

docker logs server

This command prints all the logs generated by an application

You may want to see live logs to analyse the application in real-time.

docker logs server --follow

Or you can also track the record of the last n logs.

# Last 30 logs
docker logs server --tail 30

When you run your application on a local machine or you are storing the logs in a container instead of a third-party service, restricting the size of logs is crucial. If not restricted, it can stop your system unexpectedly.

# Limit log file size to 10 MB.
docker run --rm -d --log-opt max-size=10m -p 8088:8088 --name server myserver:latest
# Keep up to 3 rotated log files (i.e., 30 MB total).
docker run --rm -d --log-opt max-size=10m --log-opt max-file=3 -p 8088:8088 --name server myserver:latest

Copy Files

Sometimes, you may want to copy a file to/from your container. You can copy a config file to your container without re-creating the image. Or you can copy the logs from your container for analysis or debugging purposes.

docker cp ./config.ini server:/app/config/config.ini

Now you can restart your application and load the new configuration.

docker cp server:/usr/logs/server.log ./server.log

You can analyse the log file or upload it to any service that can analyse the log.

One more interesting use of Docker cp is using a different OS. If you are using Windows and need to generate a file in a Linux environment, so it can be compatible with your servers in the cloud. You can create a Linux container, generate the file, and copy it from the container. Then push the file to your server. One great example is AWS Lambda Layer. You can check this article.

Healthcheck

This is a part of Dockefile configuration. You can check the health of your application or an application on which your app depends. For example, your app depends on a PostgreSQL database. You need to check if the database is on and running.

FROM python:3.11-slim

RUN apt-get update && apt-get install -y \
    netcat \
    postgresql-client \
    && rm -rf /var/lib/apt/lists/*

WORKDIR /app

COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt

COPY ./src ./src

# Add healthcheck to ensure PostgreSQL is available
HEALTHCHECK --interval=10s --timeout=5s --retries=3 \
  CMD pg_isready -h localhost -p 3600 || exit 1

CMD ["python", "/app/src/server.py"]

Or you can use the curl command.

# Add healthcheck to ensure endpoint returns status code Ok
HEALTHCHECK --interval=10s CMD curl -f http://localhost:5000/health || exit 1
0
Subscribe to my newsletter

Read articles from Ritwik Math directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ritwik Math
Ritwik Math

As a seasoned senior web developer with a wealth of experience in Python, Javascript, web development, MySQL, MongoDB, and React, I am passionate about crafting exceptional digital experiences that delight users and drive business success.