Exécuter notre projet avec Docker Compose

Dans ce module, nous allons utiliser la commande Docker Compose pour exécuter notre image Docker.

4 min read

Créer un fichier Docker Compose

Voici notre image Docker :

1
FROM --platform=amd64 node:18-alpine As base
2
3
FROM base AS builder
4
5
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
6
RUN apk add --no-cache libc6-compat
7
RUN apk update
8
# Set working directory
9
WORKDIR /app
10
RUN npm install --global turbo
11
COPY --chown=node:node . .
12
RUN turbo prune @virgile/backend --docker
13
14
# Add lockfile and package.json's of isolated subworkspace
15
FROM base AS installer
16
RUN apk add --no-cache libc6-compat
17
RUN apk update
18
WORKDIR /app
19
20
# First install the dependencies (as they change less often)
21
COPY .gitignore .gitignore
22
COPY --chown=node:node --from=builder /app/out/json/ .
23
COPY --chown=node:node --from=builder /app/out/package-lock.json ./package-lock.json
24
RUN npm install
25
26
# Build the project
27
COPY --from=builder /app/out/full/ .
28
COPY turbo.json turbo.json
29
30
# Uncomment and use build args to enable remote caching
31
ARG TURBO_TEAM
32
ENV TURBO_TEAM=$TURBO_TEAM
33
34
ARG TURBO_TOKEN
35
ENV TURBO_TOKEN=$TURBO_TOKEN
36
ENV TZ=Europe/Paris
37
ENV NODE_ENV="production"
38
39
ADD backend/prisma backend/prisma
40
RUN cd backend && npx prisma generate
41
42
RUN npm run build
43
44
FROM base AS runner
45
WORKDIR /app
46
47
# Don't run production as root
48
RUN addgroup --system --gid 1001 nodejs
49
RUN adduser --system --uid 1001 remix-api
50
USER remix-api
51
52
# ENV TZ=Europe/Paris
53
# ENV NODE_ENV="production"
54
55
COPY --chown=remix-api:nodejs --from=installer /app/backend/package.json ./backend/package.json
56
COPY --chown=remix-api:nodejs --from=installer /app/backend/dist ./backend/dist
57
COPY --chown=remix-api:nodejs --from=installer /app/node_modules ./node_modules
58
COPY --chown=remix-api:nodejs --from=installer /app/node_modules/@virgile/frontend ./node_modules/@virgile/frontend
59
COPY --chown=remix-api:nodejs --from=installer /app/node_modules/@virgile/typescript-config ./node_modules/@virgile/typescript-config
60
COPY --chown=remix-api:nodejs --from=installer /app/node_modules/@virgile/eslint-config ./node_modules/@virgile/eslint-config
61
COPY --chown=remix-api:nodejs --from=installer /app/backend/prisma ./backend/prisma
62
63
COPY --chown=remix-api:nodejs --from=builder /app/backend/start.sh ./backend/start.sh
64
65
ENTRYPOINT [ "backend/start.sh" ]

Et voici le fichier docker-compose.dev.yml que nous allons configurer ensemble :

docker-compose.dev.yml
1
services:
2
monorepo_dev:
3
environment:
4
- REDIS_URL=redis://redis_dev:6379
5
- NODE_ENV=development
6
- DATABASE_URL
7
8
container_name: nestjs-remix-monorepo-dev
9
build:
10
context: .
11
dockerfile: Dockerfile
12
# image: varkoff/nestjs-remix-monorepo:dev
13
restart: always
14
ports:
15
- 3000:3000
16
redis_dev:
17
image: redis:latest
18
restart: always
19
ports:
20
- '6379:6379'
21
command: ["redis-server"]
22
volumes:
23
- ./cache:/data

Comprendre Docker Compose

Docker Compose est conçu pour orchestrer plusieurs conteneurs Docker. Par exemple, une base de donnée Redis, une application frontend et une application backend.

Dans notre cas, nous n'utilisons qu'un seul service (pour l'instant). Il s'appelle monorepo_dev.

Voici les différentes instructions :

  • environment: contient les variables d'environnements. Pour chaque variable d'environnement que vous utilisez dans le projet, vous devez ABSOLUMENT la rajouter ici. Sinon elle ne sera jamais détectée par notre application.
  • container_name: le nom de notre conteneur. Utile lorsque nous voudrons lancer plusieurs conteneurs, par exemple un environnement de staging et un environnement de production.
  • build: le contexte de notre build. En production, nous allons télécharger l'image Docker directement depuis Docker Hub. En local, nous allons utiliser le Dockerfile pour créer notre propre image Docker. (Pour s'assurer que l'application se build sans erreur.)
  • restart: cette instruction permet de redémarrer le conteneur (notre application) en cas de crash. Très utile.
  • ports: Nous effectuons ici du reverse proxy. Par défaut, notre application tourne dans une image virtualisée, elle est inaccessible en dehors du conteneur. Avec cette instruction, nous bindons le port 3000 de notre machine hôte (ordinateur en local) sur le port 3000 de l'image virtualisée. Cela signifie que Docker va nous autoriser à accéder à son port 3000.
  • volumes: Cette instruction est nécessaire lorsque nous utilisons une base de données, ou que nous souhaitons conserver les fichiers sauvegardés sur le disque dur du serveur. Si cette instruction n'est pas présente, les fichiers seront supprimés à chaque fois que le conteneur est relancé (à chaque mise en production).

Maintenant, nous allons créer le même fichier qui lancera notre application en production.

Il est similaire, à l'exception qu'il n'effectuera pas un build sur le serveur qui lance l'application. Le build sera lancé en amont durant la CI/CD avec Github Actions.

Pour différencier les deux images en inspectant les logs avec la commande docker ps, nous nommons le container nestjs-remix-monorepo-prod au lieu de nestjs-remix-monorepo-dev.

docker-compose.prod.yml
1
services:
2
monorepo_prod:
3
environment:
4
- REDIS_URL=redis://redis_prod:6379
5
- NODE_ENV
6
- DATABASE_URL
7
8
9
container_name: nestjs-remix-monorepo-prod
10
image: varkoff/nestjs-remix-monorepo:production
11
restart: always
12
ports:
13
- 3000:3000
14
redis_prod:
15
image: redis:latest
16
restart: always
17
command: ["redis-server"]
18
volumes:
19
- /dev/volumes/nestjs-remix/production/sessions/:/data

Création d'un compte Docker Hub

Nous devons maintenant créer un compte gratuit sur le site Docker Hub.

Une fois connecté, nous allons créer un nouveau repository. Nous allons lui donner le même nom que l'on a défini dans notre fichier docker-compose.prod.yml à savoir nestjs-remix-monorepo.

L'instruction image: varkoff/nestjs-remix-monorepo:production signifie que notre image Docker se nommera varkoff/nestjs-remix-monorepo ou varkoff représente le nom de votre compte DOcker, et production le tag de l'image.

Sauf que le repository ne contient pas encore d'image hébergée. Nous venons tout juste de le créer.

Dans la prochaine leçon, nous allons configurer les Github Actions pour automatiquement mettre à jour notre application sur le serveur de production.