Vor nicht allzu langer Zeit musste ich mehrere Ansible-Playbooks schreiben, um einen Server für das Deployment einer Rails-Anwendung vorzubereiten. Überraschenderweise fand ich kein einfaches Schritt-für-Schritt-Handbuch. Ich wollte nicht einfach ein fremdes Playbook kopieren, ohne zu verstehen, was passiert, und letztendlich musste ich die Dokumentation lesen und alles selbst zusammenstellen. Vielleicht kann ich jemandem mit diesem Artikel helfen, den Prozess zu beschleunigen.
Zunächst einmal ist es wichtig zu verstehen, dass Ansible Ihnen eine benutzerfreundliche Schnittstelle bietet, um eine vordefinierte Liste von Aktionen auf einem oder mehreren entfernten Servern über SSH auszuführen. Hier gibt es keine Magie, man kann kein Plugin installieren und bekommt sofort ein Zero-Downtime-Deployment für seine Anwendung mit Docker, Monitoring und weiteren Features. Um ein Playbook zu schreiben, müssen Sie wissen, was genau Sie tun möchten und wie Sie es tun. Daher sind vorgefertigte Playbooks von GitHub oder Artikel wie "Kopieren und ausführen – es wird funktionieren" für mich nicht akzeptabel.
Was benötigen wir?
Wie bereits erwähnt, um ein Playbook zu erstellen, müssen Sie wissen, was Sie tun möchten und wie Sie es tun werden. Lassen Sie uns klären, was wir benötigen. Für eine Rails-Anwendung brauchen wir einige Systempakete: nginx, postgresql (redis usw.). Außerdem benötigen wir eine bestimmte Version von Ruby. Am besten installieren Sie es über rbenv (rvm, asdf…). Es ist immer eine schlechte Idee, das Ganze als root-Benutzer zu starten, daher sollten Sie einen separaten Benutzer erstellen und ihm die entsprechenden Rechte einrichten. Anschließend müssen wir unseren Code auf den Server hochladen, die Konfigurationen für nginx, postgres usw. kopieren und alle diese Dienste starten.
Die Reihenfolge der Schritte ist wie folgt:
- Als Root einloggen
- Systempakete installieren
- Neuen Benutzer erstellen, Rechte und SSH-Schlüssel einrichten
- Systempakete (nginx usw.) konfigurieren und starten
- Benutzer in der Datenbank erstellen (kann auch gleich die Datenbank anlegen)
- Mit dem neuen Benutzer einloggen
- rbenv und Ruby installieren
- Bundler installieren
- Anwendungscode hochladen
- Puma-Server starten
Die letzten Schritte können mit Capistrano durchgeführt werden, denn es kann standardmäßig Code in die Release-Verzeichnisse kopieren, bei erfolgreichem Deployment das Release über einen Symlink umschalten, Konfigurationen aus dem gemeinsamen Verzeichnis kopieren, Puma neu starten usw. Das alles ist auch mit Ansible möglich, aber warum?
Dateistruktur
Ansible hat eine strikte für all seine Dateien, daher ist es am besten, alles in einem separaten Verzeichnis zu organisieren. Es ist dabei nicht so wichtig, ob es sich im Rails-Anwendungsordner oder separat befindet. Dateien können auch in einem separaten Git-Repository gespeichert werden. Persönlich finde ich es am praktischsten, 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 mithilfe einer speziellen Syntax definiert ist, was Ansible tun soll und wie. Lass uns unser erstes Playbook erstellen, das nichts macht:
---
- name: Einfaches Playbook
hosts: alleHier sagen wir einfach, dass unser Playbook heißt, Einfaches Playbook und dass der Inhalt für alle Hosts ausgeführt werden soll. Wir können es im /ansible-Verzeichnis unter dem Namen playbook.yml speichern und versuchen, es auszuführen:
ansible-playbook ./playbook.yml
SPIL [Einfaches Playbook] ************************************************************************************************************************************
überspringen: keine Hosts übereingestimmtAnsible sagt, dass es keine Hosts kennt, die der Liste "all" entsprechen. Diese müssen in einer speziellen .
Lassen Sie uns diese im gleichen Ansible-Verzeichnis erstellen:
123.123.123.123So einfach geben wir den Host an (idealerweise den Host Ihres VPS für Tests oder Sie können auch localhost angeben) und speichern ihn unter dem Namen inventory.
Sie können versuchen, Ansible mit der Inventory-Datei zu starten:
ansible-playbook ./playbook.yml -i inventory
SPIL [Einfaches Playbook] ************************************************************************************************************************************
AUFTRAG [Fakten sammeln] ************************************************************************************************************************************
SPIL-ZUSAMMENFASSUNG ************************************************************************************************************************************Wenn Sie SSH-Zugriff auf den angegebenen Host haben, wird Ansible sich verbinden und Informationen über das entfernte System sammeln. (Standardauftrag [Fakten sammeln]) und anschließend einen kurzen Bericht über die Ausführung bereitstellen (SPIL-ZUSAMMENFASSUNG).
Standardmäßig wird der Benutzername verwendet, mit dem Sie im System angemeldet sind. Auf dem Host wird dieser wahrscheinlich nicht vorhanden sein. Im Playbook kann der Benutzer, der für die Verbindung verwendet werden soll, mittels der Direktive remote_user festgelegt werden. Oftmals ist die Information über das entfernte System nicht notwendig, und es ist sinnvoll, keine Zeit mit deren Sammlung zu verschwenden. Auch diese Aufgabe kann abgeschaltet werden:
---
- name: Einfaches Playbook
hosts: all
remote_user: root
become: true
gather_facts: noVersuchen Sie erneut, das Playbook auszuführen, und stellen Sie sicher, 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 angegeben: become auf 'true'/'yes' setzen, um die Privilegieneskalation zu aktivieren. obwohl nicht ganz klar ist, warum).
Möglicherweise erhalten Sie einen Fehler, weil Ansible den Python-Interpreter nicht bestimmen kann; ihn können Sie manuell angeben:
ansible_python_interpreter: /usr/bin/python3 Über den Speicherort von Python können Sie mit dem Befehl whereis python.
Installation von Systempaketen
Das Standardangebot von Ansible umfasst zahlreiche Module zur Arbeit mit verschiedenen Systempaketen, 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, weshalb ich zur Installation der Pakete apt-get und . Wenn Sie ein anderes Betriebssystem verwenden, könnte ein anderes Modul erforderlich sein (denken Sie daran, dass ich zu Beginn gesagt habe, dass es wichtig ist, im Voraus zu wissen, was und wie wir es tun werden). Der Syntax wird jedoch wahrscheinlich ähnlich sein.
Ergänzen wir unser Playbook um die 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 (Task) 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 spezifischen Modulsyntax, was es tun soll. In diesem Fall apt: update_cache=yes — aktualisiert die Systempakete mit dem Modul apt. Der zweite Befehl ist etwas komplizierter. Wir übergeben dem Modul apt eine Liste von Paketen und sagen ihm, dass diese state installiert werden sollen present, d.h. wir geben an, dass diese Pakete installiert werden sollen. In ähnlicher Weise können wir ihm sagen, dass sie entfernt oder aktualisiert werden sollen, indem wir einfach state. Beachten Sie, dass wir für die Arbeit von Rails mit PostgreSQL das Paket postgresql-contrib benötigen, das wir jetzt installieren. Das sollte man ebenfalls wissen und berücksichtigen; Ansible wird das nicht automatisch erledigen.
Versuchen Sie, das Playbook erneut auszuführen und zu überprüfen, ob die Pakete installiert werden.
Erstellung neuer Benutzer.
Für die Benutzerverwaltung gibt es in Ansible ebenfalls ein Modul – user. Lassen Sie uns eine weitere Aufgabe hinzufügen (ich habe bereits bekannte Teile des Playbooks hinter Kommentaren verborgen, um sie 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 seine Shell und sein Passwort fest. Dabei begegnen wir mehreren Problemen. 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. Lassen Sie uns zunächst den Benutzernamen und das Passwort in Variablen auslagern; am Ende des Artikels zeige ich Ihnen, wie Sie das Passwort verschlüsseln können.
---
- name: Einfaches Playbook
# ...
tasks:
# ...
- name: Neuen Benutzer hinzufügen
user:
name: "{{ user }}"
shell: /bin/bash
password: "{{ user_password | password_hash('sha512') }}"Mit doppelten geschweiften Klammern werden in Playbooks Variablen festgelegt.
Die Werte der Variablen geben wir in der Inventory-Datei an:
123.123.123.123
[all:vars]
user=my_user
user_password=123qweasdAchten Sie auf die Direktive [all:vars] — sie zeigt an, dass der folgende Textblock Variablen (vars) enthält, die für alle Hosts (all) anwendbar sind.
Auch die Konstruktion "{{ user_password | password_hash('sha512') }}". Das Besondere ist, dass Ansible den Benutzer nicht über user_add hinzufügt, wie Sie es manuell tun würden. Sondern speichert alle Daten direkt, weshalb wir das Passwort auch vorher in einen Hash umwandeln müssen, was dieser Befehl erledigt.
Lassen Sie uns den Benutzer zur sudo-Gruppe hinzufügen. Bevor wir das tun, müssen wir jedoch sicherstellen, dass es diese Gruppe gibt, denn niemand wird das für uns übernehmen:
---
- 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"Es ist ganz einfach, wir haben auch ein Modul group zur Erstellung von Gruppen, mit einer Syntax, die sehr ähnlich wie apt ist. Danach reicht es, diese Gruppe dem Benutzer zuzuweisen (groups: "sudo").
Es ist auch nützlich, diesem Benutzer einen SSH-Schlüssel hinzuzufügen, damit wir uns ohne Passwort einloggen können:
---
- 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"
- name: SSH-Schlüssel bereitstellen
authorized_key:
user: "{{ user }}"
key: "{{ lookup('file', '~/.ssh/id_rsa.pub') }}"
state: presentIn diesem Fall ist die Konstruktion interessant "{{ lookup('file', '~/.ssh/id_rsa.pub') }}" — es kopiert den Inhalt der Datei id_rsa.pub (Ihr Name kann abweichen), also den öffentlichen Teil des SSH-Schlüssels und fügt ihn der Liste der autorisierten Schlüssel für den Benutzer auf dem Server hinzu.
Rollen
Alle drei Aufgaben zur Erstellung können leicht einer Gruppe von Aufgaben zugeordnet werden, und es wäre sinnvoll, diese Gruppe getrennt vom Haupt-Playbook zu speichern, um dessen Umfang nicht unnötig zu vergrößern. Dafür gibt es in Ansible .
Gemäß der zu Beginn angegebenen Verzeichnisstruktur müssen die Rollen in ein separates Verzeichnis roles gelegt werden, für jede Rolle ein separates Verzeichnis mit dem entsprechenden Namen, 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 ausgeführt wird, wenn die Rolle mit dem Playbook verbunden wird, in der können andere Dateien der Rolle eingebunden werden). Jetzt können wir alle Aufgaben, die sich auf den Benutzer beziehen, in diese Datei übertragen:
# 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, die Rolle user zu verwenden:
---
- name: Einfaches Playbook
hosts: alle
remote_user: root
gather_facts: nein
tasks:
- name: System aktualisieren
apt: update_cache=yes
- name: Systemabhängigkeiten installieren
apt:
name: git,nginx,redis,postgresql,postgresql-contrib
state: present
roles:
- userEs könnte auch sinnvoll sein, das System vor allen anderen Aufgaben zu aktualisieren. Dazu kann der Block umbenannt werden, tasks in dem sie definiert sind, pre_tasks.
Konfiguration von Nginx
Nginx sollte bereits installiert sein; jetzt müssen wir es konfigurieren und starten. Lassen Sie uns dies direkt in der Rolle machen. Wir erstellen die Verzeichnisstruktur:
- ansible
- roles
- nginx
- files
- tasks
- main.yml
- templatesNun benötigen wir Dateien und Vorlagen. Der Unterschied zwischen ihnen besteht darin, dass Ansible Dateien direkt so kopiert, wie sie sind. Vorlagen hingegen müssen die Erweiterung j2 haben und können die Werte von Variablen mit den gleichen doppelten geschweiften Klammern verwenden.
Lassen Sie uns Nginx in main.yml einfügen. Dafür haben wir das Systemd-Modul:
# Copy nginx configs and start it
- name: enable service nginx and start
systemd:
name: nginx
state: started
enabled: yesHier geben wir nicht nur an, dass Nginx gestartet werden soll (also es zu starten), sondern sagen auch gleich, dass es aktiviert sein muss.
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 oder selbstgeschrieben). Außerdem erstellen wir eine Konfigurationsdatei für unsere Anwendung im Verzeichnis sites_available (dies ist nicht zwingend erforderlich, aber nützlich). Im ersten Fall verwenden wir das Modul copy zum Kopieren von Dateien (die Datei muss sich in /ansible/roles/nginx/files/nginx.conf). Im zweiten Fall kopieren wir eine Vorlage und ersetzen die Variablenwerte. Die Vorlage sollte sich in /ansible/roles/nginx/templates/my_app.j2). Sie könnte ungefähr 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 in die Vorlage einfügt, bevor sie kopiert werden. Dies ist nützlich, wenn Sie das Playbook für verschiedene Hostgruppen verwenden. Zum Beispiel können wir unsere Inventory-Datei 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 erledigen. Allerdings werden die Variablen für den Staging-Host von denen der Produktion abweichen, und das betrifft nicht nur die Rollen und Playbooks, sondern auch die Nginx-Konfigurationen. {{ inventory_hostname }} Es ist nicht erforderlich, dies im Inventory-Datei anzugeben — das ist und dort wird der Host gespeichert, für den das Playbook gerade ausgeführt wird.
Wenn Sie eine Inventory-Datei für mehrere Hosts haben möchten, aber nur für eine Gruppe ausführen möchten, können Sie dies mit folgendem Befehl tun:
ansible-playbook -i inventory ./playbook.yml -l "staging"Eine andere Möglichkeit ist, separate Inventory-Dateien für verschiedene Gruppen zu haben. Oder Sie können die beiden Ansätze kombinieren, wenn Sie viele verschiedene Hosts haben.
Lassen Sie uns zur Nginx-Konfiguration zurückkehren. Nach dem Kopieren der Konfigurationsdateien müssen wir einen Symlink in sites-enabled auf my_app.conf aus sites-available erstellen. Und Nginx neu starten.
... # alter Code in mail.yml
- name: Erstelle Symlink zu sites-enabled
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 es ganz einfach – wieder Ansible-Module mit einem ziemlich standardmäßigen Syntax. Aber es gibt einen Punkt. Es macht keinen Sinn, Nginx jedes Mal neu zu starten. Ist Ihnen aufgefallen, dass wir keine Befehle der Art "mach das so" schreiben? Der Syntax sieht eher so aus: "dieser Teil soll so konfiguriert sein". Und meistens funktioniert Ansible genau so. Wenn die Gruppe bereits existiert oder das Systempaket schon installiert ist, prüft Ansible das und überspringt die Aufgabe. Ebenso werden Dateien nicht kopiert, wenn sie vollständig mit dem übereinstimmen, was bereits auf dem Server vorhanden ist. Wir können das nutzen und Nginx nur dann neu starten, wenn die Konfigurationsdateien geändert wurden. 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, erfolgt die Kopie, und die Variable wird registriert. restart_nginx. Und nur wenn diese Variable registriert wurde, erfolgt der Neustart des Dienstes.
Natürlich muss auch die Nginx-Rolle in das Haupt-Playbook eingefügt werden.
Konfiguration von PostgreSQL
Wir müssen PostgreSQL unter Verwendung von systemd aktivieren, genau wie wir es mit nginx gemacht haben, und zusätzlich einen Benutzer erstellen, den wir für den Zugriff auf die Datenbank sowie die Datenbank selbst verwenden werden.
Erstellen wir eine Rolle /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 detailliert erläutern, wie man Variablen im Inventory hinzufügt, das wurde bereits häufig behandelt, ebenso wie die Syntax der Module postgresql_db und postgresql_user. Weitere Informationen finden Sie in der Dokumentation. Hier ist die interessante Direktive become_user: postgres. Die Sache ist, dass standardmäßig nur der Benutzer postgres Zugriff auf die PostgreSQL-Datenbank hat und dieser nur lokal ist. Diese Direktive ermöglicht es uns, Befehle im Namen dieses Benutzers auszuführen (vorausgesetzt, wir haben Zugriff).
Es kann auch sein, dass Sie eine Zeile in die pg_hba.conf schreiben müssen, um dem neuen Benutzer Zugang zur Datenbank zu gewähren. Das kann man auch so machen, wie wir die nginx-Konfiguration geändert haben.
Und natürlich muss die PostgreSQL-Rolle in das Haupt-Playbook hinzugefügt werden.
Installation von Ruby über rbenv
In Ansible gibt es keine Module für die Arbeit mit rbenv, und es wird durch das Klonen eines Git-Repositories installiert. Daher wird diese Aufgabe die unkonventionellste. Lassen Sie uns eine Rolle dafür erstellen /ansible/roles/ruby_rbenv/main.yml und beginnen, sie auszufüllen:
# Install rbenv and ruby
- name: Install rbenv
become_user: "{{ user }}"
git: repo=https://github.com/rbenv/rbenv.git dest=~/.rbenvWir verwenden erneut die Direktive become_user, um als den speziellen Benutzer zu arbeiten, den wir dafür eingerichtet haben. Da rbenv in dessen Home-Verzeichnis installiert wird und nicht global. Außerdem verwenden wir das Git-Modul, um das Repository anzugeben, indem wir repo und dest angeben.
Als nächstes müssen wir rbenv init in die bashrc eintragen und rbenv auch in den PATH hinzufügen. Dafür nutzen 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 bashrc hinzufügen
become_user: "{{ user }}"
lineinfile:
path: ~/.bashrc
state: present
line: 'eval "$(rbenv init -)"'Danach müssen wir ruby_build installieren:
- name: ruby-build installieren
become_user: "{{ user }}"
git: repo=https://github.com/rbenv/ruby-build.git dest=~/.rbenv/plugins/ruby-buildUnd schließlich installieren wir Ruby. Dies geschieht über rbenv, also einfach mit einem 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 und wie. Allerdings stoßen wir hier auf das Problem, dass Ansible den Code in der bashrc vor der Ausführung der Befehle nicht ausführt. Daher muss rbenv direkt in diesem Skript definiert werden.
Das nächste Problem besteht darin, dass der Befehl in der Shell bezüglich Ansible keinen Zustand hat. Das heißt, eine automatische Überprüfung, ob diese Ruby-Version installiert ist oder nicht, erfolgt nicht. Wir müssen dies selbst tun:
- 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 jetzt 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 unsere Rolle ruby_rbenv zum Haupt-Playbook hinzufügen.
Geteilte Dateien.
Insgesamt könnte man die Einrichtung hier abschließen. Als Nächstes bleibt es, Capistrano zu starten, das den Code selbst kopiert, die erforderlichen Verzeichnisse erstellt und die Anwendung ausführt (sofern alles korrekt konfiguriert ist). Oft benötigt Capistrano jedoch weitere Konfigurationsdateien, wie z. B. database.yml oder .env Diese können auf die gleiche Weise kopiert werden wie die Dateien und Vorlagen für Nginx. Es gibt jedoch eine kleine Besonderheit. Vor dem Kopieren der Dateien müssen Strukturen für die Verzeichnisse 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 Verzeichnisse, falls erforderlich.
Ansible Vault
Wir sind bereits darauf gestoßen, dass in Variablen geheime Daten wie das Benutzerpasswort enthalten sein können. Wenn Sie eine .env Datei für die Anwendung erstellt haben, und database.yml es sollten noch mehr solcher kritischen Daten vorhanden sein. Es wäre gut, diese vor neugierigen Blicken zu verstecken. Dazu verwenden wir .
Erstellen wir eine Datei für Variablen. /ansible/vars/all.yml (Hier können Sie verschiedene Dateien für verschiedene Hostgruppen erstellen, ganz ähnlich wie in der Inventardatei: 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 sehen, was sich nach dem Aufruf dieses Befehls in der Datei befindet.
Mit Hilfe von ansible-vault decrypt Die Datei kann entschlüsselt, bearbeitet und danach erneut verschlüsselt werden.
Für die Arbeit ist es nicht notwendig, die Datei zu entschlüsseln. Sie speichern sie in verschlüsselter Form und führen das Playbook mit dem Argument --ask-vault-pass aus.. Ansible wird nach dem Passwort fragen, die Variablen abrufen und die Aufgaben ausführen. Alle Daten bleiben verschlüsselt.
Die vollständige Befehlszeile für mehrere Hostgruppen und Ansible Vault würde ungefähr so aussehen:
ansible-playbook -i inventory ./playbook.yml -l "staging" --ask-vault-passIch werde Ihnen den gesamten Text der Playbooks und Rollen nicht geben; das müssen Sie selbst schreiben. Denn Ansible ist so eine Sache – wenn Sie nicht verstehen, was zu tun ist, wird es das auch nicht für Sie tun.
Quelle: habr.com
