Konfigurimi i serverit për implementimin e aplikacionit Rails me Ansible

Kohë më parë, më ishte nevojitur të shkruaja disa ansible playbooks për përgatitjen e serverit për deploy të një aplikacioni Rails. Dhe, me befasinë time, nuk gjeta një manual të thjeshtë hapi-pas-hapi. Nuk doja të kopjoja playbook-un e dikujt tjetër pa kuptuar atë që po ndodhte dhe përfundimisht, më duhej të lexoj dokumentacionin, duke mbledhur gjithçka vetë. Ndoshta do të mund të ndihmoj dikë të përshpejtojë këtë proces përmes këtij artikulli.

E para, është e rëndësishme të kuptoni se ansible ju ofron një ndërfaqe të lehtë për të kryer një listë të paracaktuar veprimesh në një server (servera) të largët përmes SSH. Nuk ka ndonjë magji këtu, nuk mund të vendosni një plugin dhe të merrni një deploy zero downtime të aplikacionit tuaj me docker, monitorim dhe gjëra të tjera. Për të shkruar një playbook duhet të dini se çfarë dëshironi të bëni dhe si ta bëni atë. Prandaj, unë nuk jam i kënaqur me playbook-et e gatshme nga GitHub, ose artikuj që thonë: “Kopjoni dhe ekzekutoni, do të funksionojë”.

Çfarë na nevojitet?

Siç e thashë, për të shkruar një playbook duhet të dini se çfarë dëshironi të bëni dhe si ta bëni atë. Le të përcaktojmë se çfarë na nevojitet. Për aplikacionin Rails, do të na duhen disa paketa системike: nginx, postgresql (redis, etj). Përveç kësaj, na duhet ruby i një versioni të caktuar. E rekomandoj ta instaloni atë përmes rbenv (rvm, asdf…). Të gjitha këto nuk duhet të ekzekutohen nga përdoruesi root — gjithmonë është një ide e keqe, prandaj duhet të krijoni një përdorues të veçantë dhe t’i konfiguroheni të drejtat. Pas kësaj, është e nevojshme të ngarkohet kodi ynë në server, të kopjohen konfigurimet për nginx, postgres, etj dhe të nisën të gjitha këto shërbime.

Në fund, sekuenca e veprimeve është kështu:

  1. Bëjmë login si root.
  2. instalojmë paketat sistemike.
  3. krijojmë një përdorues të ri, konfigurojmë të drejtat, çelësin ssh.
  4. konfigurojmë paketat sistemike (nginx etj) dhe i nisim ato.
  5. Krijojmë një përdorues në DB (mund ta krijojmë menjëherë edhe bazën).
  6. Bëjmë login me përdoruesin e ri.
  7. Instalojmë rbenv dhe ruby.
  8. Instalojmë bundler.
  9. Ngarkojmë kodin e aplikacionit.
  10. Nisim serverin Puma.

Dhe këto hapat e fundit mund të bëhen me ndihmën e Capistrano, të paktën ai di nga e drejta të kopjojë kodin në direktoritë e lëshimeve, të kalojë në lidhjen simlink kur deploy është i suksesshëm, të kopjojë konfigurimet nga direktoria e ndarë, ta rinstalojë puma dhe etj. Të gjitha këto mund të bëhen edhe me Ansible, por përse?

Struktura e skedarëve

Ansible ka një strukturë të rreptë dokumentesh për të gjitha skedarët e tij, prandaj është më mirë t'i mbani të gjitha në një direktore të veçantë. Nuk ka rëndësi nëse do të jetë brenda aplikacionit Rails, apo ndaras. Mund të ruani skedarët në një depo git të veçantë. Më së shumti, më është dukur më e lehtë të krijoj një direktore ansible në /config të aplikacionit Rails dhe të ruaj të gjitha në një depo.

Playbook i Thjeshtë

Playbook është një skedar yml, në të cilin me ndihmën e një sintakse të veçantë është përshkruar se çfarë dhe si duhet ta bëjë ansible. Le të krijojmë playbook-in tonë të parë, që nuk bën asgjë:

---
- name: Playbook i Thjeshtë
  hosts: all

Këtu ne thjesht po themi se playbook-i ynë quhet Playbook i Thjeshtë dhe se përmbajtja e tij duhet të执行het për të gjitha hostet. Mund ta ruajmë atë në direktoren /ansible me emrin playbook.yml dhe të provojmë ta ranimojmë:

ansible-playbook ./playbook.yml

PLAY [Playbook i Thjeshtë] ************************************************************************************************************************************
skipping: no hosts matched

Ansible thotë se nuk di hostet që përputhen me listën e të gjitha. Duhet t'i rendisim ato në një skedar inventar.

Le të krijojmë atë në të njëjtën direktore ansible:

123.123.123.123

Këtu thjesht tregojmë hostin (idealisht hostin tuaj VPS për teste, ose mund të shkruani localhost) dhe e ruajmë me emrin inventory.
Mund të provoni ta ekzekutoni ansible me skedarin e inventarit:

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

TASK [Mblidh Fakte] ************************************************************************************************************************************

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

Nëse keni qasje përmes ssh në hostin e specifikuar, ansible do të lidhet dhe do të mbledhë informacion rreth sistemit të largët. (detyra standarde TASK [Mblidh Fakte]) pas së cilës do të japë një raport të shkurtër për ekzekutimin (PLAY RECAP).

Sipas të dhënave, emri i përdoruesit që përdoret për lidhjen është ai me të cilin jeni loguar në sistem. Në host, ndoshta nuk do të jetë. Në skedarin e playbook-it mund të tregoni se cilin përdorues duhet të përdorni për lidhjen me ndihmën e direktivës remote_user. Gjithashtu, informacioni rreth sistemit të largët shpesh mund të mos jetë i nevojshëm dhe nuk ia vlen të humbni kohë për ta mbledhur. Këtë detyrë gjithashtu mund ta çaktivizoni:

---
- name: Playbook i Thjeshtë
  hosts: all
  remote_user: root
  become: true
  gather_facts: no

Provoni përsëri të nisni playbook dhe sigurohuni që lidhja funksionon. (Nëse keni specifikuar përdoruesin root, gjithashtu duhet të specifikoni direktivën become: true, për të fituar të drejtat e rritura. Siç shkruhet në dokumentacion: become e vendosur në 'true'/'yes' për të aktivizuar shkallëzimin e privilegjeve. ndonëse nuk është krejt e qartë se përse).

Mund të merrni një gabim të shkaktuar nga fakti që ansible nuk mund të përcaktojë interpreterin e python, atëherë mund ta specifikoni manualisht:

ansible_python_interpreter: /usr/bin/python3 

ku e keni python mund ta merrni me komandën whereis python.

Instalimi i paketeve sistemore

Në instalimin standard të Ansible përfshihen shumë module për punë me paketa të ndryshme sistemore, duke na i lehtësuar kështu krijimin e skripteve bash në çdo rast. Tani do na nevojitet një nga këto module për të përditësuar sistemin dhe instaluar paketat sistemore. Unë në VPS kam Ubuntu Linux, kështu që për të instaluar paketat përdor apt-get dhe modulin për të.Nëse përdorni një sistem operativ tjetër, ndoshta do të nevojitet një modul tjetër (mbani mend se në fillim thashë që duhet ta dimë paraprakisht se çfarë dhe si do të bëjmë). Megjithatë, sintaksa ndoshta do të jetë e ngjashme.

Do ta plotësojmë playbook-un tonë me detyrat e para:

---
- 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 — është pikërisht detyra që ansible do të ekzekutojë në serverët e largët. Ne japim emrin detyrës për të ndjekur ekzekutimin e saj në log. Dhe e përshkruajmë, duke përdorur sintaksën e modulit përkatës, se çfarë duhet të bëjë. Në këtë rast apt: update_cache=yes — thotë të përditësojë paketat e sistemit duke përdorur modulën apt. Komanda e dytë është disi më e komplikuar. Ne i kalojmë modulit apt një listë paketash dhe i themi që state duhet të bëhen present, domethënë i themi të instalojë këto paketa. Në të njëjtën mënyrë, mund të themi që t'i hiqni ose t'i përditësoni, thjesht duke ndryshuar state. Vini re se për punën e rails me postgresql na nevojitet paketa postgresql-contrib, të cilën po e instalojmë tani. Kjo duhet të dihet dhe të bëhet, ansible vetë nuk do ta bëjë këtë.

Provoni të ekzekutoni playbook-in përsëri dhe kontrolloni që paketat të instalohen.

Krijimi i përdoruesve të rinj.

Për të punuar me përdoruesit, Ansible ka gjithashtu një modul — user. Do të shtojmë një task tjetër (kam fshehur pjesët e njohura të playbook-ut me komente, që të mos e kopjoj tërë çdo herë):

---
- name: Playbook i thjeshtë
  # ...
  tasks:
    # ...
    - name: Shto një përdorues të ri
      user:
        name: my_user
        shell: /bin/bash
        password: "{{ 123qweasd | password_hash('sha512') }}"

Ne po krijojmë një përdorues të ri, vendosim shell-in dhe fjalëkalimin e tij. Dhe këtu përballemi me disa probleme. Çfarë bëjmë nëse emrat e përdoruesve duhet të jenë të ndryshëm për hoste të ndryshme? Po ashtu, të ruash fjalëkalimin në formë të hapur në playbook është një ide shumë e keqe. Fillimisht, do të nxjerrim emrin e përdoruesit dhe fjalëkalimin në variabla, dhe më afër fundin e artikullit do të tregoj se si ta enkriptojmë fjalëkalimin.

---
- name: Playbook i thjeshtë
  # ...
  tasks:
    # ...
    - name: Shto një përdorues të ri
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"

Me anë të dyfishtë të kllapave në playbook-e vendosen variablat.

Vlerat e variablave do t'i specifikojmë në skedarin e inventarit:

123.123.123.123

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

Kujdesini për drejtpërsëdrejti [all:vars] — thotë se blloku i ardhshëm i tekstit është variablat (vars) dhe janë të aplikueshme për të gjithë hostet (all).

Po ashtu interesante është konstrukti "{{ user_password | password_hash('sha512') }}". Çështja është se ansible nuk e krijon përdoruesin përmes user_add siç do ta bënit manualisht. Ai ruan të dhënat drejtpërsëdrejti, për këtë arsye duhet ta konvertojmë fjalëkalimin në një hash paraprakisht, gjë që e bën kjo komandë.

Le të shtojmë përdoruesin tonë në grupin sudo. Megjithatë, përpara kësaj duhet të sigurohemi që një grup i tillë ekziston sepse askush tjetër nuk do ta bëjë këtë për ne:

---
- name: Playbook i thjeshtë
  # ...
  tasks:
    # ...
    - name: Siguro një grup 'sudo'
      group:
        name: sudo
        state: present
    - name: Shto një përdorues të ri
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"

Gjithçka është mjaft e thjeshtë, kemi gjithashtu modul grup për të krijuar grupe, me një sintaksë shumë të ngjashme me apt. Pas kësaj mjafton të regjistrojmë këtë grup te përdoruesi (groups: "sudo").
Po ashtu është e dobishme të shtojmë këtij përdoruesi një çelës ssh, që të mund të hyjmë nën të pa fjalëkalim:

---
- name: Playbook i thjeshtë
  # ...
  tasks:
    # ...
    - name: Siguro një grup 'sudo'
      group:
      name: sudo
        state: present
    - name: Shto një përdorues të ri
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"
    - name: Nxjerr çelësin SSH
      authorized_key:
        user: "{{ user }}"
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        state: present

Në këtë rast është interesante struktura "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — ajo kopjon përmbajtjen e skedës id_rsa.pub (emri mund të jetë ndryshe), pra pjesën publike të çelësit ssh dhe e ngarkon atë në listën e çelësave të autorizuar për përdoruesin në server.

Rolet

Të tria këto detyra për krijimin e përdoruesit mund të përfshihen lehtësisht në një grup detyrash, dhe do të ishte mirë ta mbani këtë grup ndarë nga playbook-u kryesor, në mënyrë që të mos shfaqet shumë i madh. Për këtë në ansible ekzistojnë rollet.
Sipas strukturës së skedarëve të specifikuar në fillim, rolet duhet të vendosen në një drejtorinë të veçantë roles, për çdo rol — një drejtorinë të veçantë me emrin përkatës, brenda drejtorisë tasks, files, templates, etj.
Të krijojmë strukturën e skedarëve: ./ansible/roles/user/tasks/main.yml (main — ky është skedari kryesor që do të ngarkohet dhe ekzekutohet kur lidhet roli me playbook-un, në të mund të lidhni skedarë të tjerë të rolit). Tani mund të transferoni në këtë skedar të gjitha detyrat që lidhen me përdoruesin:

# 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-un kryesor duhet të specifikoni të përdorni rolin user:

---
- name: Playbook i thjeshtë
  hosts: all
  remote_user: root
  gather_facts: no

  tasks:
    - name: Përditëso sistemin
      apt: update_cache=yes
    - name: Instaloni varësitë e sistemit
      apt:
        name: git,nginx,redis,postgresql,postgresql-contrib
        state: present

  roles:
    - user

Gjithashtu, ndoshta ka kuptim të kryeni përditësimin e sistemit para detyrave të tjera, për këtë mund të riemëroni bllokun tasks në të cilin ato janë të përcaktuara si pre_tasks.

Konfigurimi i nginx

Nginx duhet të jetë tashmë i instaluar, duhet ta konfigurojmë dhe ta nxjerrim në punë. Le të bëjmë këtë menjëherë në rol. Krijojmë strukturën e skedarëve:

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

Tani na duhen skedarët dhe shabllonët. Diferenca midis tyre është se skedarët ansible i kopjon drejtpërdrejt, ashtu si janë. Ndërsa shabllonët duhet të kenë zgjerimin j2 dhe brenda tyre mund të përdoren vlerat e variablave duke përdorur të njëjtat dyfish figura të skëmbëve.

Le të përfshijmë nginx në main.yml skedarin. Për këtë kemi modul systemd:

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

Këtu ne jo vetëm që themi se nginx duhet të jetë started (domethënë ta nisim atë), por gjithashtu i themi se ai duhet të jetë enabled.
Tani do të kopjojmë skedarët e konfigurimit:

# 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'

Krijojmë skedarin kryesor të konfigurimit për nginx (mund ta marrim direkt nga serveri ose ta shkruajmë vetë). Gjithashtu krijojmë skedarin e konfigurimit për aplikacionin tonë në drejtorinë sites_available (nuk është e detyrueshme, por është e dobishme). Në rastin e parë, ne përdorim modulin copy për të kopjuar skedarët (skedari duhet të jetë në /ansible/roles/nginx/files/nginx.conf). Në rastin e dytë, kopjojmë modelin, duke futur vlerat e variablave. Modeli duhet të jetë në /ansible/roles/nginx/templates/my_app.j2). Dhe mund të duket diçka si ky:

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 }};
  ....
}

Vini re insertet {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — këto janë të gjitha variabla, vlerat e të cilëve ansible do t'i vendosë në model para kopjimit. Kjo është e dobishme nëse përdorim një playbook për grupe të ndryshme hostesh. Për shembull, mund të shtojmë skedarin tonë të 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

Nëse tani e fillojmë playbook-un tonë, do të ekzekutojë detyrat e përmendura për të dy hostet. Por në këtë rast, për hostin staging variablat do të jenë ndryshe nga production, dhe jo vetëm në role dhe playbooks, por edhe në konfigurimet e nginx. {{ inventory_hostname }} nuk duhet të theksohet në skedarin e inventory — kjo është një variabël speciale ansible dhe aty ruhet hosti për të cilin ekzekutohet playbook-u në atë moment.
Nëse dëshironi të keni një skedar inventory për disa hoste, por të ekzekutoni vetëm për një grup, mund ta bëni këtë me komandën e mëposhtme:

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

një alternativë tjetër — të keni skedarë inventory të ndarë për grupe të ndryshme. Ose mund të kombinoni dy qasje, nëse keni shumë hoste të ndryshëm.

Kthehemi te konfigurimi i nginx. Pas kopjimit të skedarëve të konfigurimit, na nevojitet të krijojmë një simlink në sites_enabled për my_app.conf nga sites_available. Dhe të rindiznim nginx.

... # kodi i vjetër në mail.yml

- name: Krijo simlink në sites-enabled
  file:
    src: \/etc\/nginx\/sites-available\/my_app.conf
    dest: \/etc\/nginx\/sites-enabled\/my_app.conf
    state: link

- name: rindizni nginx
  service:
    name: nginx
    state: restarted

Këtu është shumë e thjeshtë — përsëri modulet ansible me sintaksë mjaft standarde. Por ka një moment. Nuk ka kuptim të rinit nginx çdo herë. A keni vënë re se ne nuk shkruajmë komanda të tipit: «bëni këtë kështu», sintaksa duket më shumë si «kësaj duhet të ketë një gjendje të tillë». Dhe shpesh, kështu punon ansible. Nëse grupi tashmë ekziston, ose paketa sistemike është instaluar, ansible do ta kontrollojë këtë dhe do ta kalojë detyrën. Po ashtu, skedarët nuk do të kopohen nëse ata përputhen plotësisht me ato që janë tashmë në server. Ne mund ta përfitojmë këtë dhe të rinisim nginx vetëm nëse skedarët e konfigurimit kanë ndryshuar. Për këtë ekziston direktiva 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

Nëse ndonjë prej skedarëve të konfigurimit ndryshon, do të kryhet kopjimi dhe do të regjistrohet variabla restart_nginx. Dhe vetëm nëse kjo variablë është regjistruar, do të kryhet rinisja e shërbimit.

Natyrisht, duhet të shtoni rolin nginx në playbook-un kryesor.

Konfigurimi i postgresql

Na nevojitet të aktivizojmë postgresql me anë të systemd njësoj siç e bëmë me nginx, si dhe të krijojmë një përdorues, të cilin do ta përdorim për qasje në bazën e të dhënave dhe vetë bazën e të dhënave.
Le të krijojmë një 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 }}"

Nuk do të përshkruaj se si të shtoni variabla në inventory, kjo është bërë shumë herë, ashtu siç është sintaksa e modulit postgresql_db dhe postgresql_user. Më shumë të dhëna mund të gjenden në dokumentacion. Këtu është më interesant direktiva become_user: postgres. Çështja është se, sipas parashikimeve, qasja në bazën e të dhënave postgresql është vetëm për përdoruesin postgres dhe vetëm lokalisht. Kjo direktivë na lejon të ekzekutojmë komanda në emër të këtij përdoruesi (nëse natyrisht kemi qasje).
Po ashtu, ndoshta do t'ju duhet të shkruani një rresht në pg_hba.conf për të hapur aksesin e përdoruesit të ri në bazë. Këto mund të bëhen njësoj siç e ndryshuam konfigurimin e nginx.

Natyrisht, duhet të shtoni rolin postgresql në playbook-un kryesor.

Instalimi i ruby përmes rbenv

Në ansible nuk ka module për punën me rbenv, dhe ai instalohet duke klonuar depot git. Prandaj, kjo detyrë bëhet më e pazakontë. Le të krijojmë një rol për të /ansible/roles/ruby_rbenv/main.yml dhe të fillojmë ta mbushim:

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

Ne po përdorim përsëri direktivën become_user për të punuar si përdoruesi që kemi krijuar për këto qëllime. Duke qenë se rbenv instalohet në direktorinë e tij home dhe jo globalisht. Po ashtu, ne përdorim modulit git për të klonuar repozitorin, duke specifikuar repo dhe dest.

Më pas, na nevojitet të shkruajmë rbenv init në bashrc dhe atje të shtojmë rbenv në PATH. Për këtë, kemi modulit lineinfile:

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

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

Pasi të bëhet kjo, duhet të instaloni ruby_build:

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

Dhe, në fund, instaloni ruby. Kjo bëhet përmes rbenv, thjesht me komandë bash:

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

Ne tregojmë se cila komandë do të ekzekutohet dhe me çfarë. Megjithatë, këtu do të përballemi me faktin se ansible nuk ekzekuton kodin e pranishëm në bashrc përpara ekzekutimit të komandave. Kështu që, rbenv do të duhet të përcaktohet drejtpërdrejt në këtë skenar.

Problemi tjetër është se komanda shell nuk ka gjendje në këndvështrimin e ansible. domethënë nuk do të ketë verifikim automatik nëse kjo version ruby është instaluar apo jo — kjo duhet ta bëjmë vetë:

- name: Instaloni 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

Dhe mbetet të instaloni bundler:

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

Po ashtu të shtoni rolin tonë ruby_rbenv në playbook-in kryesor.

Skedarët e ndarë.

Në përgjithësi, këtu mund të përfundohej konfigurimi. Më pas, mbetet të nisni capistrano dhe ajo do të kopjojë kodin vetë, do të krijojë katalogët e nevojshëm dhe do të niste aplikacionin (nëse është vendosur gjithçka saktë). Megjithatë, shpesh capistrano kërkon skedarë të tjerë konfigurohuese si database.yml или .env Ato mund të kopjohen njëlloj si skedarët dhe shabllonët për nginx. Ka vetëm një hollësi. Para kopjimit të skedarëve, është e nevojshme të krijoni strukturën e katalogëve për ta, diçka të tillë:

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

ne specifikojmë vetëm një direktori dhe ansible do ta krijojë automatikisht ata prind, nëse është e nevojshme.

Ansible Vault

Kemi has already encountered cases where sensitive data such as user passwords can be found in variables. If you created .env a file for the application, and database.yml there should be even more critical data in it. It’s best to hide them from prying eyes. For this purpose, we use ansible vault.

Let's create a file for the variables /ansible/vars/all.yml (here you can create different files for different groups of hosts, just like in the inventory file: production.yml, staging.yml, etc.).
In this file, you need to transfer all variables that need to be encrypted, using standard yaml syntax:

# 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

After that, you can encrypt this file with the command:

ansible-vault encrypt ./vars/all.yml

Naturally, when encrypting, you will need to set a password for decryption. You can check what will be inside the file after invoking this command.

With the help of ansible-vault decrypt the file can be decrypted, modified, and then re-encrypted.

To work, you don't need to decrypt the file. You keep it encrypted and run the playbook with the argument --ask-vault-pass. Ansible will ask for the password, retrieve the variables, and execute the tasks. All data will remain encrypted.

The complete command for several groups of hosts and ansible vault will look something like this:

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

And I won’t provide you with the full text of the playbooks and roles, you write it yourself. Because ansible is such a thing — if you don’t understand what needs to be done, it won’t do it for you either.

Burimi: habr.com

Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS 🔥 Blini hostim të besueshëm për faqe interneti me mbrojtje DDoS, serverë VPS VDS - ProHoster