Docker
Теория: Подготовка собственного образа
На Docker Hub выложено множество готовых образов, которые используются администраторами и разработчиками: интерпретаторы и компиляторы языков, веб-сервера, базы данных и многое другое. Большую часть из них можно использовать на серверах без изменений, передав какие-то переменные окружения. Но для любого разрабатываемого приложения нужно создавать свой собственный образ. В него войдет код приложения и все его зависимости. Даже когда нам будет нужно изменить всего лишь конфигурацию, например Nginx, все равно придется создать свой собственный образ, в который добавлен конфигурационный файл.
В этом уроке мы научимся создавать Docker-образ на примере JavaScript проекта: данный язык программирования достаточно распространен в среде разработчиков. Но все описанные принципы так же будут подходить и для других языков. Для создания образа будем использовать популярный микрофреймворк fastify.
Для начала создадим каркас приложения с помощью готового шаблона:
Эта команда создаст шаблон приложения в директории /out запущенного контейнера, которая, на самом деле, является директорией /var/tmp/docker-fastify-example на нашей машине. В итоге у нас получается такая структура проекта:
Для запуска этого приложения, нам нужно выполнить две основные задачи: установить зависимости и запустить сервер. Без Docker это выглядит так:
Установку зависимостей нужно выполнить еще до создания образа, так как во время первой установки формируется файл package-lock.json. Он нужен для фиксации зависимостей: с его помощью мы гарантируем, что в образе будут использоваться ровно те зависимости, которые мы подключали во время разработки. Сделать это можно следующим образом:
Теперь директория с приложением выглядит так:
Сборка и публикация Docker-образа
Docker создает образ на основе файла Dockerfile, в котором описываются необходимые команды. Мы начнем сразу с примера:
В основном, команды Dockerfile интуитивно понятны. Видно, что мы "упаковываем" приложение в образ, выполняем установку зависимостей и описываем то, как его запустить. Подробнее о командах мы поговорим позже, а сейчас посмотрим, как собирается, запускается и пушится образ в Docker Hub.
Для сборки образа в директории с Dockerfile нужно выполнить команду указанную ниже:
Сборка образа занимает какое-то время: нужно подождать, пока выполнятся все команды. Как результат, в списке образов появляется образ с именем hexlet/docker-fastify-example и тегом latest. Его можно запустить и убедиться в работоспособности:
Для полной проверки, откройте в браузере ссылку http://localhost:3000 и убедитесь что сайт открылся. Остался последний шаг — загрузить образ на Docker Hub. Для этого понадобится подготовительная работа:
- Регистрация https://hub.docker.com/
- Подключение к аккаунту через запуск команды
docker loginв терминале. Docker попросит ввести имя пользователя и пароль - Создание репозитория с именем docker-fastify-example в личном кабинете
Теперь, чтобы загрузить образ в Docker Hub, мы должны дать ему правильное имя. По соглашению, часть имени Docker-образа до символа /, должна совпадать с именем вашего пользователя Docker Hub. Чтобы так сделать, вам необходимо запустить команду сборки еще раз:
Теперь можно пушить:
Если репозиторий публичный, то скачать и запустить этот образ сможет любой человек, с доступом в интернет.
Теги
Теги у Docker-репозиториев изменяемые. Если изменить образ и снова его запушить с тем же тегом, образ поменяется. Для тега latest это ожидаемое поведение, а вот для версий нет. За этим нужно следить самостоятельно и не менять образ для уже существующих тегов. Если меняется образ, то правильно создавать новый тег:
Команды Dockerfile
Dockerfile состоит из команд, которые выполнятся сверху вниз по очереди, формируя файловую систему образа. Каждая последующая команда "видит" результаты предыдущей команды. Ниже мы разберем наиболее популярные команды, которые встречаются в большинстве образов.
FROM
Образ — это в первую очередь файловая система, которая формируется на базе команд описанных в Dockerfile. Docker берет какую-то первоначальную файловую систему и затем изменяет ее в соответствии с описанием. Получившаяся структура файлов и становится образом. Откуда берется первоначальная файловая система?
Практически все образы в Docker формируются не с нуля, а на базе уже существующих образов. Образы формируют дерево, в котором одни образы наследуют файловые системы других образов начиная с базового образа scratch.
Команда FROM задает образ, чья файловая система берется за основу. Все последующие команды, которые изменяют файловую систему, работают уже с ней. Потому команда FROM идет первой в Dockerfile.
WORKDIR
Команда WORKDIR задает рабочий каталог, относительно которого выполняются все действия во время формирования образа и при входе в контейнер:
WORKDIR автоматически создает директорию, если ее еще нет.
COPY
Команда COPY копирует файлы и директории с хост-машины внутрь Docker-образа. Она принимает два параметра: первый — что копируем, второй — куда копируем и под каким именем. Второй параметр может принимать три варианта:
- Абсолютный путь, копирование происходит ровно по нему
- Относительный путь, копирование происходит относительно установленной рабочей директории
WORKDIR - Точка, файл или директория копируется как есть в рабочую директорию
Если точка идет первым параметром, то это обозначает что копироваться будет директория целиком.
Для полного понимания принципов работы команды COPY, нужно представлять что такое контекст. Помните, когда мы указывали точку во время сборки образа? Это и есть контекст:
Контекст — это директория, относительно которой работает первый параметр в COPY. Обычно контекстом указывают ту директорию, которая содержит Dockerfile. Но это не обязательно, ведь контекстом может быть и другая директория:
Во время сборки образа, контекст целиком копируется внутрь системных директорий Docker, из которых в образ переносится все, что указано в команде COPY. Из-за этого иногда возникают проблемы. Контекст может содержать директории, которые не должны попадать в образ, например, .git, или зависимости установленные локально (node_modules), так как они все равно устанавливаются заново во время сборки. Чтобы избежать их попадания во внутрь, нужно создать файл .dockerignore и указать там те директории и файлы, которые не должны быть частью контекста. Принцип работы файла такой же, как и у .gitignore.
node_modules
.git
logs
tmp
Игнорирование таких директорий и файлов дает дополнительный плюс. Чем меньше размер контекста, тем быстрее он копируется. Если не следить за его размером, то процесс копирования может увеличиться до десятков секунд и даже минут.
RUN
Команда RUN выполняет переданную строчку в терминале от пользователя root. С ее помощью вносятся основные изменения в файловую систему, добавляются пакеты, ставятся зависимости и так далее. Команд RUN может быть добавлено любое количество, обычно делают по одной команде на одно действие.
RUN выполняется в не интерактивном режиме, это значит, что если выполняемая команда запросит пользовательский ввод, например разрешение на установку чего-либо, то мы не сможем выбрать ответ yes. Поэтому все команды в RUN запускают в неинтерактивном режиме:
CMD
CMD задает команду, которая выполняется при запуске контейнера по умолчанию. Она используется только в том случае, если контейнер был запущен без указания команды
ENV
Задает переменные окружения. Команды, выполняющиеся после ENV, видят эти переменные и могут их использовать.
С этой командой нужно быть острожнее. Переменные окружения созданы для того, чтобы их можно было менять, а их указание в Dockerfile фиксирует значения. По этому случаю, в Dockerfile обычно указывают только те переменные окружения, которые не зависят от среды запуска, как в примере выше. Нам в любом случае надо указать, что сервер должен запускаться на 0.0.0.0, иначе его будет невозможно увидеть снаружи. В большинстве ситуаций переменные окружения передаются снаружи для конкретного запуска:






