Достаточно много интереса проявляется среди технического сообщества к Docker и Ansible, я надеюсь, что после прочтения данной статьи, вы тоже разделите этот интерес. Вы так же получите навыки практического применения Ansible и Docker в настройке сервера и окружения для Rails приложения.
«Почему бы просто не взять и использовать Heroku?», спросите вы.
Прежде всего, я могу запустить Docker и Ansible на любой машине, с любым хостинг провайдером. Во вторых, я предпочитаю гибкость, удобству. Я могу, таким же образом, запускать все что угодно, не только web приложения. Ну и напоследок, потому что я эксперементатор в душе, я получаю удовольствие от понимания того как оно все вместе работает. Фундаментальная основа Heroku это Linux контейнер. Та же технология лежит и в основе Docker’a. На самом деле, одним из девизов Docker’a является «Контейнеризация это новая виртуализация»
Почему Ansible?
После 4 лет активного использования Chef, инфраструктура как код стала действительно утомительной для меня. Я проводил больше времени за кодом, который управлял моей инфраструктурой, но не за самой инфраструктурой. Любое изменение, не важно насколько маленькое, потребует большое количество усилий для незначительной выгоды.
С Ansible, с одной стороны есть данные описывающие инфраструктуру, с другой ограничения между взаимодействием других компонентов. Эта модель намного проще, она позволяет намного быстрее двигаться дальше, позволяя сосредоточится на том, что делает моя инфраструктура. Как и в Unix модели, Anisible предоставляет модули с единичной ответственностью, которые могут быть объедены бесконечным множеством способов.
Ansible не имеет зависимостей кроме как Python и SSH. Ей не нужно устанавливать агентов на удаленные машины, она не оставляет всякого рода мусора после своей работы. Более того, она поставляется со стандартной библиотекой модулей для управления всем чем угодно, начиная от пакетного менеджера в облаке, заканчивая базами данных.
Почему Docker?
Docker позиционирует себя как самый надежный и удобный способ развертывания процесса на машине. Это может быть все что угодно, от mysqld до redis, заканчивая Rails приложением. Таким же эффективным способом, как git снимки и распределение кода, Docker делает тоже самое с процессами. Он гарантирует, что будет предоставлено все, что нужно для запуска этого процесса, независимо от машины на которой он запущен.
Основная, но понятная ошибка, сравнивание Docker контейнера с VM. Здесь применяется принцип единственной ответственности, запуск одного процесса на контейнер дает простоту изменяемости и поддержки. Эта модель выдержала испытание временем в философии Unix, это дает прочный фундамент для действий.
Настройка
Не покидая свой терминал, я могу получить от Ansible настроенную машину на одном из следующих хостингов: AWS, Linode, Rackspace или DigitalOcean. Если быть более конкретным, я с помощью Ansible создаю новый дроплет с 2ГБ памяти на DigitalOcean в регионе Amsterdam 2 за 1 минуту 25 секунд. В течение еще 1 минуты и 50 секунд я могу получить настроенную систему с Docker’ом и некоторыми другими установками. Теперь, имея базовую систему, я могу развернуть свое приложение. Заметьте, я не настраивал какой либо язык программирования или базу данных. Docker сам позаботился об этом.
Ansible исполняет все команды на удаленных машинах через SSH. Мои SSH ключи, лежащие в локальном ssh-agent’е будут удаленно расшарены через SSH сессию Ansible. Когда, на удаленной машине, код моего приложения будет клонирован или обновлен никаких данных для авторизации в git не потребуется, для авторизации будет использован проброшенный ssh-agent с локальной машины.
Docker и зависимости приложения
Я нахожу забавным тот факт, что большинство разработчиков точно указывают версию ЯП, модули для Python, Ruby гемы или node.js модули, нужные для их приложения, но когда дело доходит до чегото важного, к примеру сервер БД или сервер очередей, они используют то что доступно на данный момент. Я думаю это одна из причин DevOps движения, разработчики берут на себя ответственность за окружение в котором они запускают приложение. Docker делает эту задачу легче и проще, добавляя дольку прагматизма и уверенности к существующим практикам.
Мое приложение определяет зависимости от процессов, таких как MySQL 5.5 и Redis 2.8 включая .docker_container_dependencies
файл
gerhard/mysql:5.5
gerhard/redis:2.8
Ansible playbook увидит этот файл и скажет Docker’у взять правильные образа из индекса образов и запустить их как контейнеры. Также эти контейнеры слинкуются в контейнер моего приложения. Если вы хотите подробностей о работе линковки контейнеров, загляните в анонс Docker 0.6.5
Мое приложение так же идет с Dockerfile, который специфичен для Ruby Docker образа. Когда образ будет установлен, шаги в Dockerfile гарантируют, что будет установлена правильная версия Ruby.
FROM howareyou/ruby:2.0.0-p353
ADD ./ /terrabox
RUN \
. /.profile ;\
rm -fr /terrabox/.git ;\
cd /terrabox ;\
bundle install --local ;\
echo '. /.profile && cd /terrabox && RAILS_ENV=test bundle exec rake db:create db:migrate && bundle exec rspec' > /test-terrabox ;\
echo '. /.profile && cd /terrabox && export RAILS_ENV=production && rake db:create db:migrate && bundle exec unicorn -c config/unicorn.rails.conf.rb' > /run-terrabox ;\
# END RUN
ENTRYPOINT ["/bin/bash"]
CMD ["/run-terrabox"]
EXPOSE 3000
Первый шаг — это скопировать весь код моего приложения в образ Docker’a и загрузить глобальные переменные окружения, добавленные предыдущими образами. Образ Ruby Docker, например, добавляет в PATH пути для корректной загрузки правильной версии Ruby.
Далее, я удаляю историю git, поскольку она не нужна в данном контексте. Я устанавливаю все гемы и затем создаю /test-terrabox файл, который запускается только для тестов. Целью всего этого является иметь «canary» версию, которая проверяет приложение и все его зависимости, что все Docker контейнеры слинкованы правильно и все тесты пройдены успешно, перед актуальным запуском приложения.
Команда, которая вызывается при запуске нового контейнера, определена в шаге CMD.
Запуск /run-terrabox команды, определен как часть процесса сборки, сразу после процесса запуска тестов.
Последняя инструкция в Dockerfile пробрасывает порт 3000 изнутри Docker контейнера на хост машину из под которой запущен Docker. Который могут использовать сервер или балансировщик нагрузки для проксирования запросов в мое приложение контейнера.
Запуск Rails приложения внутри Docker контейнера
Для среднего Rails приложения, с где-то примерно 100 гемами и множеством интеграционных тестов, запускаемых из под Rails, запуск всего этого занимает 8 минут 16 секунд на 2GB и 2 ядерной машине, без каких либо локальных Docker образов. Если у меня уже есть Ruby, Mysql и Redis образа на этой машине, это займет 4 минуты 45 секунд. Более того, если у меня уже есть эталонный образ моего приложения, то это занимает 2 минуты 23 секунды. Если посмотреть, в перспективе, развертывание нового Rails приложения, включая такие зависимости как MySQL и Redis занимает не более 2 минут.
Так же следует заметить, что развертывание приложения включает в себя запуск всех тестов, которые занимают, впритык, минуту времени.
Без преувеличений, Docker становится простым средством непрерывной интеграции, которое оставляет за собой только тестовые контейнеры для исследования, если тесты не проходят, или стартует новый контейнер приложения с последней версией, когда тесты проходят. Внезапно, я могу проверить новый код с моими заказчиками за минуты, который гарантированно будет изолирован от других версий приложения на уровне операционной системы. В отличии от традиционных VM, которые загружаются за минуты, Docker на это тратит секунды. Более того, раз создав образ Docker’a и убедившись, что все тесты пройдены для специфичной версии моего приложения, я могу залить образ в приватный реестр, который в свою очередь, может быть загружен другими машинами с Docker’ом и запущен как новый контейнер, и все это за секунды.
Заключение
Ansible позволил мне взглянуть по новому на процесс управления инфраструктурой. Docker дал мне уверенность и стабильность, когда речь заходит об одном из важнейших шагов разработки, фазы доставки. В комбинации, они не имеют себе равных.
Получить с нуля полностью настроенное Rails приложение за каких то 12 минут это впечатляюще для любого стандарта. Получить базовую Continuous Integration систему бесплатно с возможностью предпросмотра разных версий приложении бок о бок, без затрагивания «рабочей» версии, запущенных на той же машине, это невероятно мощно. Это делает меня радостным, и достигнув конца статьи, я могу только надеяться, что вы разделите мою радость со мной.