Greboca  

Blog : NullPointerException  -  Déploiement d’un projet Ruby-on-Rails

 -  Juin 2020 - 

Un petit article mémo technique sur comment déployer un projet Ruby-on-Rails.

Installation de Ruby

Le principal problème est que Ruby est mal packagé et obsolète sur la plupart des environnements Debian-like, pourtant les plus utilisés pour du déploiement en production. Debian stable ne fournit par exemple que du 2.5.1, datant de 2018 et fait le grand saut en 2.7 pour testing. 2.6.x qui est la version la plus couramment utilisée n’existe pas…

L’autre problème est que chaque projet Ruby vient avec son propre lot de dépendances, qui sont trop souvent tout aussi inexistantes ou obsolètes dans les paquets officiels Debian. Les outils officiels Ruby de gestion des dépendances que sont gem ou sa surcouche plus haut niveau bundler vont casser votre système si vous les utilisez, puisque vous allez mélanger des outils systèmes installés via dpkg/apt avec des dépendances provenant de gem/bundler.

Il est du coup plus que recommandé de passer par rbenv et ruby-build pour installer un système Ruby complet décorrélé du système et permettra de gérer finement les versions Ruby disponibles pour chaque projet.

L’installation est réalisée ainsi, ici pour du Ruby 2.6.6. Personnellement j’utilise jemalloc comme gestionnaire mémoire, plus performant et moins consommateur que l’allocateur standard.

export RBENV_ROOT="/opt/rbenv"
git clone --depth=1 --branch=v1.1.2 https://github.com/rbenv/rbenv "$RBENV_ROOT"
eval "$(rbenv init -)"

mkdir -p "$RBENV_ROOT/plugins"
git clone --depth=1 --branch=v20200520 https://github.com/rbenv/ruby-build "$RBENV_ROOT/plugins/ruby-build"

export RBENV_VERSION="2.6.6"
export RUBY_CONFIGURE_OPTS="--with-jemalloc"
apt install -y autoconf bison libssl-dev libyaml-dev libreadline-dev zlib1g-dev libncurses-dev libffi-dev libgdbm-dev libdb-dev libpq-dev libxml-dev libjemalloc-dev
rbenv install "$RBENV_VERSION"
gem install bundler

Pour charger rbenv par la suite :

export RBENV_ROOT=/opt/rbenv
eval "$(rbenv init -)"

Pour définir quelle version de Ruby utiliser en fonction de vos besoin :

  • Global pour tout le système : rbenv global 2.6.6 ou $RBENV_ROOT/version
  • Pour un projet/répertoire donné : rbenv local 2.6.6 ou $PWD/.ruby-version
  • Ponctuellement via la variable d’environnement RBENV_VERSION

Vous pouvez utiliser la version spéciale system pour utiliser l’environnement du système plutôt que celui de rbenv.

Démarrage du serveur Rails

Je passe généralement par Puma qui est dorénavant la référence en tant que serveur d’application pour Ruby-on-Rails et celui par défaut. Et en production, je privilégie les UNIX domain sockets à TCP, ça a l’avantage de ne pas consommer du port inutilement et de ne pas être dépendant d’un pare-feu pour la sécurité.

La subtilité ici est surtout de gérer correctement rbenv. Il faut bien initialiser correctement l’environnement pour utiliser la bonne version de Ruby et des dépendances, et non tomber sur celles du système. rbenv vient dorénavant avec un wrapper (rbenv exec) qui facilite le boulot. Je binstub aussi a minima puma & rails (bundle binstubs puma dans votre projet RoR), pour éviter d’avoir à bundle exec partout à chaque commande.

Dans ./config/puma.rb, la modification pour supporter les UNIX domain sockets :

port = ENV['PORT']
bind = ENV['BIND']
port ||= 3000 unless bind
port port if port
bind bind if bind

/etc/systemd/system/{your-project}-puma.service :

[Unit]
Description=Puma HTTP Server for {your-project}
After=network.target
Requires={your-project}-puma.socket

[Service]
Type=simple
User=www-data
WorkingDirectory={your-project-dir}
Environment=RAILS_ENV=production
Environment=RBENV_VERSION=2.6.6
Environment=BIND=unix://tmp/sockets/puma.sock
ExecStart=/opt/rbenv/bin/rbenv exec {your-project-dir}/bin/rails serve
Restart=always

[Install]
WantedBy=multi-user.target

/etc/systemd/system/{your-project}-puma.socket :

[Unit]
Description=Puma HTTP Server Accept Sockets for {your-project}

[Socket]
SocketUser=www-data
ListenStream=unix://{your-project-dir}/tmp/sockets/puma.sock
NoDelay=true
ReusePort=true
Backlog=1024

[Install]
WantedBy=sockets.target

Proxy inverse

J’utilise nginx comme proxy inverse pour servir les pages.

Le contenu du répertoire public est servi directement, le reste est renvoyé à puma.

/etc/nginx/sites-available/{your-project}:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate /etc/ssl/private/{your-project}.crt;
    ssl_certificate_key /etc/ssl/private/{your-project}.pem;

    server_name {your-project};
    root {your-project-dir}/public/;
    access_log /var/log/nginx/{your-project}.log;
    error_log /var/log/nginx/{your-project}.error.log;
    index index.html index.htm;
    client_max_body_size 20M;

    more_set_headers "Content-Security-Policy: default-src 'none'; style-src 'self'; script-src 'self'; img-src data: 'self'; font-src 'self';" always;

    location /assets/ {
        expires max;
        more_set_headers "Cache-Control: public" always;
    }

    location / {
        proxy_pass http://unix:/{your-project-dir}/tmp/sockets/puma.sock:;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto https;
        proxy_pass_header Server;
        proxy_buffering off;
        proxy_redirect off;
        proxy_http_version 1.1;
    }

    error_page 500 501 502 503 504 /500.html;
}

par

Blog : NullPointerException

Blog d’un groupe crypto-terroriste individuel auto-radicalisé sur l’Internet digital

Le RGPD en 10 minutes (ou un peu plus) [5/5]

 -  Décembre 2022 - 

Après les 4 articles précédents posant les bases de ce que devrait être le respect du RGPD, une analyse plus personnelle du problème pour répondre à (...)


Le RGPD en 10 minutes (ou un peu plus) [4/5]

 -  Novembre 2022 - 

Disclaimer:Le texte qui va suivre n’est PAS une analyse juridique et ne doit certainement pas être considéré comme un conseil juridique. Je ne suis (...)


Le RGPD en 10 minutes (ou un peu plus) [3/5]

 -  Novembre 2022 - 

Disclaimer:Le texte qui va suivre n’est PAS une analyse juridique et ne doit certainement pas être considéré comme un conseil juridique. Je ne suis (...)


Le RGPD en 10 minutes (ou un peu plus) [2/5]

 -  Novembre 2022 - 

Disclaimer:Le texte qui va suivre n’est PAS une analyse juridique et ne doit certainement pas être considéré comme un conseil juridique. Je ne suis (...)


Le RGPD en 10 minutes (ou un peu plus) [1/5]

 -  Novembre 2022 - 

Disclaimer:Le texte qui va suivre n’est PAS une analyse juridique et ne doit certainement pas être considéré comme un conseil juridique. Je ne suis (...)