Dockerizer une application React

Aujourd’hui nous allons voir comment dockerizer une application React ( avec create-react-app )
L’idée derrière la dockerization de votre application est de pouvoir à la fois développer et livrer votre application dans les mêmes conditions.
Outre les surprises de déploiement que nous pouvions avoir lors d’un déploiement sur un serveur baremetal, les herbergeurs modernes proposent désormais des services pouvant faire tourner facilement vos containers.
Docker-compose
J’utilise docker-compose pour tous mes projets pour gérer mes stacks applicatives. Cela permet d’orchestrer plusieurs services pour une même application.
Exemple : l’application en elle-même, le storybook associé ( banc de test ), ou encore un autre services comme un serveur de mock d’API.
Environnement de développement
## docker-compose.dev.yml
version: '3.7'
services:
your-app:
image: your-register.fr/app-name:dev
build:
context: .
dockerfile: Dockerfile.dev
args:
PROJECT_PATH: '.'
APP_PORT: '20011'
STORYBOOK_PORT: '20012'
volumes:
- ./node_modules:/app/node_modules
- .:/app:delegated
storybook:
image: your-register.fr/app-name:dev
build:
context: .
dockerfile: Dockerfile.dev
args:
PROJECT_PATH: "."
APP_PORT: "20003"
STORYBOOK_PORT: "20004"
volumes:
- /app/node_modules
- .:/app
command: ["start-storybook", "-p", "$STORYBOOK_PORT", "-c", ".storybook"]
## Dockerfile.dev
FROM node:16.11.1-alpine3.11
ARG APP_ROOT=/app
ARG APP_PORT=3000
ARG STORYBOOK_PORT=9000
ENV APP_PORT $APP_PORT
ENV STORYBOOK_PORT $STORYBOOK_PORT
WORKDIR $APP_ROOT
COPY package.json yarn.lock ./
RUN apk update && \
apk add --no-cache bash
COPY $PROJECT_PATH/ ./
ENTRYPOINT [ "/app/entrypoint-dev.sh" ]
CMD [ "react-scripts", "start" ]
## entrypoint-dev.sh
#!/bin/sh
set -e
yarn install
exec "$@"
Utilisation
1/ Build les images
docker-compose -f docker-compose.dev.yml build
2/ Lancer les containers
docker-compose -f docker-compose.dev.yml up
Environnement de production
J’utilise un Dockerfile spécifique pour la production, notamment pour tirer partie du “multi-stage builds”: cela permet de diminuer la taille finale de l’image.
Pour en savoir plus : https://docs.docker.com/develop/develop-images/multistage-build/
## docker-compose.production.yml
version: "3.7"
services:
your-app:
image: your-register.fr/app-name:latest
build:
context: .
dockerfile: Dockerfile
args:
PROJECT_PATH: "."
APP_PORT: "20011"
volumes:
- /app/node_modules
- .:/app
## Dockerfile
FROM node:16.11.1-alpine3.11 as builder ## utiliser votre version de node
ARG APP_ROOT=/app
WORKDIR $APP_ROOT
COPY . ./
RUN yarn install && \
yarn react-scripts build
########
########
FROM nginx:alpine
ARG APP_ROOT=/app
COPY $APP_ROOT/build /usr/share/nginx/html
COPY deploy/nginx.conf /etc/nginx/nginx.conf
WORKDIR /usr/share/nginx/html
RUN apk add --no-cache bash
EXPOSE 80
CMD ["/bin/bash", "-c", "nginx -g \"daemon off;\""]
### nginx.conf
# auto detects a good number of processes to run
worker_processes auto;
#Provides the configuration file context in which the directives that affect connection processing are specified.
events {
# Sets the maximum number of simultaneous connections that can be opened by a worker process.
worker_connections 8000;
# Tells the worker to accept multiple connections at a time
multi_accept on;
}
http {
# what times to include
include /etc/nginx/mime.types;
# what is the default one
default_type application/octet-stream;
# Sets the path, format, and configuration for a buffered log write
log_format compression '$remote_addr - $remote_user [$time_local] '
'"$request" $status $upstream_addr '
'"$http_referer" "$http_user_agent"';
server {
# listen on port 80
listen 80;
# save logs here
access_log /var/log/nginx/access.log compression;
# where the root here
root /usr/share/nginx/html;
# what file to server as index
index index.html index.htm;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to redirecting to index.html
try_files $uri $uri/ /index.html;
}
# Media: images, icons, video, audio, HTC
location ~* \.(?:jpg|jpeg|gif|png|ico|cur|gz|svg|svgz|mp4|ogg|ogv|webm|htc)$ {
expires 1M;
access_log off;
add_header Cache-Control "public";
}
# Javascript and CSS files
location ~* \.(?:css|js)$ {
try_files $uri =404;
expires 1y;
access_log off;
add_header Cache-Control "public";
}
# Any route containing a file extension (e.g. /devicesfile.js)
location ~ ^.+\..+$ {
try_files $uri =404;
}
gzip on;
gzip_http_version 1.0;
gzip_comp_level 5; # 1-9
gzip_min_length 256;
gzip_proxied any;
gzip_vary on;
# MIME-types
gzip_types
application/atom+xml
application/javascript
application/json
application/rss+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-web-app-manifest+json
application/xhtml+xml
application/xml
font/opentype
image/svg+xml
image/x-icon
text/css
text/plain
text/x-component;
}
}