Approccio sistemico alle variabili in Ansible

ansible devops codestyle

Ciao! Mi chiamo Denis Kaljužnyj lavoro come ingegnere nel dipartimento di automazione dei processi di sviluppo. Ogni giorno nuove versioni delle applicazioni vengono distribuite su centinaia di server dell'azienda. In questo articolo condivido la mia esperienza nell'utilizzo di Ansible per questi scopi.

Questa guida propone un modo di organizzare le variabili nel deployment. È destinata a chi già utilizza ruoli nei propri playbook e ha letto BestPractices, ma si trova di fronte a problemi simili:

  • Trovare una variabile nel codice, non è immediato capire a cosa serve;
  • Ci sono diversi ruoli, e le variabili devono essere collegate a un valore comune, ma non ci riesco;
  • Ci sono difficoltà nel spiegare agli altri come è strutturata la logica delle variabili nei vostri playbook.

Abbiamo affrontato questi problemi nei progetti della nostra azienda, da cui siamo giunti a delle regole per la definizione delle variabili nei nostri playbook, che in qualche modo hanno risolto queste problematiche.

Approccio sistemico alle variabili in Ansible

Variabili nei ruoli

Un ruolo è un Oggetto autonomo del sistema di deployment. Come ogni oggetto del sistema, deve avere un'interfaccia di interazione con il resto del sistema. Questa interfaccia è fornita dalle variabili di ruolo.

Prendiamo, per esempio, un ruolo api, che installa un'applicazione Java su un server. Quali variabili potrebbe avere?

Approccio sistemico alle variabili in Ansible

Le variabili di ruolo possono essere suddivise in 2 categorie per tipo:

1. Proprietà
    a) indipendenti dall'ambiente
    b) dipendenti dall'ambiente
2. Relazioni
    a) ascoltatori 
    b) richieste all'interno del sistema
    c) richieste all'ambiente

Le variabili di proprietà sono variabili che definiscono il comportamento del ruolo.

Le variabili di richiesta sono variabili il cui valore viene utilizzato per identificare risorse esterne, rispetto al ruolo.

Le variabili ascoltatori sono variabili il cui valore è utilizzato per formare le 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 defaults del ruolo. Tuttavia, le variabili di tipo 1b e 2c non possono essere riempite con valori diversi da 'example', poiché cambiano da stand a stand in base all'ambiente.

Stile di codice

  • Il nome della variabile deve necessariamente iniziare con il nome del ruolo. Questo permetterà in seguito di capire facilmente da quale ruolo proviene la variabile e cosa rappresenta.
  • Quando si utilizzano variabili nei ruoli, è necessario seguire il principio di incapsulamento e utilizzare variabili definite o nel ruolo stesso o nei ruoli da cui quello attuale dipende.
  • Cerca di non utilizzare dizionari per le variabili. Ansible non consente di ridefinire facilmente singoli valori all'interno di un dizionario.

    Esempio di cattiva variabile:

    myrole_user:
        login: admin
        password: admin

    Qui login è una variabile indipendente dal contesto, mentre password è dipendente. Ma
    poiché sono combinate in un dizionario, dovrai definirla completamente
    ogni volta. Il che è molto scomodo. Meglio così:

    myrole_user_login: admin
    myrole_user_password: admin

Variabili nei playbook di deploy

Durante la creazione di un playbook di deploy (d'ora in poi playbook), ci atteniamo alla regola che deve essere collocato in un repository separato. Così come i ruoli: ognuno nel proprio repository git. Questo permette di comprendere che ruoli e playbook sono oggetti indipendenti del sistema di deploy e che le modifiche a un oggetto non devono influenzare il funzionamento dell'altro. Questo si ottiene modificando i valori predefiniti delle variabili.

Nella stesura del playbook, per sintetizzare, esiste la possibilità di ridefinire i valori predefiniti delle variabili di ruolo in due luoghi: nelle variabili del playbook e nelle variabili di inventario.

mydeploy                        # Directory di deploy
├── deploy.yml                  # Playbook di deploy
├── group_vars                  # Directory delle variabili del playbook
│   ├── all.yml                 # File per le variabili di tutti i sistemi
│   └── myapi.yml               # File per le variabili delle proprietà del gruppo myapi
└── inventories                 #
    └── prod                    # Directory dell'ambiente prod
      ├── prod.ini              # File di inventario
      └── group_vars            # Directory per le variabili di inventario
          └── myapi             #
              ├── vars.yml      # Variabili indipendenti dalle varie condizioni del gruppo myapi
              └── vault.yml     # Segreti (sempre indipendenti dalle condizioni) *

* — Variabili e Vaults

La differenza è che le variabili del playbook vengono sempre utilizzate quando vengono richiamati playbook situati allo stesso livello. E quindi, queste variabili sono perfette per modificare i valori predefiniti delle variabili non dipendenti dal contesto. E viceversa, le variabili di inventario verranno utilizzate solo per ambienti specifici, il che è ideale per variabili che dipendono dal contesto.

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

Ciò significa che già a questo punto è necessario decidere se la variabile è dipendente dall'ambiente o meno e collocarla nel posto giusto.

Ad esempio, in un progetto la variabile che controlla l'attivazione di SSL è stata a lungo dipendente dall'ambiente, poiché non siamo riusciti ad attivare SSL per motivi indipendenti da noi in una delle postazioni. Dopo che abbiamo risolto questo problema, è diventata indipendente dall'ambiente e si è spostata nelle variabili del playbook.

Variabili di proprietà per gruppi

Espandiamo il nostro modello nel disegno 1, aggiungendo 2 gruppi di server con una diversa applicazione Java, ma con impostazioni diverse.

Approccio sistemico alle variabili in Ansible

Immaginiamo come apparirà il playbook in questo caso:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

Abbiamo tre gruppi nel playbook, quindi è consigliato creare altrettanti file di gruppo in group_vars delle variabili di inventario e del playbook. Un file di gruppo in questo caso è la descrizione di un componente della vostra applicazione principale nel playbook. Aprendo il file di gruppo nelle variabili del playbook, vedrete subito tutte le differenze dal comportamento predefinito dei ruoli installati nel gruppo. Nelle variabili di inventario: differenze nel comportamento del gruppo da postazione a postazione.

Stile del codice

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

Variabili di collegamento

Tuttavia, questo riguarda le variabili di proprietà, ma come ci si deve comportare con le variabili di collegamento?
La loro differenza sta nel fatto che devono avere lo stesso valore in gruppi diversi.

Inizialmente c'era un'idea di utilizzare una costruzione mostruosa del tipo:
hostvars[groups['bbauth'][0]]['auth_bind_port'], ma si è subito rinunciato
poiché presenta degli svantaggi. Prima di tutto, la complessità. In secondo luogo, la dipendenza da un host specifico nel gruppo. In terzo luogo, è necessario raccogliere i dati da tutti gli host prima dell'inizio del deploy, se non vogliamo ottenere un errore di variabile non definita.

In definitiva si è deciso di utilizzare le variabili di collegamento.

Variabili di collegamento — sono variabili che appartengono al playbook e necessarie per collegare gli oggetti del sistema.

Le variabili di connessione vengono compilate nelle variabili comuni del sistema group_vars/all/vars e si formano estraendo tutte le variabili degli ascoltatori da ciascun gruppo, aggiungendo all'inizio della variabile il nome del gruppo da cui è stato estratto l'ascoltatore.

In questo modo si garantisce uniformità e non sovrapposizione dei nomi.

Proviamo a collegare le variabili dell'esempio precedente:

Approccio sistemico 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"

Estrarre nelle variabili comuni group_vars/all/vars tutti gli ascoltatori e aggiungere nel nome il nome del gruppo:

# 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, cambiando il valore del connettore, saremo sicuri che la richiesta si riferisca lì dove è situata la porta.

Stile del codice

  • Poiché ruoli e gruppi sono oggetti diversi nel sistema, è necessario che abbiano nomi diversi, affinché le variabili di connessione mostrino chiaramente a quale gruppo di server appartengono e non a un ruolo nel sistema.

File dipendenti dall'ambiente

Nei ruoli possono essere utilizzati file che differiscono da ambiente a ambiente.

Un esempio di tali file può essere rappresentato dai certificati SSL. È poco conveniente conservarli in formato di testo
in una variabile. È invece pratico conservare il percorso all'interno della variabile.

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

Poiché è evidente che il certificato della chiave cambierà da ambiente a ambiente, questa è una variabile dipendente dall'ambiente, ed è quindi necessario collocarla nel file
group_vars/myapi/vars inventario delle variabili, e contenere il valore 'per esempio'.

In questo caso, è più conveniente posizionare il file della chiave nel repository dell'playbook al percorso
files/prod/certs/myapi.key, quindi il valore della variabile sarà:
api_ssl_key_file: "prod/certs/myapi.key". La comodità sta nel fatto che le persone responsabili della distribuzione del sistema su un determinato stand hanno anche un posto dedicato nel repository per conservare i propri file. Allo stesso tempo, rimane la possibilità di indicare un percorso assoluto al certificato sul server, nel caso in cui i certificati siano forniti da un altro sistema.

Diversi stand in un'unica ambiente

Spesso sorge la necessità di distribuire più stand praticamente identici all'interno di un ambiente con minime differenze. In questo caso, suddividiamo le variabili dipendenti dall'ambiente in quelle che rimangono invariate all'interno di questo ambiente e quelle che cambiano. E portiamo queste ultime direttamente nei file di inventario. Dopo quest'operazione, diventa possibile creare un altro inventario direttamente nella directory dell'ambiente.

Riutilizzerà le variabili di gruppo dell'inventario e avrà anche la possibilità di ridefinire alcune variabili direttamente per le proprie esigenze.

Struttura finale delle directory per il progetto di deploy:

mydeploy                        # Directory di deploy
├── deploy.yml                  # Playbook di deploy
├── files                       # Directory per i file di deploy
│   ├── prod                    # Directory per i file dipendenti dall'ambiente dello stand prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Directory per i file dipendenti dall'ambiente dello stand test1
├── group_vars                  # Directory delle variabili del playbook
│   ├── all.yml                 # File per le variabili relative all'intero sistema
│   ├── myapi.yml               # File per le variabili delle proprietà del gruppo myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Directory dell'ambiente prod
    │   ├── group_vars          # Directory per le variabili dell'inventario
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Variabili dipendenti dall'ambiente del gruppo myapi
    │   │   │   └── vault.yml   # Segreti (sempre dipendenti dall'ambiente)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Inventario dello stand prod
    └── test                    # Directory dell'ambiente test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Inventario dello stand test1 nell'ambiente test
        └── test2.ini           # Inventario dello stand test2 nell'ambiente test

Sintesi finale

Dopo aver organizzato le variabili secondo l'articolo: ogni file con variabili è responsabile di un compito specifico. E poiché il file ha compiti specifici, è stato possibile nominare un responsabile per la correttezza di ogni file. Ad esempio, il responsabile della correttezza della compilazione delle variabili del playbook è il developer del deployment del sistema, mentre il responsabile della compilazione delle variabili di inventario è direttamente l'amministratore il cui ambiente è descritto nell'inventario.

I ruoli sono diventati un'unità autonoma di sviluppo con la propria interfaccia, il che ha permesso al developer di ruolo di sviluppare funzionalità, anziché adattare il ruolo al sistema. Questo problema riguardava in particolare i ruoli generali per tutti i sistemi nella campagna.

Gli amministratori dei sistemi non devono più comprendere il codice di deployment. Tutto ciò che è richiesto da loro per un deployment di successo è compilare i file delle variabili dipendenti dall'ambiente.

Letteratura

  1. Documentazione

Autore

Kalyuzhny Denis Alexandrovich

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster