Besoin : une page de maintenance pour des services hébergés sur un cluster kubernetes. Contraintes pour cette page de maintenance :

  • être stateless
  • avoir la possibilité de donner une raison à la maintenance en cours

Je voulais tester Caddy v2 depuis un petit moment, ca a été l’occasion pour ce besoin.

L’idée retenue :

  • Un serveur caddy en reverse proxy d’un minio qui héberge les données de ma page de maintenance (html, css et un simple js).
  • Il a une route /reason qui me permettra de changer la raison de la maintenance.
  • Tous les uris non connus sont renvoyés vers mon index.html pour que la page de maintenance fonctionne pour tous les uris (et ne pas chercher n’importe quoi via le reverse proxy).
  • L’ingress controller Nginx utilise cette page de maintenance en default-backend.

Configurer le Minio (ou votre s3)

  • Je créé un nouveau bucket pour héberger le contenu de la page de maintenance :
mc mb minio/maintenance
  • Je copie le contenu de ma page de maintenance :
mc cp --recursive data/ minio/maintenance
  • Je donne un accès publique à ces fichiers :
mc policy set download minio/maintenance

Si vous avez à votre disposition un backend HTTP x ou y, la suite de cet exemple fonctionnera.

Caddy

La configuration Caddy en json est disponible sur ce gist. Attention : l’api Caddy est exposée sur toutes les interfaces. A adapter selon votre besoin. Il y a peut-être moyen de faire plus simple avec Caddy, je découvre tout juste :)

Tester en local avec Docker

Pour tester en local sur votre machine, vous pouvez utiliser tout simplement cette commande :

docker run  -p 10080:80 -p 2019:2019 -v $PWD/caddy.json:/caddy.json -it caddy caddy run -config /caddy.json

En pointant votre navigateur sur http://localhost:10080, Caddy devrait servir les pages de votre backend. Pour mon exemple, ca ressemble à ca : maintenance_1.png

Je peux modifier la raison de la maintenance avec un curl :

 curl \
        localhost:2019/config/apps/http/servers/maintenance/routes/0/handle/0/body \
        -X POST \
        -H "Content-Type: application/json" \
        -d '"<b>Le cluster Etcd du K8S est HS :(</b>"'

maintenance_2.png

Cette petite magie fonctionne grace au JS trouvé ici. J’ai dans ma page HTML :

<div w3-include-html="/reason" class="reason"></div>

Le JS charge donc la raison de ma maintenance via la route Caddy /reason qui sert le contenu envoyé par le POST Json.

Pour information, il est aussi possible de changer la configuration et de forcer le rechargement :

curl localhost:2019/load \
  -X POST \
  -H "Content-Type: application/json" \
  -d @caddy.json

Déployer sur un kubernetes

Rien de particulier à rajouter à ce niveau là. A vous d’adapter pour exposer en toute sécurité et selon votre besoin l’api Caddy.

apiVersion: v1
kind: Service
metadata:
  name: maintenance
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: http
  - name: api
    port: 2019
    protocol: TCP
    targetPort: api
  sessionAffinity: None
  type: ClusterIP
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: maintenance
spec:
  replicas: 1
  selector:
    matchLabels:
      name: maintenance
  template:
    metadata:
      labels:
        name: maintenance
    spec:
      containers:
      - image: caddy:v2.2.1
        command:
          - caddy
          - run
          - -config
          - /etc/caddy/caddy.json
        name: maintenance
        ports:
        - containerPort: 80
          name: http
          protocol: TCP
        - containerPort: 2109
          name: api
          protocol: TCP
        resources:
          limits:
            cpu: 200m
            memory: 200Mi
          requests:
            cpu: 100m
            memory: 64Mi
        volumeMounts:
        - name: maintenance-config
          mountPath: /etc/caddy/caddy.json
          subPath: caddy.json
        securityContext:
          capabilities:
            drop:
            - ALL
            add:
            - NET_BIND_SERVICE
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 60
      volumes:
      - name: maintenance-config
        configMap:
          name: maintenance-config

Changer le default-backend de votre ingress

Sur l’ingress devant votre service, rajoutez l’annotation suivante pour utiliser cette page de maintenance si les endpoints du service défini dans l’ingress sont HS :

nginx.ingress.kubernetes.io/default-backend: maintenance

A savoir qu’il est également possible de renvoyer certaines erreurs http sur cette page de maintenance avec l’annotation nginx.ingress.kubernetes.io/custom-http-errors.

Plus d’information dans la documentation.

Vous pouvez tester le bon fonctionnement de la page de maintenance avec un scale à 0 des pods du service derrière cet ingress.

Bref :) Tout ceci est là pour vous inspirer et sera à adapter en fonction de votre stratégie de mise à jour, déploiement … Bon tests !