Quando il sistema "Infrastructure as Code" si espande oltre l'esempio "Hello World" senza pianificazione, il codice tende a non essere strutturato. Le configurazioni non pianificate sono impostate come hardcoded. La manutenibilità diminuisce drasticamente.
Utilizza questo documento, insieme al relativo esempio di codice, per strutturare i tuoi deployment in modo più efficiente e su larga scala.
Inoltre, applica la convenzione di denominazione e le best practice interne per tutti i team. Questo documento è destinato a un pubblico tecnicamente avanzato e presuppone che tu abbia una conoscenza di base di Python, Google Cloud Infrastructure, Deployment Manager e, in generale, Infrastructure as Code.
Prima di iniziare
- Se vuoi utilizzare gli esempi a riga di comando in questa guida, installa lo strumento a riga di comando gcloud.
- Se vuoi utilizzare gli esempi di API in questa guida, configura l'accesso API.
Più ambienti con un singolo codebase
Per i deployment di grandi dimensioni con più di una decina di risorse, le best practice standard richiedono di utilizzare una quantità significativa di proprietà esterne (parametri di configurazione), in modo da evitare stringhe hardcoded e logica a modelli generici. Molte di queste proprietà sono parzialmente duplicate a causa di ambienti simili, ad esempio ambienti di sviluppo, test o produzione, e servizi simili. Ad esempio, tutti i servizi standard sono in esecuzione su uno stack LAMP simile. Seguendo queste best practice, ottieni un ampio insieme di proprietà di configurazione con un'elevata quantità di duplicati che possono diventare difficili da gestire, per cui aumentano le probabilità di errori umani.
La seguente tabella è un esempio di codice per illustrare le differenze tra la gerarchia e una singola configurazione per deployment. La tabella evidenzia un duplicato comune di una singola configurazione. Utilizzando la configurazione gerarchica, la tabella mostra come spostare le sezioni ripetute a un livello superiore nella gerarchia per evitare ripetizioni e ridurre le possibilità di errore umano.
Modello | Configurazione gerarchica senza ridondanza | Configurazione singola con ridondanza |
---|---|---|
|
|
N/A |
|
|
|
|
|
|
|
|
|
Per gestire meglio un codebase di grandi dimensioni, utilizza un layout gerarchico strutturato con un'unione di proprietà di configurazione a cascata. Per farlo, devi utilizzare più file per la configurazione, anziché uno solo. Inoltre, collabori con le funzioni helper e condividi parte del codebase nella tua organizzazione.
L'esempio di codice che accompagna il documento, Organization_with_departments
, contiene un codebase con una struttura predefinita ma personalizzabile, uno script di supporto per l'unione delle configurazioni, le funzioni helper per la denominazione e un set completo di configurazioni di esempio. Puoi trovare questo esempio funzionante nel repository GitHub di esempi di Deployment Manager.
La strutturazione a cascata del codice offre diversi vantaggi:
- Quando suddividi la configurazione in più file, migliori la struttura e la leggibilità delle proprietà. Evitate inoltre di duplicarli.
- Puoi progettare l'unione gerarchica per i valori in modo logico, creando file di configurazione di primo livello riutilizzabili tra progetti o componenti.
- Puoi definire ogni proprietà una sola volta (diversa da sovrascrivere), per evitare di dover gestire il traffico dei nomi nei nomi delle proprietà.
- I modelli non devono conoscere l'ambiente reale, perché la configurazione appropriata viene caricata in base alle variabili appropriate.
Strutturare il codebase in modo gerarchico
Un deployment di Deployment Manager contiene una configurazione YAML o un file di schema, oltre a diversi file Python. Insieme, questi file formano il codebase di un deployment. I file Python possono avere scopi diversi. Puoi utilizzare i file Python come modelli di deployment, come file di codice generali (classi di supporto) o come file di codice che archiviano le proprietà di configurazione.
Per strutturare il codice in modo gerarchico, utilizza alcuni file Python come file di configurazione anziché il file di configurazione standard. Questo approccio offre una maggiore flessibilità rispetto al collegamento del deployment a un singolo file YAML.
Considerare l'infrastruttura come codice reale
Un principio importante per il codice pulito è Non ripetere per se stessi (DRY). Definisci tutto una sola volta. Questo approccio rende il codebase più pulito, più facile da rivedere e convalidare e più facile da gestire. Quando una proprietà deve essere modificata in un unico posto, il rischio di errori umani diminuisce.
Per un codebase più leggero con file di configurazione più piccoli e pochi duplicati, utilizza queste linee guida per strutturare le configurazioni seguendo il principio DRY.
Organizzazioni, dipartimenti, ambienti e moduli
I principi fondanti per strutturare il codebase in modo pulito e gerarchico sono l'uso di organizzazioni, reparti, ambienti e moduli. Questi principi sono facoltativi e estendibili. Per un diagramma della gerarchia del codebase di esempio, che segue questi principi, consulta la gerarchia di configurazione.
Nel diagramma seguente, viene eseguito il deployment di un modulo in un ambiente. La fusione della configurazione seleziona i file di configurazione appropriati a ogni livello in base al contesto in cui viene utilizzato. Definisce inoltre automaticamente il sistema e il reparto.
Nell'elenco che segue, i numeri rappresentano l'ordine di sovrascrittura:
Proprietà organizzative
Si tratta del livello più alto della struttura. A questo livello, puoi archiviare le proprietà di configurazione, come
organization_name
,organization_abbreviation
, che utilizzi nella tua convenzione di denominazione e le funzioni helper che vuoi condividere e applicare a tutti i team.Proprietà dei reparti
Le organizzazioni contengono reparti, se hai reparti all'interno della tua struttura. Nel file di configurazione di ciascun reparto, condividi le proprietà che non sono utilizzate da altri reparti, ad esempio
department_name
ocost_center
.Proprietà di sistema (progetto)
Ogni reparto contiene sistemi. Un sistema è uno stack software ben definito, ad esempio la tua piattaforma di e-commerce. Non è un progetto Google Cloud, ma un ecosistema di servizi funzionante.
A livello di sistema, il tuo team ha molta più autonomia rispetto ai livelli superiori. Qui puoi definire le funzioni helper (come
project_name_generator()
,instance_name_generator()
oinstance_label_generator()
) per i parametri a livello di team e di sistema (ad esempio,system_name
,default_instance_size
onaming_prefix
).Proprietà dell'ambiente
È probabile che il tuo sistema abbia più ambienti simili (
Dev
,Test
oProd
(e, facoltativamente,QA
eStaging
) che siano abbastanza simili. Idealmente, utilizzano lo stesso codebase e differiscono solo a livello di configurazione. A livello di ambiente, puoi sovrascrivere le proprietà comedefault_instance_size
per le configurazioniProd
eQA
.Proprietà dei moduli
Se il tuo sistema è di grandi dimensioni, suddividilo in più moduli invece di mantenerlo come un unico blocco monolitico. Ad esempio, potresti spostare la rete principale e la sicurezza in blocchi separati. Puoi anche separare i livelli di backend, frontend e database in moduli separati. I moduli sono modelli sviluppati da terze parti, a cui si aggiunge solo la configurazione appropriata. A livello di modulo, puoi definire proprietà pertinenti solo per determinati moduli, incluse le proprietà progettate per sovrascrivere le proprietà a livello di sistema ereditate. I livelli di ambiente e modulo sono suddivisioni parallele in un sistema, ma i moduli seguono gli ambienti durante il processo di unione.
Proprietà del modulo specifiche per l'ambiente
Alcune proprietà del modulo potrebbero dipendere anche dall'ambiente, ad esempio dalle dimensioni dell'istanza, dalle immagini ed endpoint. Le proprietà del modulo specifiche per l'ambiente sono il livello più specifico e l'ultimo punto dell'unione a cascata per la sovrascrittura dei valori definiti in precedenza.
Classe helper per l'unione delle configurazioni
La classe config_merger
è una classe helper che carica automaticamente i file di configurazione appropriati e unisce i relativi contenuti in un unico dizionario.
Per utilizzare la classe config_merger
, devi fornire le seguenti informazioni:
- Il nome del modulo.
- Il contesto globale, che contiene il nome dell'ambiente.
La chiamata alla funzione statica ConfigContext
restituisce il dizionario di configurazione unito.
Il seguente codice mostra come utilizzare questo corso:
module = "frontend"
specifica il contesto in cui vengono caricati i file delle proprietà.- L'ambiente viene scelto automaticamente da
context.properties["envName"]
. La configurazione globale.
cc = config_merger.ConfigContext(context.properties, module) print cc.configs['ServiceName']
Dietro le quinte, questa classe di supporto deve essere in linea con le strutture di configurazione, caricare tutti i livelli nell'ordine corretto e sovrascrivere i valori di configurazione appropriati. Per cambiare i livelli o l'ordine di sovrascrittura, modifica la classe di unione della configurazione.
Durante l'uso quotidiano e di routine, in genere non è necessario toccare questo corso. In genere, modifichi i modelli e i file di configurazione appropriati, quindi utilizzi il dizionario di output con tutte le configurazioni al suo interno.
Il codebase di esempio contiene i seguenti tre file di configurazione impostati come hardcoded:
org_config.py
department_config.py
system_config.py
Puoi creare file di configurazione dell'organizzazione e del reparto come link simbolici durante l'avvio del repository. Questi file possono trovarsi in un repository di codice separato, poiché non fanno logicamente parte del codebase di un team di progetto, ma sono condivisi tra l'intera organizzazione e il dipartimento.
L'unione della configurazione cerca anche i file che corrispondono ai livelli rimanenti della struttura:
envs/[environment name].py
[environment name]/[module name].py
modules/[module name].py
File di configurazione
Deployment Manager utilizza un file di configurazione, ovvero un singolo file per un deployment specifico. Non può essere condiviso tra deployment.
Quando utilizzi la classe config-merger
, le proprietà di configurazione vengono
completamente scollegate da questo file di configurazione perché non lo utilizzi.
Utilizza invece una raccolta di file Python, che offre una maggiore flessibilità in un deployment. Questi file possono essere condivisi anche tra deployment.
Qualsiasi file Python può contenere variabili, che ti consentono di archiviare la tua configurazione in un modo strutturato, ma distribuito. L'approccio migliore è utilizzare dizionari con una struttura concordata. La unione della configurazione cerca un dizionario chiamato configs
in ogni file nella catena di unione. Le entità configs
separate vengono unite in un'unica sezione.
Durante l'unione, quando una proprietà con lo stesso percorso e lo stesso nome appare più volte nei dizionari, la combinazione viene sovrascritta. In alcuni casi questo comportamento è utile, ad esempio quando un valore predefinito viene sovrascritto da un valore specifico del contesto. Tuttavia, ci sono molti altri casi in cui vuoi evitare di sovrascrivere la proprietà. Per evitare la sovrascrittura di una proprietà, aggiungi alla pagina uno spazio dei nomi separato per renderla unica. Nell'esempio seguente aggiungi uno spazio dei nomi, creando un ulteriore livello nel dizionario di configurazione, che crea un subdizionario.
config = { 'Zip_code': '1234' 'Count': '3' 'project_module': { 'admin': 'Joe', } } config = { 'Zip_code': '5555' 'Count': '5' 'project_module_prod': { 'admin': 'Steve', } }
Lezioni di aiuto e convenzioni di denominazione
Le convenzioni di denominazione sono il modo migliore per tenere sotto controllo l'infrastruttura di Deployment Manager. Non vuoi utilizzare nomi vaghi o generici, come my project
o test instance
.
L'esempio seguente è una convenzione di denominazione delle istanze a livello di organizzazione:
def getInstanceName(self, name): return '-'.join(self.configs['Org_level_configs']['Org_Short_Name'], self.configs['Department_level_configs']['Department_Short_Name'], self.configs['System_short_name'], name, self.configs["envName"])
Fornendo una funzione helper è facile assegnare un nome a ogni istanza in base alla convenzione concordata. Semplifica inoltre la revisione del codice perché nessun nome istanza proviene da altri elementi oltre a questa funzione. La funzione raccoglie automaticamente i nomi dalle configurazioni di livello superiore. Questo approccio aiuta a evitare input non necessari.
Puoi applicare queste convenzioni di denominazione alla maggior parte delle risorse di Google Cloud e alle etichette. Le funzioni più complesse possono persino generare un insieme di etichette predefinite.
Struttura delle cartelle del codebase di esempio
La struttura delle cartelle del codebase di esempio è flessibile e personalizzabile. Tuttavia, è parzialmente hardcoded nell'unione della configurazione e nel file di schema di Deployment Manager, il che significa che se apporti una modifica, devi riflettere queste modifiche nei file di unione e di schema di configurazione.
├── global │ ├── configs │ └── helper └── systems └── my_ecom_system ├── configs │ ├── dev │ ├── envs │ ├── modules │ ├── prod │ └── test ├── helper └── templates
La cartella globale contiene file condivisi tra diversi team di progetto. Per semplicità, la cartella di configurazione contiene la configurazione dell'organizzazione e tutti i file di configurazione dei reparti. In questo esempio non esiste una classe helper separata per i reparti. Puoi aggiungere qualsiasi classe aiutante a livello di organizzazione o di sistema.
La cartella globale può trovarsi in un repository Git separato. Puoi fare riferimento ai rispettivi file dai singoli sistemi. Potete anche utilizzare link simbolici, ma potrebbero creare confusione o interruzioni in determinati sistemi operativi.
├── configs │ ├── Department_Data_config.py │ ├── Department_Finance_config.py │ ├── Department_RandD_config.py │ └── org_config.py └── helper ├── config_merger.py └── naming_helper.py
La cartella Systems contiene uno o più sistemi diversi. I sistemi sono separati l'uno dall'altro e non condividono le configurazioni.
├── configs │ ├── dev │ ├── envs │ ├── modules │ ├── prod │ └── test ├── helper └── templates
La cartella di configurazione contiene tutti i file di configurazione univoci di questo sistema, che fanno riferimento anche alle configurazioni globali tramite link simbolici.
├── department_config.py -> ../../../global/configs/Department_Data_config.py ├── org_config.py -> ../../../global/configs/org_config.py ├── system_config.py ├── dev │ ├── frontend.py │ └── project.py ├── prod │ ├── frontend.py │ └── project.py ├── test │ ├── frontend.py │ └── project.py ├── envs │ ├── dev.py │ ├── prod.py │ └── test.py └── modules ├── frontend.py └── project.py Org_config.py: config = { 'Org_level_configs': { 'Org_Name': 'Sample Inc.', 'Org_Short_Name': 'sampl', 'HQ_Address': { 'City': 'London', 'Country': 'UK' } } }
Nella cartella helper, puoi aggiungere ulteriori corsi helper e fare riferimento ai corsi globali.
├── config_merger.py -> ../../../global/helper/config_merger.py └── naming_helper.py -> ../../../global/helper/naming_helper.py
Nella cartella dei modelli, puoi archiviare o fare riferimento ai modelli di Deployment Manager. Anche i link simbolici funzionano qui.
├── project_creation -> ../../../../../../examples/v2/project_creation └── simple_frontend.py
Utilizzare il codebase di esempio
Il modo migliore per iniziare ad applicare questa pratica gerarchica come base dell'infrastruttura come codice è clonare il codebase di esempio e copiare i contenuti della cartella gerarchica_config.
Dai un'occhiata al repository di esempio.
git clone https://github.com/GoogleCloudPlatform/deploymentmanager-samples.git cd deploymentmanager-samples/community/hierarchical_configuration/Organization_with_departments/systems/my_ecom_system gcloud config set deployment_manager/glob_imports True
Per configurare il sistema, usa i seguenti link simbolici per fare riferimento ai file globali nel contesto locale.
ln -sf ../../../global/helper/config_merger.py helper/config_merger.py ln -sf ../../../global/helper/naming_helper.py helper/naming_helper.py ln -sf ../../../global/configs/org_config.py configs/org_config.py
Seleziona il reparto appropriato dall'elenco globale.
ln -sf ../../../global/configs/Department_Data_config.py configs/department_config.py
Per impostare il contesto dell'ambiente corretto, utilizza il flag
--properties
per specificare la proprietàenvName
. Questa proprietà ti consente di eseguire lo stesso codice, scegliendo come target ambienti diversi, con lo stesso comando.[MY-PROJECT-ID]
rappresenta l'ID del tuo progetto Google Cloud.[MY-PROJECT-ID] gcloud deployment-manager deployments create hierarchy-org-example-dev --template env_demo_project.py --properties=envName:dev gcloud deployment-manager deployments create hierarchy-org-example-test --template env_demo_project.py --properties=envName:test gcloud deployment-manager deployments create hierarchy-org-example-prod --template env_demo_project.py --properties=envName:prod
Best practice
Le seguenti best practice possono aiutarti a strutturare il codice in modo gerarchico.
File di schema
Nel file di schema è obbligatorio specificare ogni deployment che utilizzi in qualsiasi modo durante il deployment. Aggiungere un'intera cartella rende il codice più breve e generico.
- Corsi helper:
- path: helper/*.py
- File di configurazione:
- path: configs/*.py - path: configs/*/*.py
- Importazioni collettive (stile glob)
gcloud config set deployment_manager/glob_imports True
Deployment multipli
Una best practice prevede che un sistema contenga più deployment, ovvero che utilizzino gli stessi insiemi di configurazioni, anche se si tratta di moduli diversi, ad esempio networking, firewall, backend, frontend. Potresti dover accedere all'output di questi deployment da un altro deployment. Puoi eseguire una query sull'output del deployment quando è pronto e salvarlo nella cartella delle configurazioni. Puoi aggiungere questi file di configurazione durante il processo di unione.
Link simbolici
I link simbolici sono supportati dai comandi gcloud deployment-manager
e i file collegati vengono caricati correttamente. Tuttavia, i link simbolici non sono supportati in ogni sistema operativo.
Gerarchia della configurazione
Il seguente diagramma fornisce una panoramica dei diversi livelli e delle relative relazioni. Ogni rettangolo rappresenta un file proprietà, come indicato dal nome file in rosso.
Ordine di unione sensibile al contesto
La fusione della configurazione seleziona i file di configurazione appropriati a ogni livello in base al contesto in cui viene utilizzato ciascun file. Il contesto è un modulo di cui esegui il deployment in un ambiente. Questo contesto definisce automaticamente il sistema e il reparto.
Nel diagramma seguente, i numeri rappresentano l'ordine della sovrascrittura nella gerarchia:
Passaggi successivi
- Vedi altri deployment di esempio nel repository GitHub di Deployment Manager.
- Scopri di più su modelli e deployment.