
Vorrei parlare di un nuovo strumento CLI che ho sviluppato per risolvere un problema che esiste da tempo.
Problema
Terraform è ormai uno standard nella comunità Devops/Cloud/IT. È uno strumento molto utile per gestire l'infrastructure as code. Ci sono molti vantaggi in Terraform, ma anche molte insidie e problematiche.
Con Terraform è facile creare nuove risorse e poi gestirle, modificarle o eliminarle. Ma cosa fare per chi ha un'immensa infrastruttura cloud non creata con Terraform? Riscrivere e ricostruire tutto il cloud è costoso e poco sicuro.
Ho affrontato questo problema in due aziende; il caso più semplice è quando si desidera avere tutto sotto controllo in Git come file Terraform, ma si hanno più di 250 bucket e scriverli a mano non è pratico.
Sì Sin dal 2014 esisteva un progetto chiamato terrafom, chiuso nel 2016, con la speranza che sarebbe stata disponibile l'importazione.
Insomma, è tutto come nell'immagine ma da destra a sinistra.
Avvisi: L'autore ha vissuto per gran parte della sua vita al di fuori della Russia e scrive poco in russo. Attenzione agli errori di ortografia.
Soluzioni
1. Esistono soluzioni pronte e consolidate per AWS. . Quando ho provato a ottenere i miei oltre 250 bucket, ho capito che la situazione era critica. AWS ha già introdotto molte nuove opzioni da tempo, ma terraforming non ne è a conoscenza e, in generale, utilizza Ruby. . Dopo le 2 di pomeriggio ho inviato. per aggiungere più funzionalità, ma ho capito che questa soluzione non è affatto adeguata.
Come funziona terraforming: prende i dati dall'SDK AWS e genera tf e tfstate tramite un template.
Ci sono 3 problemi:
1. Ci sarà sempre un ritardo negli aggiornamenti.
2. I file tf a volte possono risultare danneggiati.
3. Il tfstate viene raccolto separatamente dal tf e non sempre corrisponde.
È davvero difficile ottenere un risultato in cui `terraform plan` indichi che non ci sono modifiche.
2. `terraform import` è un comando incorporato in terraform. Come funziona?
Scrivi un file TF vuoto con il nome e il tipo di risorsa, poi esegui `terraform import` e passi l'ID della risorsa. Terraform si rivolge al provider, riceve i dati e crea il file tfstate.
Ci sono 3 problemi:
1. Otteniamo solo il file tfstate, mentre il tf è vuoto e deve essere scritto manualmente o convertito da tfstate.
2. Può lavorare solo con una risorsa alla volta e non supporta tutte le risorse. E cosa devo fare di nuovo con oltre 250 bucket?
3. È importante conoscere l'ID delle risorse, quindi è necessario avvolgerlo in un codice che recupera l'elenco delle risorse.
In generale, il risultato è parziale e non scala bene.
La mia soluzione
Requisiti:
1. Possibilità di creare file tf e tfstate per le risorse. Ad esempio, scaricare tutti i bucket / security group / load balancer e ciò che `terraform plan` restituisce quando non ci sono modifiche.
2. Necessito di 2 cloud: GCP + AWS.
3. Una soluzione globale che sia facile da aggiornare ogni volta senza far perdere tempo per ogni risorsa, con 3 giorni di lavoro.
4. Rendere open source — il problema è comune a tutti.
Il linguaggio Go — perché lo amo, e c'è una libreria per la creazione di file HCL utilizzata in terraform + molto codice in terraform che può essere utile.
Percorso
Primo tentativo
Ho iniziato con una soluzione semplice. Comunicazione con il cloud tramite SDK per il recupero della risorsa desiderata e conversione in campi per terraform. Il tentativo è fallito subito con il security group perché non mi è piaciuto passare 1.5 giorni a convertire solo il security group (ci sono molte risorse). È lungo e poi i campi possono cambiare / aggiungersi.
Secondo tentativo
Basato sull'idea descritta. . Basta prendere e convertire tfstate in tf. Ci sono tutti i dati e i campi sono gli stessi. Come ottenere un tfstate completo per molte risorse? Qui viene in aiuto il comando `terraform refresh`. Terraform prende tutte le risorse in tfstate e, grazie all'ID, estrae i dati e li scrive in tfstate. Quindi, creando un tfstate vuoto solo con nomi e ID, lanciando `terraform refresh` otteniamo un tfstate completo. Evviva!
Ora ci dedicheremo alla scrittura ricorsiva del convertitore da tfstate a tf. Per chi non ha mai letto tfstate, si tratta di JSON, ma in modo speciale.
Ecco la sua parte importante 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",
Qui c'è:
1. id — stringa
2. metadata — array di dimensione 1 contenente un oggetto con i campi descritti sotto
3. spec — hash di dimensione 1 contenente key,value
In breve, un formato divertente, può essere anche profondo su più livelli
"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", In generale, chiunque desideri un compito di programmazione per un colloquio può semplicemente chiedere di scrivere un parser per questo 🙂
Dopo lunghe prove per scrivere un parser senza bug, ho trovato parte del suo codice in Terraform, precisamente la parte più importante. E tutto sembrava funzionare bene.
Tentativo tre
Un provider Terraform è un binario che contiene codice con tutte le risorse e la logica per lavorare con le API dei cloud. Ogni cloud ha il proprio provider e Terraform lo chiama attraverso il proprio protocollo RPC tra i due processi.
Adesso ho deciso di interagire direttamente con i provider Terraform tramite chiamate RPC. È venuto fuori in modo elegante e ha permesso di aggiornare i provider Terraform a versioni più recenti, ottenendo nuove funzionalità senza cambiare codice. Inoltre, si è scoperto che non tutti i campi in tfstate devono essere in tf, ma come scoprirlo? Solo chiedendo al provider. Poi è iniziato un’altra lunga e complicata avventura di costruzione di espressioni regolari per cercare campi all'interno di tfstate a tutti i livelli di profondità.
Alla fine, è emerso un utile strumento CLI che ha un'infrastruttura comune per tutti i provider di terraform, e aggiungere un nuovo provider è facile. Inoltre, l'aggiunta di risorse richiede poco codice. E ci sono varie funzionalità come le connessioni tra le risorse. Certo, ci sono stati molti problemi che non possono essere tutti descritti.
Ho chiamato la creatura Terrafomer.
Finale
Con Terrafomer abbiamo generato 500-700 mila righe di codice tf + tfstate da due cloud. Siamo riusciti a prendere elementi legacy e a iniziare a modificarli solo tramite terraform, come nelle migliori idee di infrastructure as code. È pura magia quando prendi un grande cloud e lo ottieni attraverso un comando in forma di file terraform operativi. E poi grep/replace/git e così via.
Ho elaborato e messo in ordine, ottenuto le autorizzazioni. L'ho rilasciato su GitHub per tutti giovedì (02.05.19).
Ho già ricevuto 600 stelle, 2 pull request per l'aggiunta del supporto a openstack e kubernetes. Buone recensioni. In generale, il progetto è utile per le persone.
Lo consiglio a chiunque voglia iniziare a lavorare con Terraform senza dover riscrivere tutto.
Sarei felice di ricevere pull requests, issue, stelle.
Demo
Fonte: habr.com
