
Бих искал да ви разкажа за новия CLI инструмент, който написах, за да реша една стара проблема.
Проблема
Terraform отдавна стана стандарт в DevOps/Cloud/IT общността. Този инструмент е много удобен и полезен за работа с инфраструктура като код. Има много предимства в Terraform, но също така много уловки и предизвикателства.
С Terraform е много удобно да създавате нови неща и после да ги управлявате, меняте или изтривате. А какво да правят тези, които имат огромна облачна инфраструктура, която не е създадена с Terraform? Да прекратяват и възстановят всичко в облака е скъпо и небезопасно.
Срещал съм подобен проблем на две работни места; най-простият пример е, когато искате всичко да бъде в git под формата на Terraform файлове, а вие имате 250+ кофи и не ви се ще да ги написвате ръчно за Terraform.
Има Още от 2014-та в Terraform, която затвориха през 2016-та, с надежда за import.
Общо, всичко е като на картинката, само от дясно на ляво.
Предупреждения: Авторът живее половината си живот извън Русия и рядко пише на руски. Внимание за грешки в правописа.
Решения
1. Има готови и стари решения за AWS. . Когато се опитах да получа моите 250+ кофи, осъзнах, че всичко е в лошо състояние. AWS отдавна е добавила много нови опции, а terraforming не знае за тях и по принцип е на Ruby. . След 2 часа следобед изпратих , за да добавя повече възможности там и осъзнах, че такова решение изобщо не е подходящо.
Как работи terraforming? Той взима данни от AWS SDK и генерира tf и tfstate чрез темплет.
Тук има три проблема:
1. Винаги ще има забавяне в обновленията.
2. tf файловете понякога излизат повредени.
3. tfstate се събира отделно от tf и не винаги съвпада.
В действителност е трудно да се получи резултат, при който `terraform plan` да каже, че няма промени.
2. `terraform import` — вградена команда в Terraform. Как работи?
Пишете празен TF файл с името и типа на ресурса, след което стартирате `terraform import` и подавате ID на ресурса. Terraform се свързва с доставчика, получава данните и прави tfstate файл.
Тук има три проблема:
1. Получаваме само tfstate файл, а tf е празен, който е необходимо ръчно да напишете или да конвертирате от tfstate.
2. Може да работи само с един ресурс на един път и не поддържа всички ресурси. И какво ще правя отново с 250+ кофите?
3. Трябва да знаете ID на ресурсите — тоест е необходимо да обвържете това в код, който извлича списъка с ресурси.
В действителност резултатът е частичен и не мащабируем добре.
Моето решение.
Изисквания:
1. Възможност за създаване на tf и tfstate файлове по ресурси. Например, изтегляне на всички бакети/групи за сигурност/балансиратели на натоварване и какво `terraform plan` връща, че няма промени.
2. Нужни са 2 облака GCP + AWS.
3. Глобално решение, което лесно да се обновява всеки път и да не се губи време за всеки ресурс по 3 дни работа.
4. Да стане Open source — проблемът е общ за всички.
Езикът Go — затова го обичам, и на него има библиотека за създаване на HCL файлове, която се използва в terraform + много код в terraform, който може да бъде полезен.
Път.
Първи опит.
Започнах с прост вариант. Обаждания в облака чрез SDK за нужния ресурс и конвертиране на него в полета за terraform. Опитът умря веднага при групата за сигурност, защото не ми хареса 1.5 дни да конвертирам само група за сигурност (а ресурсите са много). Дълго е и после полетата могат да се променят/добавят.
Втори опит.
Основава се на описаната идея. . Просто да вземеш и да конвертираш tfstate в tf. Всички данни са там и полетата са същите. Как да получим пълен tfstate за много ресурси?? Тук на помощ дойде командата `terraform refresh`. terraform взема всички ресурси в tfstate и по ID извлича данни за тях и записва всичко в tfstate. Тоест, да се създаде празен tfstate само с имена и ID, стартираме `terraform refresh` и получаваме пълни tfstate. Ура!
Сега ще се заемем с рекурсивното писане на конвертор за tfstate в tf. За тези, които никога не са чели tfstate, то това е JSON, но по-специален.
Ето важната му част attributes.
"attributes": {
"id": "default/backend-logging-load-deployment",
"metadata.#": "1",
"metadata.0.annotations.%": "0",
"metadata.0.generate_name": "",
"metadata.0.generation": "24",
"metadata.0.labels.%": "1",
"metadata.0.labels.app": "backend-logging",
"metadata.0.name": "backend-logging-load-deployment",
"metadata.0.namespace": "default",
"metadata.0.resource_version": "109317427",
"metadata.0.self_link": "\/apis\/apps\/v1\/namespaces\/default\/deployments\/backend-logging-load-deployment",
"metadata.0.uid": "300ecda1-4138-11e9-9d5d-42010a8400b5",
"spec.#": "1",
"spec.0.min_ready_seconds": "0",
"spec.0.paused": "false",
"spec.0.progress_deadline_seconds": "600",
"spec.0.replicas": "1",
"spec.0.revision_history_limit": "10",
"spec.0.selector.#": "1",
Тук има:
1. id — string.
2. metadata — масив с размер 1 и в него обект с полета, описани по-долу.
3. spec — хеш с размер 1 и в него ключ, стойност.
Накратко, весел формат, всичко може да бъде и в дълбочина до няколко нива.
"spec.#": "1",
"spec.0.min_ready_seconds": "0",
"spec.0.paused": "false",
"spec.0.progress_deadline_seconds": "600",
"spec.0.replicas": "1",
"spec.0.revision_history_limit": "10",
"spec.0.selector.#": "1",
"spec.0.selector.0.match_expressions.#": "0",
"spec.0.selector.0.match_labels.%": "1",
"spec.0.selector.0.match_labels.app": "backend-logging-load",
"spec.0.strategy.#": "0",
"spec.0.template.#": "1",
"spec.0.template.0.metadata.#": "1",
"spec.0.template.0.metadata.0.annotations.%": "0",
"spec.0.template.0.metadata.0.generate_name": "",
"spec.0.template.0.metadata.0.generation": "0",
"spec.0.template.0.metadata.0.labels.%": "1",
"spec.0.template.0.metadata.0.labels.app": "backend-logging-load",
"spec.0.template.0.metadata.0.name": "",
"spec.0.template.0.metadata.0.namespace": "",
"spec.0.template.0.metadata.0.resource_version": "",
"spec.0.template.0.metadata.0.self_link": "",
"spec.0.template.0.metadata.0.uid": "",
"spec.0.template.0.spec.#": "1",
"spec.0.template.0.spec.0.active_deadline_seconds": "0",
"spec.0.template.0.spec.0.container.#": "1",
"spec.0.template.0.spec.0.container.0.args.#": "3", Ако някой иска задача за програмиране за интервю, просто поискайте да напишете парсер за това 🙂
След дълги опити да напиша парсер без грешки, намерих част от него в кода на terraform, всъщност най-важната част. И всичко изглеждаше, че работи нормално.
Опит три
Terraform provider — това са бинарки, в които съдържат код с всички ресурси и логика за работа с API на облаците. Всяко облако има свой provider и самият terraform просто ги извиква чрез своя протокол RPC между два процеса.
Сега реших да се свържа директно с terraform providers чрез RPC извиквания. Получи се красиво и даде възможност да сменям terraform providers с по-нови и да получавам нови възможности, без да променям кода. Също така се оказа, че не всички полета в tfstate трябва да са в tf, а как да го разберем? Само като попитам provider за това. После започна още една рекурсивна порнография по създаването на регулярни изрази с търсене на полета вътре в tfstate на всички нива навътре.
В крайна сметка се получи полезен CLI инструмент, който има обща инфраструктура за всички terraform providers и може лесно да се добави нов. Освен това добавянето на ресурси заема малко код. Плюс всякакви екстри, като свързване между ресурсите. Разбира се, имаше много различни проблеми, които не могат да бъдат описани всичките.
Нарекох зверушката Terrafomer.
Финал
С помощта на Terrafomer генерирахме между 500 и 700 хиляди реда код tf + tfstate от две облачни среди. Успяхме да вземем легаси неща и да започнем да ги обработваме само чрез terraform, както е в най-добрите идеи за инфраструктура като код. Просто магия, когато вземеш огромно облако и го получаваш чрез команда под формата на работещи terraform файлове. А после grep/replace/git и така нататък.
Изчислих и подредих, получих разрешения. Пуснах го на гитхаб за всички в четвъртък (02.05.19).
Вече имам 600 звезди, 2 pull request-ен за добавяне на поддръжка за openstack и kubernetes. Добри отзиви. Всъщност проектът е полезен за хората.
Препоръчвам на всички, които искат да започнат да работят с Terraform и да не презаписват всичко за това.
Ще се радвам на pull requests, issues, звезди.
Демо
Източник: habr.com
