Konfigurimi i serverit për shpërndarjen e aplikacionit Rails me Ansible

Kohë më parë, më nevojitej të shkruaja disa ansible playbooks për të përgatitur serverin për implementimin e një aplikacioni Rails. Dhe, për habi, nuk gjeta një manual të thjeshtë hap pas hapi. Nuk doja të kopjoja një playbook të huaj pa kuptuar atë që po ndodhte, dhe në fund më duhej të lexoj dokumentacionin, duke mbledhur gjithçka vetë. Ndoshta ndokush mund të ndihmoj të shpejtoj këtë proces me anë të këtij artikulli.

E para, duhet të kuptoni se ansible ju ofron një ndërfaqe të përshtatshme për të kryer një listë veprimesh të paracaktuara në një server (servers) të largët përmes SSH. Nuk ka magji këtu, nuk mund të vendosni një plug-in dhe të merrni nga ky kuti një implementim të aplikacionit tuaj me zero downtime 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 këtë. Prandaj nuk më kënaqin playbook-të e gatshme nga GitHub, ose artikujt si: “Kopjoni dhe filloni, - do të punojë”.

Çfarë na nevojitet?

Как я уже говорил, для того чтобы написать плейбук надо знать, что вы хотите сделать и как это сделать. Давайте определимся с тем, что нам нужно. Для Rails приложения нам понадобится несколько системных пакетов: nginx, postgresql (redis, e.t.c.). Помимо этого нам нужен ruby определенной версии. Ставить его лучше всего через rbenv (rvm, asdf…). Запускать все это из под root пользователя — всегда плохая идея, поэтому надо создать отдельного пользователя, и настроить ему права. После этого необходимо залить наш код на сервер, скопировать конфиги для nginx, postgres, e.t.c. и запустить все эти сервисы.

В итоге последовательность действий такая:

  1. Логинимся под рутом
  2. устанавливаем системные пакеты
  3. создаем нового пользователя, настраиваем права, ssh ключ
  4. настраиваем системные пакеты (nginx e.t.c) и запускаем их
  5. Создаем пользователя в БД (можно сразу и базу создать)
  6. Логинимся новым пользователем
  7. Устанавливаем rbenv и ruby
  8. Устанавливаем бандлер
  9. Заливаем код приложения
  10. Запускаем Puma сервер

Për më tepër, fazat e fundit mund të kryhen duke përdorur capistrano, së paku ajo mund të kopjojë kodin në drejtoritë e lëshimit, të kalojë në lëshimin me një lidhje simbolike kur ka sukses në vendosje, të kopjojë konfigurimet nga drejtoritë e ndara, të rindezë puma, 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ë të skedarëve për të gjithë skedarët e tij, kështu që është më mirë t'i mbash të gjitha në një drejtorinë të veçantë. Për më tepër, nuk ka rëndësi nëse është brenda aplikacionit rails apo veçmas. Mund të ruash skedarët në një repo git të veçantë. Më shumti më ka ndihmuar të krijoj një drejtorinë ansible në /config të aplikacionit rails dhe të ruaj të gjitha në një repo.

Playbook i Thjeshtë

Playbook është një skedar yml, në të cilin me një sintaksë të veçantë përshkruhet se çfarë dhe si Ansible duhet të bëjë. Le të krijojmë playbook-in tonë të parë, i cili nuk bëni 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ë zbatohen për të gjitha hostet. Mund ta ruajmë atë në drejtorinë /ansible me emrin playbook.yml dhe të provojmë ta ekzekutojmë:

ansible-playbook ./playbook.yml

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

Ansible thotë se nuk njeh hostet që përputhen me listën all. Duhet t'i rendisim në një file inventari.

Le ta krijojmë atë në të njëjtën dosje ansible:

123.123.123.123

Këtu thjesht tregojmë hostin (idealisht hostin e VPS tuaj për teste, ose mund ta shënoni localhost) dhe e ruajmë me emrin inventory.
Mund të provoni të lançoni ansible me file inventari:

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

TASK [Gathering Facts] ************************************************************************************************************************************

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

Nëse keni akses përmes ssh në hostin e specificuar, ansible do të lidhet dhe do të mbledhë informacion rreth sistemit të largët. (detyrë e defoltit [Gathering Facts]) pas së cilës do të japë një raport të shkurtër për ekzekutimin (PLAY RECAP).

Me shkallë të paracaktuar, emri i përdoruesit që përdoret për lidhjen është ai me të cilin keni hyrë në sistem. Në host, ndoshta, nuk do të jetë. Në skedarin playbook mund të specifikoni cilin përdorues të përdorni për lidhjen përmes drejtorisë remote_user. Gjithashtu, informacioni rreth sistemit të largët shpesh nuk do t'ju nevojitet dhe nuk ia vlen të humbni kohë duke e mbledhur atë. Këtë detyrë mund ta ç’shkëputni gjithashtu:

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

Provoni përsëri të ekzekutoni playbook dhe sigurohuni që lidhja funksionon. (Nëse keni caktuar përdoruesin root, duhet gjithashtu të specifikoni drejtorinë become: true për të marrë të drejtat e rritura. Siç është shkruar në dokumentacion: become vendosur në ‘true’/’yes’ për të aktivizuar shkallëzimin e privilegjeve. ndonëse nuk është shumë e qartë se përse).

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

ansible_python_interpreter: /usr/bin/python3 

ku ndodhet python mund ta mësoni me komandën whereis python.

Instalimi i pakove sistemike

Ansible vjen me shumë module për të punuar me pako të ndryshme sistemore, duke na lejuar të mos shkruajmë skripta bash për çdo arsye. Tani, do të na nevojitet një nga këto module për të azhurnuar sistemin dhe instaluar pako sistemike. Unë kam Ubuntu Linux në VPS tim, prandaj për instalimin e pakove përdor apt-get dhe modulin për të.Nëse përdorni një sistem operativ tjetër, ndoshta do t'ju nevojitet një modul tjetër (mos harroni që në fillim thamë se duhet të dimë paraprakisht se çfarë do të bëjmë). Megjithatë, sintaksa do të jetë për të gjitha gjasat e ngjashme.

Le të plotësojmë playbook-un tonë me detyrat e para:

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

  tasks:
    - name: Azhurnoni sistemin
      apt: update_cache=yes
    - name: Instaloni varësitë sistemore
      apt:
        name: git,nginx,redis,postgresql,postgresql-contrib
        state: present

Detyra — është në fakt detyra që ansible do të kryejë në serverat e largët. Ne i japim detyrës një emër që të mund ta ndjekim realizimin 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ë azhurnoni paketat e sistemit me modulin apt. Komanda e dytë është pak më e komplikuar. Ne i transmetojmë modulit apt një listë paketash dhe i themi se gjendja duhet të bëhet pranishëm, domethënë i themi të instalojë këto paketa. Në një mënyrë të ngjashme, mund të themi t'i fshijmë ose të azhurnojmë, thjesht duke ndryshuar gjendja. Vini re se për të punuar rails me postgresql na nevojitet paketa postgresql-contrib, e cila po e instalojmë tani. Kjo është diçka që duhet ta dimë dhe ta bëjmë, ansible vetë nuk do ta bëjë këtë.

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

Krijimi i përdoruesve të rinj.

Për të punuar me përdoruesit, Ansible gjithashtu ka një modul — user. Do të shtojmë një tjetër detyrë (e kam fshehur pjesët e njohura të playbook-ut pas komenteve, që të mos e kopjojmë tërë atë ç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 pohtësojmë një përdorues të ri, i vendosim atij schell dhe fjalëkalim. Dhe menjëherë hasim disa probleme. Çfarë ndodh nëse emrat e përdoruesve duhet të jenë të ndryshëm për hoste të ndryshme? Dhe gjithashtu, ruajtja e fjalëkalimit në tekst të hapur në playbook është një ide shumë e keqe. Fillimisht, le të nxjerrim emrin e përdoruesit dhe fjalëkalimin në variabla, dhe më afër funditt të artikullit do të tregoj se si të enkriiptojmë fjalëkalimin.

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

me anë të dyhollë të krahëve, variablat vendosen në playbooks.

Ne do t'i tregojmë vlerat e variablave në file-in e inventarit:

123.123.123.123

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

Vini re direktivën [all:vars] — ajo tregon se blloku i ardhshëm i tekstit janë variablat (vars) dhe ata zbatohen për të gjitha 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ë gjitha të dhënat direkt, prandaj ne gjithashtu duhet ta transformojmë paraprakisht fjalëkalimin në një hash, gjë që e bën kjo komandë.

Le të shtojmë përdoruesin tonë në grupin sudo. Megjithatë, para kësaj është e nevojshme të sigurohemi që ky grup ekziston, sepse askush tjetër nuk do ta bëjë këtë për ne:

---
- name: Simple playbook
  # ...
  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"

E gjithë kjo është mjaft e thjeshtë, ne gjithashtu kemi një modul grup për krijimin e grupeve, me një sintaksë shumë të ngjashme me apt. Pas kësaj, mjafton të përcaktojmë këtë grup për përdoruesin (groups: "sudo").
Gjithashtu është e dobishme të shtojmë një çelës ssh për këtë përdorues, në mënyrë që të mund të bëjmë login pa fjalëkalim:

---
- name: Simple playbook
  # ...
  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: Deploy Çelësi SSH
      authorized_key:
        user: "{{ user }}"
        key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        state: present

Në këtë rast është interesante Konstrukti "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — ajo kopjon përmbajtjen e skedarit id_rsa.pub (emri juaj mund të jetë i ndryshëm), 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.

Rollet

Të treja detyrat për krijimin e përdoruesit mund të grupohen lehtësisht në një grup detyrash, dhe do të ishte mirë të ruheshin këtë grup veçmas nga playbook-u kryesor, në mënyrë që të mos zgjatej shumë. Për këtë, në ansible ekzistojnë rollet.
Sipas strukturës së skedarëve të specifikuar në fillim, rolet duhet të vendosen në një direktor të veçantë roles, për çdo rol — një direktor të veçantë me emrin e njëjtë, brenda direktorëve 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ë lidhen skedarë të tjerë të rolit). Tani mund të transferojmë në këtë skedar të gjitha detyrat që i përkasin përdoruesit:

# 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ë përcaktohet përdorimi i rolit user:

---
- emri: Playbook i thjeshtë
  hosts: të gjithë
  remote_user: root
  gather_facts: jo

  detyrat:
    - emri: Përditëso sistemin
      apt: update_cache=po
    - emri: Instaloni varësitë e sistemit
      apt:
        emri: git,nginx,redis,postgresql,postgresql-contrib
        gjendja: e pranishme

  rolet:
    - përdorues

Gjithashtu, ndoshta ka kuptim të kryhen përditësimet e sistemit më herët se detyrat e tjera, për këtë mund të riemërosh bllokun detyrat në të cilin ato janë të definuara në pre_tasks.

Konfigurimi i nginx

Nginx duhet të jetë instaluar, duhet ta konfiguroni dhe ta aktivizoni. Le të bëjmë këtë menjëherë në rol. Krijojmë strukturën e skedarëve:

- ansible
  - rolet
    - nginx
      - skedarë
      - detyra
        - main.yml
      - shabllone

Tani na nevojiten skedarët dhe shabllonet. Diferenca midis tyre është se ansible kopjon skedarët drejtpërdrejt, ashtu siç janë. Ndërsa shabllonet duhet të kenë zgjerimin j2 dhe në to mund të përdoren vlerat e variablave përmes atyre dyfishta kllapave.

Le të përfshijmë nginx në main.yml skedarin. Për këtë kemi modulin 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ë fillohet (pra ta aktivizojmë), por menjëherë u japim atij urdhrin që të jetë i aktivizuar.
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'

Ne krijojmë skedarin kryesor të konfigurimit për nginx (mund ta marrim drejtpërdrejt nga serveri, ose ta shkruajmë vetë). Po ashtu, krijojmë skedarin e konfigurimit për aplikacionin tonë në drejtorinë sites_available (kjo nuk është e detyrueshme, por është e dobishme). Në rastin e parë 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ë shablon duke vendosur vlerat e variablave. Shabloni duhet të jetë në /ansible/roles/nginx/templates/my_app.j2). Dhe mund të duket ndryshe si kjo:

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

Kujdesi për vënien e variablave {{ 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ë shablon para kopjimit. Kjo është e dobishme nëse përdorim playbook për grupe të ndryshme hostesh. Për shembull, mund ta plotësojmë skedarin tonë të inventarit:

[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 e fillojmë tani playbook-un tonë, ai do të kryejë detyrat e specifikuara për të dy host-at. Por për host-in staging, variablat do të ndryshojnë nga ata të production, jo vetëm në role dhe playbook-e, por edhe në konfigurimet e nginx. {{ inventory_hostname }} nuk duhet të specifikohet në skedarin inventory — kjo është një variabël speciale ansible dhe aty ruhet host-i për të cilin ekzekutohet aktualisht playbook-u.
Nëse dëshironi të keni një skedar inventory për disa host-a, por të ekzekutoni vetëm për një grup, kjo mund të bëhet me komandën e mëposhtme:

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

një tjetër opsion është të keni skeda të veçanta inventory për grupe të ndryshme. Ose mund të kombinoni dy qasje, nëse keni shumë host-a të ndryshëm.

Le të kthehemi te konfigurimi i nginx. Pas kopjimit të skedave të konfigurimit, na nevojitet të krijojmë një simlink në sites_enabled për my_app.conf nga sites_available. Dhe të rinisnim 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: rinis nginx
  service:
    name: nginx
    state: restarted

Këtu është e thjeshtë — përsëri modulet ansible me një sintaksë mjaft standard. Por ka një moment. Nuk ka sens të rindezësh nginx çdo herë. E keni vënë re se nuk shkruajmë komanda të tipit: «bëj këtë kështu», sintaksa duket më shumë si «ky duhet të ketë një gjendje të tillë». Dhe më shpesh kështu funksionon 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ë kopjohen nëse ata përputhen plotësisht me atë që është tashmë në server. Mund të përfitojmë nga kjo dhe ta rindezim nginx vetëm nëse skedarët e konfigurimit janë 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ë nga skedarët e konfigurimit ndryshohet, do të kryhet kopjimi dhe do të regjistrohet një variabël restart_nginx. Dhe vetëm nëse kjo variabël është regjistruar, do të kryhet rindezja e shërbimit.

Sigurisht, duhet të shtojmë rolin nginx në playbook-un kryesor.

Konfigurimi i postgresql

Na duhet të aktivizojmë postgresql duke përdorur systemd ashtu siç 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.
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ë shtojmë variabla në inventory, kjo është bërë shumë herë më parë, ashtu si edhe sintaksa e modulave postgresql_db dhe postgresql_user. Më shumë të dhëna mund të gjenden në dokumentacion. Këtu është veçanërisht interesante директива become_user: postgres. Çështja është se për default, qasja në bazën e të dhënave postgresql e ka vetëm përdoruesi postgres dhe vetëm lokal. Kjo директива na lejon të ekzekutojmë komanda në emër të këtij përdoruesi (nëse natyrisht kemi qasje).
Gjithashtu, ndoshta do t’ju duhet të shtoni një rresht në pg_hba.conf për të hapur qasjen e përdoruesit të ri në bazë. Kjo mund të bëhet ashtu siç e ndryshuam konfiguraionin e nginx.

Edhe sigurisht duhet të shtojmë rolin postgresql në playbookin kryesor.

Instalimi i ruby përmes rbenv

Në ansible nuk ka module për të punuar me rbenv, dhe ai instalohet duke klonuar një depo git. Prandaj, kjo detyrë bëhet më e pazakontë. 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 kemi përdorur përsëri direktivën become_user për të punuar nën përdoruesin që e krijuam për këto qëllime. Për shkak se rbenv instalohet në direktorinë e tij të shtëpisë dhe jo globalisht. Po ashtu, ne përdorim modulën git për të klonuar repozitorin, duke treguar repo dhe dest.

Më pas, na nevojitet të shtojmë rbenv init në bashrc dhe atje të shtojmë rbenv në PATH. Për këtë kemi modulin 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 -)"'

Pas kësaj, duhet të instalojmë ruby_build:

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

Dhe, përfundimisht, instaloni ruby. Kjo bëhet përmes rbenv, vetëm me komandën 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ë duhet të ekzekutohet dhe me çfarë. Megjithatë, këtu do të hasim se ansible nuk ekzekuton kodin që ndodhet në bashrc para se të ekzekutojë komandat. Prandaj, rbenv do të duhet të përcaktohet drejtpërdrejt në këtë skritp.

Problemi i ardhshëm është se komandat shell nuk kanë gjendje nga këndvështrimi i ansible. Kjo do të thotë se nuk do të ketë kontroll automatik nëse kjo version i ruby është instaluar apo jo — ne mund ta bëjmë këtë 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ë instalohet bundler:

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

Dhe përsëri të shtojmë rolin tonë ruby_rbenv në playbook-un kryesor.

Skedarë të ndarë.

Në përgjithësi, në këtë pikë do të qenë mjaft për të përfunduar konfigurimin. Më pas mbetet të nisni capistrano dhe ai do të kopjojë vetë kodin, do të krijojë katalogët e nevojshëm dhe do të nisë aplikacionin (nëse gjithçka është e konfigurueshme siç duhet). Megjithatë, shpesh capistrano kërkon skedarë të tjerë konfiguruese, si database.yml ose .env Mund t'i kopjojmë të njëjtë si skedarët dhe shabllonet për nginx. Ka vetëm një nuancë. Para se të kopjoni skedarin, duhet të krijoni strukturën e katalogëve për to, diçka si kjo:

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

ne tregahemi vetëm një direktor të vetëm dhe ansible automatikisht do të krijojë prindërit, nëse është e nevojshme.

Ansible Vault

Ne kemi hasur se në variablat mund të ndodhen të dhëna sekrete siç është fjala kalim e përdoruesit. Nëse keni krijuar .env një skedë për aplikacionin dhe database.yml aty duhet të ketë edhe më shumë të dhëna kritike. Është mirë t’i fshihni ato nga sytë e të tjerëve. Për këtë përdoret ansible vault.

Le të krijojmë një skedë për variablat /ansible/vars/all.yml (këtu mund të krijoni skeda të ndryshme për grupe të ndryshme hostesh, ashtu si në skedën e inventarit: production.yml, staging.yml, etj.).
Në këtë skedë duhet të transfertoni të gjitha variablat që duhet të jenë të enkriptuara, duke përdorur sintaksën standarde 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

Pas saj, kjo skedë mund të enkriptohet me komandën:

ansible-vault encrypt ./vars/all.yml

Natyrisht, gjatë enkriptimit duhet të vendosni një fjalëkalim për dekriptimin. Mund të shihni se çfarë do të ndodhet brenda skedës pas thirrjes së kësaj komande.

Me ndihmën e ansible-vault decrypt skeda mund të dekriptohet, të ndryshohet dhe më pas të enkriptohet sërish.

Për punë nuk është e nevojshme të dekriptohet skeda. Ju e ruani atë në formë të enkriptuar dhe e nisni playbook me argumentin --ask-vault-pass. Ansible do të kërkojë fjalëkalimin, do të nxjerrë variabelat dhe do të ekzekutojë detyrat. Të dhënat do të mbeten të koduara.

E gjithë ekipi për disa grupe hostesh dhe ansible vault do të duket kështu:

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

Dhe tekstin e plotë të playbooks dhe roleve nuk do t'ju jap, shkruani vetë. Sepse ansible është një gjë e tillë - nëse nuk kupton çfarë duhet bërë, nuk do ta bëjë as ai.

Burimi: habr.com

Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS 🔥 Bleni hostim të besueshëm për faqe me mbrojtje nga DDoS, serverë VPS VDS | ProHoster