Le labo de Nico

ūüĒ≠ūüé∂ūüēļ

Pourquoi Docker ?

6 ao√Ľt 2020
Table des matières

Pour travailler ensemble

Tout le monde d√©veloppe sur diff√©rentes machines. Il n'est pas rare que plusieurs coll√®gues aient install√© diff√©rentes version d'une m√™me library, d'un package manager, du runtime de leur application... (cc node/npm ūüĎč)

Lorsque l'application de d√©veloppement est dockeris√©e, tous les devs (et la prod !) sont soumis aux m√™mes r√®gles, ce qui √©vite le syndr√īme de "mais √ßa marche sur mon PC" tout en r√©duisant la configuration n√©cessaire √† installer l'environnement de dev. Voir juste apr√®s !

Pour moins perdre de temps à configurer

La configuration, c'est du temps passé à ne pas être productif. Plus il y a d'environnements dans lesquels une application doit tourner, plus il y a de configuration à faire. Docker permet d'abstraire l'environnement dans lequel tourne l'application et donc de ne faire que le minimum nécessaire.

Une conséquence notable : pour ajouter un service à la stack, plus forcément besoin d'apprendre en détails une technologie. Il suffit d'ajouter une image existante de celle-ci, et de simplement spécifier les variables d'environnement nécessaires. Par exemple, pour ajouter une database PostgreSQL, il suffit au minimum de renseigner un root password et de monter un volume pour persister les données.

Pour séparer les problèmes

Comme chaque service est aussi simple que possible, chacun n'a que très peu de dépendances avec le reste du monde. On voit que, par nature, les conteneurs nous poussent à n'avoir que le minimum vital de dépendances entre services.

Par dépendances, je pense en particulier à :

  • Syst√®me, programmes, libraries install√©es
  • Configurations associ√©es √† ce setup
  • Volumes (fichiers)
  • Acc√®s r√©seau

Pour gérer et sécuriser les connexions réseau

Car oui : c'est vite fait de faire fuiter le port d'une database, et quand elle n'est pas s√©curis√©e c'est g√™nant ūüė¨

L'id√©e est de penser un service comme une bo√ģte noire qui expose un ou plusieurs ports. Les services communiquent entre eux gr√Ęce aux volumes mont√©s en commun ou √† travers des ports internes. En effet, les services appartenant √† un m√™me r√©seau peuvent par d√©faut s'acc√©der entre eux.

Pièges et trucs à savoir

Sous Windows et Mac, les conteneurs tournent sur une VM. Du coup c'est lent, pas natif et les lectures/√©critures disque peuvent ralentir l'√©dition de fichiers. La solution est simple : utilise Linux ūüėŹ (ou un Windows/Mac puissant)

Lorsqu'un même volume (dossier) est monté en écriture dans deux conteneurs à la fois, on peut se retrouver avec des comportements inattendus : chez moi, l'intégralité du dossier généré par un run de build disparaissait en même temps que le conteneur à la fin du run. Je recommande donc vivement de ne monter en écriture qu'un seul fichier ou dossier à la fois.

C'est possible de ne partager en écriture qu'une partie d'une même arborescence commune. Par exemple, si backend et frontend partagent code en lecture mais que backend doit écrire dans code/target et frontend doit écrire dans code/dist, alors les volumes peuvent être surchargés comme ceci :

backend:
  volumes:
    - ./code:/code:ro
    - ./code/target:/code/target

webapp:
  volumes:
    - ./code:/code:ro
    - ./code/dist:/code/dist

Les images Docker peuvent vite prendre de l'espace disque. Mais avec le temps, la communauté opte de plus en plus pour des versions minimalistes de chaque image. Autrefois avec une version slim de Debian, et aujourd'hui souvent avec une version Alpine Linux qui permet aux images de faire moins de 10Mo de base ! Préfère-donc utiliser les tags -alpine de chaque image.

Enfin, la gestion des permissions est d√©routante au d√©but. Docker tourne en root par d√©faut et a de bonnes raisons de le faire : autant dire que j'appr√©cie de pouvoir ouvrir les ports 80 ou 443 en dev comme en prod ūüôā

Pour mieux comprendre pourquoi et comment bien gérer ses permissions : voir cet article.

Par o√Ļ commencer?

En prenant le temps de lire la doc dans l'ordre, tout ne peut que bien se passer :

  1. Comprends Docker
  2. Comprends docker-compose pour simplement faire cohabiter les services entre eux
  3. Installe docker et docker-compose
  4. Ajoute ton user au groupe docker pour piloter le daemon sans sudo
  5. C'est parti ! N'oublie pas de garder la configuration la plus simple possible.
Exemple

Une stack complète React/Rust/PostgreSQL pourrait être lancée avec la simple config suivante à la racine d'un projet :

version: "3"
services:
  db:
    image: postgres:alpine
    environment:
      - POSTGRES_PASSWORD=yolo
    volumes:
      - ./db/data:/var/lib/postgresql/data
  backend:
    image: rust:slim
    environment:
      - PGHOST=db
      - PGDATABASE=monservice
      - PGUSER=root
      - PGPASSWORD=yolo
    volumes:
      - ./backend/:/home/backend
      - ./webapp/:/home/webapp:ro
    command: cargo run
    ports:
      - 4000:4000
  webapp:
    image: node:alpine
    environment:
      - YARN_VERSION=1.22
      - BACKEND_URL=http://backend:4000/
    volumes:
      - ./webapp/:/home/webapp
    command: sh -c "yarn install && CI=true yarn start"
    ports:
      - 80:8080

docker-compose up -d et c'est parti directement sur localhost ! ūüíĽ