Zum Hauptinhalt springen

Ruby Applikationen betreiben

Dieser Artikel erklärt, wie man Ruby-Anwendungen auf Nines Managed Servern betreibt.

Container-Deployment

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

  1. Stelle sicher, dass die .bashrc korrekt geladen wird
  2. Überprüfe, ob der Dienst korrekt gestartet wurde: systemctl --user status app.service
  3. Überprüfe die Log-Ausgabe: journalctl --user -f

Weitere Informationen zur Verwendung von Systemd findest du im folgendem Support-Artikel.

5. Webserver konfigurieren

https

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.