Pagse-set up ng server para mag-deploy ng Rails application gamit ang Ansible

Hindi pa nagtagal kailangan kong magsulat ng ilang Ansible playbook para ihanda ang server para sa pag-deploy ng application ng Rails. At, nakakagulat, wala akong nakitang simpleng step-by-step manual. Hindi ko nais na kopyahin ang playbook ng ibang tao nang hindi nauunawaan kung ano ang nangyayari, at sa huli ay kailangan kong basahin ang dokumentasyon, na kinokolekta ang lahat ng aking sarili. Marahil ay matutulungan ko ang isang tao na pabilisin ang prosesong ito sa tulong ng artikulong ito.

Ang unang bagay na dapat maunawaan ay ang ansible ay nagbibigay sa iyo ng isang maginhawang interface upang magsagawa ng isang paunang natukoy na listahan ng mga aksyon sa isang malayong (mga) server sa pamamagitan ng SSH. Walang magic dito, hindi ka makakapag-install ng plugin at makakuha ng zero downtime deployment ng iyong application na may docker, monitoring at iba pang goodies out of the box. Upang magsulat ng isang playbook, dapat mong malaman kung ano ang eksaktong gusto mong gawin at kung paano ito gagawin. Iyon ang dahilan kung bakit hindi ako nasisiyahan sa mga nakahandang playbook mula sa GitHub, o mga artikulo tulad ng: "Kopyahin at patakbuhin, gagana ito."

Ang kailangan natin?

Gaya ng nasabi ko na, para makapagsulat ng playbook kailangan mong malaman kung ano ang gusto mong gawin at kung paano ito gagawin. Magpasya tayo kung ano ang kailangan natin. Para sa isang application ng Rails kakailanganin namin ang ilang mga pakete ng system: nginx, postgresql (redis, atbp). Bilang karagdagan, kailangan namin ng isang tiyak na bersyon ng ruby. Pinakamabuting i-install ito sa pamamagitan ng rbenv (rvm, asdf...). Ang pagpapatakbo ng lahat ng ito bilang root user ay palaging isang masamang ideya, kaya kailangan mong lumikha ng isang hiwalay na user at i-configure ang kanyang mga karapatan. Pagkatapos nito, kailangan mong i-upload ang aming code sa server, kopyahin ang mga config para sa nginx, postgres, atbp at simulan ang lahat ng mga serbisyong ito.

Bilang resulta, ang pagkakasunud-sunod ng mga aksyon ay ang mga sumusunod:

  1. Mag-login bilang root
  2. i-install ang mga pakete ng system
  3. lumikha ng bagong user, i-configure ang mga karapatan, ssh key
  4. i-configure ang mga pakete ng system (nginx atbp) at patakbuhin ang mga ito
  5. Lumilikha kami ng isang gumagamit sa database (maaari kang agad na lumikha ng isang database)
  6. Mag-login bilang bagong user
  7. I-install ang rbenv at ruby
  8. Pag-install ng bundler
  9. Pag-upload ng code ng aplikasyon
  10. Inilunsad ang server ng Puma

Bukod dito, ang mga huling yugto ay maaaring gawin gamit ang capistrano, hindi bababa sa labas ng kahon maaari itong kopyahin ang code sa mga direktoryo ng paglabas, ilipat ang paglabas gamit ang isang symlink sa matagumpay na pag-deploy, kopyahin ang mga config mula sa isang nakabahaging direktoryo, i-restart ang puma, atbp. Ang lahat ng ito ay maaaring gawin gamit ang Ansible, ngunit bakit?

Istraktura ng file

Ang Ansible ay mahigpit istraktura ng file para sa lahat ng iyong mga file, kaya pinakamahusay na panatilihin ang lahat ng ito sa isang hiwalay na direktoryo. Bukod dito, hindi napakahalaga kung ito ay nasa mismong aplikasyon ng riles, o hiwalay. Maaari kang mag-imbak ng mga file sa isang hiwalay na git repository. Sa personal, nakita kong pinaka-maginhawang lumikha ng isang mahusay na direktoryo sa /config na direktoryo ng application ng riles at iimbak ang lahat sa isang imbakan.

Simpleng Playbook

Ang Playbook ay isang yml file na, gamit ang espesyal na syntax, ay naglalarawan kung ano ang dapat gawin ng Ansible at kung paano. Gawin natin ang unang playbook na walang ginagawa:

---
- name: Simple playbook
  hosts: all

Dito lang natin sinasabi na ang playbook natin ay tinatawag Simple Playbook at na ang mga nilalaman nito ay dapat isagawa para sa lahat ng mga host. Maaari naming i-save ito sa / ansible na direktoryo na may pangalan playbook.yml at subukang tumakbo:

ansible-playbook ./playbook.yml

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

Sinabi ni Ansible na hindi nito alam ang anumang mga host na tumutugma sa lahat ng listahan. Dapat silang nakalista sa isang espesyal file ng imbentaryo.

Gawin natin ito sa parehong ansible na direktoryo:

123.123.123.123

Ito ay kung paano namin tukuyin lamang ang host (ideal ang host ng aming VPS para sa pagsubok, o maaari kang magrehistro ng localhost) at i-save ito sa ilalim ng pangalan inventory.
Maaari mong subukang magpatakbo ng ansible gamit ang isang file ng imbentaryo:

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

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

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

Kung mayroon kang ssh na access sa tinukoy na host, ang ansible ay kumonekta at mangolekta ng impormasyon tungkol sa remote system. (default na TASK [Gathering Facts]) pagkatapos nito ay magbibigay ito ng maikling ulat sa execution (PLAY RECAP).

Bilang default, ginagamit ng koneksyon ang username kung saan ka naka-log in sa system. Malamang na wala ito sa host. Sa playbook file, maaari mong tukuyin kung sinong user ang gagamitin para kumonekta gamit ang remote_user directive. Gayundin, ang impormasyon tungkol sa isang malayuang sistema ay maaaring madalas na hindi kailangan sa iyo at hindi ka dapat mag-aksaya ng oras sa pagkolekta nito. Ang gawaing ito ay maaari ding i-disable:

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

Subukang patakbuhin muli ang playbook at tiyaking gumagana ang koneksyon. (Kung tinukoy mo ang root user, kailangan mo ring tukuyin ang become: true directive para makakuha ng matataas na karapatan. Gaya ng nakasulat sa dokumentasyon: become set to β€˜true’/’yes’ to activate privilege escalation. bagaman hindi lubos na malinaw kung bakit).

Marahil ay makakatanggap ka ng isang error na sanhi ng katotohanan na hindi matukoy ng ansible ang interpreter ng Python, pagkatapos ay maaari mong tukuyin ito nang manu-mano:

ansible_python_interpreter: /usr/bin/python3 

Maaari mong malaman kung saan mayroon kang python gamit ang utos whereis python.

Pag-install ng mga pakete ng system

Kasama sa karaniwang pamamahagi ng Ansible ang maraming mga module para sa pagtatrabaho sa iba't ibang mga pakete ng system, kaya hindi namin kailangang magsulat ng mga script ng bash para sa anumang dahilan. Ngayon kailangan namin ng isa sa mga module na ito upang i-update ang system at i-install ang mga package ng system. Mayroon akong Ubuntu Linux sa aking VPS, kaya para mag-install ng mga pakete ay ginagamit ko apt-get ΠΈ module para dito. Kung gumagamit ka ng ibang operating system, maaaring kailangan mo ng ibang module (tandaan, sinabi ko sa simula na kailangan nating malaman nang maaga kung ano at paano natin gagawin). Gayunpaman, ang syntax ay malamang na magkapareho.

Dagdagan natin ang ating playbook ng mga unang gawain:

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

Ang gawain ay eksaktong gawain na gagawin ng Ansible sa mga malalayong server. Binibigyan namin ng pangalan ang gawain upang masubaybayan namin ang pagpapatupad nito sa log. At inilalarawan namin, gamit ang syntax ng isang partikular na module, kung ano ang kailangan nitong gawin. Sa kasong ito apt: update_cache=yes - sabi na i-update ang mga pakete ng system gamit ang apt module. Ang pangalawang utos ay medyo mas kumplikado. Ipinapasa namin ang isang listahan ng mga pakete sa apt module at sinasabi na ang mga ito state dapat maging present, ibig sabihin, sinasabi naming i-install ang mga package na ito. Sa katulad na paraan, maaari naming sabihin sa kanila na tanggalin ang mga ito, o i-update ang mga ito sa pamamagitan lamang ng pagbabago state. Pakitandaan na para gumana ang rails sa postgresql kailangan namin ang postgresql-contrib package, na ini-install namin ngayon. Muli, kailangan mong malaman at gawin ito; hindi ito gagawin ng ansible sa sarili nitong.

Subukang patakbuhin muli ang playbook at tingnan kung naka-install ang mga package.

Paglikha ng mga bagong user.

Para makipagtulungan sa mga user, mayroon ding module ang Ansible - user. Magdagdag pa tayo ng isa pang gawain (itinago ko ang mga kilalang bahagi ng playbook sa likod ng mga komento upang hindi ito ganap na kopyahin sa bawat oras):

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

Lumilikha kami ng bagong user, nagtakda ng schell at password para dito. At pagkatapos ay magkakaroon tayo ng maraming problema. Paano kung kailangang magkaiba ang mga username para sa iba't ibang host? At ang pag-iimbak ng password sa malinaw na teksto sa playbook ay isang napakasamang ideya. Upang magsimula, ilagay natin ang username at password sa mga variable, at sa dulo ng artikulo ay ipapakita ko kung paano i-encrypt ang password.

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

Nakatakda ang mga variable sa mga playbook gamit ang double curly braces.

Ipahiwatig namin ang mga halaga ng mga variable sa file ng imbentaryo:

123.123.123.123

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

Pakitandaan ang direktiba [all:vars] - sinasabi nito na ang susunod na bloke ng teksto ay mga variable (vars) at ang mga ito ay naaangkop sa lahat ng mga host (lahat).

Interesante din ang disenyo "{{ user_password | password_hash('sha512') }}". Ang bagay ay hindi na-install ng ansible ang gumagamit sa pamamagitan ng user_add tulad ng gagawin mo nang mano-mano. At direktang ini-save nito ang lahat ng data, kung kaya't kailangan din nating i-convert ang password sa isang hash nang maaga, na kung ano ang ginagawa ng utos na ito.

Idagdag natin ang ating user sa sudo group. Gayunpaman, bago ito kailangan nating tiyakin na may ganoong grupo dahil walang gagawa nito para sa atin:

---
- 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"

Ang lahat ay medyo simple, mayroon din kaming isang module ng grupo para sa paglikha ng mga grupo, na may isang syntax na halos kapareho sa apt. Pagkatapos ay sapat na upang irehistro ang pangkat na ito sa gumagamit (groups: "sudo").
Kapaki-pakinabang din na magdagdag ng ssh key sa user na ito upang makapag-log in kami gamit ito nang walang password:

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

Sa kasong ito, ang disenyo ay kawili-wili "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" β€” kinokopya nito ang mga nilalaman ng id_rsa.pub file (maaaring iba ang iyong pangalan), iyon ay, ang pampublikong bahagi ng ssh key at ina-upload ito sa listahan ng mga awtorisadong key para sa user sa server.

Mga tungkulin

Ang lahat ng tatlong gawain para sa paglikha ng paggamit ay madaling mauuri sa isang pangkat ng mga gawain, at magandang ideya na iimbak ang pangkat na ito nang hiwalay mula sa pangunahing playbook upang hindi ito lumaki nang masyadong malaki. Para sa layuning ito, mayroon ang Ansible mga tungkulin.
Ayon sa istraktura ng file na ipinahiwatig sa pinakadulo simula, ang mga tungkulin ay dapat ilagay sa isang hiwalay na direktoryo ng mga tungkulin, para sa bawat tungkulin ay mayroong isang hiwalay na direktoryo na may parehong pangalan, sa loob ng mga gawain, mga file, mga template, atbp na direktoryo
Gumawa tayo ng istraktura ng file: ./ansible/roles/user/tasks/main.yml (pangunahin ang pangunahing file na ilo-load at isasagawa kapag ang isang tungkulin ay konektado sa playbook; ang iba pang mga papel na file ay maaaring konektado dito). Ngayon ay maaari mong ilipat ang lahat ng mga gawaing nauugnay sa user sa file na ito:

# 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

Sa pangunahing playbook, dapat mong tukuyin na gamitin ang tungkulin ng user:

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

Gayundin, maaaring makatuwiran na i-update ang system bago ang lahat ng iba pang mga gawain; para magawa ito, maaari mong palitan ang pangalan ng block tasks kung saan ang mga ito ay tinukoy sa pre_tasks.

Pagse-set up ng nginx

Dapat ay mayroon na tayong naka-install na Nginx; kailangan natin itong i-configure at patakbuhin ito. Gawin na natin agad sa role. Gumawa tayo ng istraktura ng file:

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

Ngayon kailangan namin ng mga file at template. Ang pagkakaiba sa pagitan ng mga ito ay ang ansible na kinokopya ang mga file nang direkta, gaya ng dati. At ang mga template ay dapat may j2 extension at maaari silang gumamit ng mga variable na halaga gamit ang parehong double curly braces.

Paganahin natin ang nginx main.yml file. Para dito mayroon kaming systemd module:

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

Dito hindi lamang natin sinasabi na dapat simulan ang nginx (iyon ay, inilunsad natin ito), ngunit agad nating sinasabi na dapat itong paganahin.
Ngayon, kopyahin natin ang mga configuration file:

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

Lumilikha kami ng pangunahing file ng pagsasaayos ng nginx (maaari mo itong kunin nang direkta mula sa server, o isulat ito mismo). At gayundin ang configuration file para sa aming aplikasyon sa sites_available na direktoryo (ito ay hindi kinakailangan ngunit kapaki-pakinabang). Sa unang kaso, ginagamit namin ang copy module para kumopya ng mga file (dapat nasa loob ang file /ansible/roles/nginx/files/nginx.conf). Sa pangalawa, kinopya namin ang template, pinapalitan ang mga halaga ng mga variable. Ang template ay dapat na nasa /ansible/roles/nginx/templates/my_app.j2). At maaaring ganito ang hitsura nito:

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

Bigyang-pansin ang mga pagsingit {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} β€” ito ang lahat ng mga variable na ang mga halaga ng Ansible ay papalitan sa template bago kopyahin. Ito ay kapaki-pakinabang kung gagamit ka ng playbook para sa iba't ibang grupo ng mga host. Halimbawa, maaari naming idagdag ang aming file ng imbentaryo:

[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

Kung ilulunsad namin ngayon ang aming playbook, isasagawa nito ang mga tinukoy na gawain para sa parehong mga host. Ngunit sa parehong oras, para sa isang staging host, ang mga variable ay magiging iba mula sa mga production, at hindi lamang sa mga tungkulin at playbook, kundi pati na rin sa nginx configs. {{ inventory_hostname }} hindi kailangang tukuyin sa file ng imbentaryo - ito espesyal na ansible variable at ang host kung saan kasalukuyang tumatakbo ang playbook ay naka-imbak doon.
Kung gusto mong magkaroon ng file ng imbentaryo para sa ilang host, ngunit tumakbo lang para sa isang grupo, maaari itong gawin gamit ang sumusunod na command:

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

Ang isa pang opsyon ay ang magkaroon ng hiwalay na mga file ng imbentaryo para sa iba't ibang grupo. O maaari mong pagsamahin ang dalawang diskarte kung marami kang magkakaibang host.

Bumalik tayo sa pag-set up ng nginx. Pagkatapos kopyahin ang mga configuration file, kailangan naming gumawa ng symlink sa sitest_enabled sa my_app.conf mula sa sites_available. At i-restart ang 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

Ang lahat ay simple dito - muli ansible module na may isang medyo karaniwang syntax. Ngunit may isang punto. Walang punto sa pag-restart ng nginx sa bawat oras. Napansin mo ba na hindi kami nagsusulat ng mga utos tulad ng: "gawin ito tulad nito", ang syntax ay mas mukhang "ito ay dapat magkaroon ng ganitong estado". At kadalasan ito ay eksakto kung paano gumagana ang ansible. Kung umiiral na ang grupo, o naka-install na ang system package, susuriin ito ng ansible at laktawan ang gawain. Gayundin, hindi makokopya ang mga file kung ganap na tumugma ang mga ito sa kung ano ang nasa server na. Maaari naming samantalahin ito at i-restart ang nginx lamang kung ang mga file ng pagsasaayos ay nabago. Mayroong isang direktiba sa pagpaparehistro para dito:

# 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

Kung magbabago ang isa sa mga configuration file, gagawa ng kopya at irerehistro ang variable restart_nginx. At kung ang variable na ito ay nairehistro na lamang ang serbisyo ay mai-restart.

At, siyempre, kailangan mong idagdag ang papel ng nginx sa pangunahing playbook.

Pagse-set up ng postgresql

Kailangan nating paganahin ang postgresql gamit ang systemd sa parehong paraan tulad ng ginawa natin sa nginx, at lumikha din ng user na gagamitin natin para ma-access ang database at ang database mismo.
Gumawa tayo ng papel /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 }}"

Hindi ko ilalarawan kung paano magdagdag ng mga variable sa imbentaryo, ito ay nagawa nang maraming beses, pati na rin ang syntax ng postgresql_db at postgresql_user modules. Higit pang impormasyon ay matatagpuan sa dokumentasyon. Ang pinaka-kagiliw-giliw na direktiba dito ay become_user: postgres. Ang katotohanan ay na sa pamamagitan ng default, ang postgres user lamang ang may access sa postgresql database at lokal lamang. Ang direktiba na ito ay nagpapahintulot sa amin na magsagawa ng mga utos sa ngalan ng user na ito (kung mayroon kaming access, siyempre).
Gayundin, maaaring kailanganin mong magdagdag ng linya sa pg_hba.conf upang payagan ang isang bagong user na ma-access ang database. Magagawa ito sa parehong paraan tulad ng binago namin ang nginx config.

At siyempre, kailangan mong idagdag ang postgresql role sa pangunahing playbook.

Pag-install ng ruby ​​​​sa pamamagitan ng rbenv

Ang Ansible ay walang mga module para sa pagtatrabaho sa rbenv, ngunit na-install ito sa pamamagitan ng pag-clone ng isang git repository. Samakatuwid, ang problemang ito ay nagiging pinaka-hindi pamantayan. Gumawa tayo ng papel para sa kanya /ansible/roles/ruby_rbenv/main.yml at simulan natin itong punan:

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

Muli naming ginagamit ang become_user na direktiba upang gumana sa ilalim ng user na ginawa namin para sa mga layuning ito. Dahil naka-install ang rbenv sa home directory nito, at hindi sa buong mundo. At ginagamit din namin ang git module para i-clone ang repository, na tumutukoy sa repo at dest.

Susunod, kailangan nating irehistro ang rbenv init sa bashrc at idagdag ang rbenv sa PATH doon. Para dito mayroon kaming lineinfile module:

- 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 -)"'

Pagkatapos ay kailangan mong i-install ang ruby_build:

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

At sa wakas ay i-install ang ruby. Ginagawa ito sa pamamagitan ng rbenv, iyon ay, sa simpleng utos ng 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

Sinasabi namin kung aling utos ang isasagawa at kung ano. Gayunpaman, narito natin ang katotohanan na ang ansible ay hindi nagpapatakbo ng code na nilalaman sa bashrc bago patakbuhin ang mga utos. Nangangahulugan ito na ang rbenv ay kailangang direktang tukuyin sa parehong script.

Ang susunod na problema ay dahil sa ang katunayan na ang shell command ay walang estado mula sa isang ansible punto ng view. Iyon ay, walang awtomatikong pagsusuri kung ang bersyon na ito ng ruby ​​​​ay naka-install o hindi. Magagawa natin ito sa ating sarili:

- 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

Ang natitira na lang ay mag-install ng bundler:

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

At muli, idagdag ang aming papel na ruby_rbenv sa pangunahing playbook.

Nakabahaging mga file.

Sa pangkalahatan, maaaring makumpleto ang pag-setup dito. Susunod, ang lahat na natitira ay upang patakbuhin ang capistrano at ito ay kopyahin ang code mismo, lumikha ng mga kinakailangang direktoryo at ilunsad ang application (kung ang lahat ay na-configure nang tama). Gayunpaman, ang capistrano ay madalas na nangangailangan ng mga karagdagang configuration file, gaya ng database.yml o .env Maaari silang kopyahin tulad ng mga file at template para sa nginx. Mayroon lamang isang subtlety. Bago kopyahin ang mga file, kailangan mong lumikha ng isang istraktura ng direktoryo para sa kanila, tulad nito:

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

tumukoy lamang kami ng isang direktoryo at ang ansible ay awtomatikong lilikha ng mga magulang kung kinakailangan.

Ansible Vault

Napag-alaman na namin na ang mga variable ay maaaring maglaman ng lihim na data gaya ng password ng user. Kung nakalikha ka .env file para sa aplikasyon, at database.yml pagkatapos ay dapat mayroong higit pang mga kritikal na data. Magiging mainam na itago ang mga ito mula sa prying eyes. Para sa layuning ito ito ay ginagamit ansible vault.

Gumawa tayo ng file para sa mga variable /ansible/vars/all.yml (dito maaari kang lumikha ng iba't ibang mga file para sa iba't ibang grupo ng mga host, tulad ng sa file ng imbentaryo: production.yml, staging.yml, atbp).
Ang lahat ng mga variable na dapat i-encrypt ay dapat ilipat sa file na ito gamit ang karaniwang yml 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

Pagkatapos kung saan maaaring ma-encrypt ang file na ito gamit ang command:

ansible-vault encrypt ./vars/all.yml

Naturally, kapag nag-encrypt, kakailanganin mong magtakda ng password para sa pag-decryption. Makikita mo kung ano ang nasa loob ng file pagkatapos tawagan ang utos na ito.

Sa pamamagitan ng ansible-vault decrypt ang file ay maaaring i-decrypt, mabago at pagkatapos ay i-encrypt muli.

Hindi mo kailangang i-decrypt ang file para gumana. Iimbak mo itong naka-encrypt at patakbuhin ang playbook na may argumento --ask-vault-pass. Hihilingin ng Ansible ang password, kunin ang mga variable, at isasagawa ang mga gawain. Ang lahat ng data ay mananatiling naka-encrypt.

Ang kumpletong command para sa ilang grupo ng mga host at ansible vault ay magiging ganito ang hitsura:

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

Ngunit hindi ko ibibigay sa iyo ang buong teksto ng mga playbook at mga tungkulin, isulat mo ito sa iyong sarili. Dahil ganoon ang ansible - kung hindi mo naiintindihan kung ano ang kailangang gawin, hindi ito gagawin para sa iyo.

Pinagmulan: www.habr.com

Magdagdag ng komento