Strutturazione di Deployment Manager per l'utilizzo su larga scala

Quando il tuo account "Infrastructure as Code" va oltre "Hello World" Ad esempio, senza pianificare, il codice tende a diventare non strutturato. Non pianificata sono impostate come hardcoded. La manutenibilità si riduce drasticamente.

Utilizza questo documento, insieme all'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 ai tuoi team. Questo documento è rivolto a un pubblico tecnicamente avanzato e presuppone che tu abbia una conoscenza di base di Python, dell'infrastruttura Google Cloud, di Deployment Manager e, in generale, di Infrastructure as Code.

Prima di iniziare

Più ambienti con un unico codebase

Per deployment di grandi dimensioni con più di una decina di risorse, le best practice standard per utilizzare una quantità significativa di proprietà esterne (configurazione parametri), in modo da evitare l'hardcoded di stringhe e la logica in modo da modelli di machine learning. Molte di queste proprietà sono parzialmente duplicate a causa di ambienti simili, come ambienti di sviluppo, test o produzione, e servizi simili. Ad esempio, tutti i servizi standard sono in esecuzione su un modello LAMP simile stack. Il rispetto di queste best practice comporta un ampio insieme di proprietà di configurazione con un'elevata quantità di duplicazioni che possono diventare difficili da gestire, aumentando così la possibilità di errori umani.

La tabella seguente è un esempio di codice per illustrare le differenze tra configurazione gerarchica o singola per ogni deployment. La tabella evidenzia una duplicazione comune in un'unica configurazione. Usando l'architettura configurazione, la tabella mostra come spostare le sezioni che si ripetono a un livello superiore della gerarchia per evitare ripetizioni e ridurre le probabilità di errori umani.

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, utilizza più file per la configurazione anziché uno solo. Inoltre, utilizzi le funzioni helper e condividere parte del codebase all'interno dell'organizzazione.

L'esempio di codice allegato al documento Organization_with_departments, contiene un codebase con una struttura predefinita ma personalizzabile, per unire le configurazioni, le funzioni helper per la denominazione e un set completo di configurazioni di esempio. Puoi trovare questo esempio funzionante nel Repository GitHub di esempio di Deployment Manager.

La strutturazione e la definizione di una struttura gerarchica del codice offrono diversi vantaggi:

  • Suddividere la configurazione in più file migliora il la struttura e la leggibilità delle proprietà. Inoltre, eviti di duplicarli.
  • Progetta l'unione gerarchica per applicare i valori a cascata in modo logico, creando file di configurazione di primo livello riutilizzabili in vari progetti componenti.
  • Devi definire ciascuna proprietà una sola volta (tranne le sovrascritture), evitando così di dover per gestire lo spazio dei nomi nei nomi delle proprietà.
  • Non è necessario che i modelli conoscano l'ambiente effettivo, poiché venga caricata la configurazione appropriata in base alle variabili appropriate.

Strutturare il codebase in modo gerarchico

Un deployment di Deployment Manager contiene una configurazione YAML o un file schema, oltre a diversi file Python. Insieme, questi file formano codebase di un deployment. I file Python possono servire diversi scopi. Puoi Utilizzare i file Python come modelli di deployment, come file di codice generali (helper classi) o come file di codice in cui sono archiviate le proprietà di configurazione.

Per strutturare il codebase in modo gerarchico, utilizza alcuni file Python come di configurazione standard, anziché il file di configurazione standard. Questo approccio offre una maggiore flessibilità rispetto al collegamento del deployment a un singolo .

Trattare l'infrastruttura come vero codice

Un principio importante per il codice pulito è Don't Repeat Yourself (DRY). Definisci tutto una sola volta. Questo approccio rende il codebase più pulito, rivedere, convalidare e semplificare la gestione. Quando una proprietà deve essere modificata in un solo punto, il rischio di errore umano diminuisce.

Per una base di codice più leggera con file di configurazione più piccoli e una 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 la base di codice in modo pulito e gerarchico sono l'utilizzo di organizzazioni, reparti, ambienti e moduli. Questi principi sono facoltativi ed estensionabili. 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 combinazione delle configurazioni seleziona i file di configurazione appropriati su 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

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

  1. Proprietà organizzative

    Si tratta del livello più alto della 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 a tutti i team.

  2. Proprietà del reparto

    Le organizzazioni comprendono reparti, se nel tuo gruppo sono presenti alla struttura del centro di costo. 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 è un software ben definito come la tua piattaforma di e-commerce. Non è un progetto Google Cloud, ma un ecosistema di servizi funzionante.

    A livello di sistema, il team ha molta più autonomia rispetto ai livelli sopra. Qui puoi definire le funzioni helper (come project_name_generator(), instance_name_generator() o instance_label_generator()) per i parametri a livello di team e sistema (per ad esempio system_name, default_instance_size o naming_prefix).

  4. Proprietà dell'ambiente

    È probabile che il tuo sistema abbia più ambienti, come Dev, Test o Prod (e, facoltativamente, QA e Staging), 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 è di grandi dimensioni, suddividilo 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 backend, i livelli di frontend e di database in moduli separati. I moduli sono modelli sviluppati da terze parti, in cui aggiungi solo i configurazione. A livello di modulo, puoi definire le proprietà che pertinenti solo per particolari moduli, incluse le proprietà progettate per le proprietà a livello di sistema ereditate. L'ambiente e il modulo i livelli sono divisioni parallele in un sistema, ma i moduli seguono gli ambienti nel processo di unione.

  6. Proprietà dei moduli specifici dell'ambiente

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

Classe di assistenza per l'unione delle configurazioni

La classe config_merger è una classe helper che carica automaticamente di configurazione appropriati e unisce i relativi contenuti in un'unica 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 della funzione statica ConfigContext restituisce la configurazione unita. dizionario.

Il seguente codice mostra come utilizzare questa classe:

  • module = "frontend" specifica il contesto in cui vengono caricati i file di 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, questo corso di supporto deve allinearsi alla tua configurazione strutture, caricare tutti i livelli nell'ordine corretto e sovrascrivere di configurazione appropriati. Per cambiare i livelli o l'ordine di sovrascrittura: modificherai la classe di unione delle configurazioni.

Nell'uso quotidiano e di routine, solitamente non è necessario toccare questo . In genere, si modificano i modelli e i file di configurazione appropriati, e quindi utilizzare il dizionario di output con tutte le configurazioni al suo interno.

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 trovarsi un repository di codice separato, poiché non fa logicamente ma condiviso nell'intera organizzazione e nel reparto.

L'unione della configurazione cerca anche i file corrispondenti ai livelli rimanenti della tua struttura:

  • envs/[environment name].py
  • [environment name]/[module name].py
  • modules/[module name].py

File di configurazione

Deployment Manager utilizza un unico file di configurazione per un deployment specifico. Non può essere condiviso tra i diversi deployment.

Quando utilizzi la classe config-merger, le proprietà di configurazione vengono è stato scollegato completamente da questo file di configurazione perché non lo stai utilizzando. Puoi invece usare una raccolta di file Python, che offre molte altre flessibilità in un deployment. Questi file possono essere condivisi anche tra i deployment.

Qualsiasi file Python può contenere variabili, il che ti consente di memorizzare la configurazione in modo strutturato, ma distribuito. L'approccio migliore è utilizzare i dizionari con una struttura concordata. La fusione delle configurazioni cerca dizionario chiamato configs in ogni file nella catena di unione. I singoli configs vengono uniti in un unico canale.

Durante l'unione, se una proprietà con lo stesso percorso e nome viene visualizzata più volte nei dizionari, l'unione della configurazione la sovrascrive. In alcuni casi, questo comportamento è utile, ad esempio quando un valore predefinito viene soprascritto da un valore specifico del contesto. Tuttavia, esistono molti altri casi in cui è consigliabile evitare di sovrascrivere la proprietà. Per evitare l'overwriting di una proprietà, aggiungi uno spazio dei nomi separato per renderla univoca. Nel seguente Ad esempio, aggiungi uno spazio dei nomi creando un livello aggiuntivo di configurazione automatica, che crea un sottodizionario.

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

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

Classi di supporto e convenzioni di denominazione

Le convenzioni di denominazione sono il modo migliore per mantenere il tuo Deployment Manager e l'infrastruttura sotto controllo. Non utilizzare nomi vaghi o generici, come my project o test instance.

Il seguente esempio è una convenzione di denominazione per le 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"])

Fornire una funzione di supporto semplifica la denominazione di ogni istanza in base alla convenzione concordata. Semplifica anche la revisione del codice perché non c'è un nome istanza non proviene da questa funzione. La funzione seleziona automaticamente i nomi da configurazioni di livello superiore. Questo approccio aiuta a evitare input non necessario.

Puoi applicare queste convenzioni di denominazione alla maggior parte delle risorse Google Cloud e per le etichette. Funzioni più complesse possono anche generare un insieme di etichette predefinite.

Struttura della cartella del codice di esempio

La struttura delle cartelle del codebase di esempio è flessibile e personalizzabile. Tuttavia, è parzialmente codificato per l'unione delle configurazioni e il file di schema di Deployment Manager, il che significa che se apporti una modifica, devi riflettere queste modifiche nei file di unione delle configurazioni e di schema.

├── 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, una classe helper per i reparti. Puoi aggiungere qualsiasi classe di supporto a livello di organizzazione o sistema.

La cartella globale può trovarsi in un repository Git separato. Puoi fare riferimento ai suoi dei singoli sistemi. Puoi anche usare link simbolici, potrebbero causare confusione o interruzioni in alcuni 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 dei sistemi contiene uno o più sistemi diversi. I sistemi separate l'una dall'altra e non condividono le configurazioni.

├── configs
│   ├── dev
│   ├── envs
│   ├── modules
│   ├── prod
│   └── test
├── helper
└── templates

La cartella di configurazione contiene tutti i file di configurazione univoci a questo sistema, facendo riferimento anche alle configurazioni globali .

├── 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 altre classi helper e fare riferimento al classi globali.

├── config_merger.py -> ../../../global/helper/config_merger.py
└── naming_helper.py -> ../../../global/helper/naming_helper.py

Nella cartella templates, puoi archiviare o fare riferimento a Deployment Manager modelli di machine learning. Anche i link simbolici funzionano qui.

├── project_creation -> ../../../../../../examples/v2/project_creation
└── simple_frontend.py

Utilizzo della base di codice di esempio

Il modo migliore per iniziare ad applicare questa prassi gerarchica come base Infrastructure as Code consiste nel clonare il codebase di esempio e copiare i contenuti nella cartella ger_configuration.

  1. 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
    
  2. Per configurare il sistema, utilizza 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
    
  3. Seleziona il reparto appropriato dall'elenco globale.

    ln -sf ../../../global/configs/Department_Data_config.py configs/department_config.py
    
  4. Per impostare il contesto corretto per l'ambiente, utilizza il flag --properties per specificare la proprietà envName. Questa proprietà ti consente di eseguire lo stesso codice, con targeting di diversi ambienti, 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, è un requisito di Deployment Manager elencare ogni file in qualsiasi modo durante il deployment. L'aggiunta di un'intera cartella rende il codice più breve e generico.

  • Classi di supporto:
- path: helper/*.py
  • File di configurazione:
- path: configs/*.py
- path: configs/*/*.py
  • Importazioni collettive (in stile glob)
gcloud config set deployment_manager/glob_imports True

Deployment multipli

È una best practice che un sistema contenga più deployment, ovvero che usano gli stessi insiemi di configurazioni, anche se sono moduli diversi, ad esempio networking, firewall, backend, frontend. Potresti dover accedere all'output di questi implementazioni da un altro deployment. Puoi eseguire query sull'output del deployment quando è pronto e salvarlo nella cartella delle configurazioni. Puoi aggiungere questi file di configurazione durante il processo di unione.

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 di configurazione

Il seguente diagramma è una panoramica dei diversi livelli e delle relative relazioni. Ogni rettangolo rappresenta un file di proprietà, come indicato dal nome del file in rosso.

Gerarchia di configurazione con diversi livelli e le relative relazioni evidenziate.

Ordine di unione sensibile al contesto

L'unione delle configurazioni seleziona i file di configurazione appropriati su 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 seguente diagramma, i numeri rappresentano l'ordine di sovrascrittura nella gerarchia:

Diagramma dell'ordine di sovrascrittura

Passaggi successivi