Капитан грузового судна, или Как начать использовать Docker в своих проектах

Docker является open source инструментом, который автоматизирует разворачивание приложения внутри программного контейнера. Мы перевели для вас руководство по работе с Docker для новичков.

Простейший способ понять идею Docker — это сравнить его с обычным контейнером для транспортировки. Когда-то давно компании, занимающиеся транспортировкой, столкнулись со следующими проблемами:

  • Как перевозить разные (несовместимые) друг с другом типы грузов, например, еду и химикаты или стекло и кирпичи?
  • Как работать с упаковками различных размеров, используя один и тот же транспорт?

С тех пор, как появились контейнеры, стало возможным помещать кирпичи поверх стекла, а химикаты — хранить рядом с едой. Груз различных размеров может быть помещен в стандартный контейнер, который загружен/разгружен одним и тем же транспортом.

Но вернемся к контейнерам в программной разработке.

Написанным кодом порою приходится делиться с другими людьми, например, разработчиками. Кроме того, в комплекте необходимо отправлять и все его зависимости вроде библиотек, веб-сервера, баз данных и так далее. Вы можете столкнуться с ситуацией, когда приложение работает на компьютере, но не может запуститься на тестовом сервере, компьютере разработчика или тестировщика.

В чем отличие от виртуализации?

Традиционно виртуальные машины используются, чтобы избежать неожиданного поведения. Главная проблема с ними заключается в том, что «дополнительная ОС» поверх основной ОС добавляет к проекту гигабайты места. Большую часть времени ваш сервер будет держать несколько виртуальных машин, что займет ещё больше пространства. Другой значительный недостаток — медленная загрузка.

Docker устраняет эти проблемы разделением ядра между всеми контейнерами, которые работают как отдельные процессы основной ОС.

Надо понимать, что Docker — не первая и не единственная платформа, основанная на контейнерах. Однако на данный момент он является самым большим и самым мощным инструментом на рынке.

Docker

Зачем нам нужен Docker?

Список преимуществ таков:

  • Быстрый процесс разработки. Нет нужды в установке сторонних программ вроде PostgreSQL, Redis, Elasticsearch. Они могут быть запущены в контейнерах.
  • Удобная инкапсуляция приложений. Вы можете предоставить свое приложение как единое целое.
  • Одинаковое поведение на локальном компьютере и тестовом/производственном сервере.
  • Простой и понятный мониторинг.
  • Легко масштабируется. Если вы сделали свое приложение правильно, то оно будет готово к масштабированию не только в Docker.

Поддерживаемые платформы

Linux является нативной платформой Docker, так как последний основан на особенностях, предоставляемых ядром операционной системы. Несмотря на это, вы можете запустить его на macOS или Windows. Разница состоит лишь в том, что Docker на них инкапсулирован в маленькую виртуальную машину. В настоящий момент Docker для этих ОС достиг значительного уровня удобства использования и очень похож на нативное приложение.

Более того, есть много дополнительных приложений, таких как Kitematic или Docker Machine, которые помогут установить Docker и управлять им на отличных от Linux платформах.

Установка

Вы можете посмотреть инструкцию по установке на официальном сайте. Если вы запустили Docker на Linux, вам следует выполнить все последующие команды из-под root или добавить своего пользователя в группу docker и перелогиниться:

Терминология

  • Контейнер — текущий экземпляр, который инкапсулирует необходимое ПО. Контейнеры всегда создаются из образов. Могут открывать порты и тома для взаимодействия с другими контейнерами и/или внешним миром, а также легко удаляются и пересоздаются заново за короткий промежуток времени.
  • Образ — основной элемент для каждого контейнера. После его создания каждый шаг кэшируется и может быть использован повторно (модель копирования при записи). В зависимости от образа может понадобиться некоторое время для его построения. Из них могут быть сразу запущены контейнеры.
  • Порт — TCP/UDP-порт в своем обычном представлении. Порты могут быть открыты для внешнего мира (доступы через основную ОС) или подсоединены к другим контейнерам — доступны только из тех контейнеров и невидимы для внешнего мира.
  • Том — может быть описан как общая папка. Тома инициализируются при создании контейнера. Они спроектированы для хранения данных независимо от жизненного цикла контейнера.
  • Реестр — сервер, который хранит образы Docker. Функционирует по аналогии с Github — можно вытянуть образ, чтобы развернуть его локально, а затем закинуть обратно в реестр.
  • Docker Hub — реестр с веб-интерфейсом, созданный Docker Inc. Он хранит большое количество Docker-образов с разным ПО. Docker Hub является источником «официальных» Docker-образов, созданных командой Docker. Официальные образы содержат списки своих потенциальных уязвимостей. Эта информация доступна для каждого авторизированного пользователя. Доступны два типа аккаунтов: бесплатные и платные. На бесплатном аккаунте может быть один приватный образ и бесконечное количество общедоступных образов.

Docker

Пример 1: Hello world

Настало время запустить ваш первый контейнер:

Вывод консоли:

  • docker run — команда, запускающая контейнер.
  • ubuntu — образ, который вы запускаете, например, образ ОС Ubuntu. Когда вы определяете его, Docker начинает поиск на Docker-хосте. Если образ не существует локально, тогда он будет вытянут из общественного реестра — Docker Hub.
  • /bin/echo 'Hello world' — команда, которая будет запущена внутри нового контейнера. Данный контейнер просто выведет «Hello world» и остановит выполнение.

Давайте попробуем создать интерактивную оболочку внутри Docker-контейнера:

  • -t — флаг, добавляющий псевдотерминал внутри нового контейнера.
  • -i — флаг, который позволяет установить интерактивное соединение, взяв стандартный ввод (STDIN) контейнера.
  • –rm — флаг, автоматически удаляющий контейнер после завершения процесса. По умолчанию контейнеры не удаляются. Этот контейнер существует, пока установлена текущая сессия оболочки, и уничтожается, когда мы покидаем сессию (например, SSH-сессию с удаленным сервером).

Если вы хотите оставить контейнер работающим после завершения сессии, превратите его в демон-процесс:

  • –name daemon — устанавливает имя демона для нового контейнера. Если вы не определите имя, то Docker сгенерирует и присвоит его автоматически.
  • -d — флаг запускает контейнер в фоновом режиме (демонизирует его).

Давайте взглянем, какие контейнеры у нас есть на текущий момент:

Вывод консоли:

  • docker ps — команда для вывода списка контейнеров.
  • -a — флаг, который показывает все контейнеры (без этого флага ps выведет только работающие контейнеры).

Команда ps показала, что у нас всего два контейнера:

  • gifted_nobel (имя для этого контейнера сгенерировано автоматически, оно будет другим на вашем компьютере) — это первый контейнер, который мы создали, выводит один раз «Hello world».
  • daemon — это третий контейнер, который мы создали, работает как фоновый процесс.

Замечание: здесь нет второго контейнера (с интерактивной оболочкой), потому что мы установили флаг –rm. Как следствие, этот контейнер автоматически удалился сразу после выполнения.

Давайте проверим логи и посмотрим, что фоновый контейнер делает прямо сейчас:

Вывод консоли:

  • docker logs — объединяет логи контейнера.
  • -f — флаг для слежения за выводом логов (работает так же, как tail -f).

Теперь остановим фоновый контейнер:

Удостоверимся, что контейнер остановился:

Вывод консоли:

Контейнер остановился. Мы можем запустить его заново:

Удостоверимся, что он работает:

Вывод консоли:

Теперь остановим его опять и удалим все контейнеры вручную:

Чтобы удалить все контейнеры, мы можем использовать следующую команду:

  • docker rm — команда для удаления контейнера.
  • -f — флаг для rm, который останавливает контейнер, если он работает (принудительное удаление).
  • -q — флаг для ps, который выводит только ID контейнеров.

Пример 2: Nginx

Начиная с этого примера, вам понадобятся несколько дополнительных файлов, которые вы можете найти в Github-репозитории. Скачать файл можно, нажав на эту ссылку.

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

Сменим директорию на examples/nginx:

Вывод консоли:

  • -p — преобразование портов HOST PORT:CONTAINER PORT.
  • -v — монтирование тома HOST DIRECTORY:CONTAINER DIRECTORY.

Важно: команда run принимает только абсолютные пути. В нашем примере мы использовали $(pwd), чтобы установить абсолютный путь текущей директории.

Теперь вы можете открыть localhost в вашем браузере.

Или можно изменить /example/nginx/index.html (который смонтирован как том в /usr/share/nginx/html внутри контейнера) и обновить страницу.

Получим информацию о контейнере test-nginx:

Эта команда показывает широкую системную информацию об установке Docker. Эта информация включает в себя версию ядра, количество контейнеров и образов, открытые порты, смонтированные тома и так далее.

Пример 3: написание Dockerfile

Чтобы создать Docker-образ, вам необходимо создать Dockerfile. Это просто текстовый файл с инструкциями и аргументами. Вот описание инструкций, которые мы будем использовать в нашем следующем примере:

  • FROM — устанавливает базовый образ.
  • RUN — выполняет команду в контейнере.
  • ENV — устанавливает переменную окружения.
  • WORKDIR — устанавливает рабочую директорию.
  • VOLUME — создает точку монтирования для тома.
  • CMD — устанавливает исполняемый файл для контейнера.

Вы можете просмотреть справку по Dockerfile, чтобы узнать больше подробностей.

Теперь создадим образ, который будет получать содержимое сайта с помощью curl и записывать его в текстовый файл. Нам надо передать URL сайта через переменную окружения SITE_URL. Результирующий файл будет помещен в директорию, смонтированную в качестве тома:

Dockerfile готов. Теперь настало время создать сам образ.

Перейдем в examples/curl и выполним следующую команду для создания образа:

Вывод консоли:

  • docker bulid — создает новый локальный образ.
  • -t — устанавливает именную метку для образа.

Теперь у нас есть новый образ, и мы можем посмотреть список существующих:

Вывод консоли:

Мы можем создать и запустить контейнер из образа. Попробуем сделать это со стандартными параметрами:

Чтобы увидеть результаты, сохраненные в файле, выполним команду:

Попробуем с facebook.com:

И снова посмотрим результаты:

Пример 4: связь контейнеров Python + Redis

Docker compose — единственный правильный способ соединять контейнеры друг с другом. В этом примере мы соединим контейнеры Python и Redis:

Перейдем в examples/compose и выполним следующую команду:

Вывод консоли:

Данный пример увеличит счетчик просмотров в Redis. Откройте localhost в браузере и проверьте.

Теперь вы можете поиграться с различными образами из Docker Hub или, если хотите, создать свои собственные образы, соблюдая лучшие практики, описанные ниже. Единственное, что можно добавить насчет использования docker-compose: всегда давайте точные имена вашим томам в docker-compose.yml (если в образе есть тома). Это простое правило спасет вас от проблем при проверке ваших томов.

В этому случае redis_data будет именем внутри docker-compose.yml, для настоящего имени тома будет добавлен префикс с именем проекта.

Чтобы просмотреть список томов:

Вывод консоли:

Без точного имени тома там будет находиться UUID. Вот пример с локального компьютера:

Стайлгайды Docker

У Docker есть некоторые ограничения и требования, которые зависят от архитектуры системы (приложений, которые вы упаковываете в контейнеры). Можно игнорировать эти требования или найти какие-нибудь пути обхода, но в этом случае вы не получите все преимущества использования Docker. Настоятельно рекомендуется следовать следующим советам:

  • 1 приложение = 1 контейнер.
  • Запускайте процесс на переднем плане (не используйте systemd, upstart или другие похожие инструменты).
  • Для хранения данных вне контейнера используйте тома.
  • Не иcпользуйте SSH (если вам надо залезть внутрь контейнера, используйте docker exec).
  • Избегайте ручных настроек или действий внутри контейнера.
  • Включайте только необходимый контекст — используйте .dockerignore файл (как .gitignore в git).
  • Избегайте установки ненужных пакетов — это займет лишнее дисковое пространство.
  • Используйте кэш. Добавьте контекст, который часто меняется, например, исходный код вашего проекта, в конец Dockerfile — кэш Docker будет использоваться более эффективно.
  • Будьте осторожны с томами. Вы должны помнить, какие данные находятся в томах. Поскольку тома постоянны и не исчезают вместе с контейнерами, следующий контейнер будет использовать данные, которые были созданы предыдущим контейнером.
  • Используйте переменные окружения: RUN, EXPOSE, VOLUME. Это сделает ваш Dockerfile более гибким.

Заключение

В заключение стоит отметить, что Docker стал одним из необходимых инструментов разработчика, наравне с различными IDE и Git.

Перевод статьи «Here’s How You Start Using Docker»