Configurarea serverului pentru desfășurarea aplicației Rails utilizând Ansible

Recent, I needed to write several Ansible playbooks to prepare a server for deploying a Rails application. Surprisingly, I couldn't find a simple step-by-step manual. I didn't want to copy someone else's playbook without understanding what was happening, so I ended up reading the documentation and piecing everything together myself. Perhaps I can help someone speed up this process with this article.

First of all, it’s important to understand that Ansible provides you with a convenient interface to execute a predefined list of actions on a remote server (or servers) through SSH. There’s no magic here; you can’t just install a plugin and get zero downtime deployment of your application with Docker, monitoring, and other features right out of the box. To write a playbook, you need to know exactly what you want to do and how to do it. Therefore, I am not satisfied with ready-made playbooks from GitHub, or articles that say: 'Copy and run — it will work.'

What do we need?

As I mentioned, to write a playbook, you need to know what you want to accomplish and how to achieve it. Let’s determine what we need. For a Rails application, we will need several system packages: nginx, postgresql (redis, etc.). In addition, we need Ruby of a specific version. It’s best to install it through rbenv (rvm, asdf…). Running all this as the root user is always a bad idea, so you should create a separate user and set up permissions for them. After that, it’s necessary to upload our code to the server, copy the configs for nginx, postgres, etc., and start all these services.

As a result, the sequence of actions is as follows:

  1. Log in as root
  2. Install system packages
  3. Create a new user, set permissions, ssh key
  4. Configure system packages (nginx, etc.) and start them
  5. Create a user in the database (you can also create the database right away)
  6. Log in as the new user
  7. Install rbenv and Ruby
  8. Install the bundler
  9. Upload the application code
  10. Start the Puma server

Furthermore, the last steps can be performed using Capistrano; at least it can natively copy code to release directories, switch the release via a symlink after a successful deployment, copy configs from the shared directory, restart Puma, etc. All of this can also be done using Ansible, but why?

File structure

Ansible are o structură strictă a fișierelor pentru toate fișierele sale, așa că cel mai bine este să le păstrați într-un director separat. Nu este atât de important dacă este în aplicația Rails sau separat. Puteți stoca fișierele într-un repository git separat. Personal, mi s-a părut cel mai convenabil să creez un director ansible în /config-ul aplicației Rails și să păstrez totul într-un singur repository.

Playbook simplu

Playbook-ul este un fișier yml în care, utilizând o sintaxă specială, se descrie ce și cum trebuie să facă Ansible. Să creăm primul playbook care nu face nimic:

---
- nume: Playbook simplu
  hosts: toate

Aici pur și simplu spunem că playbook-ul nostru se numește Playbook simplu și că conținutul său trebuie să fie executat pentru toate gazdele. Îl putem salva în directorul /ansible cu numele playbook.yml și să încercăm să-l rulăm:

ansible-playbook ./playbook.yml

PLAY [Playbook simplu] ************************************************************************************************************************************
skipping: no hosts matched

Ansible spune că nu știe gazdele care corespund listei toate. Acestea trebuie enumerate într-un fișier special de inventar.

Să-l creăm în același director ansible:

123.123.123.123

Aici specificăm pur și simplu gazda (ideal gazda VPS-ului dumneavoastră pentru teste, sau puteți specifica localhost) și îl salvăm cu numele inventory.
Puteți încerca să rulați ansible cu fișierul de inventar:

ansible-playbook ./playbook.yml -i inventory
PLAY [Playbook simplu] ************************************************************************************************************************************

TASK [Colectarea informațiilor] ************************************************************************************************************************************

PLAY RECAP ************************************************************************************************************************************

Dacă aveți acces prin ssh la gazda specificată, Ansible se va conecta și va aduna informații despre sistemul remote. (TASK-ul implicit [Colectarea informațiilor]) după care va da un raport scurt despre execuție (PLAY RECAP).

În mod implicit, pentru conexiune se folosește numele utilizatorului cu care sunteți logat în sistem. Pe gazda respectivă, acesta, cel mai probabil, nu va exista. În fișierul playbook puteți specifica ce utilizator să folosească pentru conectare prin directiva remote_user. De asemenea, informațiile despre sistemul remote vă pot fi adesea inutile și nu merită să pierdeți timp pentru colectarea lor. Această sarcină poate fi, de asemenea, dezactivată:

---
- nume: Playbook simplu
  hosts: toate
  remote_user: root
  become: true
  gather_facts: nu

Încercați din nou să rulați playbook-ul și să vă asigurați că conexiunea funcționează. (Dacă ați indicat utilizatorul root, trebuie de asemenea să specificați directiva become: true pentru a obține drepturi administrative. Așa cum este menționat în documentație: become set to ‘true’/’yes’ pentru a activa escaladarea privilegiilor. deși nu este foarte clar de ce).

Este posibil să primiți o eroare cauzată de faptul că ansible nu poate determina interpretul Python, atunci îl puteți specifica manual:

ansible_python_interpreter: /usr/bin/python3 

locația Python-ului poate fi obținută folosind comanda whereis python.

Instalarea pachetelor de sistem

În distribuția standard a Ansible se găsesc multe module pentru lucrul cu diverse pachete de sistem, motiv pentru care nu trebuie să scriem scripturi bash pentru orice. Acum ne va trebui unul dintre aceste module pentru a actualiza sistemul și a instala pachete de sistem. Eu am Ubuntu Linux pe VPS, așa că pentru instalarea pachetelor folosesc apt-get și module pentru acesta. Dacă folosiți un alt sistem de operare, s-ar putea să aveți nevoie de un alt mod (amintiți-vă, la început am spus că trebuie să știm dinainte ce și cum vom face). Totuși, sintaxa va fi probabil similară.

Să completăm playbook-ul nostru cu primele sarcini:

---
- name: Simple playbook
  hosts: all
  remote_user: root
  become: true
  gather_facts: no

  tasks:
    - name: Update system
      apt: update_cache=yes
    - name: Install system dependencies
      apt:
        name: git,nginx,redis,postgresql,postgresql-contrib
        state: present

Task — este de fapt sarcina pe care ansible o va executa pe serverele remote. Noi dăm sarcinii un nume pentru a-i urmări execuția în jurnal. Și descriem, folosind sintaxa modulului specific, ce trebuie să facă. În acest caz apt: update_cache=yes — indică să actualizăm pachetele sistemului folosind modulul apt. A doua comandă este puțin mai complexă. Transmitem modulului apt o listă de pachete și spunem că acestea state ar trebui să devină existent, adică spunem să instalăm aceste pachete. În mod similar, putem spune să le ștergem sau să le actualizăm, pur și simplu schimbând state. Rețineți că pentru ca rails să funcționeze cu postgresql, avem nevoie de pachetul postgresql-contrib, pe care îl instalăm acum. Acest lucru trebuie să fie știut și făcut, ansible nu va face asta de la sine.

Încercați să rulați din nou playbook-ul și verificați că pachetele se vor instala.

Crearea de noi utilizatori.

Pentru a lucra cu utilizatorii, Ansible are de asemenea un modul - user. Vom adăuga o nouă sarcină (am ascuns părțile cunoscute ale playbook-ului în comentarii, pentru a nu-l copia în întregime de fiecare dată):

---
- name: Playbook simplu
  # ...
  tasks:
    # ...
    - name: Adaugă un utilizator nou
      user:
        name: my_user
        shell: /bin/bash
        password: "{{ 123qweasd | password_hash('sha512') }}"

Creăm un nou utilizator, îi setăm shell-ul și parola. Dar ne confruntăm imediat cu câteva probleme. Ce ar trebui să facem dacă numele utilizatorilor trebuie să fie diferite pentru diferite gazde? Și stocarea parolei în clar în playbook este o idee foarte proastă. La început, vom muta numele utilizatorului și parola în variabile, iar mai aproape de sfârșitul articolului voi arăta cum să criptăm parola.

---
- name: Playbook simplu
  # ...
  tasks:
    # ...
    - name: Adaugă un utilizator nou
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"

Prin intermediul acoladelor duble în playbooks sunt definite variabilele.

Valorile variabilelor le vom specifica în fișierul de inventory:

123.123.123.123

[all:vars]
user=my_user
user_password=123qweasd

Observați directiva [all:vars] — aceasta indică faptul că următorul bloc de text este variabile (vars) și se aplică pentru toate gazdele (all).

De asemenea, construcția "{{ user_password | password_hash('sha512') }}". Problema este că ansible nu adaugă utilizatorul prin user_add așa cum ați face manual. Ci stochează toate datele direct, motiv pentru care parola trebuie să fie prealabil transformată într-un hash, ceea ce face această comandă.

Să adăugăm utilizatorul nostru în grupul sudo. Totuși, înainte de asta, trebuie să ne asigurăm că acest grup există, pentru că nimeni nu se va ocupa de asta pentru noi:

---
- name: Playbook simplu
  # ...
  tasks:
    # ...
    - name: Asigură un grup 'sudo'
      group:
        name: sudo
        state: present
    - name: Adaugă un utilizator nou
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"

Totul este suficient de simplu, avem de asemenea modul group pentru a crea grupuri, cu o sintaxă foarte similară cu apt. După care, tot ce trebuie să facem este să atribuim acest grup utilizatorului (groups: "sudo").
De asemenea, este util să adăugăm cheia ssh acestui utilizator, astfel încât să ne putem conecta fără parolă:

---
- name: Playbook simplu
  # ...
  tasks:
    # ...
    - name: Asigură un grup 'sudo'
      group:
      name: sudo
        state: present
    - name: Adaugă un utilizator nou
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"
    - name: Distribuie cheia SSH
      authorized_key:
        user: "{{ user }}"
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        state: present

În acest caz, construcția este interesantă "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — ea copiază conținutul fișierului id_rsa.pub (poate că numele este diferit), adică partea publică a cheii SSH și o încarcă în lista de chei autorizate pentru utilizator pe server.

Roluri

Toate cele trei sarcini pentru crearea utilizatorului pot fi ușor legate de un singur grup de sarcini, și ar fi bine să păstrăm acest grup separat de playbook-ul principal, pentru a nu se extinde prea mult. Pentru aceasta, în ansible există roluri.
Conform structurii de fișiere specificate la început, rolurile trebuie să fie plasate într-un director separat roles, fiecare rol — într-un director separat cu numele corespunzător, în interiorul directorului tasks, files, templates etc.
Să creăm structura de fișiere: ./ansible/roles/user/tasks/main.yml (main — acesta este fișierul principal care va fi încărcat și executat atunci când se atașează rolul la playbook, în el pot fi conectate alte fișiere de rol). Acum putem muta în acest fișier toate sarcinile referitoare la utilizator:

# Create user and add him to groups
- name: Ensure a 'sudo' group
  group:
    name: sudo
    state: present

- name: Add a new user
  user:
    name: "{{ user }}"
    shell: /bin/bash
    password: "{{ user_password | password_hash('sha512') }}"
    groups: "sudo"

- name: Deploy SSH Key
  authorized_key:
    user: "{{ user }}"
    key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
    state: present

În playbook-ul principal, trebuie să specificați să folosiți rolul user:

---
- name: Playbook simplu
  hosts: all
  remote_user: root
  gather_facts: no

  tasks:
    - name: Actualizează sistemul
      apt: update_cache=yes
    - name: Instalează dependențele sistemului
      apt:
        name: git,nginx,redis,postgresql,postgresql-contrib
        state: present

  roles:
    - user

De asemenea, poate că are sens să executăm actualizarea sistemului înaintea tuturor celorlalte sarcini, pentru aceasta putem redenumi blocul tasks în care sunt definite pre_tasks.

Configurarea nginx

Nginx ar trebui să fie deja instalat, trebuie să-l configurăm și să-l pornim. Să facem acest lucru imediat în rol. Creăm structura de fișiere:

- ansible
  - roles
    - nginx
      - files
      - tasks
        - main.yml
      - templates

Acum avem nevoie de fișiere și șabloane. Diferența dintre ele este că Ansible copiază fișierele direct, așa cum sunt. Iar șabloanele trebuie să aibă extensia j2 și în ele putem folosi valori de variabile utilizând aceleași dublu acolade.

Să includem nginx în fișierul main.yml. Pentru aceasta avem modulul systemd: fișier. Pentru aceasta, avem modulul systemd:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

Aici nu doar că spunem că nginx trebuie să fie pornit (adică îl pornim), dar spunem și că trebuie să fie activat.
Acum vom copia fișierele de configurare:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

- name: Copy the nginx.conf
  copy:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes

- name: Copy template my_app.conf
  template:
    src: my_app_conf.j2
    dest: /etc/nginx/sites-available/my_app.conf
    owner: root
    group: root
    mode: '0644'

Creăm fișierul principal de configurare nginx (îl putem lua direct de pe server sau îl putem scrie noi înșine). De asemenea, creăm fișierul de configurare pentru aplicația noastră în directorul sites_available (acesta nu este obligatoriu, dar este util). În primul caz, folosim modulul copy pentru a copia fișierele (fișierul trebuie să se afle în /ansible/roles/nginx/files/nginx.conf). În al doilea — copiem șablonul, înlocuind valorile variabilelor. Șablonul trebuie să se afle în /ansible/roles/nginx/templates/my_app.j2). Poate arăta cam așa:

upstream {{ app_name }} {
  server unix:{{ app_path }}\/shared\/tmp\/sockets\/puma.sock;
}

server {
  listen 80;
  server_name {{ server_name }} {{ inventory_hostname }};
  root {{ app_path }}\/current\/public;

  try_files $uri\/index.html $uri.html $uri @{{ app_name }};
  ....
}

Vă rugăm să rețineți inserțiile {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — acestea sunt toate variabile care vor fi înlocuite în șablon de ansible înainte de copiere. Acest lucru este util dacă utilizați un playbook pentru grupuri diferite de gazde. De exemplu, putem completa fișierul nostru de inventory:

[production]
123.123.123.123

[staging]
231.231.231.231

[all:vars]
user=my_user
user_password=123qweasd

[production:vars]
server_name=production
app_path=\/home\/www\/my_app
app_name=my_app

[staging:vars]
server_name=staging
app_path=\/home\/www\/my_stage
app_name=my_stage_app

Dacă acum vom rula playbook-ul nostru, acesta va executa sarcinile indicate pentru ambele gazde. Dar pentru gazda staging, variabilele vor fi diferite față de production, și nu doar în roluri și playbook-uri, ci și în configurațiile nginx. {{ inventory_hostname }} nu trebuie să specificați în fișierul de inventory — aceasta este o variabilă specială ansible și aici se află gazda pentru care se execută playbook-ul în acest moment.
Dacă doriți să aveți un fișier de inventory pentru mai multe gazde, dar să rulați doar pentru un singur grup, puteți face acest lucru cu comanda următoare:

ansible-playbook -i inventory .\/playbook.yml -l "staging"

o altă variantă — a avea fișiere de inventory separate pentru grupuri diferite. Sau puteți combina cele două abordări, dacă aveți multe gazde diferite.

Să ne întoarcem la configurarea nginx. După copierea fișierelor de configurare, trebuie să creăm un symlink în sites_enabled pentru my_app.conf din sites_available. Și să repornim nginx.

... # cod vechi în mail.yml

- name: Create symlink to sites-enabled
  file:
    src: \/etc\/nginx\/sites-available\/my_app.conf
    dest: \/etc\/nginx\/sites-enabled\/my_app.conf
    state: link

- name: restart nginx
  service:
    name: nginx
    state: restarted

Aici totul este simplu — din nou module ansible cu o sintaxă destul de standard. Dar există un detaliu. Nu are sens să reporniți nginx de fiecare dată. Ați observat că nu scriem comenzi de genul: „faceți asta așa”, sintaxa arată mai degrabă ca „acesta trebuie să aibă o astfel de stare”. Și de cele mai multe ori ansible funcționează exact așa. Dacă grupul există deja sau pachetul sistemului este deja instalat, ansible va verifica asta și va sări peste sarcină. De asemenea, fișierele nu vor fi copiate dacă sunt identice cu cele ce există deja pe server. Putem profita de acest lucru și să repornim nginx doar dacă fișierele de configurare au fost modificate. Pentru aceasta există directiva register:

# Copy nginx configs and start it
- name: enable service nginx and start
  systemd:
    name: nginx
    state: started
    enabled: yes

- name: Copy the nginx.conf
  copy:
    src: nginx.conf
    dest: /etc/nginx/nginx.conf
    owner: root
    group: root
    mode: '0644'
    backup: yes
  register: restart_nginx

- name: Copy template my_app.conf
  template:
    src: my_app_conf.j2
    dest: /etc/nginx/sites-available/my_app.conf
    owner: root
    group: root
    mode: '0644'
  register: restart_nginx

- name: Create symlink to sites-enabled
  file:
    src: /etc/nginx/sites-available/my_app.conf
    dest: /etc/nginx/sites-enabled/my_app.conf
    state: link

- name: restart nginx
  service:
    name: nginx
    state: restarted
  when: restart_nginx.changed

Dacă unul dintre fișierele de configurare se schimbă, atunci va fi efectuată copierea și va fi înregistrată variabila restart_nginx. Și doar dacă această variabilă a fost înregistrată, se va efectua repornirea serviciului.

Și, bineînțeles, trebuie să adăugăm rolul nginx în playbook-ul principal.

Configurarea postgresql

Trebuie să activăm postgresql prin systemd exact așa cum am făcut cu nginx, iar de asemenea să creăm un utilizator pe care îl vom utiliza pentru accesul la baza de date și baza de date în sine.
Să creăm un rol /ansible/roles/postgresql/tasks/main.yml:

# Create user in postgresql
- name: enable postgresql and start
  systemd:
    name: postgresql
    state: started
    enabled: yes

- name: Create database user
  become_user: postgres
  postgresql_user:
    name: "{{ db_user }}"
    password: "{{ db_password }}"
    role_attr_flags: SUPERUSER

- name: Create database
  become_user: postgres
  postgresql_db:
    name: "{{ db_name }}"
    encoding: UTF-8
    owner: "{{ db_user }}"

Nu voi detalia cum să adăugați variabile în inventory, aceasta a fost făcută deja de multe ori, la fel ca și sintaxa modulelor postgresql_db și postgresql_user. Mai multe informații pot fi găsite în documentație. Aici este cea mai interesantă directivă become_user: postgres. Ceea ce este important, este că prin default accesul la baza de date postgresql este permis doar utilizatorului postgres și doar local. Această directivă ne permite să executăm comenzi în numele acestui utilizator (dacă, desigur, avem acces).
De asemenea, este posibil să fie necesar să adăugați o linie în pg_hba.conf pentru a deschide accesul noului utilizator la bază. Acest lucru se poate face la fel cum am modificat configurația nginx.

Și, desigur, trebuie să adăugăm rolul postgresql în playbook-ul principal.

Instalarea ruby prin rbenv

În ansible nu există module pentru lucru cu rbenv, și acesta se instalează prin clonarea unui repository git. Așadar, această sarcină devine cea mai neobișnuită. Să creăm un rol pentru aceasta /ansible/roles/ruby_rbenv/main.yml și să începem să-l completăm:

# Install rbenv and ruby
- name: Install rbenv
  become_user: "{{ user }}"
  git: repo=https://github.com/rbenv/rbenv.git dest=~/.rbenv

Folosim din nou directiva become_user pentru a lucra sub utilizatorul creat de noi pentru aceste scopuri. Deoarece rbenv este instalat în directorul său home, nu global. De asemenea, folosim modulul git pentru a clona repository-ul, specificând repo și dest.

Apoi trebuie să adăugăm rbenv init în bashrc și să adăugăm rbenv în PATH. Pentru aceasta avem modulul lineinfile:

- name: Adaugă rbenv în PATH
  become_user: "{{ user }}"
  lineinfile:
    path: ~\/\.bashrc
    state: present
    line: 'export PATH="${HOME}\/\.rbenv\/bin:${PATH}"'

- name: Adaugă rbenv init în bashrc
  become_user: "{{ user }}"
  lineinfile:
    path: ~\/\.bashrc
    state: present
    line: 'eval "$(rbenv init -)"'

După aceea, trebuie să instalăm ruby_build:

- name: Instalează ruby-build
  become_user: "{{ user }}"
  git: repo=https:\/\/github.com\/rbenv\/ruby-build.git dest=~\/\.rbenv\/plugins\/ruby-build

Și, în final, instalează ruby. Acest lucru se face prin rbenv, adică pur și simplu printr-o comandă bash:

- name: Instalează ruby
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}\/\.rbenv\/bin:${PATH}"
    eval "$(rbenv init -)"
    rbenv install {{ ruby_version }}
  args:
    executable: \/bin\/bash

Specificăm ce comandă să executăm și cu ce. Totuși, aici ne vom confrunta cu faptul că ansible nu execută codul din bashrc înainte de a rula comenzile. Asta înseamnă că rbenv trebuie definit chiar în acest script.

Următoarea problemă este că comanda shell nu are un statut din perspectiva ansible. Adică nu va exista o verificare automată dacă această versiune de ruby este instalată sau nu — trebuie să facem asta noi înșine:

- name: Instalează ruby
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}\/\.rbenv\/bin:${PATH}"
    eval "$(rbenv init -)"
    if ! rbenv versions | grep -q {{ ruby_version }}
      then rbenv install {{ ruby_version }} && rbenv global {{ ruby_version }}
    fi
  args:
    executable: \/bin\/bash

Și rămâne să instalăm bundler:

- name: Instalează bundler
  become_user: "{{ user }}"
  shell: |
    export PATH="${HOME}\/\.rbenv\/bin:${PATH}"
    eval "$(rbenv init -)"
    gem install bundler

Și din nou, adăugați rolul nostru ruby_rbenv în playbook-ul principal.

Fișiere partajate.

În general, aici s-ar putea încheia configurarea. Urmează să pornim capistrano și acesta va copia singur codul, va crea directoarele necesare și va porni aplicația (dacă totul este configurat corect). Totuși, deseori capistrano necesită fișiere de configurare suplimentare, cum ar fi database.yml sau .env Acestea pot fi copiate exact la fel ca fișierele și șabloanele pentru nginx. Există doar o mică nuanță. Înainte de a copia fișierele, trebuie creată structura directoarelor pentru ele, ceva de genul:

# Copy shared files for deploy
- name: Ensure shared dir
  become_user: "{{ user }}"
  file:
    path: "{{ app_path }}/shared/config"
    state: directory

specificăm doar un singur director, iar ansible va crea automat părinții, dacă este necesar.

Ansible Vault

Am dat peste situații în care variabilele pot conține date sensibile, cum ar fi parola utilizatorului. Dacă ați creat .env un fișier pentru aplicație, și database.yml atunci ar trebui să existe și mai multe astfel de date critice. Ar fi bine să le ascundem de ochii curioșilor. Pentru aceasta se folosește ansible vault.

Să creăm un fișier pentru variabile /ansible/vars/all.yml (aici se pot crea diferite fișiere pentru diferite grupuri de gazde, la fel ca în fișierul inventory: production.yml, staging.yml etc.).
În acest fișier trebuie aduse toate variabilele care trebuie să fie criptate, folosind sintaxa standard yml:

# System vars
user_password: 123qweasd
db_password: 123qweasd

# ENV vars
aws_access_key_id: xxxxx
aws_secret_access_key: xxxxxx
aws_bucket: bucket_name
rails_secret_key_base: very_secret_key_base

După aceea, acest fișier poate fi criptat cu comanda:

ansible-vault encrypt .\/vars\/all.yml

Desigur, la criptare va trebui să stabiliți o parolă pentru decriptare. Puteți vedea ce va conține fișierul după executarea acestei comenzi.

Cu ajutorul ansible-vault decrypt fișierul poate fi decriptat, modificat și apoi criptat din nou.

Pentru funcționare nu este nevoie să decriptați fișierul. Îl păstrați într-o formă criptată și rulați playbook-ul cu argumentul --ask-vault-pass. Ansible vă va cere parola, va extrage variabilele și va executa sarcinile. Toate datele vor rămâne criptate.

Comanda completă pentru mai multe grupuri de gazde și ansible vault va arăta aproximativ așa:

ansible-playbook -i inventory .\/playbook.yml -l "staging" --ask-vault-pass

Și nu vă voi oferi textul complet al playbook-urilor și rolurilor, scrieți-le singuri. Pentru că ansible este astfel — dacă nu înțelegi ce trebuie făcut, atunci nici el nu îți va face.

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