В рамках данной статьи будут рассмотрены наиболее частые антипаттерны при проектировании docker образов, а так же будет представлено оптимальное решение для каждого
Использование избыточного базового образа
Один из распространенных антипаттернов при использовании Docker - это использование избыточного базового образа.
# Плохо
FROM ubuntu
Оптимальное решение - использовать наиболее легковесный базовый образ, который содержит только необходимые компоненты, в роли которого может выступать alpine.
# Хорошо
FROM alpine
Сравнение размера двух образов:
root@gusev:~# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest 8ca4688f4f35 9 days ago 7.34MB
ubuntu latest 3565a89d9e81 13 days ago 77.8MB
Множество инструкций
Из-за особенностей работы docker, каждая выполненная инструкция - выполняет “снэпшот” состояния файловой системы контейнера на текущий момент, так что следует уменьшать количество этих самых инструкций. Пример плохой реализации Dockerfil’a:
# Плохо
FROM ubuntu
RUN apt update
RUN apt install -y nginx
RUN apt install -y curl
RUN touch /my_file
RUN chmod +x /my_file
Оптимальным вариантом будет следующий:
# Плохо
FROM ubuntu
RUN apt update && \
apt install -y nginx && \
apt install -y curl && \
touch /my_file && \
chmod +x /my_file
Не оптимальная установка пакетов
Часто можно наблюдать в Dockerfile такую конструкцию:
# Плохо
FROM alpine
RUN apk update && \
apk add curl wget nginx
Данная конструкция не является оптимальной, т.к. выполняется обновление локальных кэшей, а это занимает драгоценное место, оптимальным решением будет следующее:
# Хорошо
FROM alpine
RUN apk add --no-cache curl wget nginx
В случае, если используется пакетный менеджер apt:
# Хорошо
FROM ubuntu
RUN apt update && \
apt install -y curl wget nginx && \
rm -rf /var/lib/apt/lists/*
Использование последних версий
Зачастую в Dockerfile можно часто встретить конструкции по типу:
# Плохо
FROM alpine
RUN apk add --no-cache curl wget nginx
Эта конструкция является плохой, т.к. через время, может появиться необходимость пересобрать контейнер, но вероятно возникнет ошибка из-за того, что были обновлены артефакты которые используются при сборке. Оптимальный вариант:
# Хорошо
FROM alpine:3.18.4
RUN apk add --no-cache curl==8.3.0-r0 wget==1.21.4-r0 nginx==1.24.0-r6
Multi-stage сборки
В Docker есть возможность реализовать multi-stage сборки, если кратко, то суть сводится к тому, что в Dockerfile описывается множество этапов, причем из каждого артефакты передаются в следующие. Отказ от использования такой возможности, может сильно увеличить размер конечного образа.
# Плохо
FROM alpine:3.18.4
WORKDIR /app_tmp
COPY . .
RUN apk add --no-cache hugo && hugo --destination=/app --baseURL=https://oldtyt.xyz
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /app
RUN apk add --no-cache curl nginx
ENTRYPOINT ["nginx", "-g", "daemon off;"]
Оптимальным же решением будет следующее:
# Хорошо
FROM alpine:3.18.4 as builder
WORKDIR /app
COPY . .
RUN apk add --no-cache hugo && hugo --destination=/app_out --baseURL=https://oldtyt.xyz
FROM alpine:3.18.4
COPY --from=builder /app_out /app
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /app
RUN apk add --no-cache curl nginx
ENTRYPOINT ["nginx", "-g", "daemon off;"]
Healthcheck
Healthcheck - это инструкция, которую Docker может использовать для проверки работоспособности запущенного контейнера. По большей части healthcheck проверяют доступность страницы/порта приложения. Один из вариантов реализации:
FROM alpine:3.18.4 as builder
WORKDIR /app
COPY . .
RUN apk add --no-cache hugo && hugo --destination=/app_out --baseURL=https://oldtyt.xyz
FROM alpine:3.18.4
COPY --from=builder /app_out /app
COPY nginx.conf /etc/nginx/nginx.conf
WORKDIR /app
RUN apk add --no-cache curl nginx
HEALTHCHECK --interval=5s --timeout=10s --retries=3 CMD curl -IL 127.0.0.1 | grep 200 || exit 1
ENTRYPOINT ["nginx", "-g", "daemon off;"]