Strutturazione di Deployment Manager per l'utilizzo su larga scala

Quando il sistema "Infrastructure as code" cresce oltre l'esempio "Hello World" senza pianificazione, il codice tende a diventare non strutturato. Le configurazioni non pianificate sono hardcoded. La manutenibilità diminuisce drasticamente.

Utilizza questo documento per strutturare le implementazioni in modo più efficiente e su larga scala.

Inoltre, applica la convenzione di denominazione e le best practice interne in tutti i team. Questo documento è destinato a un pubblico tecnicamente avanzato e presuppone una conoscenza di base di Python, Google Cloud dell'infrastruttura, di Deployment Manager e, in generale, dell'infrastruttura come codice.

Prima di iniziare

Più ambienti con un'unica base di codice

Per le implementazioni di grandi dimensioni con più di una dozzina di risorse, le best practice standard richiedono l'utilizzo di una quantità significativa di proprietà esterne (parametri di configurazione), in modo da evitare di codificare stringhe e logica in modelli generici. Molte di queste proprietà sono parzialmente duplicate a causa di ambienti simili, ad esempio ambiente di sviluppo, test o produzione, e servizi simili. Ad esempio, tutti i servizi standard vengono eseguiti su uno stack LAMP simile. Seguire queste best practice comporta un ampio insieme di proprietà di configurazione con un elevato grado di duplicazione che può diventare difficile da gestire, aumentando così la possibilità di errori umani.

La tabella seguente è un esempio di codice che illustra le differenze tra una configurazione gerarchica e una singola configurazione per deployment. La tabella evidenzia una duplicazione comune nella configurazione singola. Utilizzando la configurazione gerarchica, la tabella mostra come spostare le sezioni ripetute a un livello superiore della gerarchia per evitare ripetizioni e ridurre le possibilità di errore umano.

Modello Configurazione gerarchica senza ridondanza Configurazione singola con ridondanza

project_config.py

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP' }

N/D

frontend_config.py

config = {'ServiceName': 'frontend'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'frontend' }

backend_config.py

config = {'ServiceName': 'backend'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'backend' }

db_config.py

config = {'ServiceName': 'db'}

config = { 'ProjectId': 'qwerty123456', 'ProjectOwner': 'Bob', 'ProjectAbbrevation': 'SNP', 'ServiceName': 'db' }

Per gestire meglio un codebase di grandi dimensioni, utilizza un layout gerarchico strutturato con una unione a cascata delle proprietà di configurazione. Per farlo, utilizzi più file per la configurazione, anziché uno solo. Inoltre, utilizzi funzioni di supporto e condividi parte del codice base con la tua organizzazione.

La strutturazione e la disposizione a cascata del codice in modo gerarchico offre diversi vantaggi:

  • Se dividi la configurazione in più file, migliori la struttura e la leggibilità delle proprietà. Inoltre, eviti di duplicarli.
  • Progetti l'unione gerarchica per applicare in cascata i valori in modo logico, creando file di configurazione di primo livello riutilizzabili in progetti o componenti.
  • Definisci ogni proprietà una sola volta (a parte gli override), evitando la necessità di gestire gli spazi dei nomi nei nomi delle proprietà.
  • I tuoi modelli non devono conoscere l'ambiente effettivo, perché la configurazione appropriata viene caricata in base alle variabili appropriate.

Strutturare gerarchicamente il codebase

Un deployment di Deployment Manager contiene una configurazione YAML o un file di schema, insieme a diversi file Python. Insieme, questi file formano la base di codice 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 helper) o come file di codice che archiviano le proprietà di configurazione.

Per strutturare la base di codice in modo gerarchico, utilizzi alcuni file Python come file di configurazione, anziché il file di configurazione standard. Questo approccio offre maggiore flessibilità rispetto al collegamento del deployment a un singolo file YAML.

Trattare l'infrastruttura come codice reale

Un principio importante per il codice pulito è Non ripeterti (DRY). Definisci tutto una sola volta. Questo approccio rende la base di codice più pulita, più facile da esaminare e convalidare e più facile da gestire. Quando una proprietà deve essere modificata in un solo posto, il rischio di errore umano diminuisce.

Per un codebase più leggero con file di configurazione più piccoli e duplicazione minima, utilizza queste linee guida per strutturare le configurazioni in modo da seguire il principio DRY.

Organizzazioni, reparti, ambienti e moduli

I principi fondamentali per strutturare il codebase in modo pulito e gerarchico sono l'utilizzo di organizzazioni, reparti, ambienti e moduli. Questi principi sono facoltativi ed estendibili. Per un diagramma della gerarchia della base di codice di esempio, che segue questi principi, vedi la gerarchia di configurazione.

Nel seguente diagramma, un modulo viene implementato in un ambiente. Il unione delle configurazioni seleziona i file di configurazione appropriati a ogni livello in base al contesto in cui vengono utilizzati. Inoltre, definisce automaticamente il sistema e il reparto.

Un modulo di cui è stato eseguito il deployment in un ambiente

Nel seguente elenco, i numeri rappresentano l'ordine di sovrascrittura:

  1. Proprietà organizzative

    Questo è il livello più alto della tua struttura. A questo livello, puoi memorizzare proprietà di configurazione come organization_name, organization_abbreviation, che utilizzi nella convenzione di denominazione, e funzioni di assistenza che vuoi condividere e applicare in tutti i team.

  2. Proprietà del reparto

    Le organizzazioni contengono reparti, se ne hai nella tua struttura. Nel file di configurazione di ogni reparto, condividi le proprietà che non vengono utilizzate da altri reparti, ad esempio department_name o cost_center.

  3. 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 progettoGoogle 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 funzioni di assistenza (ad esempio project_name_generator(), instance_name_generator() o instance_label_generator()) per i parametri a livello di team e di sistema (ad esempio system_name, default_instance_size o naming_prefix).

  4. Proprietà dell'ambiente

    Il tuo sistema probabilmente ha più ambienti, come Dev, Test o Prod (e, facoltativamente, QA e Staging), che sono abbastanza simili tra loro. Idealmente, utilizzano lo stesso codebase e differiscono solo a livello di configurazione. A livello di ambiente, puoi sovrascrivere proprietà come default_instance_size per le configurazioni Prod e QA.

  5. Proprietà dei moduli

    Se il sistema è grande, dividilo in più moduli, anziché mantenerlo come un unico blocco monolitico di grandi dimensioni. Ad esempio, potresti spostare la rete e la sicurezza di base in blocchi separati. Puoi anche separare i livelli di backend, frontend e database in moduli separati. I moduli sono modelli sviluppati da terze parti, in cui aggiungi solo la configurazione appropriata. A livello di modulo, puoi definire proprietà pertinenti solo per moduli specifici, incluse quelle progettate per sovrascrivere le proprietà ereditate a livello di sistema. I livelli di ambiente e modulo sono divisioni parallele in un sistema, ma i moduli seguono gli ambienti nel processo di unione.

  6. Proprietà dei moduli specifiche per l'ambiente

    Alcune proprietà dei moduli potrebbero dipendere anche dall'ambiente, ad esempio dimensioni delle istanze, immagini, endpoint. Le proprietà del modulo specifiche per l'ambiente sono il livello più specifico e l'ultimo punto dell'unione a cascata per sovrascrivere i valori definiti in precedenza.

Classe helper per unire le 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 questa classe:

  • module = "frontend" specifica il contesto in cui vengono caricati i file di proprietà.
  • L'ambiente viene selezionato automaticamente da context.properties["envName"].
  • La configurazione globale.

    cc = config_merger.ConfigContext(context.properties, module)
    
    print cc.configs['ServiceName']
    

Dietro le quinte, questa classe helper deve allinearsi alle strutture di configurazione, caricare tutti i livelli nell'ordine corretto e sovrascrivere i valori di configurazione appropriati. Per modificare i livelli o l'ordine di sovrascrittura, modifica la classe di unione della configurazione.

Nell'uso quotidiano e di routine, in genere non è necessario toccare questa classe. In genere, modifichi i modelli e i file di configurazione appropriati, quindi utilizzi il dizionario di output con tutte le configurazioni.

La base di codice di esempio contiene i seguenti tre file di configurazione hardcoded:

  • org_config.py
  • department_config.py
  • system_config.py

Puoi creare i file di configurazione dell'organizzazione e del reparto come link simbolici durante l'inizializzazione del repository. Questi file possono risiedere in un repository di codice separato, poiché non fanno parte logicamente della codebase di un team di progetto, ma sono condivisi nell'intera organizzazione e nel reparto.

L'unione della configurazione cerca anche i file corrispondenti 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 le implementazioni.

Quando utilizzi la classe config-merger, le proprietà di configurazione sono completamente separate da questo file di configurazione perché non lo utilizzi. Utilizzi invece una raccolta di file Python, che ti offre molta più flessibilità in un deployment. Questi file possono essere condivisi anche tra le implementazioni.

Qualsiasi file Python può contenere variabili, il che ti consente di archiviare la configurazione in modo strutturato, ma distribuito. L'approccio migliore è utilizzare dizionari con una struttura concordata. L'unione della configurazione cerca un dizionario chiamato configs in ogni file della catena di unione. Questi elementi separati configs vengono uniti in un unico elemento.

Durante l'unione, quando una proprietà con lo stesso percorso e nome viene visualizzata più volte nei dizionari, l'unione della configurazione sovrascrive la proprietà. In alcuni casi, questo comportamento è utile, ad esempio quando un valore predefinito viene sovrascritto da un valore specifico per il contesto. Tuttavia, ci sono molti altri casi in cui vuoi evitare di sovrascrivere la proprietà. Per evitare la sovrascrittura di una proprietà, aggiungi uno spazio dei nomi separato per renderla unica. Nell'esempio seguente aggiungi uno spazio dei nomi creando un livello aggiuntivo nel dizionario di configurazione, che crea un dizionario secondario.

config = {
    'Zip_code': '1234'
    'Count': '3'
    'project_module': {
        'admin': 'Joe',
    }
}

config = {
    'Zip_code': '5555'
    'Count': '5'
    'project_module_prod': {
        'admin': 'Steve',
    }
}

Classi helper 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