Как я приручал Docker

main

О том, зачем нужен Docker — написано сто тысяч статей, и примерно столько же докладов. Но у меня возникла вполне реальная проблема, под которую Docker ложился идеально. Совсем недавно я начал один небольшой проект на Node.js. Сначала все шло легко и просто, но внезапно я понял, что понятия не имею, где в open source проектах хранят секретные данные. Ну, то есть, обычно всякие пароли и секретные ключи я храню в конфигурационных файлах, ведь репозитории приватные. А тут GitHub, открытый проект. После недолгого гугления оказалось, что реальные пацаны давно хранят секретные ключи в переменных окружения. Задавать пару десятков переменных окружения каждый раз вручную… Тут-то на ум и пришел Docker.

Хотя про Docker я слышал уже много, но попробовать его на чем-то реальном пока не получалось. И вот настал тот час. Сразу оговорюсь, что в статье я описываю настройку докера для локальной разработки, для продакшна такая конфигурация никак не пойдет.

Раз я строю контейнер для локальной разработки, ничего сложного мне не нужно. Мне нужен контейнер, в котором будет крутиться только один Node.js процесс. Исходник проекта я хочу подключать как внешний том из моей системы и монтировать его в директорию /var/app. При изменении исходников в моей системе Node.js процесс должен перезапускаться. Порт 3000 должен пробрасываться из контейнера в мою систему, чтобы я мог видеть свой проект в браузере. Вроде ничего сложного.

Dockerfile

Создадим Dockerfile, который описывает нужную мне структуру образа. Называться он должен ‘Dockerfile’ и никак иначе. Вот содержимое моего:

Разберем построчно.

Объявляю образ на основе последней версии Убунты. Вообще, это не самый лучший выбор. В итоге, мой образ на основе убунты занимает 485 мб. Такой же образ на основе Alpine, будет занимать в десятки раз меньше места. Но, так как я в докере новичок, хотелось иметь под рукой хорошо знакомую ось.

Просто мои координаты

Обновляю индекс пакетов

Устанавливаю nodejs и npm

Создаю директорию, в которой будет находиться проект

Устанавливаю pm2. Вообще-то локально я обычно использую nodemon, но у него есть проблемы с докером: при несовпадении временной зоны на локальной машине и в контейнере он перестает понимать, что в файле произошли изменения. Если вам критично использовать именно nodemon, можете почитать это issue.

Устанавливаю директорию /var/app как рабочую. Все дальнейшие команды будут выполняться из этой директории.

“Точка” — указывает на рабочую директорию

Запускаю npm install в рабочей директории

Запускаю pm2. Обратите внимание, что запускать нужно с параметром —no-daemon, иначе докер решит, что процесс закончил работу и остановит контейнер.

Строю образ

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

Параметр -t задает имя образа в формате namespce/name:tag.
Namespace можно не задавать, но оно понадобится, если потом захотите запушить образ в докер хаб. Да и вообще так нагляднее. Имя может быть любое, в данном случае это название моего проекта. Tag… это тег. Это может быть версия или название окружения.

Обратите внимание на точку, завершающую команду. Точка означает, что искать Dockerfile нужно в текущей директории.

После выполнения команды, образ построится и будет виден в системе:

Selection_186

Образы-контейнеры

Все это время я строил образ, хотя вроде запускать мне нужно контейнер. Важно понимать разницу между этими понятиями. Образ для каждого контейнера может быть только один, а контейнеров, запущенных на основе одного образа, может быть сколько угодно.
В терминах ООП образ — это класс, а контейнер — это объект. Причем образы даже могут наследоваться друг от друга. Первая строчка FROM как раз означает, что мой образ “наследуется” от образа последней убунты. Именно поэтому на мой компьютер скачался образ убунты, когда я выполнил docker build.

Когда я строил Dockerfile, то некоторые команды у меня начинались с RUN, а некоторые с CMD. Разница в том, что RUN выполняется на этапе сборки образа, а CMD уже после создания контейнера. Например, инструкция:

не может выполниться на этапе сборки образа, ведь исходные файлы проекта подключаются только когда мы создаем контейнер. Кстати об этом, пора создать наш контейнер.

Поднимаем контейнер

  • -ti: Создает псевдотерминал и дает возможность подключаться и отключаться от докер контейнера;
  • -d: Запускает контейнер в режиме демона, в бэкграунде;
  • -p: Пробрасывает 3000 порт контейнера на 3000 порт моей рабочей системы;
  • -v: Монтирует директорию /home/voff/work/thecoderstack рабочей системы в директорию /var/app в контейнере;
  • boliev/thecoderstack:local: имя docker образа.

Если все прошло успешно, вам покажут идентификатор контейнера, а в браузере по адресу http://localhost:3000/ станет доступен ваш проект.
Теперь все работает, но мне бы еще хотелось смотреть вывод Node.js консоли.
Чтобы посмотреть список запущенных контейнеров, выполните команду:

Selection_188

Теперь можно “войти” в контейнер указав его ID

Вернитесь в браузер, обновите страницу с проектом, и в консоль выведутся логи.

Selection_190

Чтобы выйти из контейнера не останавливая его, нажмите последовательно комбинации ctrl+p ctrl+q.

В итоге

Приручил ли я Docker? Нет, конечно нет. Docker — это такая штука, которую нельзя изучить по документации или по статьям. Нужно с ним работать, нужно его использовать, нужно подчинять его себе день за днем. Проблема также в том, что каждый его использует по своему. Нет единого мнения, стоит ли подключать код как внешний том, или делать git clone в Dockerfile. Нужен ли вообще attach? Должны ли логи быть внешним, общим для всех контейнеров, томом? Можно ли запускать в докере больше одного процесса? Вопросов по докеру — море, и правильные ответы подскажет только постоянная практика.

Не забудьте поделиться статьей с друзьями

Подписывайтесь на меня в соц. сетях

Комментарии (5)

  1. Pingback: Best Writing Service

  2. Pingback: aleister crowley little essays towards truth

  3. Pingback: Best Writing Service

  4. Pingback: Best Writing Service

  5. Илья комментирует :

    04.03.2017

    Интересная статья,спасибо!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *