SpecialistOff.NET / Вопросы / Статьи / Фрагменты кода / Резюме / Метки / Помощь / Файлы

Назад

Лучшие практики Dockerfile


Метки: dockerfile docker dnf centos fedora debian ubuntu arch linux astra linux docker-compose

Внимание! Оптимизацию можно делать только когда вы понимаете что делаете, зачем и к какому результату это приведёт. В случае сомнений делайте строго по официальной документации docker

  1. Не пытайтесь выбрать самый маленький по размеру образ. Выбирайте тот, который лучше всего знаете и который имеет необходимые инструменты (в том числе и для последующей отладки) или легко может их добавить в процессе или в будущем. Уменьшить размер и убрать лишнее можно будет после при наличии ресурсов и свободного времени.
    Это может противоречить конвенции "Не устанавливайте ненужных пакетов, даже если они могут быть полезными". Как показывает практика: время, затраченное на внедрение инструмента диагностики, иногда больше того времени, которое уходит на поиск проблемы.
  2. Первыми должны идти те слои, которые будут меняться реже всего. Обновления, добавление необходимых пакетов, настройка окружения, команды запуска, точки входа. Последними копирование ваших исходников или готовых приложений в проект.
    Плохо
    RUN dnf -y update && dnf -y install \
        package-bar \
        package-baz \
        package-foo  \
        && rm -rf /var/cache/dnf/*
    Очень плохо
    RUN dnf -y update
    RUN dnf -y install curl
    RUN rm -rf /var/cache/dnf/*
    Хорошо
    Отдельный скрипт pre-install.sh
    dnf -y update
    dnf -y install \ package-bar \ package-baz \ package-foo
    # здесь может быть ещё куча всего, включая тонкий тюнинг rm -rf /var/cache/dnf/*
    Далее выполняем его
    COPY ./deploy/pre-install.sh /root/
    RUN /root/pre-install.sh
    Примечание: Почему плохо или хорошо? По небольшому примеру трудно судить, но в реальности портянка скрипта бывает очень большая. Одно дело поставить пару-тройку пакетов, как на примерах, другое дело множество пакетов и настроек, которые не будут меняться в течении длительного периода. Длительным периодом может считаться даже месяц, т.к. считается, что в хороших компаниях релизы катают от нескольких раз в день, до несколько раз в час. Лирическое отступление: одна из метрик для DevOps: частота релизов/deployment frequency; если можно выпускать релиз десятки раз в день, при этом сократив количество ошибок, ведущих к незапланированным простоям, то, скорее всего, вы на верном пути внедрения DevOps.
    Пример портянки:
    https://gitlab.com/fuww/dockerfiles/-/blob/master/cloudshell-elixir/Dockerfile
    https://gitlab.com/getto-systems-base/labo/container/dockerfile/-/blob/release/Dockerfile
    https://gitlab.com/naturalis/lib/laravel/dockerfile/-/blob/master/Dockerfile
  3. Не пытайтесь засунуть весь скрипт в 100500 строк в Dockerfile. Лучше пусть будет пару лишних слоёв: добавление файла скрипта и его выполнение
  4. Добавляйте комментарии которые описывают цель команды, а не выполняемый командой процесс
  5. Минимизируйте количество слоёв без излишнего энтузиазма. Это касается не только Javascript, но и PHP, Python, Ruby и других программистов, у которых имеются пакетные менеджеры
    Пример
    COPY . /app
    RUN npm i
    Используется всего два слоя для копирования проекта внутрь образа. Но это приводит к тому, что каждый раз сборка выкачивает половину интернета, тратя драгоценные ресурсы (время, нагрузка на сеть, процессор и т.д.)
    Как нужно
    COPY ./package*.json /app
    RUN npm i
    COPY . /app
  6. Используйте .dockerignore. Это на самом деле очень крутая штука и имеет примерно равные возможности и предназначение как и .gitignore.
    .git
    tmp
    .env
    logs
    __pycache__
    *.pyc
  7. Устанавливайте необходимые права для нужных файлов на выполнение до того, как они будут упакованы в образ

    chmod +x entrypoint.sh
  8. Не плодите сущностей без необходимости. Другими словами у вас уже есть уникальная чистая среда. Не нужно внутри делать ещё одну, к примеру в виде pyenv.

  9. Придерживайтесь позиции: один сервис - один образ - один контейнер.

  10. Используйте мультистейдж сборки при отправке образа в реестр

    COPY package.json ./
    RUN npm install
    RUN npm run build
    
    FROM node:<version>-alpine
    COPY --from=appbuild /usr/src/app/dist ./app
    

    Внимание! Пример не рабочий, взят только для общего понимания
    Внимание! Здесь может быть холивар: некоторые программисты считают, что node_modules как аналог пакетов PIP несёт важные для работы библиотеки и необходим в составе конечного приложения

Частые вопросы

В чём разница между ADD и СOPY?

ADD умеет скачивать файлы

ADD https://releases.hashicorp.com/terraform/1.0.7/terraform_1.0.7_linux_amd64.zip /workdir

ADD умеет распаковывать архивы, но не все, а только tar

ADD helm-v3.7.0-linux-amd64.tar.gz /usr/local/bin/

В чём разницу между ENTRYPOINT и CMD

ENTRYPOINT является точкой входа

CMD является формой инструкций для образа

Такие формулировки немного сбивают с толку. ENTRYPOINT задаёт исполнителя, которому будут переданы инструкции-параметры CMD.

Пример

ENTRYPOINT ["./manage.py"]
CMD ["migrate"]

Будет по факту выполнено как

./manage.py migrate

Пример плохого

Реальный пример из книги «Docker на практике» ISBN 978-5-97060-772-5

Пример получше

FROM python:3.9

LABEL maintainer="RemiZOffAlex <remizoffalex@specialistoff.net>"
EXPOSE 5000
ENTRYPOINT ["./run.py"]

WORKDIR /usr/src/app

COPY ./deploy ./deploy

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

COPY . /usr/src/app

Не совсем Dockerfile

  1. Используйте docker-compose, для поднятия многоконтейнерного приложения. Располагайте docker-compose.yml рядом со своим проектом. Вне зависимости от того какая оркестрация будет доступна, но в суровых условиях быстрее будет развернуть пустой Docker и поверх поставить утилиту docker-compose.

Ссылки