Serverconfiguratie voor het implementeren van een Rails-applicatie met Ansible

Niet zo lang geleden moest ik enkele Ansible-playbooks schrijven om een server voor te bereiden op de deploy van een Rails-applicatie. Tot mijn verbazing kon ik geen eenvoudige stapsgewijze handleiding vinden. Ik wilde geen bestaand playbook kopiƫren zonder te begrijpen wat er aan de hand was, dus moest ik de documentatie lezen en alles zelf samenstellen. Misschien kan ik iemand helpen dit proces te versnellen met behulp van dit artikel.

Allereerst is het belangrijk om te begrijpen dat Ansible je een gebruiksvriendelijke interface biedt om een vooraf gedefinieerde lijst met acties op een of meerdere externe servers via SSH uit te voeren. Hier is geen enkele magie, je kunt geen plugin installeren en direct een zero downtime deploy van je applicatie met Docker, monitoring en andere voordelen krijgen. Om een playbook te schrijven moet je weten wat je precies wilt doen en hoe je dat kunt doen. Daarom ben ik niet tevreden met kant-en-klare playbooks van GitHub, of artikelen zoals: 'Kopieer en voer uit, het zal werken.'

Wat hebben we nodig?

Zoals ik al zei, om een playbook te schrijven moet je weten wat je wilt doen en hoe je dat kunt doen. Laten we bepalen wat we nodig hebben. Voor een Rails-applicatie hebben we een aantal systeem pakketten nodig: nginx, postgresql (redis, enz.). Daarnaast hebben we Ruby van een bepaalde versie nodig. Het installeren van Ruby gaat het beste via rbenv (rvm, asdf…). Alles onder de root-gebruiker draaien is altijd een slechte idee, daarom moeten we een aparte gebruiker aanmaken en rechten instellen. Daarna moeten we onze code naar de server uploaden, configuratiebestanden voor nginx, postgres, enz. kopiĆ«ren en al deze services opstarten.

Uiteindelijk is de volgorde van de acties als volgt:

  1. Inloggen als root
  2. Installeer de systeem pakketten
  3. Maak een nieuwe gebruiker aan, stel rechten en een SSH-sleutel in
  4. Stel de systeem pakketten in (nginx enz.) en start ze op
  5. Maak een gebruiker aan in de database (je kunt ook gelijk een database aanmaken)
  6. Log in als nieuwe gebruiker
  7. Installeer rbenv en Ruby
  8. Installeer Bundler
  9. Upload de applicatiecode
  10. Start de Puma-server

Deze laatste stappen kunnen ook met Capistrano worden uitgevoerd, in ieder geval kan Capistrano out-of-the-box de code naar de release-directories kopiƫren, de release-switch met een symlink bij een succesvolle deploy uitvoeren, configuraties uit de gedeelde directory kopiƫren, Puma herstarten, enz. Dit kan ook met Ansible, maar waarom?

Bestandsstructuur

Ansible heeft een strikte bestandsstructuur voor al zijn bestanden, daarom is het het beste om dit in een aparte directory te houden. Het maakt niet zo veel uit of dit in de rails applicatie zelf is, of apart. Je kunt bestanden in een aparte git-repository opslaan. Persoonlijk vond ik het het handigst om een directory ansible aan te maken in de /config directory van de rails applicatie en alles in ƩƩn repository op te slaan.

Eenvoudige Playbook

Een Playbook is een yml-bestand waarin met behulp van een speciale syntaxis is beschreven wat en hoe ansible moet doen. Laten we ons eerste playbook maken dat niets doet:

---
- naam: Eenvoudig playbook
  hosts: all

Hier geven we gewoon aan dat ons playbook wordt genoemd Eenvoudige Playbook en dat de inhoud ervan uitgevoerd moet worden voor alle hosts. We kunnen het opslaan in de /ansible directory met de naam playbook.yml en proberen het uit te voeren:

ansible-playbook ./playbook.yml

PLAY [Eenvoudig Playbook] ************************************************************************************************************************************
skipping: geen hosts gevonden

Ansible zegt dat het geen hosts kent die overeenkomen met de lijst all. Ze moeten worden opgesomd in een speciale inventory bestand.

Laten we het aanmaken in dezelfde ansible directory:

123.123.123.123

Zo geven we eenvoudig een host op (bij voorkeur de host van je VPS voor tests, of je kunt localhost opgeven) en slaan het op onder de naam inventaris.
Je kunt proberen ansible uit te voeren met het inventory bestand:

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

TAKEN [Verzamelen van Feiten] ************************************************************************************************************************************

PLAY SAMENVATTING ************************************************************************************************************************************

Als je toegang hebt via ssh tot de opgegeven host, dan maakt ansible verbinding en verzamelt het informatie over het externe systeem. (standaard TAKEN [Verzamelen van Feiten]) waarna het een kort verslag geeft van de uitvoering (PLAY SAMENVATTING).

Standaard wordt de gebruikersnaam gebruikt waarmee je ingelogd bent in het systeem voor de verbinding. Op de host is deze, waarschijnlijk, niet aanwezig. In het playbook bestand kun je opgeven welke gebruiker gebruikt moet worden voor de verbinding met de directive remote_user. Ook is informatie over het externe systeem voor jou vaak niet nodig en kun je tijd besparen door de verzameling ervan uit te schakelen:

---
- naam: Eenvoudig playbook
  hosts: all
  remote_user: root
  become: true
  gather_facts: nee

Probeer het playbook opnieuw uit te voeren en zorg ervoor dat de verbinding werkt. (Als je de root-gebruiker hebt opgegeven, moet je ook de directive become: true opgeven voor hogere rechten. Zoals in de documentatie staat: become ingesteld op 'true'/'yes' om privilege-escalatie te activeren. hoewel het niet helemaal duidelijk is waarom).

Mogelijk ontvang je een fout die wordt veroorzaakt doordat Ansible de Python-interpreter niet kan bepalen, dit kun je handmatig opgeven:

ansible_python_interpreter: /usr/bin/python3 

waar je Python hebt staan, kun je te weten komen met het commando whereis python.

Installatie van systeem pakketten

De standaardversie van Ansible bevat veel modules voor het werken met verschillende systeem pakketten, waardoor we niet voor elk probleem bash-scripts hoeven te schrijven. Nu hebben we een van deze modules nodig om het systeem bij te werken en systeem pakketten te installeren. Ik gebruik Ubuntu Linux op mijn VPS, dus voor het installeren van pakketten gebruik ik apt-get en de module hiervoor. Als je een ander operating systeem gebruikt, heb je mogelijk een andere module nodig (vergeet niet dat ik in het begin zei dat je van tevoren moet weten wat en hoe je het gaat doen). De syntaxis zal echter waarschijnlijk vergelijkbaar zijn.

Laten we ons playbook aanvullen met de eerste taken:

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

  taken:
    - name: Systeem bijwerken
      apt: update_cache=yes
    - name: Installeer systeemafhankelijkheden
      apt:
        naam: git,nginx,redis,postgresql,postgresql-contrib
        status: aanwezig

Taak — dit is de taak die Ansible op de externe servers zal uitvoeren. We geven de taak een naam om het uitvoeren ervan in de log bij te houden. En beschrijven, met behulp van de syntaxis van de specifieke module, wat hij moet doen. In dit geval apt: update_cache=yes — zegt dat de pakketten van het systeem moeten worden bijgewerkt met de apt-module. De tweede opdracht is iets ingewikkelder. We geven de apt-module een lijst met pakketten en vertellen dat hun state moet worden present, dat wil zeggen we zeggen deze pakketten te installeren. Op een vergelijkbare manier kunnen we zeggen dat ze moeten worden verwijderd of bijgewerkt door eenvoudigweg te veranderen state. Let op dat we voor Rails met PostgreSQL het pakket postgresql-contrib nodig hebben, dat we nu installeren. Dit moet je opnieuw weten en doen, Ansible doet dit niet automatisch.

Probeer het playbook opnieuw uit te voeren en te controleren of de pakketten worden geĆÆnstalleerd.

Nieuwe gebruikers aanmaken.

Voor het werken met gebruikers heeft Ansible ook een module - user. Laten we nog een taak toevoegen (ik heb de al bekende delen van het playbook verborgen achter opmerkingen, zodat ik het niet elke keer helemaal hoef te kopiƫren):

---
- name: Eenvoudig playbook
  # ...
  tasks:
    # ...
    - name: Voeg een nieuwe gebruiker toe
      user:
        name: my_user
        shell: /bin/bash
        password: "{{ 123qweasd | password_hash('sha512') }}"

We creƫren een nieuwe gebruiker, stellen zijn shell en wachtwoord in. En dan stoten we al snel op verschillende problemen. Wat als gebruikersnamen verschillend moeten zijn voor verschillende hosts? En het is ook een heel slecht idee om het wachtwoord in platte tekst in het playbook op te slaan. Laten we beginnen met het extraheren van de gebruikersnaam en het wachtwoord naar variabelen, en aan het eind van het artikel laat ik zien hoe we het wachtwoord kunnen versleutelen.

---
- name: Eenvoudig playbook
  # ...
  tasks:
    # ...
    - name: Voeg een nieuwe gebruiker toe
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"

Met behulp van dubbele accolades worden variabelen in playbooks ingesteld.

We zullen de waarden van de variabelen in het inventory-bestand opgeven:

123.123.123.123

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

Let op de directive [all:vars] — dit geeft aan dat de volgende tekstblock variabelen (vars) zijn en dat ze toepasbaar zijn voor alle hosts (all).

Ook interessant is de constructie "{{ user_password | password_hash('sha512') }}". Het punt is dat Ansible de gebruiker niet toevoegt via user_add zoals je dat handmatig zou doen. Maar slaat alle gegevens direct op, waardoor we het wachtwoord ook van tevoren in een hash moeten omzetten, wat deze opdracht doet.

Laten we onze gebruiker aan de sudo-groep toevoegen. Maar voordat we dit doen, moeten we ervoor zorgen dat zo'n groep bestaat, want niemand zal dat voor ons doen:

---
- name: Eenvoudig playbook
  # ...
  tasks:
    # ...
    - name: Zorg ervoor dat er een 'sudo' groep is
      group:
        name: sudo
        state: present
    - name: Voeg een nieuwe gebruiker toe
      user:
        name: "{{ user }}"
        shell: /bin/bash
        password: "{{ user_password | password_hash('sha512') }}"
        groups: "sudo"

Het is allemaal vrij eenvoudig, we hebben ook de module group voor het aanmaken van groepen, met een syntaxis die erg lijkt op apt. Daarna is het voldoende om deze groep aan de gebruiker toe te wijzen (groups: "sudo").
Het is ook nuttig om deze gebruiker een ssh-sleutel toe te voegen, zodat we zonder wachtwoord onder deze gebruiker kunnen inloggen:

---
- naam: Simpele playbook
  # ...
  taken:
    # ...
    - naam: Zorg voor een 'sudo' groep
      groep:
      naam: sudo
        staat: aanwezig
    - naam: Voeg een nieuwe gebruiker toe
      gebruiker:
        naam: "{{ gebruiker }}"
        shell: /bin/bash
        wachtwoord: "{{ gebruiker_wachtwoord | password_hash('sha512') }}"
        groepen: "sudo"
    - naam: SSH Sleutel implementeren
      authorized_key:
        gebruiker: "{{ gebruiker }}"
        sleutel: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
        staat: aanwezig

In dit geval is de constructie interessant "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — het kopieert de inhoud van het bestand id_rsa.pub (de naam kan verschillen), dat wil zeggen, het publieke deel van de ssh-sleutel en laadt het in de lijst van geautoriseerde sleutels voor de gebruiker op de server.

Rollen

Alle drie de taken voor het aanmaken van de gebruiker kunnen eenvoudig aan ƩƩn groep taken worden toegeschreven, en het zou handig zijn om deze groep apart van de hoofdbestanden op te slaan, zodat deze niet te groot wordt. Hiervoor bestaan er in ansible roles.
Volgens de in het begin aangegeven mappenstructuur moeten rollen in een aparte map roles worden geplaatst, voor elke rol — een aparte map met dezelfde naam, binnen de map taken, bestanden, sjablonen, enz.
Laten we een mappenstructuur aanmaken: ./ansible/roles/user/tasks/main.yml (main is het hoofdbestand dat zal worden geladen en uitgevoerd wanneer de rol aan de playbook wordt gekoppeld; hierin kunnen andere rolbestanden worden aangesloten). Nu kunnen we alle taken met betrekking tot de gebruiker naar dit bestand verplaatsen:

# 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

In de hoofdbestanden moet worden aangegeven dat de rol gebruiker moet worden gebruikt:

---
- naam: Simpele playbook
  hosts: alle
  remote_user: root
  gather_facts: nee

  taken:
    - naam: Bijwerkensysteem
      apt: update_cache=yes
    - naam: Installeer systeemeisen
      apt:
        naam: git,nginx,redis,postgresql,postgresql-contrib
        staat: aanwezig

  rollen:
    - gebruiker

Het kan ook zinvol zijn om het systeem eerder bij te werken dan de andere taken; hiervoor kunnen we het blok hernoemen tasks waarin ze zijn gedefinieerd in pre_tasks.

Configuratie van nginx

Nginx zou al geïnstalleerd moeten zijn, we moeten het configureren en starten. Laten we dit meteen in de rol doen. We creëren een mappenstructuur:

- ansible
  - roles
    - nginx
      - bestanden
      - taken
        - main.yml
      - sjablonen

Nu hebben we bestanden en sjablonen nodig. Het verschil tussen hen is dat ansible bestanden direct kopieert, zoals ze zijn. Sjablonen moeten de extensie j2 hebben en kunnen variabele waarden bevatten met behulp van dezelfde dubbele accolades.

Laten we nginx opnemen in main.yml bestand. Hiervoor hebben we de systemd-module:

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

Hier zeggen we niet alleen dat nginx gestart moet worden (dat wil zeggen, we starten het), maar we geven ook meteen aan dat het ingeschakeld moet zijn.
Laten we de configuratiebestanden kopiƫren:

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

We maken het hoofdconfiguratiebestand van nginx aan (je kunt het direct van de server halen of zelf schrijven). En ook het configuratiebestand voor onze applicatie in de sites_available map (dit is niet verplicht, maar nuttig). In het eerste geval gebruiken we de copy-module om bestanden te kopiƫren (het bestand moet liggen in /ansible/roles/nginx/files/nginx.conf). In het tweede geval kopiƫren we de sjabloon, waarbij we de waarden van variabelen invullen. De sjabloon moet liggen in /ansible/roles/nginx/templates/my_app.j2). En het kan er ongeveer zo uitzien:

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

Let op de invoegingen: {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — dit zijn allemaal variabelen waarvan de waarden door Ansible in de sjabloon worden ingevuld voordat ze gekopieerd worden. Dit is nuttig als je de playbook voor verschillende groepen hosts gebruikt. Bijvoorbeeld, we kunnen ons inventory-bestand aanvullen:

[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

Als we nu onze playbook uitvoeren, worden de opgegeven taken voor beide hosts uitgevoerd. Maar de variabelen voor de staging-host zullen verschillen van die voor de productie, en niet alleen in rollen en playbooks, maar ook in de nginx-configuraties. {{ inventory_hostname }} je hoeft niet in het inventory-bestand op te nemen — dit is een speciale Ansible-variabele en daar wordt de host opgeslagen waarvoor de playbook op dat moment wordt uitgevoerd.
Als je een inventory-bestand voor meerdere hosts wilt hebben, maar alleen voor ƩƩn groep wilt uitvoeren, kun je de volgende opdracht gebruiken:

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

een andere optie is om aparte inventory-bestanden voor verschillende groepen te hebben. Of je kunt twee benaderingen combineren als je veel verschillende hosts hebt.

Laten we teruggaan naar de nginx-configuratie. Na het kopiƫren van de configuratiebestanden moeten we een symlink in sites_enabled naar my_app.conf vanuit sites_available maken. En herstart nginx.

... # oude code in mail.yml

- name: Maak symlink naar sites-enabled
  file:
    src: \/etc\/nginx\/sites-available\/my_app.conf
    dest: \/etc\/nginx\/sites-enabled\/my_app.conf
    state: link

- name: Herstart nginx
  service:
    name: nginx
    state: restarted

Hier is het eenvoudig — weer ansible-modules met een vrij standaard syntaxis. Maar er is ƩƩn punt. Het heeft geen zin om nginx telkens opnieuw op te starten. U heeft misschien opgemerkt dat we geen opdrachten schrijven zoals: "doe dit zo", de syntaxis lijkt meer op "dit moet deze toestand hebben". En vaak werkt ansible precies zo. Als de groep al bestaat of het systeempakket al is geĆÆnstalleerd, dan zal ansible dit controleren en de taak overslaan. Evenzo zullen bestanden niet worden gekopieerd als ze volledig overeenkomen met wat er al op de server staat. We kunnen hiervan profiteren en nginx alleen opnieuw opstarten als de configuratiebestanden zijn gewijzigd. Hiervoor is er de directieve 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

Als een van de configuratiebestanden wijzigt, zal de kopieeractie worden uitgevoerd en wordt er een variabele geregistreerd restart_nginx. En alleen als deze variabele is geregistreerd, zal de service opnieuw worden opgestart.

En natuurlijk moet de nginx-rol aan de hoofd-playbook worden toegevoegd.

Configuratie van postgresql

We moeten postgresql inschakelen via systemd, net zoals we dat met nginx deden, en ook een gebruiker aanmaken die we zullen gebruiken om toegang te krijgen tot de database, evenals de database zelf.
Laten we een rol aanmaken /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 }}"

Ik zal niet uitleggen hoe variabelen aan inventory moeten worden toegevoegd, dat is al vele keren gedaan, net als de syntaxis van de modules postgresql_db en postgresql_user. Meer gegevens zijn te vinden in de documentatie. Het meest interessante hier is de directieve become_user: postgres. Het punt is dat standaard alleen de gebruiker postgres toegang heeft tot de postgresql-database en alleen lokaal. Deze directieve stelt ons in staat om commando's uit te voeren namens deze gebruiker (als we natuurlijk toegang hebben).
Misschien moet u ook een regel in pg_hba.conf toevoegen om toegang te geven aan de nieuwe gebruiker tot de database. Dit kan ook op dezelfde manier worden gedaan als we de nginx-configuratie wijzigden.

En natuurlijk moet de postgresql-rol aan de hoofd-playbook worden toegevoegd.

Ruby installeren via rbenv

In ansible zijn er geen modules voor het werken met rbenv, en het wordt geĆÆnstalleerd door het klonen van een git-repository. Daarom wordt deze taak de meest ongewone. Laten we een rol hiervoor aanmaken /ansible/roles/ruby_rbenv/main.yml en deze beginnen in te vullen:

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

We gebruiken opnieuw de become_user-directive om te werken vanuit de gebruiker die we hiervoor hebben aangemaakt. Aangezien rbenv wordt geĆÆnstalleerd in zijn home-directory en niet globaal. We gebruiken ook de git-module om de repository te klonen, waarbij we repo en dest opgeven.

Vervolgens moeten we rbenv init in bashrc schrijven en rbenv aan de PATH toevoegen. Hiervoor hebben we de module lineinfile:

- naam: Voeg rbenv toe aan PATH
  become_user: "{{ user }}"
  lineinfile:
    path: ~\/ .bashrc
    state: present
    line: 'export PATH="${HOME}\/ .rbenv\/bin:${PATH}"'

- naam: Voeg rbenv init toe aan bashrc
  become_user: "{{ user }}"
  lineinfile:
    path: ~\/ .bashrc
    state: present
    line: 'eval "$(rbenv init -)"'

Daarna moeten we ruby_build installeren:

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

En als laatste installeren we ruby. Dit gebeurt via rbenv, eenvoudigweg met een bash-commando:

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

We geven aan welke opdracht we willen uitvoeren en waarmee. Echter, hier stuiten we op het feit dat ansible geen code in bashrc uitvoert voordat het de opdrachten uitvoert. Dit betekent dat rbenv direct in hetzelfde script moet worden gedefinieerd.

Het volgende probleem is dat de shell-opdracht geen staat heeft vanuit het perspectief van ansible. Dit betekent dat er geen automatische controle zal zijn of deze versie van ruby is geĆÆnstalleerd of niet — dat moeten we zelf doen:

- naam: Installeer 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

En dan resteert het om bundler te installeren:

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

En weer moeten we onze ruby_rbenv rol toevoegen aan de hoofdplaybook.

Gedeelde bestanden.

Over het algemeen zou de configuratie hier kunnen eindigen. Vervolgens moet capistrano worden uitgevoerd en zal het zelf de code kopiƫren, de benodigde mappen aanmaken en de applicatie starten (als alles correct is ingesteld). Echter, capistrano vereist vaak extra configuratiebestanden, zoals database.yml of .env Deze kunnen op dezelfde manier worden gekopieerd als bestanden en sjablonen voor nginx. Er is echter ƩƩn nuance. Voor het kopiƫren van de bestanden moet de mappenstructuur worden aangemaakt, iets als dit:

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

we geven slechts ƩƩn map op en ansible zal automatisch de bovenliggende mappen aanmaken indien nodig.

Ansible Vault

We have already encountered that sensitive information such as user passwords can be found in variables. If you have created .env a file for the application, and database.yml there should be even more critical data there. It is advisable to hide them from prying eyes. For this, ansible vault.

Let's create a file for the variables. /ansible/vars/all.yml (different files can be created for different host groups, just like in the inventory file: production.yml, staging.yml, etc.).
All variables that need to be encrypted must be transferred to this file using standard 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

After that, this file can be encrypted with the command:

ansible-vault encrypt ./vars/all.yml

Of course, when encrypting, you will need to set a password for decryption. You can check what will be inside the file after executing this command.

Met behulp van ansible-vault decrypt the file can be decrypted, modified, and then encrypted again.

For operation, you do not need to decrypt the file. You keep it in encrypted form 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 multiple host groups 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 playbooks and roles, write them yourself. Because ansible is such a thing — if you don’t understand what you need to do, it won’t do it for you either.

Bron: habr.com

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers šŸ”„ Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster