Rails & Docker

Publié il y a presque 3 ans~3 min

Etre développeu(se)r, c'est bien souvent travailler en équipe :juggling::juggling::juggling:. Mais qui n'a jamais connu LA fameuse installation qui prend une journée (ou plus...) à cause d'un souci d'environnement ou de dépendances ? Heureusement, Docker est là ! Graĉe à sa logique de conteneurs, il permet en effet d'encapsuler des applicatifs avec leurs dépendances, nous délivrant ainsi des soucis de portabilité d'un projet.

Sur la base d'un projet Ruby On Rails classique, nous allons mettre en place les différents éléments nécessaires à la dockerisation de celui-ci.

Pré-requis

  1. Docker et Docker Compose
  2. Un projet Ruby On Rails (≥ 5.1) utilisant Webpacker et PostgreSQL (≥ 10)

Un projet de démonstration a été créé dans le cadre de cet article, n'hésitez donc pas à vous y référer pour plus de détails (scripts, ...) !

Dockerfile

Tout projet Docker est défini par une image d'origine, qui servira ensuite de base pour la construction des différents conteneurs (application, base de données, ...). Pour ce faire, il suffit de créer un fichier sobrement nommé Dockerfile à la racine de notre projet :

1FROM ruby:2.5.5-alpine
2
3ARG PRECOMPILEASSETS
4ARG RAILS_ENV
5
6ENV SECRET_KEY_BASE foo
7ENV RAILS_ENV ${RAILS_ENV}
8ENV RAILS_SERVE_STATIC_FILES true
9
10RUN apk add --update --no-cache \
11 build-base \
12 git \
13 postgresql-dev \
14 postgresql-client \
15 imagemagick \
16 nodejs-current \
17 yarn \
18 python2 \
19 tzdata \
20 file
21
22RUN gem install bundler
23# Install gems
24RUN mkdir /gems
25WORKDIR /gems
26COPY Gemfile .
27COPY Gemfile.lock .
28RUN bundle install -j4 --retry 3 \
29 # Remove unneeded files (cached *.gem, *.o, *.c)
30 && rm -rf /usr/local/bundle/cache/*.gem \
31 && find /usr/local/bundle/gems/ -name "*.c" -delete \
32 && find /usr/local/bundle/gems/ -name "*.o" -delete
33
34ARG INSTALL_PATH=/railsondocker
35ENV INSTALL_PATH $INSTALL_PATH
36
37WORKDIR $INSTALL_PATH
38COPY . .
39
40# Precompile assets (or not)
41RUN docker/potential_asset_precompile.sh $PRECOMPILEASSETS
42
43CMD ["docker/startup.sh"]

Le point de départ de notre fichier est une image Docker pour Ruby. On utilise ici une version dite "alpine", c'est-à-dire embarquant le minimum requis pour optimiser le poids de l'image finale générée.

Nous installons ensuite les différentes librairies nécessaires (postgresql, nodejs, ...) et les gems déclarées dans le Gemfile (avec un peu de nettoyage en fin de tâche, là encore pour optimiser le poids final).

Pour finir nous précompilons les assets (si besoin) avant de lancer la commande globale déclarée via l'instruction CMD, qui se chargera de lancer le serveur (Puma) après avoir préparé la base de données (cf. script startup.sh).

A noter l'utilisation de variables qui nous permettrons de construire une image spécifique selon l'environnement (développement, production, ...). Ces variables seront définies dans chaque fichier docker-compose.yml.

Docker Compose

Notre constructeur général étant maintenant prêt, ne reste plus qu'à définir les différents conteneurs de notre application, avec les variations éventuelles en fonction de l'environnement.

En mode développement, Rails et Webpacker sont séparés, afin d'améliorer la réactivité de l'ensemble. Le fichier docker-compose.yml est assez simple à comprendre, sa syntaxe restant assez claire :

1version: '3.0'
2services:
3 db:
4 image: postgres:11-alpine
5 ports:
6 - 5433:5432
7 environment:
8 POSTGRES_PASSWORD: postgres
9
10 webpacker:
11 image: railsondocker_development
12 command: ["./docker/start_webpack_dev.sh"]
13 environment:
14 - NODE_ENV=development
15 - RAILS_ENV=development
16 - WEBPACKER_DEV_SERVER_HOST=0.0.0.0
17 volumes:
18 - .:/railsondocker:cached
19 ports:
20 - 3035:3035
21
22 app:
23 image: railsondocker_development
24 build:
25 context: .
26 args:
27 - PRECOMPILEASSETS=NO
28 environment:
29 - RAILS_ENV=development
30 links:
31 - db
32 - webpacker
33 ports:
34 - 3000:3000
35 volumes:
36 - .:/railsondocker:cached

Nos trois conteneurs sont donc liés à des images (issues du Dockerfile, à l’exception de PostgreSQL, pour lequel nous faisons appel à une image "alpine"). On leur assigne des ports, et éventuellement des commandes à exécuter au lancement.

Dans certains cas on définira des arguments ou variables d’environnements, utiles pour le Dockerfile.

Enfin les liens entre services sont déclarés dans le conteneur app, dans la partie links.

Configuration de Rails

Maintenant que la partie Docker est mise en place, il faut effectuer quelques ajustements dans la configuration de notre projet Ruby On Rails, en particulier pour tenir compte des variables définies dans les fichiers Compose :

  • config/database.yml, pour le host et les username/password
  • config/webpacker.yml, pour le host du serveur de Webpacker
  • config/environments/*.rb, pour reporter les variables définies dans le docker-compose.yml (optionnel)

🎉

Et voilà ! Il ne reste maintenant plus qu’à lancer la commande docker-compose build pour construire notre image Docker, puis la commande docker-compose up pour lancer notre projet sur localhost:3000 ! Par la suite, on pourra ajouter de nouveaux conteneurs (ou services) : serveur mail, ... 🙂