🍪🐳 Construire un Dockerfile Rails de production : mon approche DevOps
Temps de lecture : 5 minutes
🚨 Cette édition est relativement longue car bcp d’image, votre boîte mail risque de la tronquer. Je vous conseille de cliquer sur le titre ci-dessus pour l’ouvrir dans votre navigateur web. ☝️
Hello les petits Biscuits !
Bienvenue sur la 33ème édition de Ruby Biscuit.
Vous êtes maintenant 583 abonnés 🥳
Maintenant Ruby biscuit, c’est aussi votre meilleur allié pour recruter des devs Ruby !
Si vous n’avez pas encore rejoint le club, RDV sur https://recrutement.rubybiscuit.fr
Bonne lecture.
🚀 Construire un Dockerfile Rails de production : mon approche DevOps
Je l’avoue, je suis un peu maniaque des coûts et des temps de build : je déteste qu’une image devienne obèse, ou qu’un build s’éternise… surtout quand je pourrais déjà boire un café ☕️ ! Pour tous mes projets Rails, j’ai mis au point un Dockerfile unique, rapide et léger.
Dans cet article, je vous partage mes astuces (et mes petites manies 😜) : comment j’en suis arrivé là, et surtout pourquoi j’ai fait chaque choix.
💖 Pourquoi j’aime chouchouter mon Dockerfile Rails
Le Dockerfile, c’est souvent le parent pauvre du projet, mais c’est le cœur de votre environnement d’exécution. Le négliger, c’est un peu comme oublier d’ajouter du sel dans une recette : ça peut vite tourner au désastre ! Une mauvaise config peut entraîner :
📦 Des images trop lourdes (bonjour les téléchargements interminables)
🐌 Des builds qui traînent (adieu la productivité)
🔓 Des secrets exposés (le cauchemar)
🚨 Des failles de sécurité (panique à bord)
J’ai donc décidé de rendre ce fichier maintenable, sécurisé et réutilisable sur tous mes projets.
🔧 Un Dockerfile tout-terrain pour tous les projets
Pour éviter de dupliquer un Dockerfile par projet, j’ai introduit plusieurs ARG :
Cette paramétrisation permet de :
Choisir dynamiquement la version de Ruby et de Debian ;
Installer, si nécessaire, des paquets spécifiques à un projet ;
Adapter l’environnement Rails et Node sans modifier le Dockerfile.
Par exemple, je peux lancer :
et obtenir une image taillée sur mesure.
🏗️ Image de base et dépendances communes
Dans l’étape base
, j’installe les dépendances système partagées par tous les projets :
Pourquoi ces paquets ?
libjemalloc2
améliore la gestion mémoire de Ruby ;
libpq5
est nécessaire au client PostgreSQL ;
shared-mime-info
,file
etless
sont utiles pour les scripts et diagnostics.
Grâce aux --mount=type=cache
, les index APT sont mis en cache, accélérant les builds suivants.
Configuration Ruby et mise à jour de Rubygems
Avant tout build applicatif, je définis des variables d’environnement et je mets à jour Rubygems pour bénéficier des dernières améliorations :
Astuce : Prévoir toujours un
RAILS_ENV
explicite garantit des builds reproductibles.
🛠️ Étape 1 : production-builder
L’étape production-builder
regroupe l’installation des outils et la compilation :
1. Outils de compilation
Ces paquets sont essentiels à la compilation :
build-essential
: compilateurs C/C++ pour les extensions Ruby ;libssl-dev
,libyaml-dev
,libz-dev
… : dépendances fréquentes des gems natives ;git
: pour les gems hébergées sur Git.
2. Node.js et Yarn
Cette étape prépare la compilation des assets front-end avec Webpacker ou Sprockets.
3. Gems avec Bundler
Pour profiter du cache Docker et éviter de réinstaller les gems inutilement :
Docker invalide cette couche seulement si Gemfile
ou Gemfile.lock
changent, ce qui rend les rebuilds beaucoup plus rapides.
4. Faux secrets et assets
Le .env
factice permet de satisfaire Rails sans exposer de vrais secrets ; qui sont injectés au runtime.
🎯 Étape 2 : production
L’étape finale construit l’image de prod en mode minimal :
Node.js facultatif
Je peux choisir de conserver Node.js pour debug ou m’en passer pour alléger l’image.
Copie des artefacts
Seuls les gems et les assets précompilés sont repris, tout le reste reste dans l’étape builder.
Sécurité et droits
Exécuter l’app en non-root limite les risques en cas de faille.
Exposition et lancement
Cette dernière ligne reste simple et claire pour démarrer le serveur Rails.
✂️ Un .dockerignore
chirurgical
Chaque ligne compte pour alléger le contexte :
# dossiers systèmes et dev
.git/
.bundle/
node_modules/
# logs et tmp
log/
tmp/
# secrets et configs locales
.env*
config/master.key
# assets compilés
public/assets
public/packs
Ce .dockerignore
empêche de copier tout ce qui n’est pas nécessaire au build.
📦 Un système de CI centralisé
J’ai centralisé Dockerfile, .dockerignore
, database.yml.template
et .env
factice dans un repo CI. Ce dossier est copié dans chaque projet pour garantir une cohérence et faciliter les mises à jour.
Dockerfile complet
Pour aller plus loin
Docker
Documentation officielle :
https://docs.docker.com/
Guide multi-stage : https://docs.docker.com/build/building/multi-stage/
Sécurité
Bonnes pratiques : https://docs.docker.com/develop/security-best-practices/
Performance
BuildKit : https://docs.docker.com/build/buildkit/
Ce Dockerfile m’a permis de gagner en rapidité, en légèreté et en sécurité. J’espère qu’il vous servira autant qu’il m’accompagne au quotidien !
— Quentin