Terraformer - Infrastructure To Code

Terraformer - Infrastructure To Code
Жадаў бы распавесці пра новы CLI tool які я напісаў для рашэння адной старой праблемы.

праблема

Terraform ужо даўно стаў стандартам у Devops/Cloud/IT супольнасці. Рэч вельмі зручная і карысная, каб займацца infrastructure as code. Ёсць шмат любат у Terraform і шмат відэльцаў, вострых нажоў і грабляў.
З Terraform вельмі зручна рабіць новыя рэчы і потым імі кіраваць, мяняць ці выдаляць. А што рабіць тым у каго ёсць вялізная інфраструктура ў воблаку і не створана праз Terraform? Перапісваць і пераствараць усё воблака як то дорага і небяспечна.
Я сутыкаўся з такой праблемай на 2 працах, самы просты прыклад калі хочаш што ўсё было ў гіце выглядзе тэраформаў файлаў, а ў цябе 250+ бакетаў і пісаць іх у для тэраформа рукамі як тое шмат.
Ёсць пытанне яшчэ з 2014 года ў terrafom якую закрылі ў 2016 з надзеяй што будзе import.

Наогул усё як на малюнку толькі з права на лева

Папярэджанні: Аўтар пол жыцця жыве не ў Расіі і піша на рускай мала. Асцярожна памылкі ў арфаграфіі.

Рашэнні

1. Ёсць гатовае і старое рашэнні для AWS тэрафармаванне. Калі я спрабаваў праз яго атрымаць мае 250+ бакетаў, то зразумеў што там усё дрэнна. AWS ужо даўно накідалі шмат новых опцый а terraforming пра іх не ведае і наогул у яго ruby темплет выглядаюць бедна. Пасля 2 вечарам я паслаў Pull request каб дадаць больш магчымасцяў туды і зразумеў, што такое рашэнне не падыходзіць наогул.
Як працуе terraforming ён бярэ з SDK AWS дадзеныя і генеруе tf і tfstate праз темплет.
Тут 3 праблемы:
1. Заўсёды там будзе адставання ў абнаўленнях
2. tf файлы часам выходзяць бітыя
3. tfstate збіраецца асобна ад tf і не заўсёды збягаецца
Наогул складана атрымае вынік пры якім `terraform plan` скажа што не змен

2. `terraform import` - убудаваная каманда ў terraform. Як працуе?
Пішаш пусты TF файл з імем і выглядам рэсурсу, потым запускаеш `terraform import` і перадаеш ID рэсурсу. terraform звяртаецца да правайдэра атрымлівае дадзеныя і робіць tfstate файл.
Тут 3 праблемы:
1. Атрымліваем толькі tfstate файл а tf пусты трэба рукамі пісаць ці канвертаваць з tfstate
2. Умее працаваць толькі з адным рэсурсаў кожны раз і не падтрымлівае ўсе рэсурсы. І што мне зноў рабіць з 250+ бакетамі
3. Трэба ведаць ID рэсурсаў - гэта значыць трэба абмотваць яго гэта ў код які дастае спіс рэсурсаў
Наогул вынік частковы і не маштабуецца добра

Маё рашэнні

патрабаванні:
1. Магчымасць стварыць файлы tf і tfstate па рэсурсах. Напрыклад спампаваць усе бакеты/security group/load balancer і што `terraform plan` вяртаў што няма змен
2. Трэба 2 аблокі GCP + AWS
3. Глабальнае рашэнне якое лёгка абнаўляць кожны раз і не марнаваць час на кожны рэсурс па 3 дні працы
4. Зрабіць Open source – праблема ва ўсіх такая

Мова Go - таму я люблю, і на ім ёсць бібліятэка для стварэння HCL файлаў якая выкарыстоўваецца ў terraform + шмат кода ў terraform які можа быць карысны

Шлях

Спроба першая
Пачаў просты варыянт. Звароты ў воблака праз SDK за патрэбным рэсурсам і канвертавання яго ў палі для terraform. Спроба памерла адразу на security group, таму што мне не спадабалася 1.5 дня канвертаваць толькі security group(а рэсурсаў шмат). Доўга і потым палі могуць мяняць/дададуцца

Спроба другая
Заснавана на ідэі апісанай тут. Проста ўзяць і сканвертаваць 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 - array памерам 1 і ў ім аб'ект з палямі які апісаны ніжэй
3. spec - hash памерам 1 і ў ім key, value
Карацей вясёлы фармат, усё можа быць у глыб таксама на некалькі ўзроўняў

                   "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 tool у якога агульная інфраструктура для ўсіх terraform providers і можна лёгка дадаць новага. Таксама дадання рэсурсаў займае мала кода. Плюс усякія плюшкі тыпу злучэння паміж рэсурсамі. Вядома было шмат розных праблем якія ўсё не апісаць.
Назваў звярок Terrafomer.

фінал

Мы з дапамогай Terrafomer згенеравалі 500-700 тысяч радкоў кода tf + tfstate з двух аблоках. Змаглі ўзяць легасі рэчы і пачаць іх чапаць толькі праз terraform як у лепшых ідэях infrastructure as code. Проста магія калі бярэш велізарнае воблака і атрымліваеш праз каманду яго ў выглядзе terraform файлаў працоўных. А далей grep/replace/git і гэтак далей.

Вычасаў і прывёў у парадак, атрымаў дазволы. Выпусціў на гітхаб для ўсіх у чацвер(02.05.19). github.com/GoogleCloudPlatform/terraformer
Атрымаў ужо 600 зорак, 2 pull requests дадання падтрымкі openstack і kubernetes. Добрыя водгукі. Наогул праект карысны для людзей
Раю ўсім хто жадае пачаць працаваць з Terraform і не перапісваць усё для гэтага.
Буду рады pull requests, issues, stars.

дэма
Terraformer - Infrastructure To Code

Крыніца: habr.com

Дадаць каментар