You should stop manually writing Dockerfiles

Are you also tired of manually filling out Dockerfile And docker-compose.yaml for every new project?

I always wondered if I was using known best practices when writing Docker config, and if I would accidentally introduce any vulnerabilities by manually filling out config files.

Well, now I don't have to worry about that anymore, thanks to the good people at Docker, who recently implemented a tool to do this without any fanfare.

They created a CLI utility – docker init .

docker init

A few days ago (February 6, 2024 – translator's note) Docker company released docker init. I tried it and now I want to use it in my new projects.

Why should I use docker init?

As already said, docker init is a CLI utility that creates files Dockerfile, docker-compose.yaml And .dockerignore automatically detecting some of your project's dependencies during deployment.

This simplifies the process of setting up Docker, saving time and helping to avoid vulnerabilities that could be introduced with manual configuration.

Latest version docker init supports Go, Python, Node.js, Rust, ASP.NET, PHP and Java. It is also available with Docker Desktop.

How to use docker init

Let's create a simple example project to see how docker init handles automatically detecting dependencies for Dockerfile.

Let it be a simple Flask application.

touch app.py requirements.txt
# app.py
from flask import Flask

app = Flask(__name__)

@app.route('/')
def hello_docker():
    return '<h1> hello world </h1>'

if __name__ == '__main__':
    app.run(debug=True, host="0.0.0.0")
# requirements.txt
Flask

docker init will scan the project and give you the opportunity to choose the most suitable template. After choosing a template docker init will check with you the required dependencies and their versions, automatically adding them to the generated configs.

docker init

In our example, we are using a Python application, so we will select an automatically defined template and indicate on which port and with which command our application is launched.

docker init will output the created config files and show instructions for launching

docker init will output the created config files and show instructions for launching

Let's see what the generated files look like:

# Dockerfile
# syntax=docker/dockerfile:1

# Comments are provided throughout this file to help you get started.
# If you need more help, visit the Dockerfile reference guide at
# https://docs.docker.com/engine/reference/builder/

ARG PYTHON_VERSION=3.11.7
FROM python:${PYTHON_VERSION}-slim as base

# Prevents Python from writing pyc files.
ENV PYTHONDONTWRITEBYTECODE=1

# Keeps Python from buffering stdout and stderr to avoid situations where
# the application crashes without emitting any logs due to buffering.
ENV PYTHONUNBUFFERED=1

WORKDIR /app

# Create a non-privileged user that the app will run under.
# See https://docs.docker.com/go/dockerfile-user-best-practices/
ARG UID=10001
RUN adduser \
    --disabled-password \
    --gecos "" \
    --home "/nonexistent" \
    --shell "/sbin/nologin" \
    --no-create-home \
    --uid "${UID}" \
    appuser

# Download dependencies as a separate step to take advantage of Docker's caching.
# Leverage a cache mount to /root/.cache/pip to speed up subsequent builds.
# Leverage a bind mount to requirements.txt to avoid having to copy them into
# into this layer.
RUN --mount=type=cache,target=/root/.cache/pip \
    --mount=type=bind,source=requirements.txt,target=requirements.txt \
    python -m pip install -r requirements.txt

# Switch to the non-privileged user to run the application.
USER appuser

# Copy the source code into the container.
COPY . .

# Expose the port that the application listens on.
EXPOSE 5000

# Run the application.
CMD gunicorn 'app:app' --bind=0.0.0.0:5000

The generated file contains comments for all used parameters and complies with best security practices – for example, here a separate user without root rights is created in the container under which the application is launched.

And this is what it looks like compose.yaml:

Unlike the Dockerfile, the generated compose config is quite small. A commented section with PostgreSQL settings has also been added to it – if you need it (I quite often see the use of PostgreSQL in Python applications), it will be enough to uncomment this section.

Why should I use docker init?

Using this utility dockerization your application becomes much simpler, which is important for those who have just started learning Docker. It saves time and minimizes possible errors.

The utility can adapt to your application (automatically detecting a suitable template and dependencies) and follows industry best practices – this speeds up working with containers (for example, the generated config above uses environment variables for Python optimization and arguments for improved pip caching), and also allows It's safe to deploy an application even if you don't understand best practices when working with Docker.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *