Approccio di sistema alle variabili in Ansible

stile di codice devops ansible

EHI! Mi chiamo Denis Kalyuzhny Lavoro come ingegnere nel dipartimento di automazione del processo di sviluppo. Ogni giorno, nuove build di applicazioni vengono implementate su centinaia di server delle campagne. E in questo articolo condivido la mia esperienza nell'utilizzo di Ansible per questi scopi.

Questa guida offre un modo per organizzare le variabili nella distribuzione. Questa guida è destinata a coloro che già utilizzano i ruoli nei propri playbook e l'hanno letta Migliori pratiche, ma affronta problemi simili:

  • Avendo trovato una variabile nel codice, è impossibile capire subito di cosa sia responsabile;
  • Esistono diversi ruoli e le variabili devono essere associate a un valore, ma semplicemente non funziona;
  • Avere difficoltà a spiegare agli altri come funziona la logica delle variabili nei tuoi playbook

Abbiamo riscontrato questi problemi nei progetti della nostra azienda, a seguito dei quali siamo giunti a regole per la progettazione delle variabili nei nostri playbook, che in una certa misura hanno risolto questi problemi.

Approccio di sistema alle variabili in Ansible

Variabili nei ruoli

Un ruolo è un oggetto separato del sistema di distribuzione. Come ogni oggetto di sistema, deve avere un'interfaccia per l'interazione con il resto del sistema. Tale interfaccia è rappresentata dalle variabili di ruolo.

Prendiamo ad esempio il ruolo api, che installa un'applicazione Java sul server. Quali variabili potrebbe avere?

Approccio di sistema alle variabili in Ansible

I ruoli variabili possono essere suddivisi in 2 tipologie in base alla tipologia:

1. Свойства
    a) независимые от среды
    б) зависимые от среды
2. Связи
    a) слушатели 
    б) запросы внутри системы
    в) запросы в среду

Proprietà variabili sono variabili che determinano il comportamento di un ruolo.

Variabili di interrogazione - si tratta di variabili il cui valore viene utilizzato per designare risorse esterne al ruolo.

Ascoltatori variabili - si tratta di variabili il cui valore viene utilizzato per formare variabili di richiesta.

D'altra parte, 1a, 2a, 2b sono variabili che non dipendono dall'ambiente (hardware, risorse esterne, ecc.) e possono essere riempite con valori predefiniti nel ruolo defaults. Tuttavia, è impossibile riempire le variabili di tipo 1.b e 2.c con valori diversi da "esempio", poiché cambieranno da uno stato all'altro a seconda dell'ambiente.

Stile del codice

  • Il nome della variabile deve iniziare con il nome del ruolo. Ciò renderà più semplice capire in futuro da quale ruolo proviene la variabile e di cosa è responsabile.
  • Quando si utilizzano variabili nei ruoli, è necessario assicurarsi di seguire il principio di incapsulamento e utilizzare variabili definite nel ruolo stesso o nei ruoli da cui dipende quello corrente.
  • Evitare l'uso di dizionari per le variabili. Ansible non ti consente di sovrascrivere comodamente i singoli valori in un dizionario.

    Esempio di variabile errata:

    myrole_user:
        login: admin
        password: admin

    Qui login è la variabile indipendente e password è la variabile dipendente. Ma
    poiché sono riuniti in un dizionario, dovrai specificarlo completamente
    Sempre. Il che è molto scomodo. Meglio così:

    myrole_user_login: admin
    myrole_user_password: admin

Variabili nei playbook di distribuzione

Quando compiliamo un playbook di distribuzione (di seguito denominato playbook), rispettiamo la regola secondo cui deve essere inserito in un repository separato. Lo stesso dei ruoli: ciascuno nel proprio repository git. Ciò consente di comprendere che i ruoli e il playbook sono oggetti diversi e indipendenti del sistema di distribuzione e che le modifiche apportate a un oggetto non dovrebbero influire sul funzionamento dell'altro. Ciò si ottiene modificando i valori predefiniti delle variabili.

Quando si compila un playbook, per riassumere, è possibile sovrascrivere i valori predefiniti delle variabili di ruolo in due punti: nelle variabili del playbook e nelle variabili dell'inventario.

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   └── myapi.yml               # Файл переменных свойств группы myapi
└── inventories                 #
    └── prod                    # Каталог окружения prod
        ├── prod.ini            # Инвентори файл
        └── group_vars          # Каталог для переменных инвентори
            └── myapi           #
                ├── vars.yml    # Средозависимые переменные группы myapi
                └── vault.yml   # Секреты (всегда средозависимы) *

* - Variabili e vault

La differenza è che le variabili del playbook vengono sempre utilizzate quando si chiamano playbook situati allo stesso livello. Ciò significa che queste variabili sono ottime per modificare i valori predefiniti delle variabili indipendenti dall'ambiente. Al contrario, le variabili di inventario verranno utilizzate solo per un ambiente specifico, il che è ideale per le variabili specifiche dell'ambiente.

È importante notare che la priorità variabile non ti consentirà di sovrascrivere le variabili prima nelle variabili del playbook e poi separatamente in un inventario.

Ciò significa che già in questa fase è necessario decidere se la variabile dipende dall'ambiente o meno e posizionarla nel posto appropriato.

Ad esempio, in un progetto, la variabile responsabile dell'abilitazione di SSL è dipesa per molto tempo dall'ambiente, poiché in uno degli stand non abbiamo potuto abilitare SSL per motivi indipendenti dalla nostra volontà. Dopo aver risolto questo problema, è diventato indipendente dall'ambiente e è passato alle variabili del playbook.

Variabili di proprietà per i gruppi

Espandiamo il nostro modello nella Figura 1 aggiungendo 2 gruppi di server con un'applicazione Java diversa, ma con impostazioni diverse.

Approccio di sistema alle variabili in Ansible

Immaginiamo come sarà il playbook in questo caso:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

Abbiamo tre gruppi nel playbook, quindi è consigliabile creare immediatamente lo stesso numero di file di gruppo nelle variabili di inventario group_vars e nelle variabili del playbook. Un file di gruppo in questo caso è una descrizione di un componente dell'applicazione precedente nel playbook. Quando apri un file di gruppo nelle variabili del playbook, vedi immediatamente tutte le differenze rispetto al comportamento predefinito dei ruoli installati sul gruppo. Nelle variabili di inventario: differenze nel comportamento del gruppo da stand a stand.

Stile del codice

  • Cerca di non utilizzare affatto le variabili host_vars, poiché non descrivono il sistema, ma solo un caso speciale, che in futuro porterà a domande: "Perché questo host è diverso dagli altri?", La cui risposta non è sempre facile da trovare.

Variabili di comunicazione

Tuttavia, questo è ciò che riguarda le variabili di proprietà, ma per quanto riguarda le variabili di comunicazione?
La loro differenza è che dovrebbero avere lo stesso significato in gruppi diversi.

All'inizio lo era идея utilizzare una costruzione mostruosa come:
hostvars[groups['bbauth'][0]]['auth_bind_port'], ma lo respinsero immediatamente
perché presenta degli svantaggi. Innanzitutto l'ingombro. In secondo luogo, la dipendenza da un host specifico nel gruppo. In terzo luogo, prima di iniziare la distribuzione, è necessario raccogliere i dati da tutti gli host se non vogliamo ricevere un errore da una variabile non definita.

Di conseguenza, si è deciso di utilizzare variabili di comunicazione.

Variabili di comunicazione - queste sono variabili che appartengono al playbook e sono necessarie per connettere gli oggetti di sistema.

Le variabili di comunicazione vengono popolate nelle variabili di sistema generali group_vars/all/vars e sono formati rimuovendo tutte le variabili del listener da ciascun gruppo e aggiungendo il nome del gruppo da cui il listener è stato rimosso all'inizio della variabile.

Ciò garantisce l'uniformità e la non sovrapposizione dei nomi.

Proviamo a associare le variabili dell'esempio sopra:

Approccio di sistema alle variabili in Ansible

Immaginiamo di avere variabili che dipendono l'una dall'altra:

# roles/api/defaults:
# Переменная запроса
api_auth1_address: "http://example.com:80"
api_auth2_address: "http://example2.com:80"

# roles/auth/defaults:
# Переменная слушатель
auth_bind_port: "20000"

Mettiamolo in variabili comuni group_vars/all/vars tutti gli ascoltatori e aggiungi il nome del gruppo al titolo:

# group_vars/all/vars
bbauth_auth_bind_port: "20000"
ghauth_auth_bind_port: "30000"

# group_vars/bbauth/vars
auth_bind_port: "{{ bbauth_auth_bind_port }}"

# group_vars/ghauth/vars
auth_bind_port: "{{ ghauth_auth_bind_port }}"

# group_vars/myapi/vars
api_auth1_address: "http://{{ bbauth_auth_service_name }}:{{ bbauth_auth_bind_port }}"
api_auth2_address: "http://{{ ghauth_auth_service_name }}:{{ ghauth_auth_bind_port }}"

Ora, modificando il valore del connettore, saremo sicuri che la richiesta arriverà nello stesso luogo in cui si trova la porta.

Stile del codice

  • Poiché ruoli e gruppi sono oggetti di sistema diversi, devono avere nomi diversi, quindi le variabili di collegamento indicheranno accuratamente che appartengono a un gruppo specifico di server e non a un ruolo nel sistema.

File dipendenti dall'ambiente

I ruoli possono utilizzare file che differiscono da un ambiente all'altro.

Un esempio di tali file sono i certificati SSL. Memorizzarli in formato testo
in una variabile non è molto conveniente. Ma è conveniente memorizzare il percorso all'interno di una variabile.

Ad esempio, utilizziamo la variabile api_ssl_key_file: "/path/to/file".

Poiché è ovvio che il certificato della chiave cambierà da un ambiente all'altro, questa è una variabile dipendente dall'ambiente, il che significa che dovrebbe trovarsi nel file
group_vars/myapi/vars inventario delle variabili e contengono il valore "per esempio".

Il modo più conveniente in questo caso è inserire il file chiave nel repository del playbook lungo il percorso
files/prod/certs/myapi.key, allora il valore della variabile sarà:
api_ssl_key_file: "prod/certs/myapi.key". La comodità sta nel fatto che anche le persone responsabili dell'installazione del sistema in uno specifico stand hanno uno spazio dedicato nel repository dove archiviare i propri file. Allo stesso tempo rimane possibile specificare il percorso assoluto del certificato sul server, nel caso in cui i certificati vengano forniti da un altro sistema.

Più stand in un unico ambiente

Spesso è necessario schierare più stand quasi identici nello stesso ambiente con differenze minime. In questo caso, dividiamo le variabili dipendenti dall’ambiente in quelle che non cambiano all’interno di questo ambiente e quelle che cambiano. E trasferiamo quest'ultimo direttamente nei file di inventario stessi. Dopo questa manipolazione diventa possibile creare un altro inventario direttamente nella directory dell'ambiente.

Riutilizzerà l'inventario group_vars e sarà anche in grado di ridefinire alcune variabili direttamente per se stesso.

La struttura di directory finale per il progetto di distribuzione:

mydeploy                        # Каталог деплоя
├── deploy.yml                  # Плейбук деплоя
├── files                       # Каталог для файлов деплоя
│   ├── prod                    # Католог для средозависимых файлов стенда prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Каталог для средозависимых файлов стенда test1
├── group_vars                  # Каталог переменных плейбука
│   ├── all.yml                 # Файл для переменных связи всей системы
│   ├── myapi.yml               # Файл переменных свойств группы myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Каталог окружения prod
    │   ├── group_vars          # Каталог для переменных инвентори
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Средозависимые переменные группы myapi
    │   │   │   └── vault.yml   # Секреты (всегда средозависимы)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Инвентори стенда prod
    └── test                    # Каталог окружения test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Инвентори стенда test1 в среде test
        └── test2.ini           # Инвентори стенда test2 в среде test

Riassumendo

Dopo aver organizzato le variabili secondo l'articolo: ogni file variabile è responsabile di un compito specifico. E poiché il file ha determinati compiti, è diventato possibile assegnare qualcuno responsabile della correttezza di ciascun file. Ad esempio, lo sviluppatore dell'implementazione del sistema diventa responsabile del corretto riempimento delle variabili del playbook, mentre l'amministratore la cui posizione è descritta nell'inventario è direttamente responsabile del riempimento dell'inventario delle variabili.

I ruoli sono diventati una propria unità di sviluppo con una propria interfaccia, consentendo allo sviluppatore del ruolo di sviluppare capacità anziché adattare il ruolo al sistema. Questo problema riguardava soprattutto i ruoli comuni per tutti i sistemi nella campagna.

Gli amministratori di sistema non hanno più bisogno di comprendere il codice di distribuzione. Tutto ciò che è loro richiesto per una distribuzione di successo è compilare i file delle variabili dipendenti dall'ambiente.

Letteratura

  1. Documentazione

autore

Kalyuzhny Denis Alexandrovich

Fonte: habr.com

Aggiungi un commento