
Vorrei parlare di un nuovo strumento CLI che ho scritto per risolvere un vecchio problema.
Problema
Terraform è diventato da tempo uno standard nella comunità DevOps/Cloud/IT. È uno strumento molto comodo e utile per gestire l'infrastruttura come codice. Ci sono molte bellezze in Terraform, ma anche molte forche, coltelli affilati e insidie.
Con Terraform è molto facile creare nuove risorse e poi gestirle, modificarle o eliminarle. Ma cosa fare per chi ha una grande infrastruttura nel cloud non creata tramite Terraform? Riscrivere e ricreare tutto il cloud sembra costoso e insicuro.
Ho affrontato questo problema in due lavori, il più semplice esempio è quando vuoi che tutto sia in Git sotto forma di file Terraform, ma hai più di 250 bucket e scriverli manualmente per Terraform è piuttosto laborioso.
C'è Fin dal 2014 c'è stato un tentativo di Terraform che è stato chiuso nel 2016 con la speranza di avere l'import.
Insomma, è tutto come nell'immagine, solo da destra a sinistra.
Avvertimento: L'autore vive all'estero da metà vita e scrive poco in russo. Attenzione agli errori di ortografia.
Soluzioni
1. Ci sono soluzioni vecchie e pronte per AWS. . Quando ho cercato di usare questo strumento per ottenere i miei più di 250 bucket, ho capito che la situazione era pessima. AWS ha già introdotto molte nuove opzioni, ma terraforming non lo sa e utilizza Ruby. . Dopo due serate ho mandato per aggiungere più funzionalità e ho capito che questa soluzione non è affatto adatta.
Come funziona terraforming? Prende i dati dal SDK AWS e genera tf e tfstate tramite template.
Ci sono 3 problemi:
1. Ci sarà sempre un ritardo negli aggiornamenti.
2. A volte i file tf risultano danneggiati.
3. Il tfstate viene raccolto separatamente dal tf e non sempre coincide.
È comunque difficile ottenere un risultato in cui `terraform plan` dica che non ci sono modifiche.
2. `terraform import` — è un comando integrato in Terraform. Come funziona?
Scrivi un file TF vuoto con il nome e il tipo di risorsa, poi esegui `terraform import` e trasmetti l'ID della risorsa. Terraform si rivolge al provider, ottiene i dati e crea il file tfstate.
Ci sono 3 problemi:
1. Otteniamo solo il file tfstate mentre il tf è vuoto, bisogna scriverlo manualmente o convertirlo dal tfstate.
2. Può gestire solo una risorsa alla volta e non supporta tutte le risorse. E cosa devo fare di nuovo con più di 250 bucket?
3. È necessario conoscere l'ID delle risorse — quindi bisogna incapsularlo in codice che estrae l'elenco delle risorse.
In effetti, 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/gruppi di sicurezza/balancer di carico e che `terraform plan` restituisca che non ci sono modifiche.
2. Necessito di 2 cloud GCP + AWS.
3. Soluzione globale facile da aggiornare ogni volta e che non richieda tempo per ogni risorsa per un lavoro di 3 giorni.
4. Rendre open source — è un problema comune a tutti.
Linguaggio Go — ecco perché mi piace, e c'è una libreria per creare file HCL che viene utilizzata in terraform + molto codice in terraform che potrebbe risultare utile.
Percorso
Primo tentativo
Ho iniziato con una versione semplice. Chiamate nel cloud tramite SDK per ottenere la risorsa richiesta e convertirla in campi per terraform. Il tentativo è fallito subito con il gruppo di sicurezza perché non mi è piaciuto spendere 1.5 giorni a convertire solo il gruppo di sicurezza (le risorse sono molte). È lungo e poi i campi possono cambiare/essere aggiunti.
Secondo tentativo
Basato sull'idea descritta . Semplicemente convertire tfstate in tf. Tutti i dati sono lì e i campi sono gli stessi. Come ottenere un tfstate completo per molte risorse?? Qui è intervenuto il comando `terraform refresh`. Terraform prende tutte le risorse in tfstate e in base all'ID estrae i dati e scrive tutto in tfstate. Quindi creare un tfstate vuoto solo con nomi e ID, eseguire `terraform refresh` e otteniamo tfstate completi. Evviva!
Ora ci occuperemo della scrittura ricorsiva di un convertitore da tfstate a tf. Per chi non ha mai letto tfstate, è JSON, ma particolare.
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 ci sono:
1. id — stringa
2. metadata — array di dimensione 1 e al suo interno un oggetto con i campi descritti qui sotto
3. spec — hash di dimensione 1 e al suo interno chiave, valore
In breve, un formato divertente, tutto può essere anche in profondità 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, chi vuole un compito di programmazione per un colloquio può semplicemente chiedere di scrivere un parser per questa cosa 🙂
Dopo molti tentativi di scrivere un parser senza bug, ho trovato una parte di esso nel codice di terraform, e quella era la parte più importante. E tutto sembrava funzionare bene.
Tentativo tre
Il provider terraform è un binario che contiene codice con tutte le risorse e la logica per lavorare con le API dei vari cloud. Ogni cloud ha il proprio provider e terraform lo chiama attraverso il suo protocollo RPC tra due processi.
Ora ho deciso di rivolgermi direttamente ai provider terraform attraverso chiamate RPC. È venuto tutto molto bene e ha fornito la possibilità di aggiornare i provider terraform con nuove versioni e ottenere nuove funzionalità senza cambiare il codice. Inoltre, ho scoperto che non tutti i campi in tfstate devono essere in tf, ma come scoprirlo? Solo chiedendo al provider.
Alla fine è uscito un utile strumento CLI che ha un'infrastruttura comune per tutti i provider terraform e consente di aggiungere facilmente nuovi. Inoltre, l'aggiunta di risorse richiede poco codice. E ci sono anche vari vantaggi come le connessioni tra risorse. Certamente ci sono stati molti problemi diversi che non possono essere tutti descritti.
Ho chiamato la creatura Terrafomer.
Finale
Abbiamo generato con Terrafomer 500-700mila righe di codice tf + tfstate da due cloud. Siamo riusciti a prendere cose legacy e iniziare a modificarle solo attraverso terraform, come nelle migliori idee di infrastructure as code. È pura magia quando prendi un enorme cloud e lo ottieni attraverso un comando in forma di file terraform. E poi grep/replace/git e così via.
Ho sistemato e riordinato, ho ottenuto le autorizzazioni. L'ho pubblicato su GitHub per tutti giovedì (02.05.19).
Ho già ricevuto 600 stelle, 2 pull request per aggiungere supporto a openstack e kubernetes. Buoni feedback. È un progetto davvero utile per le persone.
Lo consiglio a chiunque voglia iniziare a lavorare con Terraform senza dover riscrivere tutto.
Sarei felice di ricevere pull request, segnalazioni, stelle.
Demo
Fonte: habr.com
