Terraformer — Infrastruktura kao Kod

Terraformer — Infrastruktura kao Kod
Želim vam ispričati o novom CLI alatu koji sam napisao za rješavanje jednog starog problema.

Problem

Terraform je već dugo standard u DevOps/Cloud/IT zajednici. To je vrlo praktična i korisna stvar za rad s infrastrukturom kao kodom. Ima mnogo prednosti, ali i niz poteškoća i izazova.
S Terraformom je lako stvoriti nove resurse i kasnije njima upravljati, mijenjati ili brisati. No, što učiniti ako imate veliku infrastrukturu u oblaku koja nije napravljena putem Terraforma? Prepisivanje i ponovna izgradnja cijelog oblaka je preskupo i nesigurno.
S tim sam se susreo u dvije tvrtke, najjednostavniji primjer je kada želiš da sve bude u Gitu u obliku Terraform datoteka, a imaš više od 250 kolekcija, pa pisati ih ručno za Terraform je prilično naporno.
Postoji problem još od 2014. u Terraformu koji su zatvorili 2016. s nadom da će postojati mogućnost uvoza.

Uglavnom, sve je kao na slici, samo s desna na lijevo

Upozorenje: Autor pola života živi izvan Rusije i rijetko piše na ruskom. Opasnost od grešaka u pravopisu.

Rješenja

1. Postoje gotova i starija rješenja za AWS terraforming. Kada sam pokušao preko njega dobiti moje 250+ kolekcija, shvatio sam da je to sve loše. AWS je odavno dodao mnogo novih opcija, a terraforming o njima ne zna i zapravo koristi Ruby. Templati izgledaju skromno. Nakon 2 večeri, poslao sam Pull request da dodam više mogućnosti, ali shvatio sam da to rješenje uopće nije prikladno.
Kako radi terraforming? Uzima podatke iz AWS SDK-a i generira tf i tfstate kroz templati.
Ovdje su 3 problema:
1. Uvijek će biti kašnjenja u ažuriranjima.
2. tf datoteke ponekad ispadnu oštećene.
3. tfstate se prikuplja odvojeno od tf i ne uvijek odgovara.
Ostvariti rezultat gdje `terraform plan` kaže da nema promjena je zapravo teško.

2. `terraform import` — ugrađena naredba u terraformu. Kako to radi?
Pišete praznu TF datoteku s imenom i tipom resursa, zatim pokrećete `terraform import` i predajete ID resursa. Terraform se obraća provajderu, dobiva podatke i stvara tfstate datoteku.
Ovdje su 3 problema:
1. Dobivamo samo tfstate datoteku, a tf je prazan, treba ga ručno napisati ili konvertirati iz tfstate.
2. Može raditi samo s jednim resursom svaki put i ne podržava sve resurse. Što da radim opet s 250+ kolekcija?
3. Trebamo znati ID resursa — dakle, trebamo to obaviti u kodu koji dohvaća popis resursa.
Uglavnom, rezultat je djelomičan i ne skalira se dobro.

Moja rješenja

Zahtjevi:
1. Mogućnost stvaranja tf i tfstate datoteka na temelju resursa. Na primjer, preuzmite sve kante / sigurnosne grupe / ravnoteže opterećenja i da `terraform plan` vrati da nema promjena
2. Dva oblaka GCP + AWS
3. Globalno rješenje koje se lako ažurira svaki put i ne troši vrijeme na svaki resurs od tri dana rada
4. Napraviti open source — problem koji svi imaju

Jezik Go — zato ga volim i postoji biblioteku za stvaranje HCL datoteka koja se koristi u terraformu + puno koda u terraformu koji može biti koristan

Put

Pokušaj prvi
Započeo sam s jednostavnom verzijom. Pozivi u oblak preko SDK za potrebni resurs i konvertiranje u polja za terraform. Pokušaj je odmah propao na sigurnosnoj grupi jer mi nije odgovaralo 1,5 dana konvertirati samo sigurnosnu grupu (a resursa ima mnogo). Dugotrajno, a onda polja mogu izmijeniti/dodavati

Pokušaj drugi
Temeljeno na ideji opisanoj ovdje. Jednostavno uzeti i konvertirati tfstate u tf. Svi podaci su tamo i polja su ista. Kako dobiti puni tfstate za više resursa?? Ovdje je pomogla komanda `terraform refresh`. terraform uzima sve resurse u tfstate i po ID-u izvlači podatke i zapisuje sve u tfstate. Dakle, stvoriti prazan tfstate samo s imenima i ID-ovima, pokrenuti `terraform refresh` i dobijemo potpuni tfstate. Ura!
Sada ćemo se baviti rekursivnom pornografijom pisanja konvertera za tfstate u tf. Za one koji nikada nisu čitali tfstate, to je JSON, ali posebna vrsta.
Evo njegova važna dijela 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",

Ovdje se nalaze:
1. id — string
2. metadata — niz veličine 1 u kojem je objekt s poljima koja su opisano niže
3. spec — hash veličine 1 u kojem je key,value
Kratki zabavni format, sve može ići u dubinu na nekoliko razina.

                   "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",

Općenito, tko želi zadatak za programiranje za intervju, neka jednostavno zatraži da napišu parser za to 🙂.
Nakon dugih pokušaja da napišem parser bez grešaka, našao sam dio koda u terrafomu, a zapravo najvažniji dio. I sve je, čini se, radilo normalno.

Pokušaj tri.
Terraform provider je binarna datoteka koja sadrži kod sa svim resursima i logikom za rad s API-jem oblaka. Svaki oblak ima svoj provider, a sam terraform ih poziva putem svog RPC protokola između dva procesa.
Sada sam odlučio obraćati se izravno terraform providerima putem RPC poziva. Tako je to ispalo lijepo i omogućilo mi da mijenjam terraform providere za novije verzije i dobivam nove mogućnosti bez promjene koda. Ispostavilo se da ne sva polja u tfstate moraju biti u tf-u, a kako to saznati? Samo pitajući providera o tome. Zatim je započela još jedna rekurzivna pornografija po sastavljanju regularnih izraza s mukama pronalaženja polja unutar tfstate na svim razinama.

Na kraju smo dobili koristan CLI alat koji ima zajedničku infrastrukturu za sve Terraform provajdere i lako je dodati nove. Takođe, dodavanje resursa zahteva malo koda. Plus, tu su razne prednosti kao što su veze između resursa. Naravno, bilo je mnogo različitih problema koje je teško sve opisati.
Zvao sam ga Terrafomer.

Finale

Uz pomoć Terrafomera generisali smo 500-700 hiljada linija koda tf + tfstate iz dva oblaka. Uspeli smo da uzmemo legacijske stvari i započnemo ih menjati isključivo putem Terraforma, kao u najboljim idejama infrastrukture kao koda. Jednostavno magija kada uzmete ogroman oblak i dobijete ga uz pomoć komande u obliku radnih Terraform datoteka. A dalje grep/replace/git i tako dalje.

Sve sam izračunao i sredio, dobio dozvole. Izdano na GitHub-u za sve u četvrtak (02.05.19). github.com/GoogleCloudPlatform/terraformer
Već je dobio 600 zvezdica, 2 pull request-a za dodavanje podrške za OpenStack i Kubernetes. Dobre recenzije. Uopšte, projekat je koristan za ljude.
Preporučujem svima koji žele da počnu raditi sa Terraform-om i ne prepisuju sve za to.
Biću rad na pull request-ima, pitanjima, zvezdama.

Demo
Terraformer — Infrastruktura kao Kod

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster