Краткий мастер-класс о том, как писать код инфраструктуры
В октябре этого года я выступал с докладом на HashiConf 2018, где поделился 5 ключевыми уроками, которые мы усвоили в Gruntwork, создавая и поддерживая библиотеку из более чем 300 000 строк кода инфраструктуры, используемую в продакшене сотнями компаний. В этом блог-посте я поделюсь с вами видео и слайдами доклада, а также сокращенной письменной версией этих 5 ключевых уроков.
Видео и слайды
Lessons learned from writing over 300,000 lines of infrastructure code от Yevgeniy Brikman
Введение: DevOps находится в каменном веке
Хотя индустрия полна ультрасовременных модных слов — Kubernetes, микросервисы, сервис-меши, неизменяемая инфраструктура, большие данные, озера данных и т. д., — реальность такова, что когда вы по уши погружены в создание инфраструктуры, это не ощущается как что-то передовое.
По мне, DevOps больше похож на это:
Создание инфраструктуры производственного уровня — это сложно. И стрессово. И отнимает много времени. Очень много времени.
Вот примерно сколько времени, как следует ожидать, займет ваш следующий инфраструктурный проект, основываясь на эмпирических данных, которые мы собрали при работе с сотнями разных компаний:
Урок 1: Чек-лист инфраструктуры производственного уровня
DevOps-проекты всегда занимают гораздо больше времени, чем вы ожидаете. Всегда. Почему так происходит?
Что ж, первая причина — это бритье яка, как отлично показано в этом отрывке из сериала «Малкольм в центре внимания»:
Вторая причина заключается в том, что создание инфраструктуры производственного уровня (то есть такого типа инфраструктуры, на которую вы готовы поставить всю свою компанию) включает в себя тысячу мелких деталей. Подавляющее большинство разработчиков не знают, что это за детали, поэтому при оценке проекта вы обычно забываете о множестве критически важных и трудоемких нюансов.
Чтобы избежать этой проблемы, каждый раз, когда вы приступаете к работе над новым элементом инфраструктуры, сверяйтесь со следующим чек-листом:
Чек-лист инфраструктуры производственного уровня
Не каждый отдельный элемент инфраструктуры требует каждого пункта из списка, но вы должны осознанно и явно задокументировать, какие пункты вы реализовали, от каких решили отказаться и почему.
Посадочная зона (landing zone) применяет этот же чек-лист на один уровень выше — к учетным записям, защитным барьерам и конвейеру предоставления ресурсов, внутри которых работает вся остальная инфраструктура. О том, как описанные выше элементы соотносятся с этим уровнем, читайте в статье «Как построить архитектурно продуманную посадочную зону AWS с помощью OpenTofu/Terraform».
Урок 2: набор инструментов
По состоянию на 2018 год вот основные инструменты, которые мы используем в Gruntwork для создания и управления инфраструктурой:
- Terraform: Мы используем Terraform для подготовки всей базовой инфраструктуры, включая сети, балансировщики нагрузки, базы данных, пользователей, разрешения и все наши серверы.
- Packer: Мы используем Packer для описания и сборки образов виртуальных машин, которые мы запускаем на наших серверах.
- Docker: Некоторые из наших серверов образуют кластеры, где мы запускаем приложения в виде контейнеров Docker. Основными инструментами для кластеров Docker, которые мы используем, являются Kubernetes, ECS и Fargate.
Все эти инструменты полезны, но урок заключается не в этом. Настоящий урок в том, что сами по себе инструменты — это еще не все. Вам также необходимо изменить поведение вашей команды.
В частности, лучшие в мире инструменты ни на йоту не помогут вашей команде, если команда не настроена их использовать или у нее недостаточно времени, чтобы научиться ими пользоваться. Следовательно, главный вывод заключается в том, что использование кода инфраструктуры — это инвестиция: на старте требуются затраты, но если инвестировать с умом, в долгосрочной перспективе вы получите огромные дивиденды.
Урок 3: крупные модули признаны вредными
Новички в написании кода инфраструктуры часто описывают всю свою инфраструктуру для всех своих окружений (dev, stage, prod и т. д.) в одном файле или одном наборе файлов, которые развертываются как единое целое. Это плохая идея.
Вот лишь некоторые из недостатков:
- Медленно: если вся ваша инфраструктура описана в одном месте, выполнение любой команды займет много времени. Мы видели компании, где выполнение terraform plan занимает 5–6 минут!
- Небезопасно: если вся ваша инфраструктура управляется совместно, то для изменения чего-либо вам нужны права доступа ко всему. Это означает, что почти каждый пользователь должен быть администратором, что является еще одной плохой идеей.
- Рискованно: если все яйца лежат в одной корзине, то ошибка в любом месте может сломать все. Вы можете вносить незначительные изменения в фронтенд-приложение в dev-окружении, но из-за опечатки или выполнения не той команды удалить продакшн-базу данных.
- Сложно для понимания: чем больше у вас кода в одном месте, тем сложнее кому-то одному понять его целиком. Но если все это объединено, те части, которые вы не понимаете, могут вам навредить.
- Сложно тестировать: тестирование кода инфраструктуры — сложная задача; тестирование большого объема кода инфраструктуры практически невозможно. Мы вернемся к этому пункту позже.
- Сложно проверять: вывод таких команд, как terraform plan, становится бесполезным, так как никто не утруждает себя просмотром тысяч строк вывода плана. Более того, код-ревью становятся бесполезными:
10 строк кода = 10 проблем. 500 строк кода = «выглядит нормально». Код-ревью. — I Am Devloper (@iamdevloper) 5 ноября 2013 г.
10 строк кода = 10 проблем.
500 строк кода = «выглядит нормально».
Код-ревью.
— I Am Devloper (@iamdevloper) 5 ноября 2013 г.
Короче говоря, вы должны собирать свой код из небольших, автономных, многоразовых и компонуемых модулей. Это не новая и не спорная мысль. Вы слышали ее тысячу раз раньше, пусть и в немного других областях:
«Делай одну вещь и делай ее хорошо» — философия Unix
«Первое правило функций заключается в том, что они должны быть маленькими. Второе правило функций заключается в том, что они должны быть еще меньше». — «Чистый код»
Урок 4: код инфраструктуры без автоматических тестов не работает
Если ваш код инфраструктуры не покрыт автоматическими тестами, он не работает. Вы просто еще этого не знаете. При этом тестировать код инфраструктуры сложно. У вас нет привычного «localhost» (например, вы не можете развернуть AWS VPC на своем ноутбуке) и у вас нет привычных «модульных тестов» (например, вы не можете изолировать свой код Terraform от «внешнего» мира, так как все, что делает Terraform, — это взаимодействие с внешним миром).
Поэтому для правильного тестирования кода инфраструктуры вам обычно приходится развертывать его в реальном окружении, задействовать реальную инфраструктуру, проверять, что она делает то, что должна, а затем все это удалять (для такого стиля тестирования используется Terratest — библиотека с открытым исходным кодом, которая включает инструменты для тестирования кода Terraform, Packer и Docker, работы с API AWS, GCP и Kubernetes, выполнения команд оболочки локально и на удаленных серверах по SSH и многое другое). Это означает, что при тестировании инфраструктуры вам приходится немного переопределить термины:
- Модульные тесты развертывают и тестируют один или небольшое число тесно связанных модулей из одного типа инфраструктуры (например, тестируют модули для отдельной базы данных).
- Интеграционные тесты развертывают и тестируют несколько модулей из разных типов инфраструктуры, чтобы убедиться, что они корректно работают вместе (например, тестируют модули базы данных совместно с модулями веб-сервиса).
- Сквозные (e2e) тесты развертывают и тестируют всю вашу архитектуру целиком.
Обратите внимание, что диаграмма представляет собой пирамиду, где у нас есть много модульных тестов, меньшее количество интеграционных тестов и совсем небольшое количество сквозных (e2e) тестов. Почему? Из-за того, сколько времени занимает каждый тип теста:
Время выполнения тестов с инфраструктурой велико, особенно по мере продвижения вверх по пирамиде, поэтому вы захотите обнаружить как можно больше багов как можно ниже в пирамиде. Это означает, что вы должны:
- Создавать небольшие, простые, автономные модули (помните Урок 3?) и писать для них множество модульных тестов, чтобы обрести уверенность в том, что они работают правильно.
- Комбинировать эти небольшие, простые, проверенные в боях строительные блоки для создания более сложной инфраструктуры, которую вы будете тестировать с помощью меньшего количества интеграционных и сквозных тестов.
Урок 5: процесс релиза
Давайте теперь объединим всё, о чем говорилось в этом докладе. Вот как вы будете создавать инфраструктуру и управлять ею с этого момента:
- Изучите контрольный список инфраструктуры производственного уровня (Production-Grade Infrastructure Checklist), чтобы убедиться, что вы создаете то, что нужно.
- Определите свою инфраструктуру как код с помощью таких инструментов, как Terraform, Packer и Docker. Убедитесь, что у вашей команды есть время на освоение этих инструментов (см. Ресурсы DevOps).
- Создавайте свой код из небольших, автономных, комбинируемых модулей (или используйте готовые модули из Библиотеки инфраструктуры как кода).
- Пишите автоматизированные тесты для ваших модулей с помощью Terratest.
- Отправьте пулл-реквест (pull request), чтобы ваш код прошёл проверку.
- Выпустите новую версию своего кода.
- Продвигайте эту новую версию своего кода из одного окружения в другое.
Получите свои суперспособности DevOps на Gruntwork.io.











