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.ymlversion: '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.devFROM node:16.11.1-alpine3.11ARG APP_ROOT=/appARG APP_PORT=3000ARG STORYBOOK_PORT=9000ENV APP_PORT $APP_PORTENV STORYBOOK_PORT $STORYBOOK_PORTWORKDIR $APP_ROOTCOPY package.json yarn.lock ./RUN apk update && \  apk add --no-cache bashCOPY $PROJECT_PATH/ ./ENTRYPOINT [ "/app/entrypoint-dev.sh" ]CMD [ "react-scripts", "start" ]
## entrypoint-dev.sh#!/bin/shset -eyarn installexec "$@"

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.ymlversion: "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
## DockerfileFROM node:16.11.1-alpine3.11 as builder ## utiliser votre version de nodeARG APP_ROOT=/appWORKDIR $APP_ROOTCOPY . ./RUN yarn install && \  yarn react-scripts build################FROM nginx:alpineARG APP_ROOT=/appCOPY --from=builder $APP_ROOT/build /usr/share/nginx/htmlCOPY deploy/nginx.conf /etc/nginx/nginx.confWORKDIR /usr/share/nginx/htmlRUN apk add --no-cache bashEXPOSE 80CMD ["/bin/bash", "-c", "nginx -g \"daemon off;\""]
### nginx.conf# auto detects a good number of processes to runworker_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;    }}