Добавить в корзинуПозвонить
Найти в Дзене
Linux | Network | DevOps

Продвинутый Dockerfile: уровень выше обычного

Здесь мы разбираем продвинутые инструкции в Dockerfile: HEALTHCHECK — для проверки работы приложения, LABEL — для добавления меток, ARG — для автоматизации, SHELL — для изменения оболочки.
Мы уже разбирали данную опцию в docker-compose.yml. Но в одиночном образе (Dockerfile) тоже можно выполнять проверку на то что приложение не зависло.
Если мы собираем самодостаточный контейнер, который не будет
Оглавление

Здесь мы разбираем продвинутые инструкции в Dockerfile: HEALTHCHECK — для проверки работы приложения, LABEL — для добавления меток, ARG — для автоматизации, SHELL — для изменения оболочки.

HEALTHCHECK

Мы уже разбирали данную опцию в docker-compose.yml. Но в одиночном образе (Dockerfile) тоже можно выполнять проверку на то что приложение не зависло.

Если мы собираем самодостаточный контейнер, который не будет зависеть от docker-compose.yml, то лучше сделать HEALTHCHECK прям в Dockerfile. А если мы не собираем свой образ, а просто используем уже готовый образ в docker-compose.yml, тогда имеет смысл настроить HEALTHCHECK там.

В моём примере есть 3 сервиса :

  • db (postgres) — так как мы не собираем этот образ, то оставляем HEALTHCHECK в docker-compose.yml.
  • redis — тоже самое, оставляем HEALTHCHECK в docker-compose.yml.
  • web (python flask) — этот образ мы подготавливаем сами (с помощью Dockerfile), поэтому имеет смысл в Dockerfile прописать HEALTHCHECK.

Синтаксис:

HEALTHCHECK [OPTIONS] CMD <команда || exit 1>

  • Возможные опции:— каждые 30 секунд;
  • --interval=30s— команда должна выполниться за 3 секунды;
  • --timeout=3s— игнорировать первые 5 секунд после старта;
  • --start-period=5s— попробовать 3 раза перед пометкой контейнера как unhealthy.
  • --retries=3

Если HEALTHCHECK проваливается, контейнер остаётся запущенным, но его статус становится unhealthy. Статус можно посмотреть используя

docker ps .

LABEL

Позволяет добавлять произвольные теги: автор, версия, описание,

лицензия и т.д. Используется для документации, аудита, автоматизации.

Синтаксис:

LABEL maintainer="ivan@example.com"

LABEL version="1.0.0"

LABEL description="Flask app with Redis"

Увидеть метки можно с помощью команды

docker image inspect <образ>.

ARG

Аргументы позволяют передавать значения во время сборки (

docker build

), например, версию ПО, режим (dev/prod), конфиги.

Синтаксис:

ARG APP_ENV=production

Аргументы передаются через

--build-arg

или

docker-compose.yml

.

Аргументы похожи на переменные но используются только при сборке образа, в контейнер не передаются.

SHELL

По умолчанию команды RUN, CMD,ENTRYPOINT

выполняются через /bin/sh -c. Но можно задать свою оболочку, например,

/bin/bash.

Синтаксис:

SHELL ["/bin/bash", "-c"]

Это полезно, если вы используете сложные bash-конструкции (&&,||,$()и т.д.).

Практика

Продолжаем использовать стек из предыдущего урока: [Docker Compose — установка и базовое использование]. Рабочий каталог — docker_compose.

LABEL

Добавим в Dockerfile LABEL:

FROM python:3.11-slim

LABEL maintainer="alex@sysadminium.ru"

LABEL version="1.0.0"

LABEL description="Flask app with Redis and PostgreSQL"

WORKDIR /app

COPY requirements.txt .

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

COPY app.py .

CMD ["python", "app.py"]

Пересоберём контейнеры:

$ docker compose up --build -d

Посмотрим на метки:

$ docker image inspect docker_compose-web:latest

***

"Labels": {

"com.docker.compose.project": "docker_compose",

"com.docker.compose.service": "web",

"com.docker.compose.version": "5.0.1",

"description": "Flask app with Redis and PostgreSQL",

"maintainer": "alex@sysadminium.ru",

"version": "1.0.0"

},

***

  • .Первые 3 метки сделаныdocker compose
  • .Следующие 3 метки из нашегоDockerfile

ARG

Добавим аргументы, с помощью которых будем управлять версией Python.

Редактируем Dockerfile:

# Аргумент (должен быть объявлен до FROM)

ARG PYTHON_VERSION=3.11

# Теперь используем его в FROM

FROM python:${PYTHON_VERSION}-slim

LABEL maintainer="alex@sysadminium.ru"

LABEL version="1.0.0"

LABEL description="Flask app with Redis and PostgreSQL"

WORKDIR /app

COPY requirements.txt .

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

COPY app.py .

CMD ["python", "app.py"]

  • Если при сборке аргумент не передан, то будет использоваться версия 3.11.

Вручную мы бы собирали образ так:

$ docker build --build-arg PYTHON_VERSION=3.10 .

Но так как у нас

docker-compose.yml

, то правим его (только секцию web):

web:

build:

context: .

args:

- PYTHON_VERSION=3.10

ports:

- "${FLASK_PORT}:5000" # используем переменную из .env

environment:

- REDIS_HOST=redis

- POSTGRES_HOST=db

- POSTGRES_DB=${POSTGRES_DB} # переменная из .env

- POSTGRES_USER=${POSTGRES_USER} # переменная из .env

- POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # переменная из .env

depends_on:

redis:

condition: service_healthy

db:

condition: service_healthy

# bind mount

volumes:

- ./app.py:/app/app.py

Пересоберём, и в контейнере теперь будет python 3.10:

$ docker compose down

$ docker compose up --build -d

$ docker compose exec web python --version

Python 3.10.19

То есть с помощью аргументов стало проще изменять версию python.

Ещё раз поменяем версию python в файле

docker-compose.yml

:

PYTHON_VERSION=3.11

Пересоберём контейнер:

$ docker compose up --build -d

$ docker compose exec web python --version

Python 3.11.14

Кстати, на 3.12 наше приложение не заработает из за более новой версии psycopg2.

HEALTHCHECK

Теперь добавим проверку HEALTHCHECK в Dockerfile:

ARG PYTHON_VERSION=3.11

FROM python:${PYTHON_VERSION}-slim

LABEL maintainer="alex@sysadminium.ru"

LABEL version="1.0.0"

LABEL description="Flask app with Redis and PostgreSQL"

WORKDIR /app

COPY requirements.txt .

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

COPY app.py .

HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \

CMD bash -c 'echo > /dev/tcp/127.0.0.1/5000 || exit 1'

CMD ["python", "app.py"]

Пересоберём контейнер:

$ docker compose up --build -d

И проверим работоспособность:

$ docker compose ps

SERVICE CREATED STATUS PORTS

db 23 minutes ago Up 23 minutes (healthy) 5432/tcp

redis 23 minutes ago Up 23 minutes (healthy) 6379/tcp

web 38 seconds ago Up 27 seconds (healthy) 0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp

  • Все сервисы со статусом healthy.
  • .db и redis благодаряdocker-compose.yml
  • .web благодаряDockerfile

Можно использовать и другие проверки:

  • HEALTHCHECK CMD wget -q -O - http://localhost:5000 || exit 1
  • HEALTHCHECK CMD curl http://localhost:5000 || exit 1

Эти 2 варианта проверяют http ответ, но дополнительно нужно устанавливать в образ wget или curl.

Мой вариант не проверяет http ответ, а просто проверяет что кто-то

слушает 5000 порт в контейнере. Я так сделал, чтобы не устанавливать

wget или curl в контейнер. Возможно для продакшина нужно было установить

curl , в приложении сделать специальный endpoint для проверки. И производить HEALTHCHECK с помощью curl на этот endpoint. Например так:

HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 \

CMD curl -f http://127.0.0.1:5000/test || exit 1