# Dockerfile

<div id="bkmrk-dockerfile---%D0%BD%D0%B0%D0%B1%D0%BE%D1%80-%D0%B8">DockerFile - набор инструкций по установке и настройке контейнера. Также используется для создания преднастроенных образов. Обычно dockerfile расположен в директории, из которой вызывается команда. </div><div id="bkmrk-%D0%98%D0%BC%D1%8F-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%3A-dockerfil">Имя файла: Dockerfile (без расширения).</div><div id="bkmrk-%D0%9A%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D1%8F---">Контекст создания - набор локальных файлов/каталогов, к которым можно обращаться через copy/add. </div><div id="bkmrk-%2F-%D0%BD%D0%B5-%D1%81%D1%82%D0%BE%D0%B8%D1%82-%D0%B8%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D0%BE%D0%B2">/ не стоит использовать как контекст, полностью включается в архив</div><div id="bkmrk-%23-%D0%BA%D0%BE%D0%BC%D0%BC%D0%B5%D0%BD%D1%82%D0%B0%D1%80%D0%B8%D0%B8">\# комментарии</div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%BE%D0%B1%D1%80%D0%B0%D0%B7%D0%B0-%D0%BF%D0%BE">При сборке образа попытки использования кэша идут до первого промаха. Затем кэш не проверяется.</div><div id="bkmrk-.dockerignore---%D1%84%D0%B0%D0%B9%D0%BB">.dockerignore - файл исключений из контекста</div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D0%B8%D1%80%D1%83">При сборке компилируемых приложений лучше использовать multi-stage build.</div><div id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 18.9511%;"></col><col style="width: 30.7509%;"></col><col style="width: 50.298%;"></col></colgroup><thead><tr><td class="align-center">Основная команда</td><td class="align-center">Параметры</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>docker build</td><td> </td><td>создание образа

Создание образа из docker файла . - контекст создания (текущая папка)

```bash
docker build -t test/cowsay-dockerfile .
```

После этого образ появляется в списке образов

</td></tr><tr><td>  
</td><td>-t</td><td>имя слоя</td></tr><tr><td>  
</td><td>-f</td><td>расположение слоя</td></tr><tr><td>  
</td><td>--squash</td><td>сжимает все слои в один</td></tr><tr><td>docker history</td><td>&lt;image&gt;</td><td>инструкции создания образа</td></tr><tr><td>docker buildx</td><td>  
</td><td>для компиляции под разные платформы</td></tr></tbody></table>

</div><div class="align-center" id="bkmrk-%D0%9F%D1%80%D0%B8%D0%BD%D1%86%D0%B8%D0%BF%D1%8B">**Принципы**</div><div class="align-left" id="bkmrk-%D0%9D%D1%83%D0%B6%D0%BD%D0%BE-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%B2%D0%B0%D1%82%D1%8C-%D1%8D%D1%84%D0%B5%D0%BC">- Нужно создавать эфемерные контейнеры без данных внутри. Переменные настраиваются потом.
- Контекст сборки - директория размещения Dockerfile.
- Есть .dockerignore аналогичен .gitignore
- Не устанавливать ненужные пакеты
- Использовать кэш сборки

</div><div class="align-center" id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D1%8B%D0%B5-%D0%BF%D1%80%D0%B0%D0%B2%D0%B8%D0%BB%D0%B0-%D1%81%D0%BE%D0%B7">**Основные правила создания контейнера**</div><div class="align-left" id="bkmrk-%D0%9D%D0%B5-%D1%85%D1%80%D0%B0%D0%BD%D0%B8%D1%82%D1%8C-%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D0%B5-%2F-">- Не хранить данные / учетные данные внутри контейнера
- Не дробить поставку приложения
- Не создавать большие образы
- Не делать однослойные контейнеры
- Не создавать образы из запущенных контейнеров
- Не надо использовать только тег latest
- Не выполнять в контейнере более одного процесса
- Не запускать от имени root
- Не надо использовать явную связь с IP

</div><div class="align-center" id="bkmrk-%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82-exec-%D0%B8-%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82">**Формат exec и формат командной оболочки**</div><div id="bkmrk-%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82%C2%A0-exec">**Формат exec**</div><div id="bkmrk-json-%D0%BC%D0%B0%D1%81%D1%81%D0%B8%D0%B2">JSON-массив</div><div id="bkmrk-%D0%BF%D0%B5%D1%80%D0%B2%D1%8B%D0%B9-%D1%8D%D0%BB%D0%B5%D0%BC%D0%B5%D0%BD%D1%82-%D0%BC%D0%B0%D1%81%D1%81%D0%B8">первый элемент массива: имя выполняемого файла</div><div id="bkmrk-%D0%BE%D1%81%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D1%8B%D0%B5---%D0%BF%D0%B0%D1%80%D0%B0%D0%BC%D0%B5%D1%82%D1%80">остальные - параметры, передаваемые при запуске. </div><div id="bkmrk-%D0%A4%D0%BE%D1%80%D0%BC%D0%B0%D1%82%C2%A0-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%BD%D0%BE%D0%B9-%D0%BE%D0%B1">**Формат командной оболочки** </div><div id="bkmrk-%D1%81%D1%82%D1%80%D0%BE%D0%BA%D0%B0-%D0%BF%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9-">строка произвольной формы, передаваемая для интерпретации в /bin/sh -c. </div><div id="bkmrk-%D0%98%D1%81%D0%BF%D0%BE%D0%BB%D1%8C%D0%B7%D1%83%D0%B9%D1%82%D0%B5-%D1%84%D0%BE%D1%80%D0%BC%D0%B0%D1%82-e">Используйте формат exec, чтобы избежать случайного искажения строк командной оболочки, или в тех случаях, когда образ не содержит /bin/sh.</div><div id="bkmrk-%D0%9A%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-%D1%84%D0%B0%D0%B9%D0%BB%D0%B0%3A">Команды файла:</div><div id="bkmrk-%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D0%B0-%D0%9F%D0%B0%D1%80-1"><table border="1" style="border-collapse: collapse; width: 100%;"><colgroup><col style="width: 17.1633%;"></col><col style="width: 15.4985%;"></col><col style="width: 67.3382%;"></col></colgroup><thead><tr><td class="align-center">Основная команда</td><td class="align-center">Параметры</td><td class="align-center">Описание</td></tr></thead><tbody><tr><td>FROM</td><td>&lt;name&gt;</td><td>название базового образа</td></tr><tr><td>MAINTAINER</td><td>&lt;name&gt;</td><td>имя поддерживающего пользователя</td></tr><tr><td>USER</td><td>&lt;name&gt;</td><td>!Всегда определять! Задает пользователя (по имени или по идентификатору UID) для использования во всех последующих инструкциях RUN, CMD, ENTRYPOINT.</td></tr><tr><td>WORKDIR </td><td>  
</td><td><div>рабочий каталог для последующих RUN, CMD, ENTRYPOINT, ADD, COPY. </div><div>можно использовать несколько раз </div><div>можно относительные пути, итоговый путь относительно предыдущего WORKDIR.</div></td></tr><tr><td>RUN</td><td>&lt;commands&gt;</td><td><div>команды при инициализации образа, обычно установка пакетов</div><div>каждая создает новый слой</div></td></tr><tr><td>CMD {''}</td><td>  
</td><td><div>команда с аргументами, выполняемая после запуска контейнера. Аргументы могут быть переопределены при запуске контейнера. В файле может присутствовать лишь одна инструкция CMD.</div></td></tr><tr><td>ENTRYPOINT</td><td>&lt;program name&gt;</td><td><div>Выполняемый файл, который будет вызываться для обработки аргументов, переданных в команду docker run </div>```
ENTRYPOINT ["/usr/games/cowsay", "-k"]
```

</td></tr><tr><td>COPY</td><td>&lt;&gt; &lt;&gt;</td><td><div>копирование файла из ФС ОС в ФС образа COPY . /src копирует все из текущей папки в папку /src</div></td></tr><tr><td>VOLUME</td><td>&lt;&gt;</td><td><div>Том в ФС</div></td></tr><tr><td>ARG</td><td>  
</td><td><div>Определяет переменные среды, доступные внутри образа при сборке. Неизменяемые.</div></td></tr><tr><td>ENV</td><td>  
</td><td><div>Определяет переменные среды внутри образа, но могут изменяться. На эти переменные можно ссылаться в последующих инструкциях.*</div>```yaml
ENV MY_VERSION 1.3
RUN apt-get install -y mypackage=$MY_VERSION
```

Переменные среды со значением по умолчанию

Файл first.sh

```
echo $NODE_ENV
```

Dockerfile

```yaml
FROM alpine:latest
ARG NODE_ENV=production2
ENV NODE_ENV=${NODE_ENV}
RUN mkdir /var/www
COPY first.sh /var/www/first.sh
CMD ["sh", "/var/www/first.sh"]
```

Собираем образ

```
docker build -t bobrobot:1.0 .
```

docker-compose.yml

```
services:
  bobrobot:
    image: bobrobot:1.0
    environment:
      NODE_ENV: "first2"
```

В итоге при указании NODE\_ENV выводится first2, иначе production2

</td></tr><tr><td>EXPOSE</td><td>  
</td><td><div><div>Сообщает механизму Docker, что в контейнере будет процесс, прослушивающий порт(ы)</div><div>не оказывает воздействия на сетевую среду</div><div>нужно для аргумента -p в docker run</div></div></td></tr><tr><td>ONBUILD</td><td>  
</td><td><div><div><div>инструкция, выполняемая когда образ будет использоваться как основной уровень для другого образа. Полезным при обработке данных, добавляемых в образ-потомок </div><div>(например, инструкция копирования дополнительного кода из заданного каталога и запуска скрипта сборки, обрабатывающего скопированные данные).</div></div></div></td></tr></tbody></table>

</div><div id="bkmrk-%2A-%D0%94%D0%BB%D1%8F-%D1%80%D0%B0%D0%B7%D0%BB%D0%B8%D1%87%D0%BD%D1%8B%D1%85-%D0%BE%D0%B1%D1%80%D0%B0">* Для различных образов могут использоваться доп. переменные, иногда обязательные. Детали нужно уточнять для конкретного образа.</div><div id="bkmrk-"></div><div id="bkmrk-%D0%9F%D1%80%D0%B8-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B5-%D0%BA%D0%BE%D0%BC%D0%B0%D0%BD%D0%B4%D1%8B-%D0%B8">При сборке команды исполняются однократно. Затем при запуске образа они не повторяют исполнение. Поэтому для </div>```
FROM alpine:latest
ARG NODE_ENV=production2
ENV NODE_ENV=${NODE_ENV}
RUN mkdir /var/www
RUN echo $NODE_ENV > /var/www/first.txt
CMD ["cat", "/var/www/first.txt"]
```

<div id="bkmrk-%D0%B2%D1%8B%D0%B2%D0%BE%D0%B4-%D0%B2%D1%81%D0%B5%D0%B3%D0%B4%D0%B0-%D0%B1%D1%83%D0%B4%D0%B5%D1%82-p">вывод всегда будет production2.</div><div class="align-center" id="bkmrk-%D0%9C%D0%BD%D0%BE%D0%B3%D0%BE%D1%88%D0%B0%D0%B3%D0%BE%D0%B2%D0%B0%D1%8F-%D1%81%D0%B1%D0%BE%D1%80%D0%BA%D0%B0-">**Многошаговая сборка (Multi-stage building)**</div><div id="bkmrk-%D0%A1%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%BE%D0%B4%D0%BD%D0%BE%D0%B3%D0%BE-%D0%BE%D0%B1%D1%80%D0%B0">**Создание одного образа с участием нескольких временных**</div><div id="bkmrk-%D0%9D%D0%B5%D0%BE%D0%B1%D1%85%D0%BE%D0%B4%D0%B8%D0%BC%D0%B0-%D0%B5%D1%81%D0%BB%D0%B8-%D0%BD%D1%83%D0%B6%D0%BD">Необходима если нужно при помощи временного образа скомпилировать исходники и результат сохранить в финальный образ. Временный образ удаляется. </div>```
FROM golang:1.20-alpine AS base
WORKDIR /src
COPY go.mod go.sum .
RUN go mod download
COPY . .

FROM base AS build-client
RUN go build -o /bin/client ./cmd/client

FROM base AS build-server
RUN go build -o /bin/server ./cmd/server

FROM scratch AS prod
COPY --from=build-client /bin/client /bin/
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]
```

<div id="bkmrk--1"></div><div id="bkmrk--2"></div>Сборка образа:

```
docker build -t multi:stage .
```

**Создание нескольких образов**

Также можно создать несколько образов при помощи одного Dockerfile. Отличие в последней стадии:

```
...
FROM scratch AS prod-client
COPY --from=build-client /bin/client /bin/
ENTRYPOINT [ "/bin/client" ]

FROM scratch AS prod-server
COPY --from=build-server /bin/server /bin/
ENTRYPOINT [ "/bin/server" ]
```

Сборка образов:

```
docker build -t multi:client --target prod-client -f Dockerfile-final .
docker build -t multi:server --target prod-server -f Dockerfile-final .
```

**Мультиплатформенная сборка**

Возможно, но пока не интересно

**Использование условий для создания разных образов**

Условия можно использовать только в конструкции RUN и CMD

```yaml
RUN if [ "$TARGET" = "test" ]; then \
      cp -r /files/src/tests .; \
    fi

# Разные команды для production и тестов
CMD if [ "$TARGET" = "production" ]; then \
      exec gunicorn app.main:app --bind 0.0.0.0:8000; \
    else \
      exec pytest -v; \
    fi
```

Затем собирается образ с указанной переменной окружения

```
docker build --build-arg TARGET=production -t myapp:prod .
```

Полный пример:

```
FROM alpine:latest
ARG BUILD_ENV
RUN mkdir /var/www
WORKDIR /var/www
COPY test.txt .
COPY prod.txt .
RUN if [ "$BUILD_ENV" = "test" ]; then \
       cat test.txt > first.txt; \
    else \
       cat prod.txt > first.txt; \
    fi
CMD ["cat", "/var/www/first.txt"]
```

**Использование multistage сборки для ограничения копирования**

if не работает в команде COPY. Но иногда бывает нужен образ для тестирования без содержимого директории, а в prod с содержимым. Пример:

```
# Общая стадия для подготовки файлов
FROM alpine:latest as base
COPY common_files /app/common_files

# Стадия для prod (копирует папку)
FROM base as prod
COPY prod_folder /app/prod_folder

# Стадия для test (не копирует папку)
FROM base as test
RUN echo "Running in test mode, no prod_folder copied"

# Выбираем финальную стадию через ARG
ARG TARGET_ENV=prod
FROM ${TARGET_ENV} as final

# Остальные инструкции...
```

**Сжатие итогового образа (многоуровневого) в один уровень**

Не рекомендуется, но если надо: --squash флаг при build

**Разное**

При apt-get install использовать no-install-recommends - сильно снижает объем.