[docker-compose] Ghost 3 et Traefik v2

Tut's Linux févr. 24, 2020

Le but de cette documentation est de vous fournir un fichier .yml clé en main pour installer le CMS Ghost derrière Traefik.


Article mis à jour suite aux commentaires de ldez oncernant un soucis de configuration côté Traefik (mélange entre conf' statique et dynamique) 😉 (merci !!)

Mise à jour : 20/07/2020

  • refonte des fichiers .yml de traefik (suppression des .toml)
  • refonte du fichier docker-compose.yml
  • utilisation des variables d'environnement (fichier .env)

Ghost est un CMS pour propulser des sites web type "blog", un peu comme ComputerZ Solutions ;). Cet outil nécessite une base de données dans laquelle y seront stockées toutes les données (pages, posts et configurations). J'utilise Traefik en frontal de Ghost - plus de détails à cette adresse (CZS).

Quelques pré-requis avant de se lancer : avoir Docker (pour Ubuntu ou CentOS 7) et docker-compose installé sur votre machine. J'utilise le chemin "/srv/docker" sur mon serveur et y ait créé un dossier "conf" où sont stockés les fichiers de configuration pour les différents services, un dossier "traefikdynamic" pour les configurations personnalisés pour Traefik et enfin un dossier "logs" pour Traefik.

De plus, il est important de créer le fichier acme.json dans le dossier "conf" avec les droits nécessaires avant de créer les conteneurs :

touch /srv/docker/conf/acme.json
chmod 0600 /srv/docker/conf/acme.json

Sans plus attendre, voici le fichier docker-compose.yml :

---
version: '3.8'
services:
  traefik:
    container_name: traefik
    image: traefik:v2.2.5
    restart: unless-stopped
    ports:
      - 80:80
      - 443:443
      - 8080:8080 # used to have the traefik dashboard # used to have the traefik dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - ./conf/traefik.yml:/etc/traefik/traefik.yml:ro
      - ./conf/traefikdynamic:/traefikdynamic:ro
      - ./logs/traefik.log:/etc/traefik/applog.log
      - ./conf/acme.json:/acme.json

  sqlghost:
    container_name: sqlghost
    image: mariadb:bionic
    restart: unless-stopped
    volumes:
      - datasqlghost:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: '1'
      MYSQL_USER: ${MYSQLUSER}
      MYSQL_PASSWORD: ${MYSQLPASSWORD}
      MYSQL_DATABASE: ${MYSQLDB}

  ghost:
    container_name: cmsghost
    image: ghost:3
    restart: unless-stopped
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /etc/timezone:/etc/timezone:ro
      - dataghost:/var/lib/ghost/content
      - ./conf/config.production.json:/var/lib/ghost/config.production.json:ro
    depends_on:
      - sqlghost
    labels:
      traefik.enable: true
      traefik.http.routers.ghost-https.entrypoints: websecure
      traefik.http.routers.ghost-https.rule: Host(`ghost.czs.local`)
      traefik.http.routers.ghost-https.middlewares: compression@file, security@file
      traefik.http.routers.ghost-https.tls: true
      traefik.http.routers.ghost-https.tls.certresolver: "letsencrypt"

volumes:
  datasqlghost:
  dataghost:
docker-compose.yml

Les lignes sont explicites : création du service "traefik" pour le conteneur reverse-proxy, le service "sqlghost" à base de MariaDB et le service "webghost" pour le CMS Ghost. Seul Ghost a des labels correspondant pour Traefik - avec ces quelques lignes, votre Ghost sera "routable" vers le web et automatiquement géré en SSL.
Vous remarquerez aussi des volumes complémentaires comme "/etc/timezone" et "/etc/localtime" - utilisé pour être en relation avec le serveur hôte (qui est à l'heure via NTP) et donc avoir un horodatage cohérent. Les fichiers de configurations sont à côté du docker-compose.yml, libre à vous de les placer ailleurs (et de modifier le chemin dans la conf)

Les labels Traefik mis en place dans ce fichier docker-compose.yml sont dit en "configuration statique".

Avant de lancer les conteneurs, vous devez encore créer des fichiers, notamment deux pour Traefik :

traefik.yml

global:
  sendAnonymousUsage: false
  checkNewVersion: false

api:
  #insecure: true
  dashboard: true
  #debug: true

log:
  filePath: "/etc/traefik/applog.log"
  level: INFO

providers:
  docker:
    endpoint: unix:///var/run/docker.sock
    exposedByDefault: false
    watch: true
    swarmMode: false

  file:
    directory: /traefikdynamic
    watch: true

entryPoints:
  web:
    address: ":80"
    http:
      redirections:
        entryPoint:
          to: websecure
          scheme: https
  websecure:
    address: ":443"

certificatesResolvers:
  letsencrypt:
    acme:
      email: contact@czs.local
#      caServer: https://acme-staging-v02.api.letsencrypt.org/directory
      caServer: https://acme-v02.api.letsencrypt.org/directory
      storage: acme.json
      keyType: EC256
      httpChallenge:
        entryPoint: web
traefik.toml

Le fichier de configuration Traefik se décompose sous forme de blocs - tout est assez explicite, je ne vais pas faire une description pour chaque ligne.

Le second fichier traefik_dynamic.yml est à placer dans un dossier "traefikdynamic", dans le dossier "conf" précédement initialisé.

tls:
  options:
    default:
      minVersion: VersionTLS12
      sniStrict: true
      cipherSuites:
        - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
        - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305
        - TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256
        - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
        - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305
        - TLS_AES_128_GCM_SHA256
        - TLS_AES_256_GCM_SHA384
        - TLS_CHACHA20_POLY1305_SHA256
      curvePreferences:
        - CurveP521
        - CurveP384

http:
  middlewares:
    compression:
      compress:
        excludedContentTypes:
          - text/event-stream

    security:
      headers:
        accessControlAllowMethods:
          - GET
          - OPTIONS
          - PUT
        #accessControlAllowOriginList = "*"
        accessControlMaxAge: 100
        addVaryHeader: true
        browserXssFilter: true
        contentTypeNosniff: true
        forceSTSHeader: true
        frameDeny: true
        sslRedirect: true
        sslForceHost: true
        stsIncludeSubdomain: true
        stsPreload: true
        #ContentSecurityPolicy = "default-src 'self' 'unsafe-inline'"
        customFrameOptionsValue: SAMEORIGIN
        referrerPolicy: same-origin
        featurePolicy: vibrate 'self'
        stsSeconds: 315360000

    authentification:
      basicAuth:
        users: # admon / totolasticot
        - admon:$2y$10$4IgUsvlCwENsF18B9t7AoegQ7eWZVfWxPFRhNfBWz7F00h1X2oZFe

Ghost a aussi besoin d'un fichier de configuration pour être exploitable. Suivez bien le formalisme du fichier .json requis - ci-dessous un exemple de conf' :

{
    "url": "https://ghost.czs.local",
    "server": {
      "port": 2368,
      "host": "0.0.0.0"
    },
    "database": {
      "client": "mysql",
      "connection": {
        "host": "sqlghost",
        "user": "ghost",
        "port": "3306",
        "password": "sqlghostuserpassword",
        "database": "ghost"
      }
    },
    "privacy": {
      "useUpdateCheck": false,
      "useGravatar": false,
      "useRpcPing": false,
      "useStructuredData": true
    },
    "process": "systemd",
    "paths": {
      "contentPath": "/var/lib/ghost/content"
    }
}
config.production.json

Enfin, créer à la racine du dossier (à côté du fichier docker-compose.yml) un fichier intitulé .env, qui contiendra les variables quant aux identifiants SQL et les versions des applications.

MYSQLUSER=ghost
MYSQLPASSWORD=sqlghostuserpassword
MYSQLDB=ghost

Maintenant que vos fichiers sont créés, vous pouvez initier un "docker-compose up -d" et voir la magie s'opérer... :) Le site web sous Ghost sera accessible au nom de domaine stipulé dans la conf', Traefik effectuera automatiquement le lien/la redirection et le tout avec du HTTPS via Let's Encrypt.

Vous pouvez trouver l'ensemble des scripts à cette adresse Github.

Mettmett/docker-compose
Some examples used on ComputerZ Solutions... Contribute to Mettmett/docker-compose development by creating an account on GitHub.

Mots clés

Julien HOMMET

Bercé par l'informatique depuis mon plus jeune âge, je transforme ma passion en expertise.