Agordante Servilon por Deploji Rails-Aplikon Uzanta Ansible

Antaŭ nelonge mi bezonis verki plurajn Ansible-ludlibrojn por prepari la servilon por deploji Rails-aplikaĵon. Kaj, mirinde, mi ne trovis simplan paŝon post paŝo manlibron. Mi ne volis kopii la ludlibron de aliulo sen kompreni kio okazas, kaj finfine mi devis legi la dokumentadon, kolektante ĉion mem. Eble mi povas helpi iun akceli ĉi tiun procezon helpe de ĉi tiu artikolo.

La unua afero por kompreni estas, ke ansible provizas al vi oportunan interfacon por plenumi antaŭdifinitan liston de agoj sur fora(j) servilo(j) per SSH. Ne estas magio ĉi tie, vi ne povas instali kromprogramon kaj akiri nulan malfunkcion de via aplikaĵo kun docker, monitorado kaj aliaj bonaĵoj el la skatolo. Por skribi ludlibron, vi devas scii, kion precize vi volas fari kaj kiel fari ĝin. Tial mi ne kontentas pri pretaj ludlibroj de GitHub, aŭ artikoloj kiel: "Kopiu kaj rulu, ĝi funkcios."

Kion ni bezonas?

Kiel mi jam diris, por verki ludlibron oni devas scii kion oni volas fari kaj kiel fari ĝin. Ni decidu kion ni bezonas. Por Rails-aplikaĵo ni bezonos plurajn sistemajn pakaĵojn: nginx, postgresql (redis, ktp). Krome, ni bezonas specifan version de rubeno. Plej bone estas instali ĝin per rbenv (rvm, asdf...). Ruli ĉion ĉi kiel radika uzanto ĉiam estas malbona ideo, do vi devas krei apartan uzanton kaj agordi liajn rajtojn. Post ĉi tio, vi devas alŝuti nian kodon al la servilo, kopii la agordojn por nginx, postgres, ktp kaj komenci ĉiujn ĉi tiujn servojn.

Kiel rezulto, la sekvenco de agoj estas kiel sekvas:

  1. Ensalutu kiel radiko
  2. instali sistemajn pakaĵojn
  3. krei novan uzanton, agordi rajtojn, ssh-klavon
  4. agordu sistemajn pakaĵojn (nginx ktp) kaj rulu ilin
  5. Ni kreas uzanton en la datumbazo (vi povas tuj krei datumbazon)
  6. Ensalutu kiel nova uzanto
  7. Instalu rbenv kaj ruby
  8. Instalante la bundler
  9. Alŝutante la aplikaĵokodon
  10. Lanĉante la Puma-servilon

Plie, la lastaj stadioj povas esti faritaj per capistrano, almenaŭ el la skatolo ĝi povas kopii kodon en eldonajn dosierujojn, ŝanĝi la eldonon per simbolligo post sukcesa deplojo, kopii agordojn de komuna dosierujo, rekomenci puma, ktp. Ĉio ĉi povas esti farita uzante Ansible, sed kial?

Dosiera strukturo

Ansible havas striktan dosiero strukturo por ĉiuj viaj dosieroj, do plej bone konservi ĉion en aparta dosierujo. Krome, ne tiom gravas ĉu ĝi estos en la fervoja aplikaĵo mem, aŭ aparte. Vi povas stoki dosierojn en aparta git-deponejo. Persone, mi trovis plej oportuna krei ansible-dosierujon en la dosierujo /config de la rails-aplikaĵo kaj konservi ĉion en unu deponejo.

Simpla Ludlibro

Playbook estas yml-dosiero kiu, uzante specialan sintakson, priskribas kion Ansible devas fari kaj kiel. Ni kreu la unuan ludlibron kiu faras nenion:

---
- name: Simple playbook
  hosts: all

Ĉi tie ni simple diras, ke nia ludlibro nomiĝas Simple Playbook kaj ke ĝia enhavo estu ekzekutita por ĉiuj gastigantoj. Ni povas konservi ĝin en /ansible dosierujo kun la nomo playbook.yml kaj provu kuri:

ansible-playbook ./playbook.yml

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

Ansible diras, ke ĝi ne konas iujn ajn gastigantojn, kiuj kongruas kun la tuta listo. Ili devas esti listigitaj en specialaĵo inventaro dosiero.

Ni kreu ĝin en la sama ansible dosierujo:

123.123.123.123

Jen kiel ni simple specifas la gastiganton (idee la gastiganto de nia VPS por testado, aŭ vi povas registri lokagastiganton) kaj konservi ĝin sub la nomo inventory.
Vi povas provi ruli ansible per inventa dosiero:

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

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

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

Se vi havas ssh-aliron al la specifita gastiganto, tiam ansible konektos kaj kolektos informojn pri la fora sistemo. (defaŭlta TASKO [Kunkonti Faktojn]) post kiu ĝi donos mallongan raporton pri la ekzekuto (LUDIR RECAP).

Defaŭlte, la konekto uzas la uzantnomon sub kiu vi estas ensalutinta en la sistemon. Ĝi plej verŝajne ne estos sur la gastiganto. En la ludlibro-dosiero, vi povas specifi kiun uzanton uzi por konekti per la direktivo remote_user. Ankaŭ, informoj pri fora sistemo povas ofte esti nenecesa por vi kaj vi ne devus perdi tempon kolekti ĝin. Ĉi tiu tasko ankaŭ povas esti malŝaltita:

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

Provu ruli la ludlibron denove kaj certigu, ke la konekto funkcias. (Se vi specifis la radikan uzanton, tiam vi ankaŭ devas specifi la iĝi: vera direktivo por akiri altigitajn rajtojn. Kiel skribite en la dokumentado: become set to ‘true’/’yes’ to activate privilege escalation. kvankam ne estas tute klare kial).

Eble vi ricevos eraron kaŭzitan de la fakto, ke ansible ne povas determini la Python-interpretilon, tiam vi povas specifi ĝin permane:

ansible_python_interpreter: /usr/bin/python3 

Vi povas ekscii kie vi havas python per la komando whereis python.

Instalado de sistemaj pakoj

La norma distribuo de Ansible inkluzivas multajn modulojn por labori kun diversaj sistemaj pakoj, do ni ne devas skribi bash-skriptojn ial ajn. Nun ni bezonas unu el ĉi tiuj moduloj por ĝisdatigi la sistemon kaj instali sistemajn pakaĵojn. Mi havas Ubuntu Linukson sur mia VPS, do por instali pakaĵojn mi uzas apt-get и modulo por ĝi. Se vi uzas alian operaciumon, tiam vi eble bezonos alian modulon (memoru, mi diris komence, ke ni bezonas scii anticipe kion kaj kiel ni faros). Tamen, la sintakso plej verŝajne estos simila.

Ni kompletigu nian ludlibron per la unuaj taskoj:

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

Tasko estas ĝuste la tasko, kiun Ansible plenumos sur foraj serviloj. Ni donas nomon al la tasko por ke ni povu spuri ĝian ekzekuton en la protokolo. Kaj ni priskribas, uzante la sintakson de specifa modulo, kion ĝi devas fari. Tiuokaze apt: update_cache=yes - diras ĝisdatigi sistemajn pakaĵojn uzante la apt-modulon. La dua komando estas iom pli komplika. Ni pasas liston de pakaĵoj al la apt-modulo kaj diras, ke ili estas state devus fariĝi present, tio estas, ni diras instali ĉi tiujn pakaĵojn. Simile, ni povas diri al ili forigi ilin, aŭ ĝisdatigi ilin simple ŝanĝante state. Bonvolu noti, ke por ke reloj funkciu kun postgresql ni bezonas la postgresql-contrib-pakaĵon, kiun ni nun instalas. Denove, vi devas scii kaj fari ĉi tion; ansible per si mem ne faros tion.

Provu ruli la ludlibron denove kaj kontrolu, ke la pakaĵoj estas instalitaj.

Krei novajn uzantojn.

Por labori kun uzantoj, Ansible ankaŭ havas modulon - uzanto. Ni aldonu ankoraŭ unu taskon (mi kaŝis la jam konatajn partojn de la ludlibro malantaŭ la komentoj por ne ĉiufoje tute kopii ĝin):

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

Ni kreas novan uzanton, starigas skelon kaj pasvorton por ĝi. Kaj tiam ni renkontas plurajn problemojn. Kio se uzantnomoj devas esti malsamaj por malsamaj gastigantoj? Kaj konservi la pasvorton en klara teksto en la ludlibro estas tre malbona ideo. Por komenci, ni metu la uzantnomon kaj pasvorton en variablojn, kaj al la fino de la artikolo mi montros kiel ĉifri la pasvorton.

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

Variabloj estas fiksitaj en ludlibroj uzante duoblajn buklajn krampojn.

Ni indikos la valorojn de la variabloj en la inventardosiero:

123.123.123.123

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

Bonvolu noti la direktivon [all:vars] - ĝi diras ke la sekva bloko de teksto estas variabloj (vars) kaj ili estas aplikeblaj al ĉiuj gastigantoj (ĉiuj).

La dezajno ankaŭ estas interesa "{{ user_password | password_hash('sha512') }}". La afero estas, ke ansible ne instalas la uzanton per user_add kiel vi farus ĝin permane. Kaj ĝi konservas ĉiujn datumojn rekte, tial ni ankaŭ devas antaŭe konverti la pasvorton en haŝon, kion faras ĉi tiu komando.

Ni aldonu nian uzanton al la sudo-grupo. Tamen antaŭ tio ni devas certigi, ke tia grupo ekzistas ĉar neniu faros tion por ni:

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

Ĉio estas sufiĉe simpla, ni ankaŭ havas grupan modulon por krei grupojn, kun sintakso tre simila al apt. Tiam sufiĉas registri ĉi tiun grupon al la uzanto (groups: "sudo").
Ankaŭ utilas aldoni ssh-ŝlosilon al ĉi tiu uzanto, por ke ni povu ensaluti uzante ĝin sen pasvorto:

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

En ĉi tiu kazo, la dezajno estas interesa "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — ĝi kopias la enhavon de la id_rsa.pub dosiero (via nomo povas esti malsama), tio estas, la publika parto de la ssh-ŝlosilo kaj alŝutas ĝin al la listo de rajtigitaj ŝlosiloj por la uzanto sur la servilo.

Roloj

Ĉiuj tri taskoj por krei uzon facile povas esti klasifikitaj en unu grupon da taskoj, kaj estus bona ideo konservi ĉi tiun grupon aparte de la ĉefa ludlibro por ke ĝi ne tro grandas. Tiucele, Ansible havas roloj.
Laŭ la dosierstrukturo indikita ĉe la komenco, roloj devas esti metitaj en apartan dosierujon de roloj, por ĉiu rolo estas aparta dosierujo kun la sama nomo, ene de la dosierujo de taskoj, dosieroj, ŝablonoj ktp.
Ni kreu dosierstrukturon: ./ansible/roles/user/tasks/main.yml (ĉefa estas la ĉefa dosiero, kiu estos ŝarĝita kaj efektivigita kiam rolo estas konektita al la ludlibro; aliaj roldosieroj povas esti konektitaj al ĝi). Nun vi povas transdoni ĉiujn taskojn rilatajn al la uzanto al ĉi tiu dosiero:

# 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

En la ĉefa ludlibro, vi devas specifi uzi la uzantan rolon:

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

Ankaŭ, eble havas sencon ĝisdatigi la sistemon antaŭ ĉiuj aliaj taskoj; por fari tion, vi povas renomi la blokon tasks en kiu ili estas difinitaj en pre_tasks.

Agordo de nginx

Ni jam devus havi Nginx instalita; ni devas agordi ĝin kaj ruli ĝin. Ni faru ĝin tuj en la rolo. Ni kreu dosierstrukturon:

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

Nun ni bezonas dosierojn kaj ŝablonojn. La diferenco inter ili estas, ke ansible kopias la dosierojn rekte, kiel estas. Kaj ŝablonoj devas havi la etendon j2 kaj ili povas uzi variajn valorojn uzante la samajn duoblajn krampojn.

Ni ebligu nginx en main.yml dosiero. Por tio ni havas systemd-modulon:

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

Ĉi tie ni ne nur diras, ke nginx devas esti komencita (tio estas, ni lanĉas ĝin), sed ni tuj diras, ke ĝi devas esti ebligita.
Nun ni kopiu la agordajn dosierojn:

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

Ni kreas la ĉefan agordan dosieron nginx (vi povas preni ĝin rekte de la servilo, aŭ skribi ĝin mem). Kaj ankaŭ la agorda dosiero por nia aplikaĵo en la dosierujo sites_available (ĉi tio ne estas necesa sed utila). En la unua kazo, ni uzas la kopimodulon por kopii dosierojn (la dosiero devas esti en /ansible/roles/nginx/files/nginx.conf). En la dua, ni kopias la ŝablonon, anstataŭigante la valorojn de la variabloj. La ŝablono devus esti en /ansible/roles/nginx/templates/my_app.j2). Kaj ĝi povus aspekti kiel ĉi tio:

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

Atentu la enmetojn {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — ĉi tiuj estas ĉiuj variabloj, kies valoroj Ansible anstataŭigos en la ŝablonon antaŭ kopii. Ĉi tio estas utila se vi uzas ludlibron por malsamaj grupoj de gastigantoj. Ekzemple, ni povas aldoni nian inventardosieron:

[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

Se ni nun lanĉos nian ludlibron, ĝi plenumos la specifitajn taskojn por ambaŭ gastigantoj. Sed samtempe, por ensceniga gastiganto, la variabloj estos malsamaj ol la produktadaj, kaj ne nur en roloj kaj ludlibroj, sed ankaŭ en nginx-agordoj. {{ inventory_hostname }} ne bezonas esti specifita en la inventardosiero - ĉi tio speciala ansible variablo kaj la gastiganto por kiu la ludlibro nuntempe funkcias estas konservita tie.
Se vi volas havi inventardosieron por pluraj gastigantoj, sed ruli nur por unu grupo, tio povas esti farita per la sekva komando:

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

Alia opcio estas havi apartajn inventardosierojn por malsamaj grupoj. Aŭ vi povas kombini la du alirojn se vi havas multajn malsamajn gastigantojn.

Ni reiru al agordo de nginx. Post kopiado de la agordaj dosieroj, ni devas krei simbolligon en sitest_enabled al my_app.conf de sites_available. Kaj rekomencu 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

Ĉio ĉi tie estas simpla - denove ansiblaj moduloj kun sufiĉe norma sintakso. Sed estas unu punkto. Ne utilas rekomenci nginx ĉiufoje. Ĉu vi rimarkis, ke ni ne skribas ordonojn kiel: "faru tion ĉi tiel", la sintakso aspektas pli kiel "ĉi tio devus havi ĉi tiun staton". Kaj plej ofte ĝuste tiel funkcias ansible. Se la grupo jam ekzistas, aŭ la sistempakaĵo jam estas instalita, tiam ansible kontrolos ĉi tion kaj preterlasos la taskon. Ankaŭ dosieroj ne estos kopiitaj se ili tute kongruas kun kio jam estas sur la servilo. Ni povas utiligi ĉi tion kaj rekomenci nginx nur se la agordaj dosieroj estis ŝanĝitaj. Estas registra direktivo por ĉi tio:

# 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

Se unu el la agordaj dosieroj ŝanĝiĝas, kopio estos farita kaj la variablo estos registrita restart_nginx. Kaj nur se ĉi tiu variablo estis registrita, la servo estos rekomencita.

Kaj, kompreneble, vi devas aldoni la nginx-rolon al la ĉefa ludlibro.

Agordi postgresql

Ni devas ebligi postgresql uzante systemd same kiel ni faris kun nginx, kaj ankaŭ krei uzanton, kiun ni uzos por aliri la datumbazon kaj la datumbazon mem.
Ni kreu rolon /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 }}"

Mi ne priskribos kiel aldoni variablojn al inventaro, tio jam estis farita multfoje, same kiel la sintakson de la postgresql_db kaj postgresql_user-moduloj. Pliaj informoj troveblas en la dokumentado. La plej interesa direktivo ĉi tie estas become_user: postgres. La fakto estas, ke defaŭlte, nur la postgres-uzanto havas aliron al la postgresql-datumbazo kaj nur loke. Ĉi tiu direktivo permesas al ni ekzekuti komandojn nome de ĉi tiu uzanto (se ni havas aliron, kompreneble).
Ankaŭ, vi eble devos aldoni linion al pg_hba.conf por permesi novan uzantan aliron al la datumbazo. Ĉi tio povas esti farita en la sama maniero kiel ni ŝanĝis la nginx-agordon.

Kaj kompreneble vi devas aldoni la postgresql-rolon al la ĉefa ludlibro.

Instalante ruby ​​​​per rbenv

Ansible ne havas modulojn por labori kun rbenv, sed ĝi estas instalita per klonado de git-deponejo. Tial ĉi tiu problemo fariĝas la plej ne-norma. Ni kreu rolon por ŝi /ansible/roles/ruby_rbenv/main.yml kaj ni komencu plenigi ĝin:

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

Ni denove uzas la direktivo diven_user por labori sub la uzanto, kiun ni kreis por ĉi tiuj celoj. Ĉar rbenv estas instalita en sia hejma dosierujo, kaj ne tutmonde. Kaj ni ankaŭ uzas la git-modulon por kloni la deponejon, specifante repo kaj dest.

Poste, ni devas registri rbenv init en bashrc kaj aldoni rbenv al PATH tie. Por tio ni havas la modulon 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 -)"'

Tiam vi devas instali ruby_build:

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

Kaj finfine instalu rubenon. Ĉi tio estas farita per rbenv, tio estas, simple per la bash-komando:

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

Ni diras, kiun ordonon ekzekuti kaj kun kio. Tamen, ĉi tie ni trovas la fakton, ke ansible ne rulas la kodon enhavitan en bashrc antaŭ ol ruli la komandojn. Ĉi tio signifas, ke rbenv devos esti difinita rekte en la sama skripto.

La sekva problemo ŝuldiĝas al la fakto, ke la ŝela komando ne havas staton el ansible vidpunkto. Tio estas, ne estos aŭtomata kontrolo ĉu ĉi tiu versio de ruby ​​​​estas instalita aŭ ne. Ni mem povas fari ĉi tion:

- 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

Restas nur instali bundler:

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

Kaj denove, aldonu nian rolon ruby_rbenv al la ĉefa ludlibro.

Komunaj dosieroj.

Ĝenerale, la aranĝo povus esti kompletigita ĉi tie. Poste, restas nur ruli capistrano kaj ĝi kopios la kodon mem, kreos la necesajn dosierujojn kaj lanĉos la aplikaĵon (se ĉio estas ĝuste agordita). Tamen, capistrano ofte postulas pliajn agordajn dosierojn, kiel ekzemple database.yml.env Ili povas esti kopiitaj same kiel dosieroj kaj ŝablonoj por nginx. Estas nur unu subtileco. Antaŭ ol kopii dosierojn, vi devas krei dosierujon por ili, ion kiel ĉi tio:

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

ni specifas nur unu dosierujon kaj ansible aŭtomate kreos gepatrojn se necese.

Ansible Vault

Ni jam trovis la fakton, ke variabloj povas enhavi sekretajn datumojn kiel la pasvorton de la uzanto. Se vi kreis .env dosiero por la aplikaĵo, kaj database.yml tiam devas esti eĉ pli da tiaj kritikaj datumoj. Estus bone kaŝi ilin de malklaraj okuloj. Tiucele ĝi estas uzata ansible volbo.

Ni kreu dosieron por variabloj /ansible/vars/all.yml (ĉi tie vi povas krei malsamajn dosierojn por malsamaj grupoj de gastigantoj, same kiel en la inventardosiero: production.yml, staging.yml, ktp).
Ĉiuj variabloj, kiuj devas esti ĉifritaj, devas esti transdonitaj al ĉi tiu dosiero uzante norman yml-sintakso:

# 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

Post tio ĉi tiu dosiero povas esti ĉifrita per la komando:

ansible-vault encrypt ./vars/all.yml

Kompreneble, dum ĉifrado, vi devos agordi pasvorton por deĉifrado. Vi povas vidi kio estos en la dosiero post vokado de ĉi tiu komando.

Kun la helpo de ansible-vault decrypt la dosiero povas esti deĉifrita, modifita kaj poste ĉifrita denove.

Vi ne bezonas deĉifri la dosieron por funkcii. Vi stokas ĝin ĉifrita kaj rulas la ludlibron kun la argumento --ask-vault-pass. Ansible petos la pasvorton, prenos la variablojn kaj plenumos la taskojn. Ĉiuj datumoj restos ĉifritaj.

La kompleta komando por pluraj grupoj de gastigantoj kaj ansible volbo aspektos kiel ĉi tio:

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

Sed mi ne donos al vi la plenan tekston de ludlibroj kaj roloj, skribu ĝin mem. Ĉar ansible estas tia - se vi ne komprenas, kion oni devas fari, tiam ĝi ne faros ĝin por vi.

fonto: www.habr.com

Aldoni komenton