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 ose .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 hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS đŸ”„ Blini hosting tĂ« besueshĂ«m pĂ«r faqe interneti me mbrojtje nga DDoS, serverĂ« VPS VDS | ProHoster