Леонид МаслаковLenpaste

Написание Dockerfile

08.03.2022 (изменён 22.10.2023)

Dockerfile состоит из инструкций, одна инструкция - одна строка. Если по какой то причине инструкция получилась слишком длинной, то можно растянуть её на несколько строк поставив \ в конец, “продлеваемой”, строки.

Так же с помощью # можно сделать комментарий.

Важно понимать что каждая инструкция создаёт новый слой в образе. Эти слои хранятся в кэше и используются при повторной сборке образа. Так что логично сначала установить все зависимости, а только потом добавлять в образ код программы.

Вот пример простого Dockerfile для программы, написанной на Python:

FROM python:3.9.18

# Комментарий.
# Ещё один комментарий.

WORKDIR /app

COPY ./ ./

VOLUME /data

CMD [ "python", "./main.py" ]

FROM

У каждого образа Docker должен быть “образ-предок”. Почти для всех популярных языков программирования такой образ можно найти на Docker Hub. Вот несколько примеров:

  • Python 3 - FROM python:3.9.8,
  • NodeJS - FROM node:21.0.0,
  • Golang - FROM golang:1.21.3.

Указать название “образа-предка” можно указать несколькими способами:

<образ>
<образ>[:<тег>]
<образ>[:<хэш-сумма>]

Если тег не указан, то будет использован тег latest. То есть FROM golang и FROM golang:latest - это эквивалентные инструкции.

В обязательном порядке следует указывать полную версию образа, а ещё лучше его хэш-сумму.

# Плохо:(
FROM golang

# Тоже плохо:(
FROM golang:1.21

# Терпимо.
FROM golang:1.21.3

# Отлично:)
FROM golang@sha256:d0214956a9c50c300e430c1f6c0a820007ace238e5242c53762e61b344659e05

У читателя может возникнуть вопрос какой “образ-предок” у образов вроде debian или alpine. Ответ такой - образ scratch.

Несмотря на то что образ scratch можно найти на DockerHub, скачать его не возможно (scratch - это зарезервированное имя). Так же инструкция FROM scratch не создаст новый слой в образе как этого следовало бы ожидать.

# Тут не создаётся слой.
FROM scratch

# Первый слой.
COPY ./mybin /

# Итого создано слоёв: 1
# Первый слой.
FROM alpine:3.18.4

# Второй слой.
COPY ./mybin /

# Итого создано слоёв: 2

WORKDIR

Указывает на рабочую директорию (меняет переменную среды $PWD). Можно обойтись и без него, но лучше задать что бы везде не прописывать один и тот же путь.

FROM alpine:3.18.4

# Команда pwd вывод в консоль путь к директории
# в которой находиться пользователь.
RUN pwd

WORKDIR /other/dir
RUN pwd

WORKDIR /other/other/other
RUN pwd

# Вывод:
#   /
#   /other/dir
#   /other/other/other

ARG и ENV

ARG позволяет установить переменную, использующиеся на этапе сборке образа, после в конечном образе значение этой переменной не будет доступно. ENV устанавливает переменную среды которая будет доступна всегда, как во время сборки, так и во время запуска.

ENV IS_DOCKER="true"
ENV APP_DATA_DIR="/data"
ENV LOG_LEVEL="err"

RUN echo $APP_DATA_DIR

В переменных не следует хранить конфиденциальную информацию например пароли от БД, API ключи и так далее, так как эта информация станет частью конечного образа Docker. Следовательно любой кто сможет скачать образ, так же сможет получить доступ к вашей конфиденциальной информации.

COPY

Копирует файл внутрь образа.

# Получится так:
#  /app/main.py
#  /app/LICENSE.txt

WORKDIR /app
COPY ./main.py ./
COPY ./LICENSE ./LICENSE.txt

RUN

Выполняет команду во время сборки образа.

# Вот так команду можно разбить на несколько строк

RUN apt-get update -y && \
apt-get upgrade -y && \
apt-get install -y make gcc

# Ещё одна команда
RUN chmod 755 /usr/local/bin/myprg

ENTRYPOINT и CMD

Эти команды делают примерно одно и тоже, указывают какую команду надо выполнить при запуске контейнера.

Разница между этими двумя инструкциями в том что ENTRYPOINT выполняется всегда, а CMD может быть перезаписана с помощью аргументов командной строки при запуске контейнера Docker.

ENTRYPOINT ["/bin/ping"]
CMD ["localhost"]
# В итоги контейнер запустит /bin/ping localhost

EXPOSE

Эта команда маркирует порт который можно “пробросить” наружу. При этом даже если порт не был объявлен командной EXPOSE его всё равно можно открыть.

EXPOSE 80/tcp
EXPOSE 443/tcp
EXPOSE 2323/udp
EXPOSE 22

VOLUME

Работает по такой же логике как команда EXPOSE. Только теперь объявляются директории, которые можно “пробросить” на хост.

VOLUME /data
VOLUME /usr/share/www/html

LABEL

Позволяет добавить любые метаданные к образу.

# Данные могут быть абсолютно любыми
LABEL org.example.vendor="ACME Incorporated"
LABEL org.example.label-with-value="foo"

Ссылки