Terraformer — Infrastruktura kao kod

Terraformer — Infrastruktura kao kod
Rad bih pričao o novom CLI alatu koji sam napisao za rešavanje jednog starog problema.

Težava

Terraform je već dugo postao standard u DevOps/Cloud/IT zajednici. To je vrlo praktična i korisna stvar za rad s infrastrukturalnim kodom. Ima mnogo prednosti, ali i nekoliko zamki i opasnosti.
S Terraformom je veoma lako kreirati nove stvari i upravljati njima, menjati ili brisati. A šta je s onima koji imaju ogromnu infrastrukturu u oblaku koja nije stvorena preko Terrform-a? Prepisivati i ponovo kreirati celu infrastrukturu zvuči skupo i nesigurno.
Susretao sam se s takvim problemom na dva posla, najjednostavniji primer je kada želite da se sve završi u git-u u obliku Terraform datoteka, a vi imate 250+ kanti i pisanje njih za Terraform ručno je prilično naporno.
Je issue Još od 2014. godine u Terraformu je postojala opcija koja je zatvorena 2016. godine s nadom da će postojati mogućnost uvoza.

U svakom slučaju, sve izgleda kao na slici, samo s desna na levo.

Upozorenje: Autor živi polovinu svog života van Rusije i malo piše na ruskom. Pažnja na greške u pravopisu.

Rešitve

1. Postoje gotova i stara rešenja za AWS. terraforming. Kada sam pokušao da dobijem svojih 250+ kanti, shvatio sam da to ne funkcioniše dobro. AWS je već davno dodao mnogo novih opcija, a terraforming o njima ništa ne zna i uopšte koristi Ruby. Templati izgledaju oskudno.. Nakon 2 sata uveče, poslao sam Pull request da dodam više mogućnosti tamo i shvatio sam da takva rešenja uopšte ne odgovaraju.
Kako terraforming funkcioniše? Uzima podatke iz AWS SDK-a i generiše tf i tfstate kroz templatu.
Tu su 3 problema:
1. Uvek će postojati zaostatak u ažuriranjima.
2. tf datoteke ponekad izlaze oštećene.
3. tfstate se sastavlja odvojeno od tf i ne podudara se uvek.
Uglavnom, teško je dobiti rezultat pri kojem `terraform plan` kaže da nema promena.

2. `terraform import` — ugrađena komanda u Terraformu. Kako radi?
Napišete praznu TF datoteku s imenom i izgledom resursa, zatim pokrenete `terraform import` i prosledite ID resursa. Terraform se obraća provajderu, dobija podatke i pravi tfstate datoteku.
Tu su 3 problema:
1. Dobijamo samo tfstate datoteku, a tf je prazan, treba ga ručno napisati ili konvertovati iz tfstate.
2. Može raditi samo s jednim resursom svaki put i ne podržava sve resurse. A šta da radim s 250+ kanti?
3. Morate znati ID resursa — tj. treba ga obaviti kodom koji seče spisak resursa.
Uglavnom, rezultat je delimičan i nije dobro skalabilan.

Moje rešenje.

Zahteve:
1. Možnost ustvariti datoteke tf in tfstate iz virov. Na primer prenesti vse obleke/security group/load balancer in kar `terraform plan` vrača, da ni sprememb.
2. Potrebna sta 2 oblaka GCP + AWS.
3. Svetovna rešitev, ki jo je enostavno posodobiti vsakič in ne porabiti 3 dni dela na vsak vir.
4. Narediti odprtokodno — to je težava, s katero se soočajo vsi.

Jezik Go — ker ga imam rad, in ima knjižnico za ustvarjanje datotek HCL, ki se uporablja v terraformu + veliko kode v terraformu, ki je lahko koristna.

Pot.

Prvi poizkus.
Začel sem s preprosto različico. Klici v oblak preko SDK za zahtevane vire in njihovo pretvorbo v polja za terraform. Poizkus je takoj umrl pri security group, ker mi ni bilo všeč pretvarjati le security group 1,5 dneva (virov je veliko). Traja dolgo, potem pa se lahko polja spremenijo/dodajajo.

Drugi poizkus.
Osnovano na opisanih idejah. tukaj. Preprosto vzeti in pretvoriti tfstate v tf. Vsi podatki so tam in polja so ista. Kako pridobiti popoln tfstate za več virov?? Tukaj je pomagala ukaz `terraform refresh`. Terraform vzame vse vire v tfstate in po ID-ju iz njih potegne podatke in vse zapiše v tfstate. To pomeni, da ustvarimo prazno tfstate le z imeni in ID-ji, zaženemo `terraform refresh` in tako dobimo popolne tfstate. Ura!
Zdaj se bomo lotili rekurzivnega pisanja pretvornika za tfstate v tf. Za tiste, ki nikoli niso brali tfstate, je to JSON, vendar poseben.
Tukaj je njegov pomemben del 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",

Tukaj je:
1. id — niz.
2. metadata — tabela velikosti 1, ki vsebuje objekt s polji, kot je opisano spodaj.
3. spec — hash velikosti 1, ki vsebuje ključ, vrednost.
Na kratko, zabavna oblika, vse lahko gre globoko tudi na več nivojev.

                   "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 должны быть в Terraform, а как это узнать? Только спросить у provider об этом. Потом началась еще одна рекурсивная история с составлением регулярных выражений для поиска полей внутри tfstate на всех уровнях.

В конце получился полезный CLI tool с общей инфраструктурой для всех Terraform providers, и можно легко добавить новый. Также добавление ресурсов занимает немного кода. Плюс всякие плюшки типа соединений между ресурсами. Конечно, было много разных проблем, которые все не описать.
Назвал зверушку Terrafomer.

Zaključek

S pomočjo Terrafomerja smo generirali 500-700 tisoč vrstic tf + tfstate kode iz dveh oblakov. Uspelo nam je prevzeti nepravilnosti in začeti z njimi delati le skozi terraform, kot v najboljših praksah infrastrukture kot kode. Preprosto magično je, ko vzameš ogromno oblak in ga dobiš preko ukaza v obliki delovnih terraform datotek. In naprej grep/replace/git itd.

Izračunal in uredil sem, pridobil dovoljenja. Izdano na Githubu za vse v četrtek (02.05.19). github.com/GoogleCloudPlatform/terraformer
Že imam 600 zvezd, 2 pull requesta za dodajanje podpore openstack in kubernetes. Dobre ocene. Na splošno je projekt koristen za ljudi.
Priporočam vsem, ki želijo začeti delati s Terraformom in ne želijo vsega prepisovati.
Bil bi vesel pull requestov, težav, zvezd.

Demo
Terraformer — Infrastruktura kao kod

Izvor: habr.com

Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki 🔥 Kupite zanesljivo gostovanje za spletne strani z zaščito pred DDoS, VPS VDS strežniki | ProHoster