Terraform: инфраструктура на уровне кода — страница 63 из 65

• Terraform Enterprise. Корпоративные продукты от компании HashiCorp имеют веб-интерфейс, с помощью которого можно выполнять команды terraformplan и terraformapply, а также управлять переменными, конфиденциальными данными и правами доступа.

• Terragrunt. Это открытая обертка вокруг Terraform, которая заполняет собой некоторые пробелы в данной системе. Чуть позже в этой главе вы увидите, как с ее помощью в разных средах можно развернуть версионируемый код Terraform, минимизировав его дублирование.

• Скрипты. Вы можете сделать работу с Terraform более гибкой за счет скриптов, написанных на языках программирования общего назначения, таких как Python, Ruby или bash.


Стратегии развертывания

Система Terraform сама по себе не предлагает никаких стратегий развертывания. У нее нет встроенной поддержки скользящих или «сине-зеленых» обновлений, и она не позволяет использовать ротацию функций для большинства изменений (это, например, касается баз данных; вы либо вносите изменение, либо нет). Вам, в сущности, доступна лишь команда terraformapply, которая просто выполняет конфигурацию, предусмотренную в вашем коде. Конечно, иногда в самом коде можно реа­лизовать собственную стратегию развертывания; вспомните, скажем, скользящие обновления с нулевым временем простоя в модуле asg-rolling-deploy, который вы создали в предыдущих главах. Но, поскольку Terraform является декларативным языком, ваш контроль за развертываниями довольно ограничен.

Исходя из этих ограничений крайне важно быть готовыми к ситуациям, когда развертывание проходит неудачно. При развертывании приложения от многих видов ошибок спасает выбранная вами стратегия. Например, если приложению не удается пройти проверку работоспособности, балансировщик нагрузки никогда не направит к нему реальный трафик, поэтому пользователи не пострадают. Более того, в случае возникновения ошибок стратегии со скользящими или «сине-зелеными» обновлениями могут автоматически откатываться к предыдущей версии приложения.

Для сравнения, Terraform не поддерживает автоматический откат в ответ на ошибки. Это отчасти связано с тем, что в случае с произвольным инфраструктурным кодом это часто оказывается небезопасным или даже невозможным. Например, если приложение не удалось развернуть, его почти всегда можно откатить к более старой версии, но если неудачным оказалось развертывание изменения в коде Terraform, которое должно было удалить базу данных или остановить сервер, откатить его будет не так просто!

Более того, как вы уже видели в разделе «Подводные камни Terraform» на с. 203, ошибки в Terraform далеко не редкость. Поэтому ваша стратегия развертывания должна считать их (относительно) нормальным явлением и уметь на них как следует реагировать.

Повторные попытки. Некоторые ошибки в Terraform временные и сами исчезают при повторном выполнении terraformapply. Инструментарий для развертывания, который вы используете вместе с Terraform, должен обнаруживать известные проблемы и автоматически повторять операцию после небольшой паузы. Автоматическое повторение попыток в ответ на известные ошибки встроено в Terragrunt.

• Ошибки состояния Terraform. Время от времени Terraform не удается сохранить состояние при выполнении terraformapply. Предположим, если в ходе работы apply теряется соединение с Интернетом, неудачей завершится не только эта команда, но и попытка записать обновленный файл состояния в удаленное хранилище (такое как Amazon S3). В таких случаях Terraform сохраняет состояние на диск в файл под названием errored.tfstate. Убедитесь, что ваш CI-сервер не удаляет эти файлы (скажем, в процессе очистки рабочей области после сборки)! Если после неудачного развертывания у вас все еще остается доступ к этому файлу, при возобновлении интернет-соединения вы можете загрузить его в удаленное хранилище (например, в S3), используя команду statepush. Таким образом, информация о состоянии не будет утеряна:

$ terraform state push errored.tfstate

• Ошибки снятия блокировки. Иногда Terraform не удается снять блокировку. Например, если ваш CI-сервер выйдет из строя в ходе выполнения terraformapply, состояние так и останется заблокированным. Любой, кто попытается выполнить apply для того же модуля, получит сообщение об ошибке, в котором будет сказано, что состояние заблокировано, и указан идентификатор блокировки. Если вы совершенно точно уверены, что эта блокировка оставлена по случайности, вы можете принудительно ее снять с помощью команды force-unlock, передав ее ID из полученного сообщения об ошибке:

$ terraform force-unlock


Сервер развертывания

Все изменения в инфраструктурном коде, как и в прикладном, должны применяться с CI-сервера, а не с компьютера разработчика. Вы можете запускать команду terraformapply из Jenkins, CircleCI, Terraform Enterprise, Atlantis или с любой другой автоматизированной платформы с достаточным уровнем безопасности. Вы получаете те же преимущества, что и с прикладным кодом: это заставляет вас полностью автоматизировать ваш процесс развертывания, гарантирует, что этот процесс выполняется всегда из одного и того же окружения, и позволяет лучше контролировать доступ к промышленным средам.

Тем не менее инфраструктурный код требует чуть более сложной организации прав на развертывание, чем прикладной. CI-серверу обычно можно выдать минимальный набор прав для развертывания приложений. Например, чтобы развернуть прикладной код в ASG, CI-серверу, как правило, требуется лишь несколько конкретных прав ec2 и autoscaling. Однако для развертывания произвольных изменений в инфраструктурном коде (скажем, код Terraform может попытаться развернуть базу данных, VPC или совершенно новую учетную запись AWS) CI-сервер должен обладать произвольными, то есть администраторскими полномочиями.

Поскольку CI-серверы предназначены для выполнения произвольного кода, их сложно сделать безопасными (попробуйте подписаться на консультативную рассылку Jenkins, чтобы увидеть, насколько часто в нем объявляют о серьезных уязвимостях), поэтому давать им постоянные администраторские полномочия будет рискованно. Для минимизации этого риска можно предпринять несколько шагов.

• Не делайте свой CI-сервер доступным из публичного Интернета. То есть размещайте его в приватных подсетях, без публичного IP-адреса, чтобы он был доступен только по VPN-соединению. Это существенно повысит безопасность, но создаст дополнительные ограничения: у вас перестанут работать веб-хуки для внешних систем, в результате чего, к примеру, GitHub не сможет автоматически инициировать сборку на вашем CI-сервере. Вместо этого нужно сделать так, чтобы ваш CI-сервер сам обращался за обновлениями к системе управления версиями.

• Ограничьте доступ к CI-серверу. Разрешите обращаться к нему только по HTTP, требуйте от пользователей прохождения аутентификации и следуйте рекомендациям по укреплению серверов (например, предусмотрите строгие правила для брандмауэра, установите fail2ban, включите ведение журнала ­аудита и т. д.).

• Старайтесь не выдавать CI-серверу постоянные администраторские полномочия. Он должен иметь права, достаточные для автоматического развертывания определенных изменений в инфраструктурном коде. Но когда речь идет о чем-то более деликатном (например, о добавлении/удалении пользователей или доступе к конфиденциальным данным), соответствующие временные полномочия (срок действия которых, скажем, истекает через один час) серверу должен предоставить живой системный администратор.


Продвижение артефактов по разным окружениям

Инфраструктурные артефакты, как и прикладные, должны быть неизменяемыми, поддерживать версионирование и продвигаться от одного окружения к другому; например, версию v0.0.6 нужно продвинуть из Dev в Stage, а затем в Prod62. Здесь тоже действует простое правило: всегда тестируйте изменения в коде Terraform, прежде чем развертывать их в промышленной среде.

Поскольку в Terraform все и так автоматизировано, проверка изменений в Stage перед выкатыванием их в Prod не требует особых дополнительных усилий, но при этом позволяет выявить огромное количество ошибок. Тестирование в предпромышленном окружении особенно важно по той причине, что, как уже упоминалось в этой главе, Terraform не откатывает изменения в случае ошибки. Если при выполнении terraformapply что-то пойдет не так, придется чинить это самостоятельно. Данный процесс будет менее трудоемким и напряженным, если проводить его еще до развертывания в промышленной среде.

Процесс продвижения кода Terraform по разным окружениям аналогичен продвижению прикладных артефактов, только у него есть дополнительный этап: выполнение команды terraformplan и ручная проверка ее вывода. В случае с приложениями этот этап обычно не требуется, так как развертывание прикладного кода чаще всего проходит по одному сценарию и не несет в себе большого риска. Однако каждое развертывание инфраструктуры может быть уникальным, а ошибки, которые при этом возникают (вроде удаления базы данных), могут стоить очень дорого. Поэтому возможность в последний раз просмотреть и разобрать вывод plan, безусловно, стоит потраченного времени.

Так, к примеру, выглядит процесс продвижения модуля Terraform версии v0.0.6 по окружениям Dev, Stage и Prod.

1. Обновить среду Dev до v0.0.6 и выполнить terraformplan.

2. Запросить разбор и одобрение плана. Например, послать автоматическое сообщение по Slack.

3. Если план одобрен, развернуть версию v0.0.6 в Dev с помощью terraformapply.

4. Выполнить в Dev ручное и автоматическое тестирование.

5. Если версия v0.0.6 хорошо работает в Dev, повторить шаги 1–4, чтобы продвинуть ее в Stage.

6. Если версия v0.0.6 хорошо работает в Stage, повторить ша­ги 1–4, чтобы продвинуть ее в Prod.

Осталось разобраться с одной важной проблемой: дублированием ко­да между разными окружениями в репозитории текущей