Ce tutoriel m'a pris pas mal de temps pour parvenir à un résultat final concret et satisfaisant. Le but du jeu (initialement), était de déployer un blog sous Ghost depuis un cluster Kubernetes sur Google Cloud (GKE), solution bien connue de ce blog et de son auteur. Jusque-là vous me direz, rien de bien complexe, seulement je m'étais rajouté comme contrainte de le faire via Helm, l'outil K8s pour l'intégration des apps sur un cluster. Malheureusement je n'y suis jamais réellement parvenu, butant x fois sur le souci du certificat TLS qui restait rouge et non-sécurisé.

C'est alors que j'ai découvert la solution Cert-Manager, qui permet avec une installation simplifiée depuis Helm, de sécuriser des sites web avec Let's Encrypt et le tout de façon automatisée. Là encore ce ne fut pas une réussite, je ne suis pas parvenu à faire communiquer un déploiement de Ghost avec Helm (commande helm installe stable/ghost) et Cert-Manager sur mes différents clusters...

Puis après m'être bien creusé la tête et avoir pas mal fouillé sur le web, je suis parvenu à la conclusion suivante : je ne parviendrai pas à le faire avec Helm, alors pourquoi ne pas passer par Docker ?

Le schéma de Cert-Manager nous aider à comprendre l'architecture :

Nous avons donc un nom de domaine pour notre blog qui sera le suivant :

  • myblog.gsagnard.fr

L'idée étant d'utiliser l'image Docker de Ghost pour la déployer sur le cluster via plusieurs fichiers ; deployment, service...

Et ensuite de monter un ingress avec nginx-ingress pour faire communiquer tout cela avec le DNS et Cert-Manager qui interviendra en toute fin pour donner un certificat sécurisé et gratuit à notre blog.

OUF...allez c'est parti !!

Pour information, j'ai crée un repository pour ce projet sur Github : ici.

L'installation

En gros vous allez réutiliser mot pour mot le contenu de cet article précédent sur le même sujet, jusqu'à l'étape 4 non-comprise. La seule chose qu'il conviendra de modifier, c'est votre DNS évidemment. Je suis parti sur un sous-domaine en myblog.gsagnard.fr pour ma part.

Nous avons donc notre cluster sur GKE, installé Helm, le Tiller et Nginx-Ingress bien entendu. Nous allons désormais attaquer le dur, les fichiers de configuration yaml de notre blog Ghost. So ...

Étape 4 - Déployer un service - Ghost

Pour ce faire nous allons utiliser le fichier dit de déploiement, c'est-à-dire deployment.yaml. Ce fichier va débuter l'installation de Ghost avec ces quelques lignes qui vont faire toute la différence par rapport à ce que j'ai pu tenter jusqu'alors avec Helm notamment :

    spec:
      containers:
      - image: ghost:2.6-alpine
        imagePullPolicy: Always
        name: blog
        ports:
        - containerPort: 2368

Nous allons dire à Kubernetes de déployer l'image Docker que nous avons choisi en tant qu'application. Les modifications comme le port spécifique du container Ghost sont évidemment à ne pas oublier, sans quoi cela ne fonctionnera pas sur votre cluster.

Ensuite vient le service avec service.yaml

Puis, pour terminer cette première étape, l'ingress. Pour rappel l'ingress est ce que Kubernetes utilise pour exposer le service en dehors du cluster, vers l'internet.

ingress.yaml

Ce à quoi K8s va vous répondre successivement dans l'ordre :

deployment.extensions "blog" created
service "blog" created
ingress.extensions "blog" created

Nous allons donc chercher l'adresse IP externe du cluster :

MBP-de-admin:~ admin$ kubectl get ing
NAME      HOSTS                ADDRESS         PORTS     AGE
blog      myblog.gsagnard.fr   34.76.102.249   80, 443   23s

A partir de là, vous aurez accès à votre blog au domaine indiqué mais uniquement en http :

L'étape 5 est également identique à l'article précédent, le déploiement de Cert-Manager sur le cluster via Helm.

Étape 6 - Configurer Let's Encrypt Issuer

Là aussi elle reste quasiment à l'identique si ce n'est pour l'adresse mail renseignée dans les fichiers issuer pour le certificat Let's Encrypt.

Le fichier ingress-tls.yaml désormais :

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: blog
  annotations:
    kubernetes.io/ingress.class: "nginx"    
    certmanager.k8s.io/issuer: "letsencrypt-staging"
    certmanager.k8s.io/acme-challenge-type: http01

spec:
  tls:
  - hosts:
    - myblog.gsagnard.fr
    secretName: quickstart-example-tls
  rules:
  - host: myblog.gsagnard.fr
    http:
      paths:
      - path: /
        backend:
          serviceName: blog
          servicePort: 80

En suivant ce modèle mais évidemment ou n'oubliant pas de modifier les lignes host avec votre propre DNS.

Créez l'ingress-tls :

MBP-de-admin:~ admin$ kubectl apply -f ingress-tls.yaml
ingress.extensions "blog" configured

Cert-manager lira ces annotations et les utilisera pour créer un certificat, que vous pourrez demander et voir:

MBP-de-admin:~ admin$ kubectl get certificate
NAME                     AGE
quickstart-example-tls   5d

On peut évidemment en découvrir plus sur le certificat en question avec :

MBP-de-admin:~ admin$ kubectl describe secret quickstart-example-tls
Name:         quickstart-example-tls
Namespace:    default
Labels:       certmanager.k8s.io/certificate-name=quickstart-example-tls
Annotations:  certmanager.k8s.io/alt-names=matomo.gsagnard.fr,myblog.gsagnard.fr
              certmanager.k8s.io/common-name=myblog.gsagnard.fr
              certmanager.k8s.io/issuer-kind=Issuer
              certmanager.k8s.io/issuer-name=letsencrypt-prod

Type:  kubernetes.io/tls

Data
====
ca.crt:   0 bytes
tls.crt:  3595 bytes
tls.key:  1679 bytes

Maintenant que nous sommes sûrs que tout est configuré correctement, vous pouvez mettre à jour les annotations dans l'ingress pour spécifier l'issuer de production avec ce fichier :

kubectl apply -f ingress-tls.yaml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: blog
  annotations:
    kubernetes.io/ingress.class: "nginx"
    certmanager.k8s.io/issuer: "letsencrypt-prod"
    certmanager.k8s.io/acme-challenge-type: http01

spec:
  tls:          
  - hosts:
    - myblog.gsagnard.fr
    secretName: quickstart-example-tls
  rules:
  - host: myblog.gsagnard.fr
    http:
      paths:
      - path: /
        backend:
          serviceName: blog
          servicePort: 80

Vous devrez également supprimer le secret existant, monitoré par cert-manager, et lui demander de traiter à nouveau la demande avec l'issuer mis à jour.  

$ kubectl delete secret quickstart-example-tls

secret "quickstart-example-tls" deleted

Cela va démarrer le processus pour obtenir un nouveau certificat, et en utilisant describe vous pouvez voir le statut. Une fois le certificat de production mis à jour, vous devriez voir l'exemple blog s'exécuter sur votre domaine avec un certificat TLS signé.  Vous pouvez voir l'état actuel de la commande ACME en exécutant kubectl describe sur la ressource Order créée par cert-manager pour votre certificat. Ce qui va donner au fur et à mesure que le certificat va se créer :

à :

et donc :

Youpi un magnifique blog sous Ghost avec un certificat TLS fiable et approuvé :

:)