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; }}