Vendosja e një serveri për të vendosur një aplikacion Rails duke përdorur Ansible

Jo shumë kohë më parë më duhej të shkruaja disa libra lojërash Ansible për të përgatitur serverin për vendosjen e një aplikacioni Rails. Dhe, çuditërisht, nuk gjeta një manual të thjeshtë hap pas hapi. Nuk doja të kopjoja librin e lojërave të dikujt tjetër pa kuptuar se çfarë po ndodhte dhe në fund më duhej të lexoja dokumentacionin, duke mbledhur gjithçka vetë. Ndoshta mund të ndihmoj dikë që ta përshpejtojë këtë proces me ndihmën e këtij artikulli.

Gjëja e parë që duhet kuptuar është se ansible ju ofron një ndërfaqe të përshtatshme për të kryer një listë të paracaktuar veprimesh në një server(ët) në distancë nëpërmjet SSH. Nuk ka magji këtu, ju nuk mund të instaloni një shtojcë dhe të merrni një vendosje zero joproduktive të aplikacionit tuaj me docker, monitorim dhe të mira të tjera jashtë kutisë. Për të shkruar një libër lojërash, duhet të dini se çfarë saktësisht dëshironi të bëni dhe si ta bëni atë. Kjo është arsyeja pse unë nuk jam i kënaqur me libra të gatshëm të lojës nga GitHub, ose artikuj si: "Kopjo dhe ekzekuto, do të funksionojë".

Çfarë na nevojitet?

Siç thashë tashmë, për të shkruar një libër lojërash, duhet të dini se çfarë doni të bëni dhe si ta bëni atë. Le të vendosim se çfarë na nevojitet. Për një aplikacion Rails do të na duhen disa paketa sistemore: nginx, postgresql (redis, etj). Përveç kësaj, ne kemi nevojë për një version specifik të rubinit. Më së miri është ta instaloni nëpërmjet rbenv (rvm, asdf...). Drejtimi i gjithë kësaj si përdorues rrënjë është gjithmonë një ide e keqe, kështu që ju duhet të krijoni një përdorues të veçantë dhe të konfiguroni të drejtat e tij. Pas kësaj, ju duhet të ngarkoni kodin tonë në server, të kopjoni konfigurimet për nginx, postgres, etj dhe të filloni të gjitha këto shërbime.

Si rezultat, sekuenca e veprimeve është si më poshtë:

  1. Identifikohu si rrënjë
  2. instaloni paketat e sistemit
  3. krijoni një përdorues të ri, konfiguroni të drejtat, çelësin ssh
  4. konfiguroni paketat e sistemit (nginx etj) dhe ekzekutoni ato
  5. Ne krijojmë një përdorues në bazën e të dhënave (ju mund të krijoni menjëherë një bazë të dhënash)
  6. Hyni si përdorues i ri
  7. Instaloni rbenv dhe rubin
  8. Instalimi i paketës
  9. Ngarkimi i kodit të aplikacionit
  10. Nisja e serverit Puma

Për më tepër, fazat e fundit mund të kryhen duke përdorur capistrano, të paktën jashtë kutisë mund të kopjojë kodin në drejtoritë e lëshimit, të ndërrojë lëshimin me një lidhje simbolike pas vendosjes së suksesshme, të kopjojë konfigurimet nga një direktori e përbashkët, të rifillojë pumën, etj. E gjithë kjo mund të bëhet duke përdorur Ansible, por pse?

Struktura e skedarit

Ansible ka strikte struktura e skedarit për të gjithë skedarët tuaj, kështu që është më mirë t'i mbani të gjitha në një drejtori të veçantë. Për më tepër, nuk është aq e rëndësishme nëse do të jetë në vetë aplikacionin e binarëve, apo veçmas. Ju mund të ruani skedarët në një depo të veçantë git. Personalisht, e pashë më të përshtatshmet të krijoja një direktori ansible në drejtorinë /config të aplikacionit rails dhe të ruaj gjithçka në një depo.

Libër lojërash i thjeshtë

Playbook është një skedar yml që, duke përdorur sintaksë të veçantë, përshkruan se çfarë duhet të bëjë Ansible dhe si. Le të krijojmë librin e parë të lojërave që nuk bën asgjë:

---
- name: Simple playbook
  hosts: all

Këtu thjesht themi se libri ynë i lojërave quhet Simple Playbook dhe se përmbajtja e tij duhet të ekzekutohet për të gjithë hostet. Mund ta ruajmë në drejtorinë /ansible me emrin playbook.yml dhe përpiquni të vraponi:

ansible-playbook ./playbook.yml

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

Ansible thotë se nuk njeh asnjë host që përputhet me listën e të gjithëve. Ato duhet të renditen në një të veçantë dosjen e inventarit.

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

123.123.123.123

Kjo është mënyra se si ne thjesht specifikojmë hostin (në mënyrë ideale hostin e VPS-së tonë për testim, ose ju mund të regjistroni localhost) dhe e ruajmë atë nën emrin inventory.
Mund të provoni të ekzekutoni ansible me një skedar inventar:

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

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

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

Nëse keni akses ssh në hostin e specifikuar, atëherë ansible do të lidhet dhe do të mbledhë informacione rreth sistemit në distancë. (DETYRA e parazgjedhur [Mbledhja e fakteve]) pas së cilës do të japë një raport të shkurtër mbi ekzekutimin (PLAY RECAP).

Si parazgjedhje, lidhja përdor emrin e përdoruesit me të cilin jeni regjistruar në sistem. Me shumë mundësi nuk do të jetë në host. Në skedarin e librit të luajtjes, mund të specifikoni cilin përdorues të përdorni për t'u lidhur duke përdorur direktivën remote_user. Gjithashtu, informacioni në lidhje me një sistem në distancë shpesh mund të jetë i panevojshëm për ju dhe nuk duhet të humbni kohë për ta mbledhur atë. Kjo detyrë gjithashtu mund të çaktivizohet:

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

Provoni të ekzekutoni përsëri librin e luajtjes dhe sigurohuni që lidhja të funksionojë. (Nëse keni specifikuar përdoruesin rrënjë, atëherë duhet të specifikoni edhe direktivën e bërë: e vërtetë për të fituar të drejta të larta. Siç shkruhet në dokumentacion: become set to ‘true’/’yes’ to activate privilege escalation. edhe pse nuk është plotësisht e qartë pse).

Ndoshta do të merrni një gabim të shkaktuar nga fakti se ansible nuk mund të përcaktojë interpretuesin Python, atëherë mund ta specifikoni manualisht:

ansible_python_interpreter: /usr/bin/python3 

Mund të zbuloni se ku keni python me komandën whereis python.

Instalimi i paketave të sistemit

Shpërndarja standarde e Ansible përfshin shumë module për të punuar me paketa të ndryshme të sistemit, kështu që nuk kemi nevojë të shkruajmë skriptet bash për asnjë arsye. Tani na duhet një nga këto module për të përditësuar sistemin dhe instaluar paketat e sistemit. Unë kam Ubuntu Linux në VPS-në time, kështu që për të instaluar paketat përdor apt-get и modul për të. Nëse jeni duke përdorur një sistem tjetër operativ, atëherë mund t'ju duhet një modul tjetër (mos harroni, thashë në fillim se duhet të dimë paraprakisht se çfarë dhe si do të bëjmë). Sidoqoftë, sintaksa ka shumë të ngjarë të jetë e ngjashme.

Le të plotësojmë librin tonë të lojërave 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ë kryejë në serverët në distancë. Ne i japim detyrës një emër në mënyrë që të mund të gjurmojmë ekzekutimin e saj në regjistër. Dhe ne përshkruajmë, duke përdorur sintaksën e një moduli specifik, atë që duhet të bëjë. Në këtë rast apt: update_cache=yes - thotë për të përditësuar paketat e sistemit duke përdorur modulin apt. Komanda e dytë është pak më e ndërlikuar. Ne kalojmë një listë të paketave në modulin apt dhe themi se janë state duhet të bëhet present, domethënë themi instaloni këto paketa. Në mënyrë të ngjashme, ne mund t'u themi që t'i fshijnë ato, ose t'i përditësojnë ato thjesht duke i ndryshuar state. Ju lutemi vini re se që binarët të punojnë me postgresql ne kemi nevojë për paketën postgresql-contrib, të cilën po e instalojmë tani. Përsëri, ju duhet ta dini dhe ta bëni këtë; insible në vetvete nuk do ta bëjë këtë.

Provoni të ekzekutoni përsëri librin e luajtjes dhe kontrolloni nëse paketat janë të instaluara.

Krijimi i përdoruesve të rinj.

Për të punuar me përdoruesit, Ansible ka gjithashtu një modul - përdorues. Le të shtojmë një detyrë tjetër (kam fshehur pjesët tashmë të njohura të librit të lojërave pas komenteve në mënyrë që të mos e kopjoj plotësisht çdo herë):

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Add a new user
      user:
        name: my_user
        shell: /bin/bash
        password: "{{ 123qweasd | password_hash('sha512') }}"

Ne krijojmë një përdorues të ri, vendosim një skemë dhe fjalëkalim për të. Dhe pastaj hasim në disa probleme. Po sikur emrat e përdoruesve të jenë të ndryshëm për hoste të ndryshëm? Dhe ruajtja e fjalëkalimit në tekst të qartë në librin e lojërave është një ide shumë e keqe. Për të filluar, le të vendosim emrin e përdoruesit dhe fjalëkalimin në variabla, dhe në fund të artikullit do të tregoj se si të kriptohet fjalëkalimi.

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - name: Add a new user
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"

Variablat vendosen në librat e lojërave duke përdorur kllapa të dyfishtë kaçurrelë.

Ne do të tregojmë vlerat e variablave në skedarin e inventarit:

123.123.123.123

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

Ju lutemi vini re direktivën [all:vars] - thotë se blloku tjetër i tekstit është variablat (vars) dhe ato janë të zbatueshme për të gjithë hostet (të gjithë).

Dizajni është gjithashtu interesant "{{ user_password | password_hash('sha512') }}". Puna është se ansible nuk e instalon përdoruesin nëpërmjet user_add siç do ta bënit me dorë. Dhe i ruan të gjitha të dhënat drejtpërdrejt, kjo është arsyeja pse ne gjithashtu duhet ta kthejmë fjalëkalimin në një hash paraprakisht, gjë që bën kjo komandë.

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

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

Gjithçka është mjaft e thjeshtë, ne kemi gjithashtu një modul grupi për krijimin e grupeve, me një sintaksë shumë të ngjashme me apt. Atëherë mjafton të regjistrohet ky grup te përdoruesi (groups: "sudo").
Është gjithashtu e dobishme t'i shtohet një çelës ssh këtij përdoruesi në mënyrë që të mund të identifikohemi duke e përdorur atë pa një fjalëkalim:

---
- name: Simple playbook
  # ...
  tasks:
    # ...
    - 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ë këtë rast, dizajni është interesant "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — kopjon përmbajtjen e skedarit id_rsa.pub (emri juaj mund të jetë i ndryshëm), domethënë 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.

rol

Të tre detyrat për krijimin e përdorimit mund të klasifikohen lehtësisht në një grup detyrash dhe do të ishte mirë ta ruani këtë grup veçmas nga libri kryesor i lojës, në mënyrë që të mos rritet shumë. Për këtë qëllim, Ansible ka rolin.
Sipas strukturës së skedarit të treguar në fillim, rolet duhet të vendosen në një direktori rolesh të veçantë, për secilin rol ka një drejtori të veçantë me të njëjtin emër, brenda drejtorisë së detyrave, skedarëve, shablloneve, etj.
Le të krijojmë një strukturë skedari: ./ansible/roles/user/tasks/main.yml (main është skedari kryesor që do të ngarkohet dhe ekzekutohet kur një rol lidhet me librin e lojërave; skedarë të tjerë rolesh mund të lidhen me të). Tani mund të transferoni të gjitha detyrat që lidhen me përdoruesin në këtë skedar:

# 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ë librin kryesor të lojërave, duhet të specifikoni për të përdorur rolin e përdoruesit:

---
- name: Simple playbook
  hosts: all
  remote_user: root
  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

  roles:
    - user

Gjithashtu, mund të ketë kuptim të përditësoni sistemin përpara të gjitha detyrave të tjera; për ta bërë këtë, mund ta riemërtoni bllokun tasks në të cilën ato përcaktohen në pre_tasks.

Konfigurimi i nginx

Tashmë duhet të kemi të instaluar Nginx; duhet ta konfigurojmë dhe ta ekzekutojmë. Le ta bëjmë menjëherë në rol. Le të krijojmë një strukturë skedari:

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

Tani na duhen skedarë dhe shabllone. Dallimi midis tyre është se ansible kopjon skedarët drejtpërdrejt, siç është. Dhe shabllonet duhet të kenë shtrirjen j2 dhe ata mund të përdorin vlera të ndryshueshme duke përdorur të njëjtat mbajtëse kaçurrela të dyfishta.

Le të aktivizojmë nginx in main.yml dosje. Për këtë ne kemi një modul systemd:

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

Këtu jo vetëm që themi që nginx duhet të fillojë (d.m.th., ne e lançojmë), por themi menjëherë se duhet të aktivizohet.
Tani le 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 nginx (mund ta merrni direkt nga serveri, ose ta shkruani vetë). Dhe gjithashtu skedari i konfigurimit për aplikacionin tonë në drejtorinë sites_available (kjo nuk është e nevojshme, por e dobishme). Në rastin e parë, ne përdorim modulin e kopjimit për të kopjuar skedarët (skedari duhet të jetë brenda /ansible/roles/nginx/files/nginx.conf). Në të dytën, ne kopjojmë shabllonin, duke zëvendësuar vlerat e variablave. Shablloni duhet të jetë në /ansible/roles/nginx/templates/my_app.j2). Dhe mund të duket diçka 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 }};
  ....
}

Kushtojini vëmendje futjeve {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} - këto janë të gjitha variablat, vlerat e të cilave Ansible do t'i zëvendësojë në shabllon përpara kopjimit. Kjo është e dobishme nëse përdorni një libër lojërash për grupe të ndryshme hostesh. Për shembull, ne mund të shtojmë 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 tani e hapim librin tonë të lojërave, ai do të kryejë detyrat e specifikuara për të dy hostet. Por në të njëjtën kohë, për një host të skenës, variablat do të jenë të ndryshëm nga ato të prodhimit, dhe jo vetëm në role dhe libra lojërash, por edhe në konfigurimin e nginx. {{ inventory_hostname }} nuk ka nevojë të specifikohet në skedarin e inventarit - kjo variabël speciale ansible dhe hosti për të cilin po ekzekutohet aktualisht libri i luajtjes ruhet atje.
Nëse dëshironi të keni një skedar inventar për disa hoste, 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 mundësi është që të keni skedarë të veçantë të inventarit për grupe të ndryshme. Ose mund të kombinoni të dy qasjet nëse keni shumë host të ndryshëm.

Le të kthehemi te konfigurimi i nginx. Pas kopjimit të skedarëve të konfigurimit, duhet të krijojmë një lidhje simbolike në sitet_enabled te my_app.conf nga sites_available. Dhe rinisni nginx.

... # old code in 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

Gjithçka është e thjeshtë këtu - përsëri module të besueshme me një sintaksë mjaft standarde. Por ka një pikë. Nuk ka asnjë pikë për të rifilluar nginx çdo herë. A e keni vënë re se ne nuk shkruajmë komanda si: "bëje këtë kështu", sintaksa duket më shumë si "kjo duhet të ketë këtë gjendje". Dhe më shpesh kjo është saktësisht se si funksionon ansible. Nëse grupi ekziston tashmë, ose paketa e sistemit është instaluar tashmë, atëherë ansible do ta kontrollojë këtë dhe do ta kapërcejë detyrën. Gjithashtu, skedarët nuk do të kopjohen nëse përputhen plotësisht me atë që është tashmë në server. Ne mund të përfitojmë nga kjo dhe të rifillojmë nginx vetëm nëse skedarët e konfigurimit janë ndryshuar. Ekziston një direktivë regjistri për këtë:

# 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 një nga skedarët e konfigurimit ndryshon, do të bëhet një kopje dhe ndryshorja do të regjistrohet restart_nginx. Dhe vetëm nëse kjo variabël është regjistruar, shërbimi do të riniset.

Dhe, sigurisht, duhet të shtoni rolin nginx në librin kryesor të lojërave.

Vendosja e postgresql

Ne duhet të aktivizojmë postgresql duke përdorur systemd në të njëjtën mënyrë si kemi bërë me nginx, dhe gjithashtu të krijojmë një përdorues që do ta përdorim për të hyrë 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 }}"

Unë nuk do të përshkruaj se si të shtoni variabla në inventar, kjo tashmë është bërë shumë herë, si dhe sintaksa e moduleve postgresql_db dhe postgresql_user. Më shumë informacion mund të gjeni në dokumentacion. Direktiva më interesante këtu është become_user: postgres. Fakti është se si parazgjedhje, vetëm përdoruesi i postgres ka qasje në bazën e të dhënave postgresql dhe vetëm në nivel lokal. Kjo direktivë na lejon të ekzekutojmë komanda në emër të këtij përdoruesi (nëse kemi akses, sigurisht).
Gjithashtu, mund t'ju duhet të shtoni një linjë në pg_hba.conf për të lejuar një përdorues të ri akses në bazën e të dhënave. Kjo mund të bëhet në të njëjtën mënyrë siç kemi ndryshuar konfigurimin e nginx.

Dhe sigurisht, ju duhet të shtoni rolin postgresql në librin kryesor të lojërave.

Instalimi i rubinit nëpërmjet rbenv

Ansible nuk ka module për të punuar me rbenv, por instalohet duke klonuar një depo git. Prandaj, ky problem bëhet më jo standardi. Le të krijojmë një rol për të /ansible/roles/ruby_rbenv/main.yml dhe le të fillojmë ta plotësojmë:

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

Ne përsëri përdorim direktivën bene_user për të punuar nën përdoruesin që kemi krijuar për këto qëllime. Meqenëse rbenv është instaluar në direktorinë e tij kryesore, dhe jo globalisht. Dhe ne përdorim gjithashtu modulin git për të klonuar depon, duke specifikuar repo dhe dest.

Më pas, duhet të regjistrojmë rbenv init në bashrc dhe të shtojmë rbenv te PATH atje. Për këtë kemi modulin lineinfile:

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

- name: Add rbenv init to bashrc
  become_user: "{{ user }}"
  lineinfile:
    path: ~/.bashrc
    state: present
    line: 'eval "$(rbenv init -)"'

Atëherë duhet të instaloni ruby_build:

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

Dhe së fundi instaloni rubin. Kjo bëhet përmes rbenv, domethënë thjesht me komandën bash:

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

Ne themi se cilën komandë të ekzekutojmë dhe me çfarë. Sidoqoftë, këtu hasim në faktin se ansible nuk ekzekuton kodin e përfshirë në bashrc përpara se të ekzekutojë komandat. Kjo do të thotë që rbenv do të duhet të përcaktohet drejtpërdrejt në të njëjtin skenar.

Problemi tjetër është për faktin se komanda shell nuk ka gjendje nga një këndvështrim i arsyeshëm. Kjo do të thotë, nuk do të ketë kontroll automatik nëse ky version i rubinit është i instaluar apo jo. Ne mund ta bëjmë këtë vetë:

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

Gjithçka që mbetet është të instaloni bundler:

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

Dhe përsëri, shtoni rolin tonë ruby_rbenv në librin kryesor të lojërave.

Skedarët e përbashkët.

Në përgjithësi, konfigurimi mund të përfundojë këtu. Tjetra, gjithçka që mbetet është të ekzekutoni capistrano dhe ai do të kopjojë vetë kodin, do të krijojë drejtoritë e nevojshme dhe do të nisë aplikacionin (nëse gjithçka është konfiguruar saktë). Sidoqoftë, capistrano shpesh kërkon skedarë shtesë të konfigurimit, si p.sh database.yml ose .env Ato mund të kopjohen ashtu si skedarët dhe shabllonet për nginx. Ka vetëm një hollësi. Përpara se të kopjoni skedarët, duhet të krijoni një strukturë drejtorie 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 specifikojmë vetëm një direktori dhe ansible do të krijojë automatikisht ato prindër nëse është e nevojshme.

Ansible Vault

Tashmë kemi hasur në faktin se variablat mund të përmbajnë të dhëna sekrete si fjalëkalimi i përdoruesit. Nëse keni krijuar .env dosje për aplikim, dhe database.yml atëherë duhet të ketë edhe më shumë të dhëna të tilla kritike. Do të ishte mirë t'i fshihni nga sytë kureshtarë. Për këtë qëllim përdoret kasafortë ansible.

Le të krijojmë një skedar për variablat /ansible/vars/all.yml (këtu mund të krijoni skedarë të ndryshëm për grupe të ndryshme hostesh, ashtu si në skedarin e inventarit: production.yml, staging.yml, etj.).
Të gjitha variablat që duhet të kodohen duhet të transferohen në këtë skedar 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 së cilës ky skedar mund të kodohet me komandën:

ansible-vault encrypt ./vars/all.yml

Natyrisht, kur kriptoni, do t'ju duhet të vendosni një fjalëkalim për deshifrim. Ju mund të shihni se çfarë do të jetë brenda skedarit pasi të keni thirrur këtë komandë.

Me anë të ansible-vault decrypt skedari mund të deshifrohet, modifikohet dhe më pas të kodohet përsëri.

Ju nuk keni nevojë të deshifroni skedarin për të punuar. Ju e ruani atë të koduar dhe ekzekutoni librin e lojërave me argumentin --ask-vault-pass. Ansible do të kërkojë fjalëkalimin, do të marrë variablat dhe do të ekzekutojë detyrat. Të gjitha të dhënat do të mbeten të koduara.

Komanda e plotë për disa grupe të hosteve dhe vault ansible do të duket diçka si kjo:

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

Por nuk do t'ju jap tekstin e plotë të librave dhe roleve, shkruani vetë. Sepse ansible është e tillë - nëse nuk e kuptoni se çfarë duhet bërë, atëherë nuk do ta bëjë atë për ju.

Burimi: www.habr.com

Shto një koment