Dockerizer une application React

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 --from=builder $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;
    }
}

Tags

  • docker
  • react

Cet article à été posté le