Как часто вам приходилось настраивать окружения сервера для деплоя вашего приложения (например веб-сайта)? Наверняка чаще чем хотелось бы.

В лучшем случае, у вас был скрипт, который все это делал автоматически. В худшем случае, это могло выглядеть вот так:

  • установить базу данных D версии x.x.x
  • установить веб сервер N версии x.x и т.д.

Управление окружением, сконфигурированное подобным образом, со временем становится очень ресурсозатратным. Любое, даже незначительное изменение в конфигурации означает как минимум:

  • что каждый разработчик должен быть в курсе данных изменений
  • все эти изменения должны быть безболезненно добавлены в продакшн среду

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

Выше я описал то, что называется vendor lock-in. Для разработки приложений, в частности серверного типа, это явление становится большой проблемой. В данной статье мы рассмотрим одно из возможных решений — Docker. Вы узнаете, как создать, задеплоить и запустить приложение на его основе.



/Disclaimer:/ Это не обзорный доклад о Docker. В конце этой статьи приведен список полезной литературы, которая описывает работу с Docker лучше. Это первая точка вхождения для разработчиков, которые планируют деплоить node.js приложения при помощи Docker контейнеров.

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

Что это такое и с чем его едят?


Простыми словами, Docker — это абстракция над LXC контейнерами. Это значит, что процессы, запущенные при помощи Docker, будут видеть только себя и своих потомков. Такие процессы называются Docker контейнерами.

Для того, чтобы иметь возможность создавать какие-то абстракции на базе таких контейнеров, в Docker существует образ (/docker image/). На базе Docker образа можно конфигурировать и создавать контейнеры.

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

Знакомимся ближе


На установке долго останавливаться не будем. Процесс за последние несколько релизов упростили до нескольких кликов/команд.

В этой статье мы разберем деплой Docker приложения на примере серверного Node.js приложения. Вот его примитивный, исходный код:

 // index
const http = require('http');
 
const server = http.createServer(function(req, res) {
	res.write('hello world from Docker');
	res.end();
});
 
server.listen(3000, function() {
	console.log('server in docker container is started on port : 3000');
});

У нас есть как минимум два способа упаковки приложения в Docker контейнер:

  • создать и запустить контейнер из существующего образа при помощи command-line-interface инструмента;
  • создать собственный образ на основе готового образца.

Чаще используется второй способ.

Для начала скачаем официальный node.js образ:

docker pull node

Команда docker pull скачивает Docker образ. После этого можно выполнить команду docker run. Это создаст и запустит контейнер на базе скачанного образа.

docker run -it -d --rm -v "$PWD":/app -w=/app -p 80:3000 node node index.js 

Эта команда запустит index.js файл, произведет маппинг 3000 порта на 80 и выведет id созданного контейнера. Уже лучше! Но на одном CLI далеко не уедешь. Давайте создадим Dockerfile для нашего сервера.

FROM node
 
WORKDIR /app
RUN cp . /app
 
CMD ["node", "index.js"]

Данный Dockerfile описывает образ, от какого наследуется текущий вариант, а также директорию, в которой начнут выполнятся команды контейнера и команда копирования файлов из директории, в которой запускается сборка образа. Последняя строчка указывает, какая команда запустится в созданном контейнере.

Далее нам потребуется собрать из этого Dockerfile образ, который мы будем деплоить: docker build -t username/helloworld-with-docker:0.1.0. Данная команда создает новый образ, помечает его именем username/helloworld-with-docker и создает тег 0.1.0.

Наш контейнер готов. Мы можем запускать его при помощи команды docker run. Таким образом, мы решаем vendor lock-in проблему. Запуск приложения уже не зависит от окружения. Код доставляется вместе с Docker образом. Эти два критерия позволяют нам деплоить приложение в любое место, где мы можем запустить Docker.

Деплой


Не так страшны первые 99% как оставшиеся 99%.

После того, как мы выполнили все инструкции выше, сам процесс деплоя уже становится делом техники и вашего окружения разработки. Мы рассмотрим 2 варианта деплоя Docker:

  • ручной деплой Docker образа;
  • деплой при помощи Travis-CI.

В каждом случае, мы рассмотрим доставку образа в независимое окружение, например, staging сервер вашего продукта.

Ручной деплой


Данный вариант хорош, если у вас нет какой-либо среды непрерывной интеграции. Сперва нужно загрузить Docker образ в место, доступное staging серверу. В нашем случае это будет DockerHub. Каждому пользователю он бесплатно предоставляет один приватный репозиторий образа и неограниченное количество публичных репозиториев.

Авторизуемся для доступа к нашему DockerHub:

docker login -e username@gmail.com -u username -p userpass

Загружаем туда наш образ: docker push username/helloworld-with-docker:0.1.0.

Далее заходим на staging сервер (напоминаю, на нем уже должен быть предустановлен Docker).

Для деплоя нашего приложения на сервере нам потребуется выполнить всего одну команду:

docker run -d --rm -p 80:3000 username/helloworld-with-docker:0.1.0.

И все! Проверьте локальный регистр образов. Если не найдете нужный результат, введите username/helloworld-with-docker для проверки DockerHub регистра. Образ с таким именем найдется в регистре, так как мы его туда уже загрузили. Docker скачает его, создаст на его основе контейнер и запустит в нем ваше приложение.

Теперь каждый раз, когда вам нужно будет обновить версию вашего приложения, вы можете делать push с новым тегом и просто перезапускать каждый раз контейнер на сервере.

P.S. Данный способ не рекомендуется, если есть возможность воспользоваться Travis-CI.

Деплой при помощи Travis-CI


Первым делом добавим в Travis-CI данные DockerHub. Они будут хранится в переменных окружения.

travis encrypt DOCKER_EMAIL=email@gmail.com
travis encrypt DOCKER_USER=username
travis encrypt DOCKER_PASS=password

Затем добавим полученные ключи в .travis.yml файл. Так же мы добавим комментарий к каждому ключу для того, чтобы различать их в будущем.

env:
  global:
    - secure: "UkF2CHX0lUZ...VI/LE=" # DOCKER_EMAIL
    - secure: "Z3fdBNPt5hR...VI/LE=" # DOCKER_USER
    - secure: "F4XbD6WybHC...VI/LE=" # DOCKER_PASS

Далее нам нужно авторизоваться и загрузить образ:

after_success:
  - docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS
  - docker build -f Dockerfile -t username/hello-world-with-travis.
  - docker tag username/hello-world-with-travis 0.1.0
  - docker push username/hello-world-with-travis

Также доставка образа может запускаться из Travis-CI различными способами:

  • вручную;
  • через подключение по ssh;
  • онлайн деплой сервисы (Deploy Bot, deployhq);
  • AWS CLI;
  • Kubernates;
  • Инструменты для Docker деплоя.

Итоги


В этой статье мы рассмотрели подготовку и деплой Docker на примере простого node.js сервера двумя способами: автоматическим и автоматизированными при помощи Travis-CI. Надеюсь, эта статья принесла вам пользу.

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


  1. amarao
    06.09.2019 16:55

    С первых слов фигня.

    Цитата: Простыми словами, Docker — это абстракция над LXC контейнерами.

    Да что вы говорите? Серьёзно? docker, это абстракция над LXC? А LXC, видимо, это абстракция над kubernetes?

    RTFM: lxc и docker — это два разных проекта, оба использующих технологию linux namespaces и предоставляющих «контейнеры», причём в обоих технологиях это слово значит чуть-чуть другое.


  1. RomanKu
    08.09.2019 01:15

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


    Поскольку не нашел списка литературы, то буду считать эту статью обзорной, коей она и является