Что такое entrypoint docker
Docker: заметки веб-разработчика. Итерация вторая
Заметки состоят из 3 частей: первые две теоретические, третья практическая.
Если быть более конкретным:
Это часть номер два.
Пришел к выводу, что в первой части был излишне многословен, в этой части буду более лаконичным.
Пример с официального сайта Node.js
Пример является сокращенным и для продакшна.
Docker Compose
Пример файла docker-compose.yml :
Compose позволяет делать следующее:
Compose предоставляет следующие возможности:
docker compose
docker-compose.yml
Рассмотрим основные настройки сервисов.
Настройки, применяемые во время сборки.
build может определяться в виде строки — пути к контексту сборки:
Или в виде объекта, где context — путь к контексту, dockerfile — используемый Dockerfile и args — аргументы:
В случае с args аргументы должны быть определены в Dockerfile :
Сеть, к которой подключается контейнер во время сборки (для использования при выполнении команды RUN ):
Перезапись дефолтной команды:
Зависимость между сервисами. Это означает следующее:
Политика перезапуска — определяет, как и когда контейнер должен перезапускаться:
Перезапись дефолтной точки входа:
Извлечение переменных среды окружения из файла. Может быть единичным значением или списком.
Выставление портов без их публикации на хосте — порты будут доступны только связанным (linked) сервисам. Могут определяться только внутренние порты:
Обратите внимание: внешние контейнеры должны быть подключены хотя бы к одной сети, к которой подключен сервис.
Образ для контейнера. Может быть репозиторием/тегом или частичным идентификатором (partial identifier):
Подключение контейнера к другому сервису. Подключаемый сервис определяется с помощью названия сервиса и синонима ссылки (link alias) ( «SERVICE:ALIAS» ) или только названия:
Сети для подключения:
Короткий синтаксис позволяет делать следующее:
Длинный синтаксис позволяет настраивать дополнительные поля:
volumes
Монтирование путей хоста (host paths) или именованных томов (named volumes), определенных в виде дополнительных настроек сервиса.
Пути хоста могут монтироваться как часть определения сервиса. Их не нужно указывать в ключе volume на верхнем уровне.
Длинный синтаксис позволяет настраивать дополнительные поля:
Другие настройки, соответствующие настройкам команды docker run
Примеры определения продолжительности
Примеры определения байтовых значений
Замена переменных
ENTRYPOINT vs CMD: назад к основам
Факт 1: Требуется определить хотя бы одну инструкцию ( ENTRYPOINT или CMD ) (для запуска).
Факт 2: Если во время выполнения определена только одна из инструкций, то и CMD и ENTRYPOINT будут иметь одинаковый эффект.
Хотя этот пример и показывает, что между ENTRYPOINT и CMD нет никакой разницы, её можно увидеть, сравнив метаданные контейнеров.
Например, первый файл Dockerfile (с определенной ENTRYPOINT ):
Затем изучим процессы:
Факт 4: Режим exec является рекомендуемым.
Это связано с тем, что контейнеры задуманы так, чтобы содержать один процесс. Например, отправленные в контейнер сигналы перенаправляются процессу, запущенному внутри контейнера с идентификатором PID, равным 1. Очень познавательный опыт: чтобы проверить факт перенаправления, полезно запустить контейнер ping и попытаться нажать ctrl + c для остановки контейнера.
Контейнер, определенный с помощью режима exec, успешно завершает работу:
При использовании режима «shell» контейнер работает не так, как ожидалось.
Факт 5: Нет оболочки? Нет переменных окружения.
Факт 6: Аргументы CMD присоединяются к концу инструкции ENTRYPOINT … иногда.
Вот тут-то и начинается путаница. В руководстве есть таблица, цель которой – внести ясность в этот вопрос.
Попытаюсь объяснить на пальцах.
Строка blah blah blah blah была проигнорирована.
FACT 6b: При использовании режима exec для ENTRYPOINT аргументы CMD добавляются в конце.
Факт 7: Инструкции ENTRYPOINT и CMD могут быть переопределены с помощью флагов командной строки.
Достаточно фактов… Что же делать мне?
Это решение я собираюсь оставить на усмотрение человека, создающего Dockerfile, который может быть использован другими разработчиками.
Запуск с параметрами по умолчанию:
Переопределение CMD собственными параметрами:
Я предпочитаю по большей части этот метод, потому что он дает разработчикам свободу легко переопределять исполняемый файл оболочкой или другим исполняемым файлом.
Очистка
После запуска команд на хосте осталась куча остановленных контейнеров. Очистите их следующей командой:
Обратная связь
Джон Закконе
Капитан Докера и инженер по облачным технологиям в IBM. Специализируется на Agile, микросервисах, контейнерах, автоматизации, REST, DevOps.
Погружаемся в Docker: Dockerfile и коммуникация между контейнерами
В прошлой статье мы рассказали, что такое Docker и как с его помощью можно обойти Vendor–lock. В этой статье мы поговорим о Dockerfile как о правильном способе подготовки образов для Docker. Также мы рассмотрим ситуацию, когда контейнерам нужно взаимодействовать друг с другом.
В InfoboxCloud мы сделали готовый образ Ubuntu 14.04 с Docker. Не забудьте поставить галочку «Разрешить управление ядром ОС» при создании сервера, это требуется для работы Docker.
Dockerfile
Подход docker commit, описанный в предыдущей статье, не является рекомендованным для Docker. Его плюс состоит в том, что мы настраиваем контейнер практически так, как привыкли настраивать стандартный сервер.
Вместо этого подхода мы рекомендуем использовать подход Dockerfile и команду docker build. Dockerfile использует обычный DSL с инструкциями для построения образов Docker. После этого выполняется команда docker build для построения нового образа с инструкциями в Dockerfile.
Написание Dockerfile
Давайте создадим простой образ с веб-сервером с помощью Dockerfile. Для начала создадим директорию и сам Dockerfile.
Созданная директория — билд-окружение, в которой Docker вызывает контекст или строит контекст. Docker загрузит контекст в папке в процессе работы Docker–демона, когда будет запущена сборка образа. Таким образом будет возможно для Docker–демона получить доступ к любому коду, файлам или другим данным, которые вы захотите включить в образ.
Добавим в Dockerfile информацию по построению образа:
Также Dockerfile поддерживает комментарии. Любая строчка, начинающаяся с # означает комментарий.
Первая инструкция в Dockerfile всегда должна быть FROM, указывающая, из какого образа нужно построить образ. В нашем примере мы строим образ из базового образа ubuntu версии 14:04.
Далее мы указываем инструкцию MAINTAINER, сообщающую Docker автора образа и его email. Это полезно, чтобы пользователи образа могли связаться с автором при необходимости.
Инструкция RUN исполняет команду в конкретном образе. В нашем примере с помощью ее мы обновляем APT репозитории и устанавливаем пакет с NGINX, затем создаем файл /usr/share/nginx/html/index.html.
Мы используем этот формат для указания массива, содержащего команду для исполнения и параметры команды.
Далее мы указываем инструкцию EXPOSE, которая говорит Docker, что приложение в контейнере должно использовать определенный порт в контейнере. Это не означает, что вы можете автоматически получать доступ к сервису, запущенному на порту контейнера (в нашем примере порт 80). По соображениям безопасности Docker не открывает порт автоматически, но ожидает, когда это сделает пользователь в команде docker run. Вы можете указать множество инструкций EXPOSE для указания, какие порты должны быть открыты. Также инструкция EXPOSE полезна для проброса портов между контейнерами.
Строим образ из нашего файла
, где trukhinyuri – название репозитория, где будет храниться образ, nginx – имя образа. Последний параметр — путь к папке с Dockerfile. Если вы не укажете название образа, он автоматически получит название latest. Также вы можете указать git репозиторий, где находится Dockerfile.
В данном примере мы строим образ из Dockerfile, расположенном в корневой директории Docker.
Что произойдет, если инструкция не исполнится?
Давайте переименуем в Dockerfile nginx в ngin и посмотрим.
Использования кеша сборок для шаблонизации
Используя кеш сборок можно строить образы из Dockerfile в форме простых шаблонов. Например шаблон для обновления APT-кеша в Ubuntu:
Инструкция ENV устанавливает переменные окружения в образе. В данном случае мы указываем, когда шаблон был обновлен. Когда необходимо обновить построенный образ, просто нужно изменить дату в ENV. Docker сбросит кеш и версии пакетов в образе будут последними.
Инструкции Dockerfile
Давайте рассмотрим и другие инструкции Dockerfile. Полный список можно посмотреть тут.
Инструкция CMD указывает, какую команду необходимо запустить, когда контейнер запущен. В отличие от команды RUN указанная команда исполняется не во время построения образа, а во время запуска контейнера.
ENTRYPOINT
Часто команду CMD путают с ENTRYPOINT. Разница в том, что вы не можете перегружать ENTRYPOINT при запуске контейнера.
При запуске контейнера параметры передаются команде, указанной в ENTRYPOINT.
Можно комбинировать ENTRYPOINT и CMD.
WORKDIR
С помощью WORKDIR можно установить рабочую директорию, откуда будут запускаться команды ENTRYPOINT и CMD.
Специфицирует пользователя, под которым должен быть запущен образ. Мы можем указать имя пользователя или UID и группу или GID.
VOLUME
Инструкция VOLUME добавляет тома в образ. Том — папка в одном или более контейнерах или папка хоста, проброшенная через Union File System (UFS).
Тома могут быть расшарены или повторно использованы между контейнерами. Это позволяет добавлять и изменять данные без коммита в образ.
В примере выше создается точка монтирования /opt/project для любого контейнера, созданного из образа. Таким образом вы можете указывать и несколько томов в массиве.
Инструкция ADD добавляет файлы или папки из нашего билд-окружения в образ, что полезно например при установке приложения.
Источником может быть URL, имя файла или директория.
В последнем примере архив tar.gz будет распакован в /var/www/wordpress. Если путь назначения не указан — будет использован полный путь включая директории.
Инструкция COPY отличается от ADD тем, что предназначена для копирования локальных файлов из билд-контекста и не поддерживает распаковки файлов:
ONBUILD
Инструкция ONBUILD добавляет триггеры в образы. Триггер исполняется, когда образ используется как базовый для другого образа, например, когда исходный код, нужный для образа еще не доступен, но требует для работы конкретного окружения.
Коммуникация между контейнерами
В предыдущей статье было показано, как запускать изолированные контейнеры Docker и как пробрасывать файловую систему в них. Но что, если приложениям нужно связываться друг с другом. Есть 2 способа: связь через проброс портов и линковку контейнеров.
Проброс портов
Такой способ связи уже был показан ранее. Посмотрим на варианты проброса портов чуть шире.
Когда мы используем EXPOSE в Dockerfile или параметр -p номер_порта – порт контейнера привязывается к произвольному порту хоста. Посмотреть этот порт можно командой docker ps или docker port имя_контейнера номер_порта_в_контейнере. В момент создания образа мы можем не знать, какой порт будет свободен на машине в момент запуска контейнера.
Можно привязать UDP порты, указав /udp:
Линковка контейнеров
Связь через сетевые порты — лишь один способ коммуникации. Docker предоставляет систему линковки, позволяющую связать множество контейнеров вместе и отправлять информацию о соединении от одного контейнера другому.
Префикс DB_ был взят из alias контейнера.
Можно просто использовать информацию из hosts, например команда ping db (где db – alias) будет работать.
Заключение
В этой статье мы научились использовать Dockerfile и организовывать связь между контейнерами. Это только вершина айсберга, очень многое осталось за кадром и будет рассмотрено в будущем. Для дополнительного чтения рекомендуем книгу The Docker Book.
Готовый образ с Docker доступен в облаке InfoboxCloud.
В случае, если вы не можете задавать вопросы на Хабре, можно задать в Сообществе InfoboxCloud.
Если вы обнаружили ошибку в статье, автор ее с удовольствием исправит. Пожалуйста напишите в ЛС или на почту о ней.
Основные инструкции Docker
Это третья часть серии статей про Docker и она всецело посвящена Docker-файлам. В первой части основные концепции Docker объясняются на простых примерах из жизни. Во второй статье — краткий обзор экосистемы Docker.
Docker-образы
Docker-образ создаётся во время сборки, а Docker-контейнер — во время запуска приложения.
Docker-файл — сердце Docker’а. Он указывает Docker’у как построить образ, который будет использоваться при создании контейнера.
Контейнер состоит из ряда слоёв. Все слои доступны только для чтения, кроме последнего — он располагается над остальными. Docker-файл указывает порядок добавления слоёв.
Каждый слой — это просто файл с изменением предыдущего слоя. В Unix практически всё является файлом.
Базовый слой, его ещё называют родительским, — это начальный слой.
При загрузке Docker-образа из удалённого репозитория скачиваются только отсутствующие у вас слои. Docker экономит место и время, повторно используя уже существующие слои.
Инструкция Docker-файла — слово в верхнем регистре, которое стоит перед аргументом какой-либо команды. Каждая строка в Docker-файле может содержать инструкцию, все они обрабатываются сверху вниз. Инструкции выглядят так:
Эта статья предполагает использование Unix Docker-образа. Вы, конечно, можете использовать и Windows Docker-образ, но он медленнее, менее удобный и, вообще, его не часто применяют. Так что, пользуйтесь Unix по возможности.
Несколько Docker-инструкций
Инструкции и примеры к ним
Docker-файл чисто теоретически может содержать только одну строчку:
В этом примере хранилище образов — Ubuntu. Ubuntu — название официального Docker-репозитория, в котором и содержится данная ОС.
Заметьте, что этот Docker-файл содержит тег для базового образа: 18.04, который указывает Docker’у, какую именно версию образа нужно использовать. Если тег не указан, по умолчанию берётся последняя версия образа. Но лучше всё же указывать тег базового образа. Когда Docker-файл, приведённый выше, используется для создания локального Docker-образа впервые, он загружает слои, указанные в образе Ubuntu.
При создании Docker-контейнера, вы помещаете наверх слой, который впоследствии можно будет изменить.
Когда образ запущен, и нужно произвести некоторые изменения, файл копируется на верхний слой, доступный для редактирования. Узнать, как это делается, можно здесь.
Подробнее про Docker-файл
Кроме того, что ваш однострочный образ сжат, он ещё и медленный, предоставляет мало информации и ничего не делает во время запуска контейнера. Посмотрите на более длинный Docker-файл, который запускает более легковесный образ, а также выполняет скрипт во время запуска.
Но что же это всё обозначает?
В роли базового образа выступает официальный Python-образ с тегом 3.7.2-alpine3.8. Как вы можете увидеть из исходников, образ включает в себя Linux, Python и ничего более. Alpine-образы очень популярны, потому что они маленькие, быстрые и безопасные. Однако Alpine-образы не поставляются сразу со всеми компонентами, характерными для вашей ОС. Некоторые пакеты вам придётся установить самостоятельно.
LABEL
ENV создаёт переменную окружения, которая становится доступной во время запуска контейнера. В примере выше вы могли видеть использование переменной ADMIN при создании контейнера.
ENV удобна для обозначения констант. Если константа используется в нескольких местах файла Dockerfile, и вам понадобится изменить её значение позднее, это можно будет сделать в одном месте.
В примере выше ADD копировала файлы по URL внутрь директории контейнера. Но официальныя документация не рекомендует использовать ADD так, потому что потом вы попросту не сможете удалить файлы. А дополнительные файлы увеличивают размер образа.
Ещё пара моментов о CMD :
Готовы к большему?
В следующем примере представлены ещё несколько Docker-инструкций:
Нижележащие слои должны предоставить свое средство установки и управления пакетами. Если возникнет проблема с установкой пакетов, убедитесь, что у вас установлен менеджер пакетов.
Можно использовать RUN вместе с pip и списком нужных пакетов. Для этого объедините команды установки пакетов в одну инструкцию и разделите их символом продолжения строки ( \ ). Этот метод позволяет улучшить читаемость и уменьшить количество слоев (из-за отсутствия возможности использовать несколько RUN инструкций).
Также вы можете запустить установщик, указав ему файл, содержащий все зависимости для вашего образа. Обычно он называется requirements.txt.
WORKDIR
ENTRYPOINT
EXPOSE
Инструкция EXPOSE показывает, какой порт пробрасывать из контейнера.
VOLUME
VOLUME определяет, где контейнер будет хранить постоянные данные и получать к ним доступ.
Заключение
Написание кода в docker окружении
В компании, где я работаю — большинство сервисов запускаются и работают в docker-контейнерах.
В связи с этим, у моих коллег-новичков-в-докере часто возникает вопрос — а как писать код и запускать его в этом чёртовом контейнере.
Для человека, написавшего около сотни docker-образов и запускающего их несколько раз в день — такой вопрос уже не стоит, но когда я разбирался с докером в давние времена — мысль «Как же писать код в докере? Это же сверхнеудобно!» долго была актуальной.
В статье я опишу свои практики работы с образами docker, которые позволяют писать код «как у себя в home», и даже лучше.
Итак, что такое готовый docker-образ?
Это слепок готового сервиса, который настраивается небольшим числом переменных окружения и готов к работе сразу после старта. С docker-образом не требуется устанавливать зависимости приложения и библиотеки разработчика себе локально в систему, замусоривая её.
Запуск готового образа
Для начала разберём, как запускать образ. Предполагается, что название образа нам известно.
И это всё — имя образа.
В простейшем случае образ запускается так:
Часто образ нужно сконфигурировать переменными окружения. Откуда их брать?
Примеры подсмотренных переменных окружения:
Запускаем контейнер с переменными окружения
Если переменных несколько
В итоге, мы получили работающий и сконфигурированный сервис.
Попадаем внутрь контейнера
Для упрощения программирования «внутри» контейнера — нам нужно в него попасть. При описанном выше запуске — запускается сам сервис, но мы сами не попадаем «внутрь».
Для попадания «внутрь» — нужно переопределить команду старта образа.
Это делается указанием имени shell-оболочки после имени образа
Shell-оболочка зависит от дистрибутива, на котором построен образ.
Попробуйте один из вариантов и не ошибётесь.
После подобного запуска мы оказываемся в консоли внутри контейнера.
Инициализация сервиса
После того, как мы попали в консоль внутри контейнера — мы попали в «голый» образ. Часто образы, после запуска, проводят первичную инициализацию (формируют файлы настроек и т.д.) и после этого запускают сам сервис.
Инициализация делается через команду старта, которую мы выше заменили на shell-оболочку. Поэтому, инициализацию нужно запустить вручную. Для этого, открываем Dockerfile и смотрим содержимое инструкции CMD.
И именно его и запускаем.
Сервис инициализировался и запустился, теперь мы можем нажать Ctrl+C и снова попасть в консоль, имея контейнер, готовый к повторному запуску сервиса.
Написание кода внутри контейнера
Когда сервис запускается внутри контейнера — он использует те скрипты/бинарные файлы, которые уже находятся внутри. Как нам их редактировать?
Элементарно. Нужно редактировать их извне, в любимом редакторе, в своей домашней папке, а потом просто скопировать в контейнер и запустить.
Даём доступ контейнеру к своей домашней папке используя опцию
После запуска данной команды мы будем находиться в консоли контейнера, сможем неограниченно редактировать свой код извне и копировать его в контейнер. Домашняя папка будет доступна в контейнере в папке /d.
После копирования обновленного скрипта или бинарного файла — запускаем сервис (способом, описанным в разделе инициализации) и получаем сервис с нашими правками, работающий в исходном окружении.
В зависимости от ваших нужд — скрипты/файлы можно не копировать в контейнер, а запускать их сразу из /d/my-repo.
Граничные случаи и лайфхаки
ENTRYPOINT
Некоторые образы (довольно редко) используют команду старта в виде ENTRYPOINT. Что это такое — можно посмотреть в Dockerfile reference. Нам же нужно только помнить, что перезапись команды старта для таких образов выглядит иначе
«Облачные» окружения
Если сервисы работают в Consul, docker swarm, kubernetes окружении, то они могут использовать такие переменные окружения, которые доступны только контейнеру, запущенному в этом облаке и не будут доступны контейнеру, запущенному на компьютере разработчика.
В указанном примере используются «облачные» адреса в переменных CONSUL_HTTP_ADDR и VAULT_ADDR. В таких случаях вам нужно использовать внешние адреса данных сервисов.
Повторные запуски
Писать каждый раз полностью команду docker run — излишне. Всю команду старта с переменными удобно сохранить с sh файл. Который потом достаточно просто запускать.
Переиспользование переменных окружения
Если часть переменных окружения требуется для многих сервисов, то эти переменные можно вынести в отдельный файл и передавать контейнеру специальной опцией
Запуск без sudo
В локальной разработке запускать контейнеры с sudo — утомительно. Для исправления — добавляем своего пользователя в группу docker. После этого, вместо