IT дуэль 2017: "Битва ботов - Гексагон" создание игры - Часть 2 - дроплеты DigitalOcean и настройка Dokku приложений

digital-ocean-dokku.jpeg

В первой статье цикла мы вспомнили о правилах игры и технических характеристиках игрового движка. Теперь же опишем, как с помощью DigitalOcean и Dokku позволить игрокам писать бота на множестве языков, имея гарантированно равные ресурсы в runtime и приятную процедуру деплоя.

Ключ к решению - Docker. Сложно переоценить всю крутость этого инструмента (годная статья на хабре), с появлением которого порог вхождения в профессию DevOps изрядно понизился - ныне обычный разработчик стремительно собирает себе окружение под любые нужды, легко и непринуждённо обновляет системные зависимости, слыхом не слыхивал о конфликтах зависимостей...

Ну как, "стремительно собирает"?.. При всей простоте и элегантности инструмента, программирование docker-compose файлов и deploy скриптов для нетривиальных проектов становится вполне серьёзной DevOps задачей. К примеру, однажды мне пришлось встраиваться с legacy Ruby on Rails проектом в чуждую continuous integration систему, заточенную исключительно под PHP. Собрать Docker образ, запустить тесты на одноразовом контейнере, при удаче - развернуть образ на хост машинах через docker registry. Простая на первый взгляд задача на поверку оказалась настоящей битвой, в которой я даже как будто начал побеждать!.. Одна лишь мысль не давала покоя - "Получается слишком сложно! По сути, я сейчас пишу свой yet another heroku buildpack для ruby". В итоге, всё заработало как часы с помощью...

Dokku!! Верный боевой товарищ, суть есть высокоуровневая обёртка поверх Docker и Nginx:

  • Деплой Heroku-ready приложений через git push - Dokku сам определит, какой buildpack подходит для вашего приложения (ruby, clojure, php etc), соберёт docker образ, подготовит к работе и запустит контейнер с Zero Downtime-ом. Натуральная магия, с непривычки вызывает благоговейный трепет у разработчиков;
  • Поддержка плагинов и доступа к ним извне через ssh;
  • Гибкость как на уровне buildpack-ов, так и на уровне конкретного проекта:
  • В целом, единственно верная идеология - "Долой Job Security! Процесс созидания должен быть лёгким и приятным!".

И наконец, вишенка на торте - DigitalOcean, прекрасный облачный сервис, предоставляющий Dokku машину в качестве one-click application. Нам понадобится фиксированное количество дроплетов для игровых песочниц, и непредсказуемое количество дроплетов для игроков, по одному дроплету на команду. Дроплеты игроков будем разворачивать при регистрации команд из специально подготовленного образа, с помощью DigitalOcean API.

Подготовка мастер ssh ключа

Во время мероприятия мы будем часто взаимодействовать с удалёнными машинами - игровые песочницы будут подкладывать в дроплеты игроков ssh ключи для деплоя, игроки будут деплоить новые релизы ботов, техподдержка будет заходить на дроплеты игроков для устранения возможных проблем. Для обеспечения безопасности соединений мы всегда будем использовать SSH протокол и пары RSA ключей.

Подготовим единый мастер SSH ключ для административных нужд:

$ ssh-keygen -q -t rsa -f ~/.ssh/fourcolor -N '' -C fourcolor

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

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

DigitalOcean: дроплеты

DigitalOcean: настройки

Подготовим образ дроплета для ботов

Заказываем в DigitalOcean дроплет за $10/мес с предустановленным dokku и root мастер пользователем.

Настройки

Создать дроплеты

Выбрать размер

Добавить SSH ключи

Через 10-20 секунд получаем IP адрес созданного дроплета.

IP адрес дроплета

Открываем в браузере IP дроплета, финализируем установку Dokku, добавляя мастер ключ в Dokku пользователя.

Добавить мастер ключ в Dokku пользователя

Заходим по ssh в дроплет, подготавливаем дроплет для деплоя ботов.

# заходим под root-ом в дроплет
$ ssh root@DROPLET_IP

# включаем swap file
$ fallocate -l 1G /swapfile
$ chmod 600 /swapfile
$ mkswap /swapfile
$ echo "/swapfile               none     swap   defaults        0 0" >> /etc/fstab
$ swapon -a

# создаём Dokku приложение
$ dokku apps:create app

# устанавливаем PostgreSQL плагин, создаём базу, линкуем её с приложением
$ dokku plugin:install https://github.com/dokku/dokku-postgres
$ dokku postgres:create app_db
$ dokku postgres:link app_db app

# устанавливаем Redis плагин, создаём базу, линкуем её с приложением
$ dokku plugin:install https://github.com/dokku/dokku-redis
$ dokku redis:create app_redis
$ dokku redis:link app_redis app

# устанавливаем Memcached плагин, создаём базу, линкуем её с приложением
$ dokku plugin:install https://github.com/dokku/dokku-memcached
$ dokku memcached:create app_memcached
$ dokku memcached:link app_memcached app

# Устанавливаем и настраиваем NotifyCURL плагин для информирования игровых серверов о новых релизах
$ dokku plugin:install https://github.com/Anadea/dokku-notify-curl
$ dokku config:set --global DOKKU_NOTIFY_URL=https://internal.it-duel2017.anadea.info/releases/manage

# Отключаем VHOST, чтобы приложение отвечало просто по IP
$ dokku domains:disable app

# Выставляем 80 порт для приложения, иначе оно будет отвечать на случайном
$ dokku config:set --no-restart app DOKKU_NGINX_PORT=80

# Останавливаем дроплет. Лучше это делать из консоли а не через DigitalOcean интерфейс, что ничем не отличается от выдёргивания шнура из розетки
$ poweroff

Из панели DigitalOcean делаем образ дроплета. Именно из этого образа будут разворачиваться дроплеты для команд - каждой команде по 10-долларовому дроплету с 1Gb памяти и одним CPU.

Образ дроплета

Удаляем дроплет, он нам больше не нужен. Дроплеты для команд будут разворачиваться из подготовленного образа программно, с помощью DigitalOcean API.

Удалить дроплет

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

Подготовка игровых серверов

Для игровых песочниц заказываем DiginalOcean дроплеты с предустановленным Dokku, в этот раз 40ка-долларовые - ради многопоточности.

Настройка дроплетов для песочниц в целом схожа с настройкой дроплетов для игроков, но есть отличия:

# заходим под root-ом в дроплет
$ ssh root@DROPLET_IP

# включаем swap file
$ fallocate -l 2G /swapfile
$ chmod 600 /swapfile
$ mkswap /swapfile
$ echo "/swapfile               none     swap   defaults        0 0" >> /etc/fstab
$ swapon -a

# создаём Dokku приложение
$ dokku apps:create app

# Для Rails приложения понадобится PostgreSQL - устанавливаем плагин, создаём базу, линкуем её с приложением
$ dokku plugin:install https://github.com/dokku/dokku-postgres
$ dokku postgres:create app_db
$ dokku postgres:link app_db app

# Для Sidekiq отложенных задач понадобится Redis - устанавливаем плагин, создаём базу, линкуем её с приложением
$ dokku plugin:install https://github.com/dokku/dokku-redis
$ dokku redis:create app_redis
$ dokku redis:link app_redis app

# Добавляем имя хоста в приложение, одновременно прописываем соответствующую A запись в панели DNS регистратора
$ dokku domains:add app internal.it-duel2017.anadea.info

# Устанавливаем и настраиваем LetsEncrypt плагин для генерации бесплатных SSL сертификатов
$ dokku plugin:install https://github.com/dokku/dokku-letsencrypt
$ dokku config:set --no-restart app DOKKU_LETSENCRYPT_EMAIL=xxxxxxxx@anadea.info
# После первого деплоя приложения будет достаточно запустить две команды для получения сертификата и периодического авто обновления
# Никаких рукопашных усилий в настройке nginx и crontab не нужно, всё заработает само собой
# $ dokku letsencrypt app
# $ dokku letsencrypt:cron-job --add

# Для закрытого альфа тестирования будем использовать Basic HTTP Auth - устанавливаем плагин
$ dokku plugin:install https://github.com/dokku/dokku-http-auth
# Теперь достаточно запускать следующие команды для включения и выключения HTTP Auth
# Никаких рукопашных усилий в настройке nginx не нужно, всё заработает само собой
# $ dokku http-auth:on app test_user super_password
# $ dokku http-auth:off app

# Подкладываем мастер SSH ключ в будущие контейнеры, чтобы игровая песочница могла вызывать ssh команды в дроплетах игроков
$ dokku plugin:install https://github.com/cedricziel/dokku-deployment-keys
$ dokku deploymentkeys:create app
$ echo "PRIVATE_KEY" >  > /home/dokku/.deployment-keys/app/.ssh/id_rsa
$ echo "PUBLIC_KEY" >  > /home/dokku/.deployment-keys/app/.ssh/id_rsa.pub

# Останавливаем дроплет
$ poweroff

Останавливаем дроплет, делаем образ, разворачиваем на основе образа 5 песочниц - Финал, Гродно, Днепр, Минск, Internal. Так как песочницы будут запускаться на соответствующих поддоменах, не забываем для каждой песочницы настроить доменное имя с помощью команды dokku domains:add, настроить A записи в панели DNS регистратора, и установить SSL сертификат командой dokku letsencrypt.

Подготовка заготовок для ботов

Несмотря на всю простоту деплоя под Dokku, при написании приложения необходимо учитывать некоторые нюансы - stateless идеологию, параметризация приложения через переменные окружения, и т.п. Игроки же должны фокусироваться на игровой логике, а не на инфраструктуре - поможем им в этом! Здесь всё просто - на каждом целевом языке кратчайшим путём пишем приложение, соответствующее требованиям:

  • heroku-ready;
  • корректные ответы на все запросы игрового сервера;
  • попытка сделать всегда один и тот же игровой ход;
  • вывод в STDOUT всех входящих параметров.

Заготовки готовы!

Стоит сделать акцент на том, что я воспринимаю турнир в том числе как соревнование между языками. Именно поэтому заготовки писались кратчайшим путём, без каких-либо специфичных настроек или кода. Более того, являясь евангелистом языка Ruby, сознательно лишил Ruby заготовку автоматической миграции DB, и оставил как есть серьёзную и весьма неочевидную проблему со StrongParameters (будет описана в следующей статье) - всё по честному.

Отдельно пару слов о многопоточности - у всех команд в наличии по 1 CPU, при всём желании/умении особо не развернуться - надо просто научить бота быстро отвечать на запросы :)

Связаться