Написание 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"