Ghost est un moteur de blog medium like, open source et écrit en JavaScript. Nous nous sommes amusés à déployer un Ghost chez Scaleway en 3 commandes avec du SSL Let's Encrypt.
Mais vous allez me dire il y a une image de Ghost dispo dans la catalogue de Scaleway !. L'idée est de faire un maximum de chose soi-même ;) ainsi, nous avons choisi d'utiliser une image Docker (dispo sur tous les services de Cloud).

Voici la listes des outils que nous avons utilisé :

  • Scaleway - solution de cloud FRANÇAIS
  • Terraform - provider Scaleway
  • Ansible - modules apt, pip, copy, docker_network, docker_swarm et docker_stack
  • Docker
  • Docker Swarm
  • Docker compose
  • Traefik v2 (nouvelle version)
  • Let's Encrypt - autorité de certification gratuite
  • xip.io - "magic domain name that provides"
  • mysql - backend Ghost
  • Ghost 3

Tous les fichiers sont sur notre github (lien à la fin de l'article).

Prérequis

Terraform

Terraform se base sur les API des fourniseurs de service cloud et permet d'automatiser une grande quantité d'actions, création de VMs, création volume bloc storage, etc. mais aussi update ou downgrade de profile de VMs.

Files

3 fichiers :

  • variable.tf : Déclaration des variables utilisés dans le main.tf, remplacer IP_ADMIN par votre ip depuis laquelle vous administrerez le serveur
  • output.tf : Varialble qui s'affiche à la fin de l'exécution
  • main.tf : Déscription des actions

variable.tf

variable "ipadmin" {
  description = "Adresse IP d'admin"
  default     = "IP_ADMIN"
}

output.tf

output "Public_ip" {
  value = "${scaleway_instance_server.docker.public_ip}"
}
output "Name" {
  value = "${scaleway_instance_server.docker.name}"
}

main.tf Updater les informations suivantes :

  • SCALEWAY_ACCESS_KEY
  • SCALEWAY_SECRET_KEY
  • SCALEWAY_ORGANIZATION_ID

provider "scaleway" {
  access_key = "SCALEWAY_ACCESS_KEY"
  secret_key = "SCALEWAY_SECRET_KEY"
  organization_id = "SCALEWAY_ORGANIZATION_ID"
  zone       = "fr-par-1"
  region     = "fr-par"
}

resource "scaleway_instance_ip" "public_ip" {
  server_id = "${scaleway_instance_server.docker.id}"
}

resource "scaleway_instance_security_group" "www" {
  inbound_default_policy = "drop"
  outbound_default_policy = "accept"
  inbound_rule {
    action = "accept"
    port = "22"
    ip = "${var.ipadmin}"
  }
  inbound_rule {
    action = "accept"
    port = "80"
  }
  inbound_rule {
    action = "accept"
    port = "443"
  }
    inbound_rule {
    action = "accept"
    port = "8080"
  }
}

resource "scaleway_instance_server" "docker" {
  type = "DEV1-S"
  image = "89c80d27-ddf4-4ffa-8215-b335cce3fd05"
  tags = [ "docker", "blog" ]
  security_group_id= "${scaleway_instance_security_group.www.id}"
}

Commandes

Download des modules

terraform init

GO !

terraform apply

Et voilà, vous avez une magnifique VM qui n'attend plus que votre blog !

Ansible & Docker

Ansible est un logiciel de déploiement/configuration par SSH.

Files

  • main.yml : centralise les paramètres et actions
  • docker-compose.yml : fichier d'orchestration du déploiement de la stack - traefik v2 / mysql / ghost

main.yml

- hosts: all
  gather_facts: False

  tasks:
  - name: install packages base 
    apt: name={{ packages }} state=present update_cache=yes state=latest
    vars:
      packages:
      - python-pip

  - name: install packages pip
    pip: name={{ packages }} state=present
    vars:
      packages:
        - docker
        - pyyaml
        - jsondiff

  - name: Init a new swarm with default parameters
    docker_swarm: state=present
  
  - name: Create a network
    docker_network: name=traefik-public driver=overlay

  - name: Copy docker compose
    copy: src=docker-compose.yml dest=/opt/docker-compose.yml
  
  - name: Deploy stack from a compose file
    docker_stack:
      state: present
      name: web
      compose:
        - /opt/docker-compose.yml

docker-compose.yml

  • remplacer SCALEWAY_IP_PUBLIC par votre ip publique scaleway
  • remplacer LETSENCRYPT_EMAIL par votre adresse qui sera référencée chez Let's Encrypt
version: "3.7"

services:
  proxy:
    image: "traefik:v2.0.2"
    command:
      - "--providers.docker.endpoint=unix:///var/run/docker.sock"
      - "--providers.docker.swarmMode=true"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.docker.network=traefik-public"
      - "--entrypoints.web.address=:80"
      - "--entryPoints.websecure.address=:443"
      - "--api.insecure"
      - "--certificatesresolvers.le.acme.tlschallenge=true"
      - "--certificatesresolvers.le.acme.email=LETSENCRYPT_EMAIL"
      - "--certificatesresolvers.le.acme.storage=/le/acme.json"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "cert:/le"
    networks:
      - traefik-public
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=traefik-public"
        - "traefik.http.routers.admin.rule=Host(`traefik.SCALEWAY_IP_PUBLIC.xip.io`)"
        - "traefik.http.routers.admin.entrypoints=websecure"
        - "traefik.http.routers.admin.tls.certresolver=le"
        - "traefik.http.services.admin.loadbalancer.server.port=8080"
        - "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
        - "traefik.http.routers.http-catchall.entrypoints=web"
        - "traefik.http.routers.http-catchall.middlewares=redirect-to-https@docker"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"

  blog:
    image: ghost:latest
    environment:
      url: https://blog.SCALEWAY_IP_PUBLIC.xip.io
      database__client: mysql
      database__connection__host: db
      database__connection__user: user
      database__connection__password: example
      database__connection__database: ghost
    volumes:
      - ghost:/var/lib/ghost/content
    depends_on:
      - proxy
      - db
    networks:
      - traefik-public
      - back
    deploy:
      labels:
        - "traefik.enable=true"
        - "traefik.docker.network=traefik-public"
        - "traefik.http.routers.blog.rule=Host(`blog.SCALEWAY_IP_PUBLIC.xip.io`)"
        - "traefik.http.routers.blog.entrypoints=websecure"
        - "traefik.http.services.blog.loadbalancer.server.port=2368"   
        - "traefik.http.routers.blog.tls.certresolver=le"

  db:
    image: mysql:5
    environment:
      MYSQL_DATABASE: ghost
      MYSQL_USER: user
      MYSQL_PASSWORD: example
      MYSQL_ROOT_PASSWORD: example
    volumes:
      - db:/var/lib/mysql
    networks:
      - back

volumes:
  cert:
  ghost:
  db:

networks:
  back:
  traefik-public:
    external: true

Commandes

ansible-playbook -u root -i IP_PUBLIC_SCALEWAY main.yml

YES !!

Vous passientez 5 minutes le temps que les images docker soient download et ghost initie sa base de données et :

  • https://blog.SCALEWAY_IP_PUBLIC.xip.io : pour votre Ghost (/ghost pour l'admin) avec une redirection - http > https
  • https://SCALEWAY_IP_PUBLIC.xip.io pour accéder au dashboard de Traefik v2

Projections & Evolutions

  • xip.io est utile afin d'avoir un fqdn sans devoir faire de pointage DNS, c'est donc très partique pour les POC. Vous pouvez bien évidement utiliser votre domaine.
  • Il est recommandé de sécuriser l'accès au dashboard Traefik pour de la prod (authentification ou filtrage IP).
  • Scaleway propose un service de bloc storage, il serait intéressant d'implémenter ce service pour le stockage des médias du blog.

Clean - fin du POC

Pour supprimer l'instance et les configurations réalisées chez Scaleway, Terraform vous a simplfié les choses. Balancer cette commande :

terraform destroy
pg3io/deploy-scaleway-ghost
Contribute to pg3io/deploy-scaleway-ghost development by creating an account on GitHub.