🕒 4 мин.
Здесь мы разбираем продвинутые инструкции в 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>
- Возможные опции:
--interval=30s— каждые 30 секунд;--timeout=3s— команда должна выполниться за 3 секунды;--start-period=5s— игнорировать первые 5 секунд после старта;--retries=3— попробовать 3 раза перед пометкой контейнера как unhealthy.
Если 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 1HEALTHCHECK 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
Если понравилась статья, подпишись на мой канал в VK или Telegram.