Abordarea sistemică a variabilelor în Ansible

stilul de cod DevOps Ansible

Salut! Numele meu este Denis Kaliuzhny lucrez ca inginer în departamentul de automatizare a proceselor de dezvoltare. În fiecare zi, noi versiuni de aplicații sunt desfășurate pe sute de servere ale companiei. În acest articol, împărtășesc experiența mea cu Ansible pentru aceste scopuri.

Acest ghid oferă o modalitate de organizare a variabilelor în desfășurare. Ghidul este destinat celor care deja folosesc roluri în playbook-urile lor și au citit BestPractices, dar se confruntă cu probleme de acest tip:

  • Când găsești o variabilă în cod, nu este imediat clar ce face aceasta;
  • Sunt mai multe roluri, iar variabilele trebuie să fie asociate cu o singură valoare, dar nu se poate realiza;
  • Există dificultăți în a explica altora cum este organizată logica variabilelor în playbook-urile tale.

Am întâmpinat aceste probleme în proiectele din compania noastră, ceea ce ne-a condus la reguli de organizare a variabilelor în playbook-urile noastre, care au rezolvat într-o oarecare măsură aceste probleme.

Abordarea sistemică a variabilelor în Ansible

Variabile în roluri

Rolul este un obiect separat al sistemului de desfășurare. Ca orice obiect din sistem, acesta trebuie să aibă o interfață de interacțiune cu restul sistemului. Această interfață este reprezentată de variabilele rolului.

Să luăm ca exemplu rolul api, care instalează o aplicație Java pe server. Ce variabile ar putea avea?

Abordarea sistemică a variabilelor în Ansible

Variabilele rolului pot fi împărțite în 2 tipuri:

1. Proprietăți
    a) independente de mediu
    b) dependente de mediu
2. Relații
    a) ascultători 
    b) cereri interne în sistem
    c) cereri în mediu

Variabilele proprietate – sunt variabile care determină comportamentul rolului.

Variabilele cerere – sunt variabile a căror valoare este utilizată pentru a indica resurse externe, în raport cu rolul.

Variabilele ascultător – sunt variabile a căror valoare este utilizată pentru a forma variabilele cererei.

Pe de altă parte, 1a, 2a, 2b – acestea sunt variabile care nu depind de mediu (hardware, resurse externe etc.) și pot fi completate cu valori implicite în defaults rolului. Totuși, variabilele de tip 1.b și 2.c nu pot fi completate cu altceva decât cu ‘example’, deoarece acestea vor varia de la stand la stand, în funcție de mediu.

Stilul de cod

  • Numele variabilei trebuie să înceapă întotdeauna cu numele rolului. Acest lucru va permite mai târziu să te descurci ușor cu care rol este asociată variabila și ce responsabilitate are.
  • Când utilizați variabile în roluri, trebuie să respectați principiul încapsulării și să folosiți variabilele definite fie în rolul în sine, fie în rolurile de la care depinde rolul curent.
  • Încercați să nu folosiți dicționare pentru variabile. Ansible nu permite o redefinire convenabilă a valorilor individuale în dicționar.

    Exemplu de variabilă proastă:

    myrole_user:
        login: admin
        password: admin

    Aici login este o variabilă independentă de mediu, în timp ce password este dependentă. Dar
    deoarece acestea sunt combinate într-un dicționar, va trebui să o definiți complet
    întotdeauna. Ceea ce este foarte incomod. Mai bine astfel:

    myrole_user_login: admin
    myrole_user_password: admin

Variabilele în playbook-urile de desfășurare

Când compunem un playbook de desfășurare (denumit în continuare playbook), ne respectăm regula că acesta trebuie să fie plasat într-un depozit separat. La fel ca și rolurile: fiecare într-un depozit git. Acest lucru permite conștientizarea faptului că rolurile și playbook-ul sunt obiecte independente ale sistemului de desfășurare, iar modificările într-un obiect nu ar trebui să afecteze funcția celuilalt. Acest lucru se realizează prin modificarea valorilor implicite ale variabilelor.

Când compunem playbook-ul, pentru a generaliza, există posibilitatea de a redefini valorile implicite ale variabilelor rolului în două locuri: în variabilele playbook-ului și în variabilele de inventar.

mydeploy                        # Catalogul desfășurării
├── deploy.yml                  # Playbook-ul de desfășurare
├── group_vars                  # Catalogul variabilelor playbook-ului
│   ├── all.yml                 # Fișier pentru variabilele care leagă întreaga sistem
│   └── myapi.yml               # Fișierul variabilelor pentru grupul myapi
└── inventories                 #
    └── prod                    # Catalogul mediului prod
        ├── prod.ini            # Fișierul de inventar
        └── group_vars          # Catalogul pentru variabilele de inventar
            └── myapi           #
                ├── vars.yml    # Variabile independente de mediu ale grupului myapi
                └── vault.yml   # Secrete (întotdeauna independente de mediu) *

* — Variabile și Secrete

Diferența este că variabilele playbook-ului sunt utilizate întotdeauna la apelarea playbook-urilor situate cu el la același nivel. Prin urmare, aceste variabile sunt ideale pentru modificarea valorilor implicite ale variabilelor, care nu depind de mediu. Pe de altă parte, variabilele de inventar vor fi utilizate doar pentru un mediu specific, ceea ce este ideal pentru variabilele care depind de mediu.

Este important de menționat că prioritatea variabilelor nu vă va permite să redefineți variabilele mai întâi în variabilele playbook-ului și apoi separat într-un inventar.

Aceasta înseamnă că, deja în acest stadiu, trebuie să decideți dacă variabila este dependentă de mediu sau nu și să o plasați în locul corespunzător.

De exemplu, într-un proiect, variabila care activează SSL a fost mult timp dependentă de mediu, deoarece nu am putut activa SSL din motive independente de noi pe una dintre platforme. După ce am rezolvat această problemă, ea a devenit independentă de mediu și a fost mutată în variabilele playbook-ului.

Variabilele proprietăților pentru grupuri

Să extindem modelul nostru din figura 1, adăugând 2 grupuri de servere cu o altă aplicație Java, dar cu setări diferite.

Abordarea sistemică a variabilelor în Ansible

Să ne imaginăm cum va arăta playbook-ul în acest caz:

- hosts: myapi
  roles:
    - api

- hosts: bbauth
  roles:
    - auth

- hosts: ghauth
  roles:
    - auth

Avem trei grupuri în playbook, așa că se recomandă imediat să creați atâtea fișiere de grup în group_vars ale variabilelor din inventar și ale variabilelor playbook-ului. Un fișier de grup în acest caz este o descriere a unei componente a aplicației dvs. în playbook. Deschizând fișierul grupului din variabilele playbook-ului, vedeți imediat toate diferențele față de comportamentul implicit al rolurilor instalate pe grup. În variabilele din inventar: diferențele de comportament ale grupului de la platformă la platformă.

Cod de stil

  • Încercați să nu utilizați deloc variabilele host_vars, deoarece acestea nu descriu sistemul, ci doar un caz particular, ceea ce, pe termen lung, va duce la întrebări: "De ce acest host diferă de ceilalți?", la care răspunsul nu este întotdeauna ușor de găsit.

Variabilele de legătură

Cu toate acestea, aceasta este situația cu variabilele proprietăților, dar ce se întâmplă cu variabilele de legătură?
Diferența lor este că aceste variabile trebuie să aibă aceeași valoare în diferite grupuri.

La început a fost ideea utilizată o construcție monstruoasă de tip:
hostvars[groups['bbauth'][0]]['auth_bind_port'], dar aceasta a fost imediat abandonată
deoarece are dezavantaje. În primul rând, complexitatea. În al doilea rând, dependența de un anumit host din grup. În al treilea rând, este necesar să colectăm faptele de la toate host-urile înainte de a începe desfășurarea, dacă nu dorim să primim o eroare de variabilă nedefinită.

În cele din urmă, s-a decis să fie utilizate variabilele de legătură.

Variabilele de legătură — acestea sunt variabile care aparțin playbook-ului și sunt necesare pentru a conecta obiectele sistemului.

Variabilele de legătură sunt completate în variabilele generale ale sistemului group_vars/all/vars și sunt formulate prin extragerea tuturor variabilelor ascultătorilor din fiecare grup, adăugând la începutul variabilei numele grupului din care a fost extras ascultătorul.

Astfel, se asigură uniformitatea și non-încărcarea numelui.

Să încercăm să legăm variabilele din exemplul de mai sus:

Abordarea sistemică a variabilelor în Ansible

Să presupunem că avem variabile care depind unele de altele:

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

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

Să extragem în variabilele generale group_vars/all/vars toți ascultătorii și să adăugăm în nume numele grupului:

# 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 }}"

Acum, schimbând valoarea conectorului, vom fi siguri că cererea va merge la aceeași destinație unde se află portul.

Cod de stil

  • Deoarece rolurile și grupurile sunt obiecte diferite ale sistemului, trebuie să aibă nume diferite, astfel variabilele de legătură vor indica clar că aparțin unui anumit grup de servere, nu rolului în sistem.

Fișiere interdependent

În roluri pot fi utilizate fișiere care diferă de la mediu la mediu.

Un exemplu de astfel de fișiere sunt certificatele SSL. A le stoca în formă text
într-o variabilă nu este foarte convenabil. În schimb, este convenabil să stocăm calea către ele în interiorul variabilei.

De exemplu, folosim variabila api_ssl_key_file: "/path/to/file".

Deoarece este evident că certificatul cheii va varia de la mediu la mediu, aceasta este o variabilă dependentă de mediu, ceea ce înseamnă că trebuie să se afle în fișierul
group_vars/myapi/vars inventoriul variabilelor și să conțină valoarea 'pentru exemplu'.

Cel mai convenabil în acest caz este să așezăm fișierul cheii în repository-ul playbook-ului la calea
files/prod/certs/myapi.key, atunci valoarea variabilei va fi:
api_ssl_key_file: "prod/certs/myapi.key". Conveniența constă în faptul că oamenii responsabili pentru desfășurarea sistemului pe un anumit stand, de asemenea, au un loc dedicat în repository pentru stocarea fișierelor lor. În același timp, există posibilitatea de a specifica o cale absolută către certificat pe server, în cazul în care certificatele sunt furnizate de un alt sistem.

Mai multe standuri într-o singură mediu

Adesea există necesitatea de a desfășura mai multe standuri practic identice într-un mediu cu diferențe minime. În acest caz, împărțim variabilele specifice mediului în cele care nu se schimbă în cadrul acestui mediu și cele care se schimbă. Iar pe acestea din urmă le mutăm direct în fișierele de inventari. După această manevră, devine posibil să creăm un alt inventar direct în directorul mediului.

Acesta va reutiliza group_vars de inventar, având de asemenea capacitatea de a suprascrie unele variabile direct pentru nevoile sale.

Structura finală a directoarelor pentru proiectul de desfășurare:

mydeploy                        # Directorul desfășurării
├── deploy.yml                  # Playbook-ul de desfășurare
├── files                       # Directorul pentru fișierele desfășurării
│   ├── prod                    # Director pentru fișierele specifice mediului standului prod
│   │   └── certs               # 
│   │       └── myapi.key       #
│   └── test1                   # Director pentru fișierele specifice mediului standului test1
├── group_vars                  # Directorul variabilelor playbook-ului
│   ├── all.yml                 # Fișier pentru variabilele de legătură ale întregului sistem
│   ├── myapi.yml               # Fișier pentru variabilele grupului myapi
│   ├── bbauth.yml              # 
│   └── ghauth.yml              #
└── inventories                 #
    ├── prod                    # Directorul mediului prod
    │   ├── group_vars          # Director pentru variabilele de inventar
    │   │   ├── myapi           #
    │   │   │   ├── vars.yml    # Variabilele specifice grupului myapi
    │   │   │   └── vault.yml   # Secrete (întotdeauna specifice mediului)
    │   │   ├── bbauth          # 
    │   │   │   ├── vars.yml    #
    │   │   │   └── vault.yml   #
    │   │   └── ghauth          #
    │   │       ├── vars.yml    #
    │   │       └── vault.yml   #
    │   └── prod.ini            # Inventarul standului prod
    └── test                    # Directorul mediului test
        ├── group_vars          #
        │   ├── myapi           #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   ├── bbauth          #
        │   │   ├── vars.yml    #
        │   │   └── vault.yml   #
        │   └── ghauth          #
        │       ├── vars.yml    #
        │       └── vault.yml   #
        ├── test1.ini           # Inventarul standului test1 în mediul test
        └── test2.ini           # Inventarul standului test2 în mediul test

Sinteză

După organizarea variabilelor conform articolului: fiecare fișier cu variabile răspunde unei sarcini specifice. Dacă un fișier are sarcini definite, a devenit posibil să se desemneze o persoană responsabilă pentru corectitudinea fiecărui fișier. De exemplu, pentru corectitudinea completării variabilelor playbook-ului, responsabil devine dezvoltatorul de desfășurare a sistemului, în timp ce completarea variabilelor inventory este responsabilitatea directă a administratorului al cărui stand este descris în inventory.

Rolurile au devenit o unitate de dezvoltare autonomă cu o interfață proprie, ceea ce a permis dezvoltatorului rolului să dezvolte funcționalități, fără a adapta rolul la sistem. Această problemă a fost deosebit de relevantă pentru rolurile comune pentru toate sistemele din campanie.

Administratorii sistemelor nu mai trebuie să înțeleagă codul de desfășurare. Tot ce li se cere pentru o desfășurare de succes este să completeze fișierele variabilelor dependente de mediu.

Literatură

  1. Documentație

Autor

Kalujnyi Denis Alexandrovich

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster