Продвинутые инструкции в Dockerfile

🕒 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

По умолчанию команды RUNCMDENTRYPOINT выполняются через /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

Если понравилась статья, подпишись на мой канал в VK или Telegram.

Мы используем cookie-файлы для наилучшего представления нашего сайта. Продолжая использовать этот сайт, вы соглашаетесь с использованием cookie-файлов.
Принять
Отказаться
Политика конфиденциальности