Διαμόρφωση διακομιστή για ανάπτυξη εφαρμογής Rails χρησιμοποιώντας το Ansible

Πριν από λίγο καιρό χρειάστηκε να γράψω πολλά βιβλία Ansible για να προετοιμάσω τον διακομιστή για την ανάπτυξη μιας εφαρμογής Rails. Και, παραδόξως, δεν βρήκα ένα απλό εγχειρίδιο βήμα προς βήμα. Δεν ήθελα να αντιγράψω το playbook κάποιου άλλου χωρίς να καταλάβω τι συνέβαινε και στο τέλος έπρεπε να διαβάσω την τεκμηρίωση, συλλέγοντας τα πάντα μόνος μου. Ίσως μπορώ να βοηθήσω κάποιον να επιταχύνει αυτή τη διαδικασία με τη βοήθεια αυτού του άρθρου.

Το πρώτο πράγμα που πρέπει να καταλάβετε είναι ότι το ansible σας παρέχει μια βολική διεπαφή για την εκτέλεση μιας προκαθορισμένης λίστας ενεργειών σε έναν απομακρυσμένο διακομιστή(ες) μέσω SSH. Δεν υπάρχει κανένα μαγικό εδώ, δεν μπορείτε να εγκαταστήσετε ένα πρόσθετο και να λάβετε μηδενική ανάπτυξη χρόνου διακοπής της εφαρμογής σας με docker, παρακολούθηση και άλλα καλούδια από το κουτί. Για να γράψετε ένα playbook, πρέπει να ξέρετε τι ακριβώς θέλετε να κάνετε και πώς να το κάνετε. Γι' αυτό δεν είμαι ικανοποιημένος με έτοιμα βιβλία παιχνιδιού από το GitHub ή άρθρα όπως: "Αντιγράψτε και εκτελέστε, θα λειτουργήσει".

Αυτό που χρειαζόμαστε?

Όπως είπα ήδη, για να γράψετε ένα βιβλίο παιχνιδιού πρέπει να ξέρετε τι θέλετε να κάνετε και πώς να το κάνετε. Ας αποφασίσουμε τι χρειαζόμαστε. Για μια εφαρμογή Rails θα χρειαστούμε πολλά πακέτα συστήματος: nginx, postgresql (redis, κλπ). Επιπλέον, χρειαζόμαστε μια συγκεκριμένη έκδοση του ρουμπίνι. Είναι καλύτερο να το εγκαταστήσετε μέσω rbenv (rvm, asdf...). Η εκτέλεση όλων αυτών ως χρήστης root είναι πάντα κακή ιδέα, επομένως πρέπει να δημιουργήσετε έναν ξεχωριστό χρήστη και να διαμορφώσετε τα δικαιώματά του. Μετά από αυτό, πρέπει να ανεβάσετε τον κώδικά μας στον διακομιστή, να αντιγράψετε τις ρυθμίσεις παραμέτρων για nginx, postgres, κλπ και να ξεκινήσετε όλες αυτές τις υπηρεσίες.

Ως αποτέλεσμα, η σειρά των ενεργειών είναι η εξής:

  1. Είσοδος ως root
  2. εγκατάσταση πακέτων συστήματος
  3. δημιουργία νέου χρήστη, διαμόρφωση δικαιωμάτων, κλειδί ssh
  4. ρυθμίστε τα πακέτα συστήματος (nginx κλπ) και εκτελέστε τα
  5. Δημιουργούμε έναν χρήστη στη βάση δεδομένων (μπορείτε να δημιουργήσετε αμέσως μια βάση δεδομένων)
  6. Συνδεθείτε ως νέος χρήστης
  7. Εγκαταστήστε το rbenv και το ruby
  8. Εγκατάσταση του bundler
  9. Μεταφόρτωση του κωδικού εφαρμογής
  10. Εκκίνηση του διακομιστή Puma

Επιπλέον, τα τελευταία στάδια μπορούν να γίνουν χρησιμοποιώντας το capistrano, τουλάχιστον εκτός του κουτιού μπορεί να αντιγράψει κώδικα σε καταλόγους έκδοσης, να αλλάξει την έκδοση με έναν συμβολικό σύνδεσμο μετά την επιτυχή ανάπτυξη, να αντιγράψει ρυθμίσεις από έναν κοινόχρηστο κατάλογο, να επανεκκινήσει το puma κ.λπ. Όλα αυτά μπορούν να γίνουν χρησιμοποιώντας το Ansible, αλλά γιατί;

Δομή αρχείου

Το Ansible έχει αυστηρό δομή αρχείου για όλα τα αρχεία σας, επομένως είναι καλύτερο να τα διατηρείτε όλα σε ξεχωριστό κατάλογο. Επιπλέον, δεν είναι τόσο σημαντικό αν θα είναι στην ίδια την εφαρμογή ράγες ή χωριστά. Μπορείτε να αποθηκεύσετε αρχεία σε ξεχωριστό αποθετήριο git. Προσωπικά, το βρήκα πιο βολικό να δημιουργήσω έναν κατάλογο ansible στον κατάλογο /config της εφαρμογής rails και να αποθηκεύσω τα πάντα σε ένα αποθετήριο.

Απλό παιδαγωγικό βιβλίο

Το Playbook είναι ένα αρχείο yml που, χρησιμοποιώντας ειδική σύνταξη, περιγράφει τι και πώς πρέπει να κάνει το Ansible. Ας δημιουργήσουμε το πρώτο βιβλίο παιχνιδιού που δεν κάνει τίποτα:

---
- name: Simple playbook
  hosts: all

Εδώ λέμε απλά ότι το playbook μας ονομάζεται Simple Playbook και ότι τα περιεχόμενά του θα πρέπει να εκτελούνται για όλους τους κεντρικούς υπολογιστές. Μπορούμε να το αποθηκεύσουμε στον κατάλογο /ansible με το όνομα playbook.yml και προσπαθήστε να τρέξετε:

ansible-playbook ./playbook.yml

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

Η Ansible λέει ότι δεν γνωρίζει κεντρικούς υπολογιστές που να ταιριάζουν με τη λίστα όλων. Πρέπει να αναφέρονται σε ειδική αρχείο απογραφής.

Ας το δημιουργήσουμε στον ίδιο κατάλογο ansible:

123.123.123.123

Αυτός είναι ο τρόπος με τον οποίο απλώς καθορίζουμε τον κεντρικό υπολογιστή (ιδανικά τον κεντρικό υπολογιστή του VPS μας για δοκιμή, ή μπορείτε να εγγράψετε τον localhost) και τον αποθηκεύουμε με το όνομα inventory.
Μπορείτε να δοκιμάσετε να εκτελέσετε το ansible με ένα αρχείο αποθέματος:

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

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

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

Εάν έχετε πρόσβαση ssh στον καθορισμένο κεντρικό υπολογιστή, τότε το ansible θα συνδεθεί και θα συλλέξει πληροφορίες σχετικά με το απομακρυσμένο σύστημα. (προεπιλεγμένη ΕΡΓΑΣΙΑ [Συλλογή γεγονότων]) μετά την οποία θα δώσει μια σύντομη αναφορά για την εκτέλεση (PLAY RECAP).

Από προεπιλογή, η σύνδεση χρησιμοποιεί το όνομα χρήστη με το οποίο είστε συνδεδεμένοι στο σύστημα. Το πιθανότερο είναι ότι δεν θα είναι στον οικοδεσπότη. Στο αρχείο του βιβλίου αναπαραγωγής, μπορείτε να καθορίσετε ποιον χρήστη θα χρησιμοποιήσετε για σύνδεση χρησιμοποιώντας την οδηγία remote_user. Επίσης, οι πληροφορίες σχετικά με ένα απομακρυσμένο σύστημα μπορεί συχνά να είναι περιττές για εσάς και δεν πρέπει να χάνετε χρόνο για τη συλλογή τους. Αυτή η εργασία μπορεί επίσης να απενεργοποιηθεί:

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

Δοκιμάστε να εκτελέσετε ξανά το playbook και βεβαιωθείτε ότι η σύνδεση λειτουργεί. (Εάν προσδιορίσατε τον χρήστη root, τότε πρέπει επίσης να καθορίσετε την οδηγία be: true για να αποκτήσετε αυξημένα δικαιώματα. Όπως γράφεται στην τεκμηρίωση: become set to ‘true’/’yes’ to activate privilege escalation. αν και δεν είναι απολύτως σαφές γιατί).

Ίσως λάβετε ένα σφάλμα που προκαλείται από το γεγονός ότι το ansible δεν μπορεί να προσδιορίσει τον διερμηνέα Python, τότε μπορείτε να τον καθορίσετε με μη αυτόματο τρόπο:

ansible_python_interpreter: /usr/bin/python3 

Μπορείτε να μάθετε πού έχετε python με την εντολή whereis python.

Εγκατάσταση πακέτων συστήματος

Η τυπική διανομή του Ansible περιλαμβάνει πολλές ενότητες για εργασία με διάφορα πακέτα συστήματος, επομένως δεν χρειάζεται να γράφουμε σενάρια bash για κανένα λόγο. Τώρα χρειαζόμαστε μία από αυτές τις λειτουργικές μονάδες για να ενημερώσουμε το σύστημα και να εγκαταστήσουμε πακέτα συστήματος. Έχω Ubuntu Linux στο VPS μου, οπότε για να εγκαταστήσω πακέτα χρησιμοποιώ apt-get и μονάδα για αυτό. Εάν χρησιμοποιείτε διαφορετικό λειτουργικό σύστημα, τότε μπορεί να χρειαστείτε διαφορετική ενότητα (θυμηθείτε, είπα στην αρχή ότι πρέπει να ξέρουμε εκ των προτέρων τι και πώς θα κάνουμε). Ωστόσο, η σύνταξη πιθανότατα θα είναι παρόμοια.

Ας συμπληρώσουμε το βιβλίο μας με τις πρώτες εργασίες:

---
- 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 είναι ακριβώς η εργασία που θα εκτελέσει το Ansible σε απομακρυσμένους διακομιστές. Δίνουμε στην εργασία ένα όνομα για να μπορούμε να παρακολουθούμε την εκτέλεσή της στο αρχείο καταγραφής. Και περιγράφουμε, χρησιμοποιώντας τη σύνταξη μιας συγκεκριμένης ενότητας, τι πρέπει να κάνει. Σε αυτήν την περίπτωση apt: update_cache=yes - λέει για ενημέρωση πακέτων συστήματος χρησιμοποιώντας τη λειτουργική μονάδα apt. Η δεύτερη εντολή είναι λίγο πιο περίπλοκη. Περνάμε μια λίστα πακέτων στο apt module και λέμε ότι είναι state θα πρέπει να γίνει present, δηλαδή λέμε να εγκαταστήσουμε αυτά τα πακέτα. Με παρόμοιο τρόπο, μπορούμε να τους πούμε να τα διαγράψουν ή να τα ενημερώσουμε αλλάζοντας απλά state. Λάβετε υπόψη ότι για να λειτουργήσουν οι ράγες με το postgresql χρειαζόμαστε το πακέτο postgresql-contrib, το οποίο εγκαθιστούμε τώρα. Και πάλι, πρέπει να το γνωρίζετε και να το κάνετε αυτό· η ansible από μόνη της δεν θα το κάνει αυτό.

Δοκιμάστε να εκτελέσετε ξανά το playbook και ελέγξτε ότι τα πακέτα είναι εγκατεστημένα.

Δημιουργία νέων χρηστών.

Για να συνεργαστεί με χρήστες, το Ansible διαθέτει επίσης μια ενότητα - χρήστη. Ας προσθέσουμε μια ακόμη εργασία (έκρυψα τα ήδη γνωστά μέρη του playbook πίσω από τα σχόλια για να μην το αντιγράφω εξ ολοκλήρου κάθε φορά):

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

Δημιουργούμε έναν νέο χρήστη, ορίζουμε ένα σχήμα και έναν κωδικό πρόσβασης για αυτόν. Και τότε αντιμετωπίζουμε πολλά προβλήματα. Τι γίνεται αν τα ονόματα χρήστη πρέπει να είναι διαφορετικά για διαφορετικούς κεντρικούς υπολογιστές; Και η αποθήκευση του κωδικού πρόσβασης σε καθαρό κείμενο στο βιβλίο παιχνιδιού είναι μια πολύ κακή ιδέα. Αρχικά, ας βάλουμε το όνομα χρήστη και τον κωδικό πρόσβασης σε μεταβλητές και προς το τέλος του άρθρου θα δείξω πώς να κρυπτογραφείτε τον κωδικό πρόσβασης.

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

Οι μεταβλητές ορίζονται στα βιβλία παιχνιδιού χρησιμοποιώντας διπλά σγουρά άγκιστρα.

Θα υποδείξουμε τις τιμές των μεταβλητών στο αρχείο αποθέματος:

123.123.123.123

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

Παρακαλώ σημειώστε την οδηγία [all:vars] - λέει ότι το επόμενο μπλοκ κειμένου είναι οι μεταβλητές (vars) και ισχύουν για όλους τους κεντρικούς υπολογιστές (όλα).

Το σχέδιο είναι επίσης ενδιαφέρον "{{ user_password | password_hash('sha512') }}". Το θέμα είναι ότι το ansible δεν εγκαθιστά τον χρήστη μέσω user_add όπως θα το κάνατε χειροκίνητα. Και αποθηκεύει όλα τα δεδομένα απευθείας, γι' αυτό πρέπει επίσης να μετατρέψουμε τον κωδικό πρόσβασης σε κατακερματισμό εκ των προτέρων, κάτι που κάνει αυτή η εντολή.

Ας προσθέσουμε τον χρήστη μας στην ομάδα sudo. Ωστόσο, πριν από αυτό πρέπει να βεβαιωθούμε ότι υπάρχει μια τέτοια ομάδα γιατί κανείς δεν θα το κάνει αυτό για εμάς:

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

Όλα είναι αρκετά απλά, έχουμε επίσης μια ενότητα ομάδας για τη δημιουργία ομάδων, με σύνταξη πολύ παρόμοια με το apt. Τότε αρκεί να καταχωρήσετε αυτήν την ομάδα στον χρήστη (groups: "sudo").
Είναι επίσης χρήσιμο να προσθέσετε ένα κλειδί ssh σε αυτόν τον χρήστη, ώστε να μπορούμε να συνδεόμαστε χρησιμοποιώντας το χωρίς κωδικό πρόσβασης:

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

Σε αυτή την περίπτωση, το σχέδιο είναι ενδιαφέρον "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — αντιγράφει τα περιεχόμενα του αρχείου id_rsa.pub (το όνομά σας μπορεί να είναι διαφορετικό), δηλαδή το δημόσιο τμήμα του κλειδιού ssh και το ανεβάζει στη λίστα με τα εξουσιοδοτημένα κλειδιά για τον χρήστη στον διακομιστή.

Ρόλοι

Και οι τρεις εργασίες για τη δημιουργία χρήσης μπορούν εύκολα να ταξινομηθούν σε μία ομάδα εργασιών και θα ήταν καλή ιδέα να αποθηκεύσετε αυτήν την ομάδα ξεχωριστά από το κύριο βιβλίο, ώστε να μην μεγαλώσει πολύ. Για το σκοπό αυτό, η Ansible έχει ρόλους.
Σύμφωνα με τη δομή του αρχείου που υποδεικνύεται στην αρχή, οι ρόλοι πρέπει να τοποθετούνται σε ξεχωριστό κατάλογο ρόλων, για κάθε ρόλο υπάρχει ξεχωριστός κατάλογος με το ίδιο όνομα, μέσα στον κατάλογο εργασιών, αρχείων, προτύπων κ.λπ.
Ας δημιουργήσουμε μια δομή αρχείου: ./ansible/roles/user/tasks/main.yml (το κύριο είναι το κύριο αρχείο που θα φορτωθεί και θα εκτελεστεί όταν ένας ρόλος συνδεθεί στο βιβλίο αναπαραγωγής· μπορούν να συνδεθούν άλλα αρχεία ρόλων σε αυτό). Τώρα μπορείτε να μεταφέρετε όλες τις εργασίες που σχετίζονται με τον χρήστη σε αυτό το αρχείο:

# 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

Στο κύριο βιβλίο παιχνιδιού, πρέπει να καθορίσετε για να χρησιμοποιήσετε τον ρόλο χρήστη:

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

Επίσης, μπορεί να έχει νόημα να ενημερώσετε το σύστημα πριν από όλες τις άλλες εργασίες· για να το κάνετε αυτό, μπορείτε να μετονομάσετε το μπλοκ tasks στο οποίο ορίζονται σε pre_tasks.

Ρύθμιση του nginx

Θα πρέπει να έχουμε ήδη εγκαταστήσει το Nginx· πρέπει να το ρυθμίσουμε και να το εκτελέσουμε. Ας το κάνουμε αμέσως στον ρόλο. Ας δημιουργήσουμε μια δομή αρχείου:

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

Τώρα χρειαζόμαστε αρχεία και πρότυπα. Η διαφορά μεταξύ τους είναι ότι το ansible αντιγράφει τα αρχεία απευθείας, όπως είναι. Και τα πρότυπα πρέπει να έχουν την επέκταση j2 και μπορούν να χρησιμοποιούν μεταβλητές τιμές χρησιμοποιώντας τα ίδια διπλά σγουρά στηρίγματα.

Ας ενεργοποιήσουμε το nginx in main.yml αρχείο. Για αυτό έχουμε μια ενότητα systemd:

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

Εδώ όχι μόνο λέμε ότι πρέπει να ξεκινήσει το nginx (δηλαδή να το εκκινήσουμε), αλλά αμέσως λέμε ότι πρέπει να ενεργοποιηθεί.
Τώρα ας αντιγράψουμε τα αρχεία διαμόρφωσης:

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

Δημιουργούμε το κύριο αρχείο διαμόρφωσης nginx (μπορείτε να το πάρετε απευθείας από τον διακομιστή ή να το γράψετε μόνοι σας). Και επίσης το αρχείο ρυθμίσεων για την εφαρμογή μας στον κατάλογο sites_available (αυτό δεν είναι απαραίτητο αλλά χρήσιμο). Στην πρώτη περίπτωση, χρησιμοποιούμε τη μονάδα αντιγραφής για την αντιγραφή αρχείων (το αρχείο πρέπει να είναι μέσα /ansible/roles/nginx/files/nginx.conf). Στο δεύτερο, αντιγράφουμε το πρότυπο, αντικαθιστώντας τις τιμές των μεταβλητών. Το πρότυπο πρέπει να είναι μέσα /ansible/roles/nginx/templates/my_app.j2). Και μπορεί να μοιάζει κάπως έτσι:

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

Δώστε προσοχή στα ένθετα {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — αυτές είναι όλες οι μεταβλητές των οποίων οι τιμές το Ansible θα αντικαταστήσει στο πρότυπο πριν από την αντιγραφή. Αυτό είναι χρήσιμο εάν χρησιμοποιείτε ένα playbook για διαφορετικές ομάδες κεντρικών υπολογιστών. Για παράδειγμα, μπορούμε να προσθέσουμε το αρχείο αποθέματός μας:

[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

Εάν εκκινήσουμε τώρα το playbook μας, θα εκτελέσει τις καθορισμένες εργασίες και για τους δύο κεντρικούς υπολογιστές. Ταυτόχρονα όμως, για έναν οικοδεσπότη σταδιοποίησης, οι μεταβλητές θα είναι διαφορετικές από αυτές της παραγωγής, και όχι μόνο σε ρόλους και playbook, αλλά και σε ρυθμίσεις παραμέτρων nginx. {{ inventory_hostname }} δεν χρειάζεται να προσδιορίζονται στο αρχείο απογραφής - αυτό ειδική μεταβλητή ansible και ο κεντρικός υπολογιστής για τον οποίο εκτελείται αυτήν τη στιγμή το βιβλίο αναπαραγωγής αποθηκεύεται εκεί.
Εάν θέλετε να έχετε ένα αρχείο αποθέματος για πολλούς κεντρικούς υπολογιστές, αλλά να εκτελείται μόνο για μία ομάδα, αυτό μπορεί να γίνει με την ακόλουθη εντολή:

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

Μια άλλη επιλογή είναι να έχετε ξεχωριστά αρχεία αποθέματος για διαφορετικές ομάδες. Ή μπορείτε να συνδυάσετε τις δύο προσεγγίσεις εάν έχετε πολλούς διαφορετικούς κεντρικούς υπολογιστές.

Ας επιστρέψουμε στη ρύθμιση του nginx. Αφού αντιγράψουμε τα αρχεία διαμόρφωσης, πρέπει να δημιουργήσουμε έναν συμβολικό σύνδεσμο στο sitest_enabled στο my_app.conf από το sites_available. Και επανεκκινήστε το 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

Όλα είναι απλά εδώ - και πάλι ansible modules με μια αρκετά τυπική σύνταξη. Υπάρχει όμως ένα σημείο. Δεν έχει νόημα η επανεκκίνηση του nginx κάθε φορά. Έχετε παρατηρήσει ότι δεν γράφουμε εντολές όπως: "κάντε αυτό έτσι", η σύνταξη μοιάζει περισσότερο με "αυτό πρέπει να έχει αυτήν την κατάσταση". Και τις περισσότερες φορές έτσι ακριβώς λειτουργεί το ansible. Εάν η ομάδα υπάρχει ήδη ή το πακέτο συστήματος είναι ήδη εγκατεστημένο, τότε το ansible θα το ελέγξει και θα παρακάμψει την εργασία. Επίσης, τα αρχεία δεν θα αντιγραφούν εάν ταιριάζουν πλήρως με αυτό που υπάρχει ήδη στον διακομιστή. Μπορούμε να εκμεταλλευτούμε αυτό και να κάνουμε επανεκκίνηση του nginx μόνο εάν έχουν αλλάξει τα αρχεία ρυθμίσεων. Υπάρχει μια οδηγία για το μητρώο για αυτό:

# 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

Εάν αλλάξει ένα από τα αρχεία διαμόρφωσης, θα γίνει ένα αντίγραφο και η μεταβλητή θα καταχωρηθεί restart_nginx. Και μόνο εάν αυτή η μεταβλητή έχει καταχωρηθεί, η υπηρεσία θα επανεκκινηθεί.

Και, φυσικά, πρέπει να προσθέσετε τον ρόλο nginx στο κύριο βιβλίο παιχνιδιού.

Ρύθμιση postgresql

Πρέπει να ενεργοποιήσουμε το postgresql χρησιμοποιώντας το systemd με τον ίδιο τρόπο όπως κάναμε με το nginx και επίσης να δημιουργήσουμε έναν χρήστη που θα χρησιμοποιήσουμε για πρόσβαση στη βάση δεδομένων και στην ίδια τη βάση δεδομένων.
Ας δημιουργήσουμε έναν ρόλο /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 }}"

Δεν θα περιγράψω τον τρόπο προσθήκης μεταβλητών στο απόθεμα, αυτό έχει γίνει ήδη πολλές φορές, καθώς και η σύνταξη των λειτουργικών μονάδων postgresql_db και postgresql_user. Περισσότερες πληροφορίες μπορείτε να βρείτε στην τεκμηρίωση. Η πιο ενδιαφέρουσα οδηγία είναι εδώ become_user: postgres. Το γεγονός είναι ότι από προεπιλογή, μόνο ο χρήστης postgres έχει πρόσβαση στη βάση δεδομένων postgresql και μόνο τοπικά. Αυτή η οδηγία μας επιτρέπει να εκτελούμε εντολές για λογαριασμό αυτού του χρήστη (αν έχουμε πρόσβαση, φυσικά).
Επίσης, ίσως χρειαστεί να προσθέσετε μια γραμμή στο pg_hba.conf για να επιτρέψετε σε έναν νέο χρήστη την πρόσβαση στη βάση δεδομένων. Αυτό μπορεί να γίνει με τον ίδιο τρόπο που αλλάξαμε τη διαμόρφωση του nginx.

Και φυσικά, πρέπει να προσθέσετε τον ρόλο postgresql στο βασικό βιβλίο παιχνιδιού.

Εγκατάσταση ρουμπίνι μέσω rbenv

Το Ansible δεν διαθέτει μονάδες για εργασία με rbenv, αλλά εγκαθίσταται με κλωνοποίηση ενός αποθετηρίου git. Επομένως, αυτό το πρόβλημα γίνεται το πιο μη τυπικό. Ας της δημιουργήσουμε έναν ρόλο /ansible/roles/ruby_rbenv/main.yml και ας αρχίσουμε να το συμπληρώνουμε:

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

Χρησιμοποιούμε ξανά την οδηγία bene_user για να εργαστούμε κάτω από τον χρήστη που δημιουργήσαμε για αυτούς τους σκοπούς. Δεδομένου ότι το rbenv είναι εγκατεστημένο στον αρχικό του κατάλογο και όχι παγκοσμίως. Και χρησιμοποιούμε επίσης τη μονάδα git για να κλωνοποιήσουμε το αποθετήριο, προσδιορίζοντας το repo και το dest.

Στη συνέχεια, πρέπει να καταχωρήσουμε το rbenv init στο bashrc και να προσθέσουμε το rbenv στο PATH εκεί. Για αυτό έχουμε την ενότητα 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 -)"'

Στη συνέχεια, πρέπει να εγκαταστήσετε το ruby_build:

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

Και τέλος εγκαταστήστε το ρουμπίνι. Αυτό γίνεται μέσω του rbenv, δηλαδή απλά με την εντολή 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

Λέμε ποια εντολή να εκτελέσουμε και με τι. Ωστόσο, εδώ συναντάμε το γεγονός ότι το ansible δεν εκτελεί τον κώδικα που περιέχεται στο bashrc πριν εκτελέσει τις εντολές. Αυτό σημαίνει ότι το rbenv θα πρέπει να οριστεί απευθείας στο ίδιο σενάριο.

Το επόμενο πρόβλημα οφείλεται στο γεγονός ότι η εντολή του κελύφους δεν έχει κατάσταση από μια λογική άποψη. Δηλαδή, δεν θα υπάρχει αυτόματος έλεγχος εάν αυτή η έκδοση του ruby ​​είναι εγκατεστημένη ή όχι. Μπορούμε να το κάνουμε μόνοι μας:

- 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

Το μόνο που μένει είναι να εγκαταστήσετε το bundler:

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

Και πάλι, προσθέστε τον ρόλο μας ruby_rbenv στο κύριο βιβλίο.

Κοινόχρηστα αρχεία.

Γενικά, η ρύθμιση θα μπορούσε να ολοκληρωθεί εδώ. Στη συνέχεια, το μόνο που μένει είναι να τρέξει το capistrano και θα αντιγράψει τον ίδιο τον κώδικα, θα δημιουργήσει τους απαραίτητους καταλόγους και θα ξεκινήσει την εφαρμογή (αν όλα έχουν ρυθμιστεί σωστά). Ωστόσο, το capistrano απαιτεί συχνά πρόσθετα αρχεία διαμόρφωσης, όπως π.χ database.yml ή .env Μπορούν να αντιγραφούν όπως τα αρχεία και τα πρότυπα για το nginx. Υπάρχει μόνο μια λεπτότητα. Πριν αντιγράψετε αρχεία, πρέπει να δημιουργήσετε μια δομή καταλόγου για αυτά, κάπως έτσι:

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

καθορίζουμε μόνο έναν κατάλογο και το ansible θα δημιουργήσει αυτόματα γονικούς, εάν είναι απαραίτητο.

Ακατάλληλος θάλαμος

Έχουμε ήδη συναντήσει το γεγονός ότι οι μεταβλητές μπορούν να περιέχουν μυστικά δεδομένα όπως τον κωδικό πρόσβασης του χρήστη. Αν έχετε δημιουργήσει .env αρχείο για την αίτηση και database.yml τότε πρέπει να υπάρχουν ακόμη περισσότερα τέτοια κρίσιμα δεδομένα. Καλό θα ήταν να τα κρύψετε από τα αδιάκριτα βλέμματα. Για το σκοπό αυτό χρησιμοποιείται θησαυροφυλάκιο.

Ας δημιουργήσουμε ένα αρχείο για μεταβλητές /ansible/vars/all.yml (εδώ μπορείτε να δημιουργήσετε διαφορετικά αρχεία για διαφορετικές ομάδες κεντρικών υπολογιστών, όπως ακριβώς στο αρχείο αποθέματος: production.yml, staging.yml, κ.λπ.).
Όλες οι μεταβλητές που πρέπει να είναι κρυπτογραφημένες πρέπει να μεταφερθούν σε αυτό το αρχείο χρησιμοποιώντας την τυπική σύνταξη yml:

# System vars
user_password: 123qweasd
db_password: 123qweasd

# ENV vars
aws_access_key_id: xxxxx
aws_secret_access_key: xxxxxx
aws_bucket: bucket_name
rails_secret_key_base: very_secret_key_base

Μετά από αυτό, αυτό το αρχείο μπορεί να κρυπτογραφηθεί με την εντολή:

ansible-vault encrypt ./vars/all.yml

Φυσικά, κατά την κρυπτογράφηση, θα χρειαστεί να ορίσετε έναν κωδικό πρόσβασης για αποκρυπτογράφηση. Μπορείτε να δείτε τι θα υπάρχει μέσα στο αρχείο αφού καλέσετε αυτήν την εντολή.

Μέσω ansible-vault decrypt το αρχείο μπορεί να αποκρυπτογραφηθεί, να τροποποιηθεί και στη συνέχεια να κρυπτογραφηθεί ξανά.

Δεν χρειάζεται να αποκρυπτογραφήσετε το αρχείο για να λειτουργήσει. Το αποθηκεύετε κρυπτογραφημένο και τρέχετε το playbook με το όρισμα --ask-vault-pass. Το Ansible θα ζητήσει τον κωδικό πρόσβασης, θα ανακτήσει τις μεταβλητές και θα εκτελέσει τις εργασίες. Όλα τα δεδομένα θα παραμείνουν κρυπτογραφημένα.

Η πλήρης εντολή για πολλές ομάδες κεντρικών υπολογιστών και ansible vault θα μοιάζει κάπως έτσι:

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

Αλλά δεν θα σας δώσω το πλήρες κείμενο των βιβλίων και των ρόλων, γράψτε το μόνοι σας. Επειδή το ansible είναι έτσι - αν δεν καταλαβαίνεις τι πρέπει να γίνει, τότε δεν θα το κάνει για σένα.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο