Vor nicht allzu langer Zeit musste ich mehrere Ansible-Playbooks schreiben, um einen Server für die Bereitstellung einer Rails-Anwendung vorzubereiten. Zu meiner Überraschung fand ich kein einfaches, schrittweises Handbuch. Ich wollte kein fremdes Playbook ohne Verständnis des Geschehens kopieren und musste schließlich die Dokumentation lesen, um alles selbst zusammenzustellen. Vielleicht kann ich jemandem helfen, diesen Prozess mit diesem Artikel zu beschleunigen.
Als Erstes muss man verstehen, dass Ansible Ihnen eine benutzerfreundliche Schnittstelle für die Ausführung einer im Voraus festgelegten Liste von Aktionen auf einem oder mehreren Remote-Servern über SSH bietet. Hier gibt es keine Magie; man kann kein Plugin installieren und eine sofortige Bereitstellung seiner Anwendung mit Docker, Monitoring und anderen Vorteilen erwarten. Um ein Playbook zu schreiben, müssen Sie wissen, was genau Sie tun möchten und wie Sie es umsetzen. Daher bin ich mit fertigen Playbooks von GitHub oder Artikeln, die besagen: "Kopieren und Ausführen - es wird funktionieren", nicht zufrieden.
Was benötigen wir?
Wie ich bereits gesagt habe, um ein Playbook zu schreiben, müssen Sie wissen, was Sie erreichen möchten und wie Sie es tun. Lassen Sie uns klären, was wir brauchen. Für eine Rails-Anwendung benötigen wir mehrere Systempakete: nginx, postgresql (redis usw.). Darüber hinaus benötigen wir Ruby in einer bestimmten Version. Am besten installiert man es über rbenv (rvm, asdf…). Alles als Root-Benutzer auszuführen, ist immer eine schlechte Idee, daher sollten wir einen separaten Benutzer erstellen und ihm die entsprechenden Berechtigungen zuweisen. Danach müssen wir unseren Code auf den Server hochladen und die Konfigurationen für nginx, postgres usw. kopieren und all diese Dienste starten.
Die Reihenfolge der Schritte sieht folgendermaßen aus:
- Einloggen als Root
- Systempakete installieren
- Neuen Benutzer erstellen, Berechtigungen und SSH-Schlüssel einrichten
- Systempakete (nginx usw.) einrichten und starten
- Benutzer in der Datenbank erstellen (kann auch gleich die Datenbank erstellt werden)
- Mit dem neuen Benutzer einloggen
- rbenv und Ruby installieren
- Bundler installieren
- Code der Anwendung hochladen
- Puma-Server starten
Die letzten Schritte können mit Capistrano durchgeführt werden; zumindest kann es von Haus aus den Code in die Release-Verzeichnisse kopieren, den Release durch einen Symlink bei erfolgreicher Bereitstellung umschalten, Konfigurationen aus dem Shared-Verzeichnis kopieren, Puma neu starten usw. All dies kann auch mit Ansible gemacht werden, aber warum?
Dateistruktur
Ansible hat eine strikte für all seine Dateien, daher ist es am besten, alles in einem separaten Verzeichnis zu halten. Dabei ist es nicht so wichtig, ob es sich im Rails-Anwendungsbereich oder separat befindet. Man kann die Dateien in einem separaten Git-Repository speichern. Für mich persönlich war es am einfachsten, ein Verzeichnis namens ansible im /config-Verzeichnis der Rails-Anwendung zu erstellen und alles in einem Repository zu speichern.
Einfaches Playbook
Ein Playbook ist eine yml-Datei, in der mit einer speziellen Syntax beschrieben wird, was und wie Ansible etwas tun soll. Lassen Sie uns unser erstes Playbook erstellen, das nichts tut:
---
- name: Einfaches Playbook
hosts: allHier sagen wir einfach, dass unser Playbook den Namen hat Einfaches Playbook und dass der Inhalt auf allen Hosts ausgeführt werden soll. Wir können es im /ansible-Verzeichnis unter dem Namen speichern playbook.yml und versuchen, es auszuführen:
ansible-playbook ./playbook.yml
PLAY [Einfaches Playbook] ************************************************************************************************************************************
skipping: keine Hosts gefundenAnsible sagt, dass es keine Hosts kennt, die mit der Liste all übereinstimmen. Sie müssen in einer speziellen .
Lassen Sie uns diese im gleichen ansible-Verzeichnis erstellen:
123.123.123.123So geben wir einfach den Host an (idealerweise den Host Ihres VPS zum Testen, oder Sie können localhost eingeben) und speichern ihn unter dem Namen inventory.
Sie können versuchen, Ansible mit der Inventory-Datei auszuführen:
ansible-playbook ./playbook.yml -i inventory
PLAY [Einfaches Playbook] ************************************************************************************************************************************
TASK [Fakten sammeln] ************************************************************************************************************************************
PLAY RETURNAUSFAZIT ************************************************************************************************************************************Wenn Sie SSH-Zugriff auf den angegebenen Host haben, wird Ansible eine Verbindung herstellen und Informationen über das entfernte System sammeln. (standard TASK [Fakten sammeln]) danach wird eine kurze Zusammenfassung der Ausführung bereitgestellt (PLAY RETURNAUSFAZIT).
Standardmäßig wird der Benutzername verwendet, mit dem Sie sich im System angemeldet haben. Auf dem Host wird dieser wahrscheinlich nicht vorhanden sein. Im Playbook können Sie den Benutzer angeben, der für die Verbindung verwendet werden soll, indem Sie die Direktive remote_user verwenden. Oftmals benötigen Sie auch keine Informationen über das entfernte System und sollten keine Zeit mit deren Sammlung vergeuden. Diese Aufgabe kann ebenfalls deaktiviert werden:
---
- name: Einfaches Playbook
hosts: all
remote_user: root
become: true
gather_facts: noVersuchen Sie, das Playbook erneut zu starten und sicherzustellen, dass die Verbindung funktioniert. (Wenn Sie den Benutzer root angegeben haben, müssen Sie auch die Direktive become: true angeben, um erhöhte Rechte zu erhalten. Wie in der Dokumentation beschrieben: become auf ‚true‘/‘yes‘ setzen, um die Berechtigungseskalation zu aktivieren. obwohl es nicht ganz klar ist, warum).
Möglicherweise erhalten Sie einen Fehler, weil ansible den Python-Interpreter nicht bestimmen kann. In diesem Fall können Sie ihn manuell angeben:
ansible_python_interpreter: /usr/bin/python3 wo Ihr Python installiert ist, können Sie mit dem Befehl herausfinden whereis python.
Installation von Systempaketen
Im Standardpaket von Ansible sind viele Module zur Arbeit mit verschiedenen Systempaketen enthalten, wodurch wir nicht für jeden Anlass Bash-Skripte schreiben müssen. Jetzt benötigen wir eines dieser Module, um das System zu aktualisieren und Systempakete zu installieren. Auf meinem VPS läuft Ubuntu Linux, entsprechend verwende ich apt-get und . Wenn Sie ein anderes Betriebssystem verwenden, benötigen Sie möglicherweise ein anderes Modul (denken Sie daran, dass ich zu Beginn gesagt habe, dass Sie im Voraus wissen müssen, was und wie wir es machen). Der Syntax wird jedoch wahrscheinlich ähnlich sein.
Ergänzen wir unser Playbook mit den ersten Aufgaben:
---
- name: Einfaches Playbook
hosts: all
remote_user: root
become: true
gather_facts: no
tasks:
- name: System aktualisieren
apt: update_cache=yes
- name: Systemabhängigkeiten installieren
apt:
name: git,nginx,redis,postgresql,postgresql-contrib
state: presentEine Aufgabe ist genau das, was ansible auf den entfernten Servern ausführen wird. Wir geben der Aufgabe einen Namen, um ihre Ausführung im Protokoll zu verfolgen. Und wir beschreiben, mit der Syntax des spezifischen Moduls, was es tun soll. In diesem Fall apt: update_cache=yes – sagt, dass die Pakete des Systems mit dem Modul apt aktualisiert werden sollen. Der zweite Befehl ist etwas komplizierter. Wir übergeben dem Modul apt eine Liste von Paketen und sagen, dass deren state installiert werden sollen present, d.h. wir verlangen, dass diese Pakete installiert werden. Auf ähnliche Weise können wir angeben, dass sie gelöscht oder aktualisiert werden sollen, indem wir einfach stateändern. Beachten Sie, dass wir für die Arbeit von rails mit postgresql das Paket postgresql-contrib benötigen, das wir jetzt installieren. Auch dies muss man wissen und tun, ansible wird dies nicht von selbst erledigen.
Versuchen Sie, das Playbook erneut auszuführen und zu überprüfen, ob die Pakete installiert werden.
Erstellen neuer Benutzer.
Für die Arbeit mit Benutzern gibt es bei Ansible ebenfalls ein Modul – user. Lassen Sie uns eine weitere Aufgabe hinzufügen (ich habe die bereits bekannten Teile des Playbooks in Kommentaren verborgen, um es nicht jedes Mal vollständig kopieren zu müssen):
---
- name: Einfaches Playbook
# ...
tasks:
# ...
- name: Neuen Benutzer hinzufügen
user:
name: my_user
shell: /bin/bash
password: "{{ 123qweasd | password_hash('sha512') }}"Wir erstellen einen neuen Benutzer, setzen ihm eine Shell und ein Passwort. Und schon sehen wir uns mehreren Problemen gegenüber. Was ist, wenn die Benutzernamen für verschiedene Hosts unterschiedlich sein müssen? Außerdem ist es eine sehr schlechte Idee, das Passwort im Klartext im Playbook zu speichern. Zunächst werden wir den Benutzernamen und das Passwort in Variablen auslagern, und gegen Ende des Artikels zeige ich, wie das Passwort verschlüsselt werden kann.
---
- name: Einfaches Playbook
# ...
tasks:
# ...
- name: Neuen Benutzer hinzufügen
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"Mit den doppelten geschweiften Klammern werden in Playbooks Variablen gesetzt.
Die Werte der Variablen geben wir in der Inventardatei an:
123.123.123.123
[all:vars]
user=my_user
user_password=123qweasdBeachten Sie die Direktive [all:vars] – sie bedeutet, dass der folgende Textblock Variablen (vars) enthält, die für alle Hosts (all) angewendet werden.
Auch die Konstruktion "{{ user_password | password_hash('sha512') }}". Das Problem ist, dass Ansible den Benutzer nicht über user_add hinzufügt, wie Sie es manuell tun würden. Stattdessen speichert es alle Daten direkt, weshalb wir das Passwort auch vorher in einen Hash umwandeln müssen, was dieser Befehl tut.
Lassen Sie uns unseren Benutzer zur Sudo-Gruppe hinzufügen. Bevor wir das tun, müssen wir jedoch sicherstellen, dass diese Gruppe vorhanden ist, denn niemand wird das für uns erledigen:
---
- name: Einfaches Playbook
# ...
tasks:
# ...
- name: Sicherstellen, dass eine 'sudo'-Gruppe existiert
group:
name: sudo
state: present
- name: Neuen Benutzer hinzufügen
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"
groups: "sudo"Das ist alles ziemlich einfach, wir haben auch ein Modul group zur Erstellung von Gruppen, dessen Syntax sehr ähnlich wie bei apt ist. Danach müssen wir nur noch diese Gruppe dem Benutzer zuweisen (groups: "sudo").
Es ist auch nützlich, diesem Benutzer einen SSH-Schlüssel hinzuzufügen, damit wir uns ohne Passwort unter ihm einloggen können:
---
- name: Einfaches Playbook
# ...
Aufgaben:
# ...
- name: Eine 'sudo'-Gruppe sicherstellen
gruppe:
name: sudo
state: present
- name: Einen neuen Benutzer hinzufügen
benutzer:
name: "{{ user }}"
shell: /bin/bash
passwort: "{{ user_password | password_hash('sha512') }}"
gruppen: "sudo"
- name: SSH-Schlüssel bereitstellen
authorized_key:
benutzer: "{{ user }}"
schlüssel: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: presentIn diesem Fall ist die Konstruktion interessant "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — sie kopiert den Inhalt der Datei id_rsa.pub (bei Ihnen könnte der Name anders sein), also den öffentlichen Teil des SSH-Schlüssels und lädt ihn in die Liste der autorisierten Schlüssel für den Benutzer auf dem Server hoch.
Rollen
Alle drei Aufgaben zur Benutzererstellung können leicht einer Gruppe von Aufgaben zugeordnet werden, und es wäre sinnvoll, diese Gruppe separat vom Haupt-Playbook zu speichern, damit es nicht zu umfangreich wird. Dafür gibt es in Ansible .
Gemäß der zu Beginn angegebenen Verzeichnisstruktur sollten Rollen in ein separates Verzeichnis 'roles' gelegt werden, wobei jedes Rolle ein eigenes Verzeichnis mit einem ähnlichen Namen hat, innerhalb des Verzeichnisses 'tasks', 'files', 'templates', usw.
Lassen Sie uns die Verzeichnisstruktur erstellen: ./ansible/roles/user/tasks/main.yml (main ist die Hauptdatei, die geladen und beim Einfügen der Rolle in das Playbook ausgeführt wird; darin können andere Dateien der Rolle eingebunden werden). Jetzt können wir alle Aufgaben, die sich auf den Benutzer beziehen, in diese Datei verschieben:
# 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: presentIm Haupt-Playbook muss angegeben werden, dass die Rolle 'user' verwendet werden soll:
---
- name: Einfaches Playbook
hosts: all
remote_user: root
gather_facts: no
Aufgaben:
- name: System aktualisieren
apt: update_cache=yes
- name: Systemabhängigkeiten installieren
apt:
name: git,nginx,redis,postgresql,postgresql-contrib
state: present
Rollen:
- userEs könnte auch sinnvoll sein, das System vor allen anderen Aufgaben zu aktualisieren, dafür könnte man den Block umbenennen tasks in dem sie definiert sind zu pre_tasks.
Konfiguration von nginx
Nginx sollte bereits installiert sein, es muss konfiguriert und gestartet werden. Lassen Sie uns das sofort in der Rolle erledigen. Wir erstellen die Verzeichnisstruktur:
- ansible
- roles
- nginx
- files
- tasks
- main.yml
- templatesJetzt benötigen wir Dateien und Vorlagen. Der Unterschied zwischen ihnen besteht darin, dass Ansible Dateien direkt kopiert, wie sie sind. Vorlagen müssen die Erweiterung j2 haben, und in ihnen können Variablenwerte mit denselben doppelten geschweiften Klammern verwendet werden.
Lassen Sie uns Nginx in die main.yml Datei einfügen. Dazu haben wir das Modul systemd:
# Copy nginx configs and start it
- name: enable service nginx and start
systemd:
name: nginx
state: started
enabled: yesHier sagen wir nicht nur, dass Nginx gestartet werden soll (das heißt, wir starten es), sondern auch, dass es aktiviert sein soll.
Jetzt kopieren wir die Konfigurationsdateien:
# 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'Wir erstellen die Hauptkonfigurationsdatei für nginx (entweder direkt vom Server nehmen oder selbst schreiben). Außerdem erstellen wir die Konfigurationsdatei für unsere Anwendung im Verzeichnis sites_available (das ist nicht zwingend erforderlich, aber nützlich). Im ersten Fall verwenden wir das Modul copy, um die Dateien zu kopieren (die Datei sollte liegen in /ansible/roles/nginx/files/nginx.conf). Im zweiten Fall kopieren wir die Vorlage und setzen die Variablenwerte ein. Die Vorlage sollte liegen in /ansible/roles/nginx/templates/my_app.j2). Und sie könnte etwa so aussehen:
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 }};
....
}Beachten Sie die Platzhalter {{ app_name }}, {{ app_path }}, {{ server_name }}, {{ inventory_hostname }} — das sind alles Variablen, deren Werte Ansible vor dem Kopieren in die Vorlage einfügt. Das ist nützlich, wenn Sie das Playbook für verschiedene Gruppen von Hosts verwenden. Zum Beispiel können wir unsere Inventardatei erweitern:
[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_appWenn wir jetzt unser Playbook ausführen, wird es die angegebenen Aufgaben für beide Hosts ausführen. Für den Staging-Host werden die Variablen jedoch von der Produktion abweichen, und das nicht nur in Rollen und Playbooks, sondern auch in den nginx-Konfigurationen. {{ inventory_hostname }} Es ist nicht nötig, dies in der Inventardatei anzugeben — das ist und dort wird der Host gespeichert, für den das Playbook gerade ausgeführt wird.
Wenn Sie eine Inventardatei für mehrere Hosts haben möchten, aber nur für eine Gruppe ausführen wollen, können Sie dies mit dem folgenden Befehl tun:
ansible-playbook -i inventory .\/playbook.yml -l "staging"Eine andere Möglichkeit besteht darin, separate Inventardateien für verschiedene Gruppen zu haben. Oder Sie können beide Ansätze kombinieren, wenn Sie viele verschiedene Hosts haben.
Kehren wir zur Konfiguration von nginx zurück. Nach dem Kopieren der Konfigurationsdateien müssen wir einen Symlink in sites_enabled zu my_app.conf aus sites_available erstellen. Und nginx neu starten.
... # alter Code in mail.yml
- name: Symlink zu sites-enabled erstellen
file:
src: \/etc\/nginx\/sites-available\/my_app.conf
dest: \/etc\/nginx\/sites-enabled\/my_app.conf
state: link
- name: nginx neu starten
service:
name: nginx
state: restartedHier ist alles einfach – wieder die ansible-Module mit einer ziemlich standardisierten Syntax. Aber es gibt einen Punkt. Es macht keinen Sinn, nginx jedes Mal neu zu starten. Ihnen ist sicher aufgefallen, dass wir keine Befehle schreiben wie: „Mach das so“, sondern die Syntax eher so aussieht: „Diese Entität sollte diesen Zustand haben“. Und so funktioniert ansible in der Regel. Wenn die Gruppe bereits existiert oder das Systempaket bereits installiert ist, wird ansible dies überprüfen und die Aufgabe überspringen. Auch Dateien werden nicht kopiert, wenn sie vollständig mit dem übereinstimmen, was bereits auf dem Server vorhanden ist. Wir können dies ausnutzen und nginx nur dann neu starten, wenn sich die Konfigurationsdateien geändert haben. Dafür gibt es die Direktive 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.changedWenn eine der Konfigurationsdateien geändert wird, wird die Datei kopiert und eine Variable registriert. restart_nginx. Und nur wenn diese Variable registriert wurde, wird der Dienst neu gestartet.
Und natürlich muss die Rolle nginx dem Haupt-Playbook hinzugefügt werden.
PostgreSQL-Konfiguration
Wir müssen PostgreSQL mit systemd aktivieren, genau wie wir es mit nginx gemacht haben, und auch einen Benutzer erstellen, den wir für den Zugriff auf die Datenbank verwenden werden, sowie die Datenbank selbst.
Lesen wir eine Rolle erstellen /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 }}"Ich werde nicht näher darauf eingehen, wie man Variablen im Inventory hinzufügt, das wurde bereits oft genug gemacht, ebenso wie die Syntax der Module postgresql_db und postgresql_user. Mehr Informationen finden Sie in der Dokumentation. Die interessanteste Direktive hier ist: become_user: postgres. Der Grund dafür ist, dass der Zugriff auf die PostgreSQL-Datenbank standardmäßig nur für den Benutzer postgres und nur lokal verfügbar ist. Diese Direktive ermöglicht es uns, Befehle im Namen dieses Benutzers auszuführen (vorausgesetzt, wir haben Zugang).
Möglicherweise müssen Sie auch eine Zeile in pg_hba.conf hinzufügen, um dem neuen Benutzer Zugriff auf die Datenbank zu ermöglichen. Das können Sie genauso tun, wie wir die nginx-Konfiguration geändert haben.
Natürlich müssen wir auch die Rolle postgresql dem Haupt-Playbook hinzufügen.
Installation von Ruby über rbenv
Es gibt keine Module in ansible für die Arbeit mit rbenv, da es durch das Klonen des Git-Repositories installiert wird. Daher wird diese Aufgabe die am wenigsten standardisierte. Lassen Sie uns dafür eine Rolle erstellen /ansible/roles/ruby_rbenv/main.yml und anfangen, sie auszufüllen:
# Install rbenv and ruby
- name: Install rbenv
become_user: "{{ user }}"
git: repo=https://github.com/rbenv/rbenv.git dest=~/.rbenvWir verwenden wieder die Direktive become_user, um von dem von uns dafür erstellten Benutzer aus zu arbeiten. Da rbenv im Home-Verzeichnis dieses Benutzers und nicht global installiert wird. Außerdem verwenden wir das Modul git, um das Repository anzugeben, wobei wir repo und dest angeben.
Als nächstes müssen wir rbenv init in der bashrc eintragen und rbenv zugleich in den PATH hinzufügen. Dazu haben wir das Modul lineinfile:
- name: rbenv zum PATH hinzufügen
become_user: "{{ user }}"
lineinfile:
path: ~\/bashrc
state: present
line: 'export PATH="${HOME}\/\.rbenv\/bin:${PATH}"'
- name: rbenv init in die bashrc eintragen
become_user: "{{ user }}"
lineinfile:
path: ~\/bashrc
state: present
line: 'eval "$(rbenv init -)"'Danach muss ruby_build installiert werden:
- name: ruby-build installieren
become_user: "{{ user }}"
git: repo=https:\/\/github.com\/rbenv\/ruby-build.git dest=~\/\.rbenv\/plugins\/ruby-buildUnd schließlich ruby installieren. Das erfolgt über rbenv, also einfach mit dem Bash-Befehl:
- name: ruby installieren
become_user: "{{ user }}"
shell: |
export PATH="${HOME}\/\.rbenv\/bin:${PATH}"
eval "$(rbenv init -)"
rbenv install {{ ruby_version }}
args:
executable: \/bin\/bashWir geben an, welchen Befehl wir ausführen wollen und womit. Allerdings stoßen wir hier auf das Problem, dass Ansible keinen Code ausführt, der in der bashrc enthalten ist, bevor die Befehle ausgeführt werden. Das bedeutet, dass rbenv direkt in diesem Skript definiert werden muss.
Das nächste Problem hängt damit zusammen, dass der Shell-Befehl aus der Sicht von Ansible keinen Zustand hat. Das bedeutet, es wird keine automatische Überprüfung vorgenommen, ob diese ruby-Version installiert ist oder nicht — das müssen wir selbst erledigen:
- name: ruby installieren
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\/bashUnd nun bleibt nur noch, bundler zu installieren:
- name: bundler installieren
become_user: "{{ user }}"
shell: |
export PATH="${HOME}\/\.rbenv\/bin:${PATH}"
eval "$(rbenv init -)"
gem install bundlerUnd wieder müssen wir unsere Rolle ruby_rbenv im Haupt-Playbook hinzufügen.
Geteilte Dateien.
Im Allgemeinen könnte man die Konfiguration hier beenden. Danach bleibt es, capistrano zu starten, und es kopiert den Code von selbst, erstellt die notwendigen Verzeichnisse und startet die Anwendung (sofern alles korrekt eingerichtet ist). Allerdings benötigt capistrano oftmals zusätzliche Konfigurationsdateien, wie zum Beispiel database.yml oder .env Diese können genau wie die Dateien und Vorlagen für nginx kopiert werden. Es gibt nur einen kleinen Unterschied. Vor dem Kopieren der Dateien muss die Verzeichnisstruktur dafür erstellt werden, etwa so:
# Copy shared files for deploy
- name: Ensure shared dir
become_user: "{{ user }}"
file:
path: "{{ app_path }}/shared/config"
state: directorywir geben nur ein Verzeichnis an, und Ansible erstellt automatisch die übergeordneten, wenn nötig.
Ansible Vault
Wir haben bereits festgestellt, dass in Variablen geheime Daten wie das Benutzerpasswort gespeichert werden können. Wenn Sie eine .env Datei für die Anwendung erstellt haben, dann database.yml gibt es dort sicherlich noch mehr solcher kritischen Daten. Es wäre gut, sie vor neugierigen Blicken zu verbergen. Dazu verwenden wir .
Lassen Sie uns eine Datei für Variablen erstellen /ansible/vars/all.yml (hier können Sie verschiedene Dateien für verschiedene Gruppen von Hosts erstellen, genau wie in der Inventory-Datei: production.yml, staging.yml, usw.).
In diese Datei müssen alle Variablen übertragen werden, die verschlüsselt werden sollen, unter Verwendung der 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_baseAnschließend kann diese Datei mit folgendem Befehl verschlüsselt werden:
ansible-vault encrypt ./vars/all.ymlNatürlich müssen Sie beim Verschlüsseln ein Passwort für die Entschlüsselung festlegen. Sie können prüfen, was nach dem Aufruf dieses Befehls in der Datei enthalten sein wird.
Mit ansible-vault decrypt Die Datei kann entschlüsselt, geändert und dann erneut verschlüsselt werden.
Für die Arbeit müssen Sie die Datei nicht entschlüsseln. Sie speichern sie in verschlüsselter Form und führen das Playbook mit dem Argument --ask-vault-passaus. Ansible fragt nach dem Passwort, greift auf die Variablen zu und führt die Aufgaben aus. Alle Daten bleiben verschlüsselt.
Das gesamte Kommando für mehrere Gruppen von Hosts und ansible vault würde ungefähr so aussehen:
ansible-playbook -i inventory ./playbook.yml -l "staging" --ask-vault-passIch werde Ihnen den kompletten Text der Playbooks und Rollen nicht geben, schreiben Sie selbst. Denn ansible ist so – wenn Sie nicht verstehen, was zu tun ist, wird es das auch nicht für Sie tun.
Quelle: habr.com
