Ruby Applikationen betreiben
Dieser Artikel erklärt, wie man Ruby-Anwendungen auf Nines Managed Servern betreibt.
Wenn du deine Anwendung in einem Container ausführen möchtest, findest du in unserem Artikel zu Podman die wichtigsten Informationen.
Container ermöglichen dir, deine Anwendung in unterschiedlichen Umgebungen betreiben zu können, ohne dabei wesentliche Anpassungen an der Applikation selbst vornehmen zu müssen. Container erleichtern zudem die Verwaltung von Abhängigkeiten und helfen dir, deine Anwendung zu skalieren, indem sie sie von anderen Prozessen im System isolieren.
Anforderungen
Für das folgende Beispiel nehmen wir an, dass deine Anwendung einen Ruby Webserver wie puma, thin, etc. unterstützt. Rails wird standardmässig mit puma ausgeliefert, weshalb wir diesen in unserem Beispiel verwenden.
1. Installiere den Ruby Version-Manager
Um verschiedene Ruby-Versionen unabhängig des eingesetzten Betriebssystems zu verwenden, empfehlen wir die Nutzung eines Versionsmanagers wie rbenv.
So installiere rbenv:
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
2. Ruby-Version installieren
Die gewünschte Ruby-Version kann nun installiert werden:
rbenv install --list # installierbare Versionen auflisten
rbenv install 3.2.0
Aktualisiere anschliessend "Bundler" auf die neueste Version:
rbenv exec bundle update --bundler
3. Anwendung deployen
Das Deployment deiner Anwendung ist für jedes Projekt unterschiedlich. Mit Rails musst du Assets kompilieren, sie und den Code auf den Server hochladen und Ruby-Abhängigkeiten installieren:
Führe diese Befehle auf deinem lokalen Rechner aus
Assets kompilieren:
RAILS_ENV=production bundle exec rake assets:precompile
Dateien auf den Server kopieren:
rsync -a -v --delete --exclude='node_modules/*' --exclude='tmp/*' --exclude='vendor/*' --exclude='.git/*' ./ www-data@server.nine.ch:app/current
Sicherstellen, dass die Abhängigkeiten aktuell sind:
ssh www-data@server.nine.ch 'cd ~/app/current && BUNDLER_WITHOUT="development test" rbenv exec bundle install'
4. systemd-Service einrichten
Um sicherzustellen, dass ein Dienst weiterläuft, z.B. neu gestartet wird wenn er ausfällt oder nach einem Neustart des Servers wieder startet, empfehlen wir die Verwendung eines Systemd-Benutzerdienstes.
Dieses Beispiel lädt Umgebungsvariablen von ~/app/env und führt dann rails server -b localhost --log-to-stdout im Verzeichnis ~/app/current aus.
Die Konfiguration für eine solchen Systemd-Service hängt von deiner Anwendung ab. Das obige Beispiel geht davon aus, dass du eine Rails-Anwendung ausführst.
Erstelle die Servicedatei in ~/.config/systemd/user/rails-app.service.
[Unit]
Description=Anwendung
[Service]
Type=simple
WorkingDirectory=%h/app/current
Environment=RAILS_ENV=production
Environment=PORT=3000
EnvironmentFile=%h/app/env
ExecStart=%h/.rbenv/bin/rbenv exec bundle exec rails server -b localhost --log-to-stdout
TimeoutSec=15
Restart=on-failure
PrivateTmp=yes
ProtectSystem=full
[Install]
WantedBy=default.target
Im nächsten Schritt wird der Dienst aktiviert und gestartet:
touch ~/app/env # sicherstellen, dass die Umgebungsdatei existiert
systemctl --user daemon-reload
systemctl --user enable rails-app.service
systemctl --user start rails-app.service
Deine Anwendung sollte nun als Dienst registriert und bereits gestartet sein:
$ systemctl --user status rails-app.service
● rails-app.service - Anwendung
Loaded: loaded (/home/www-data/.config/systemd/user/rails-app.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2021-07-16 10:13:36 CEST; 5s ago
Main PID: 3615317 (ruby)
CGroup: /user.slice/user-33.slice/user@33.service/rails-app.service
└─3615317 puma 5.3.2 (tcp://localhost:3000) [current]
Jul 16 10:13:37 server rbenv[3615317]: => Run `bin/rails server --help` for more startup options
Jul 16 10:13:37 server rbenv[3615317]: Puma starting in single mode...
Jul 16 10:13:37 server rbenv[3615317]: * Puma version: 5.3.2 (ruby 3.0.2-p107) ("Sweetnighter")
Jul 16 10:13:37 server rbenv[3615317]: * Min threads: 5
Jul 16 10:13:37 server rbenv[3615317]: * Max threads: 5
Jul 16 10:13:37 server rbenv[3615317]: * Environment: production
Jul 16 10:13:37 server rbenv[3615317]: * PID: 3615317
Jul 16 10:13:37 server rbenv[3615317]: * Listening on http://127.0.0.1:3000
Jul 16 10:13:37 server rbenv[3615317]: * Listening on http://[::1]:3000
Jul 16 10:13:38 server rbenv[3615317]: Use Ctrl-C to stop
Fehlerbehebung
- Stelle sicher, dass die
.bashrckorrekt geladen wird - Überprüfe, ob der Dienst korrekt gestartet wurde:
systemctl --user status app.service - Überprüfe die Log-Ausgabe:
journalctl --user -f
Weitere Informationen zur Verwendung von Systemd findest du im folgendem Support-Artikel.
5. Webserver konfigurieren
Dadurch sollte deine Anwendung über http zugänglich sein. Um https-Unterstützung hinzuzufügen, verwende bitte proxy_letsencrypt_https, wie in nine-manage-vhosts mit Let's Encrypt dokumentiert.
Die Anwendung läuft jetzt auf einem lokalen Port. Um die Anwendung über http und https zu erreichen, muss der Webserver diese Anfragen an den lokalen Port weiterleiten.
Auf deinem Managed Server sind bereits zwei nine-manage-vhost Vorlagen für diesen Anwendungsfall vorinstalliert: proxy und proxy_letsencrypt.
Erstelle einen neuen vHost und geben den lokalen Port der Anwendung über die Variable PROXYPORT an:
$ sudo nine-manage-vhosts virtual-host create example.com --template proxy --template-variable PROXYPORT=3000 --webroot ~/exampleapp/static
Virtual Host created: example.com
example.com
===========
DOMAIN: example.com
USER: www-data
WEBROOT: /home/www-data/exampleapp/static
TEMPLATE: proxy
TEMPLATE VARIABLES: PROXYPORT
3000
ALIASES: www.example.com
example.com.server.nine.ch
Bei Aufruf der URL http://example.com.server.nine.ch wird nun deine Applikation angesprochen.
Automatisierung des Deployments
Um das Deployment deiner Ruby-Anwendung zu automatisieren, kannst du Capistrano verwenden, ein Automatisierungstool für Remote-Server. Nachfolgend sind die Schritte zur Einrichtung und Verwendung von Capistrano aufgeführt:
Capistrano installieren
Füge Capistrano zu deinem Gemfile hinzu:
group :deployment do
gem 'capistrano', require: false
gem 'capistrano-rbenv', require: false
gem 'capistrano-rbenv-install', require: false
gem 'capistrano-rails', require: false
gem 'capistrano-systemd-multiservice', require: false
end
Führe anschliessend die folgenden Befehle aus:
bundle install
cap install
Dies erstellt mehrere Standardkonfigurationsdateien für Capistrano in deinem Projekt.
Capistrano konfigurieren
Bearbeite die Datei Capfile, um die erforderlichen Capistrano-Plugins einzuschliessen:
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/scm/git'
install_plugin Capistrano::SCM::Git
require 'capistrano/rbenv'
require 'capistrano/rbenv_install'
require 'capistrano/bundler'
require 'capistrano/rails'
require "capistrano/systemd/multiservice"
install_plugin Capistrano::Systemd::MultiService.new_service("app", service_type: "user")
# Lade benutzerdefinierte Aufgaben aus `lib/capistrano/tasks`, falls vorhanden
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
Bearbeite die Datei config/deploy.rb, um die Deployment-Einstellungen zu definieren:
lock "~> 3.16"
set :application, "your_app_name"
set :repo_url, "git@example.com:me/my_repo.git"
set :branch, ENV.fetch("CI_COMMIT_REF_NAME", "main")
set :deploy_to, "/home/#{fetch :user}/#{fetch :application}"
set :keep_releases, 5
set :rbenv_type, :user
set :rbenv_ruby, File.read(".ruby-version").strip
set :rbenv_path, "/home/#{fetch :user}/.rbenv"
append :linked_files, "env"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets", "vendor/bundle", "public/system"
before "systemd:app:validate", "systemd:app:setup"
after "deploy:publishing", "systemd:app:restart"
Hinterlege die Serverdetails in der Datei config/deploy/production.rb:
server 'server.nine.ch', user: 'www-data', roles: %w{app db web}
Definiere die Ruby-Version in einer .ruby-version Datei:
3.2.0
Erstelle anschliessend ein Template für den Systemd-Service in config/systemd/app.service.erb:
[Unit]
Description=<%= fetch :application %> Rails-Anwendung
[Service]
Type=simple
WorkingDirectory=<%= current_path %>
Environment=RAILS_SERVE_STATIC_FILES=true
EnvironmentFile=<%= shared_path %>/env
ExecStart=%h/.rbenv/bin/rbenv exec bundle exec rails server -b localhost --log-to-stdout
TimeoutSec=15
Restart=on-failure
PrivateTmp=yes
ProtectSystem=full
[Install]
WantedBy=default.target
Deployment mit Capistrano
Nun kannst du deine Anwendung mit Capistrano mit dem folgenden Befehl deployen:
bundle exec cap production deploy
Capistrano kümmert sich um die Details des Deployments, einschliesslich der Verwaltung der Ruby-Version, Abhängigkeiten, Asset-Kompilierung, Dateiübertragung und Verwaltung des systemd-Dienstes.
Dieses Vorgehen stellt sicher, dass dein Deployment optimiert und weniger fehleranfällig ist, wodurch effizientere und zuverlässigere Veröffentlichungen ermöglicht werden.