WordPress : Architecture pod, volumes et workflow K3s

par

dans

Le deploiement WordPress sur K3s repose sur une architecture pod multi-containers avec separation stricte des responsabilites. Cet article detaille la structure du pod, la gestion des volumes, les ConfigMaps et le workflow de mise a jour.

Architecture du pod

Chaque pod WordPress contient quatre containers et un initContainer :

flowchart TD
    subgraph pod ["Pod WordPress"]
        direction TB
        INIT["initContainer<br/>wordpress-files<br/>Telecharge WP core"] --> EMPTY["emptyDir<br/>wordpress-data"]
        EMPTY --> NGX["nginx<br/>Serveur web"]
        EMPTY --> PHP["php-fpm<br/>Runtime PHP"]
        NGX --- NGXEXP["nginx-exporter<br/>Metriques Prometheus"]
        PHP --- PHPEXP["phpfpm-exporter<br/>Metriques Prometheus"]
    end
    PVC["PVC Longhorn<br/>wp-content"] --> NGX
    PVC --> PHP
    CM["ConfigMaps<br/>wp-config, php.ini,<br/>www.conf, nginx.conf"] --> NGX
    CM --> PHP
    style INIT fill:#3498db,color:#fff
    style EMPTY fill:#9b59b6,color:#fff
    style PVC fill:#e67e22,color:#fff
    style CM fill:#1abc9c,color:#fff

Pourquoi un initContainer ?

L'image php-fpm-hardened est construite FROM scratch : elle ne contient ni shell, ni cp, ni aucun utilitaire systeme. Il est donc impossible de copier les fichiers WordPress core au demarrage du container PHP.

L'initContainer wordpress-files tourne sous une image Alpine generique (pas une image dediee) : elle telecharge le core WordPress depuis wordpress.org/latest.tar.gz et l'extrait dans le volume partage emptyDir avant que les containers principaux ne demarrent -- il n'y a plus de version WordPress figee dans une image Docker.

Detail technique important : les pods de ce cluster n'ont pas d'acces internet direct (tout le trafic sortant HTTPS passe par le proxy Squid SSL-bump du homelab), donc l'initContainer doit faire confiance a la CA du proxy (montee via ConfigMap) et passer explicitement par le proxy avant tout appel reseau.

# Extrait du Deployment
initContainers:
- name: wordpress-files
  image: alpine:3.23
  command: ["sh", "-c"]
  args:
    - |
      cat /etc/squid-ca/squid-bump-ca.crt >> /etc/ssl/certs/ca-certificates.crt
      export http_proxy=http://vyos.home.arpa:3130 https_proxy=http://vyos.home.arpa:3130
      apk add --no-cache curl
      curl -fsSL --connect-timeout 10 --max-time 120 --retry 3 \
        https://wordpress.org/latest.tar.gz -o /tmp/wp.tar.gz
      tar xzf /tmp/wp.tar.gz -C /mnt/wordpress --strip-components=1
      chown -R 1999:1999 /mnt/wordpress
  volumeMounts:
  - name: wordpress-data
    mountPath: /mnt/wordpress

Strategie de volumes

Deux types de volumes coexistent avec des roles distincts :

emptyDir : wordpress-data

Volume ephemere cree a chaque demarrage du pod. L'initContainer y copie les fichiers core WordPress (wp-admin, wp-includes, index.php, etc.). Ce volume est partage entre les containers nginx et php-fpm. Son contenu est recree a chaque redemarrage et correspond systematiquement a la derniere version stable disponible sur wordpress.org au moment du demarrage du pod -- il n'y a plus de version figee : deux pods du meme Deployment demarres a des instants differents peuvent en theorie executer des versions WordPress legerement differentes.

PVC Longhorn : wp-content

Volume persistant gere par Longhorn pour le repertoire wp-content/ qui contient les donnees specifiques au site :

  • themes/ : themes WordPress installes
  • plugins/ : extensions installees et configurees
  • uploads/ : medias uploades par les utilisateurs
  • cache/ : fichiers de cache generes

Ce volume survit aux redemarrages et aux mises a jour de l'image WordPress.

ConfigMaps et subPath

Les fichiers de configuration sont injectes via ConfigMaps avec des montages subPath :

volumeMounts:
- name: wp-config
  mountPath: /var/www/html/wp-config.php
  subPath: wp-config.php
- name: php-config
  mountPath: /usr/local/etc/php/php.ini
  subPath: php.ini
- name: fpm-config
  mountPath: /usr/local/etc/php-fpm.d/www.conf
  subPath: www.conf

Gotcha subPath : contrairement aux montages de volume classiques, les fichiers montes avec subPath sont figes a la creation du pod. kubelet met bien a jour le contenu de la ConfigMap sur le node (~1 minute), mais le fichier monte dans le container garde sa valeur initiale. Un rollout restart du deployment est obligatoire apres chaque modification de ConfigMap.

Configuration WordPress specifique

FS_METHOD=direct

Sans cette constante dans wp-config.php, WordPress tente d'utiliser FTP pour installer les plugins et themes. Avec FS_METHOD a direct, WordPress ecrit directement sur le filesystem -- ce qui est possible grace au fsGroup: 1999 qui donne au processus PHP les droits d'ecriture sur le PVC.

AUTOMATIC_UPDATER_DISABLED

Les mises a jour automatiques de WordPress sont desactivees. Le core est deja retelecharge en version la plus recente a chaque demarrage de pod, donc a jour par construction -- inutile de laisser wp-admin le faire aussi. Et surtout, laisser wp-admin modifier le core creerait une incoherence : toute modification ecrite dans l'emptyDir serait perdue au prochain redemarrage du pod (l'emptyDir n'etant pas persistant).

Cache Varnish

Un cache Varnish est place devant le pod WordPress pour absorber le trafic de lecture. Le healthcheck Kubernetes pointe vers /healthcheck (endpoint dedie) et non vers / (page d'accueil) pour eviter de polluer le cache avec des requetes internes.

flowchart LR
    A["Traefik"] --> B["Varnish<br/>Cache HTTP"]
    B -- "Cache HIT" --> C["Reponse directe"]
    B -- "Cache MISS" --> D["nginx<br/>Serveur web"]
    D --> E["php-fpm<br/>Execution PHP"]
    E --> F["MariaDB"]
    style B fill:#9b59b6,color:#fff
    style C fill:#27ae60,color:#fff

Securite du pod

Le pod applique des contraintes de securite strictes :

securityContext:
  runAsUser: 1999
  runAsGroup: 1999
  fsGroup: 1999
  runAsNonRoot: true

Tous les containers s'executent sous l'UID 1999 (non-root). Le fsGroup garantit que les fichiers du PVC Longhorn sont accessibles en lecture/ecriture par le processus PHP. L'initContainer wordpress-files s'execute egalement en non-root.

Workflow de mise a jour WordPress

flowchart TD
    A["Demarrage/redemarrage du pod<br/>(rollout, crash, reschedule...)"] --> B["initContainer :<br/>confiance CA proxy Squid"]
    B --> C["curl wordpress.org/latest.tar.gz<br/>via proxy vyos.home.arpa"]
    C --> D["Extraction dans l'emptyDir<br/>wordpress-data"]
    D --> E["nginx / php-fpm demarrent<br/>avec le code frais"]
    style A fill:#3498db,color:#fff
    style E fill:#27ae60,color:#fff

Il n'y a plus de mise a jour a proprement parler : chaque redemarrage de pod (deploiement volontaire, crash, reschedule Kubernetes...) retelecharge automatiquement la derniere version stable de WordPress. Aucune intervention manuelle, aucun rebuild d'image, aucun bump de tag.

Ce choix sacrifie deliberement la reproductibilite au profit de la simplicite : pas de pipeline de build a maintenir pour suivre les sorties WordPress. La contrepartie est une dependance reseau supplementaire au demarrage du pod (proxy Squid + confiance CA), et l'absence de garantie qu'un rollback restaure une version WordPress precise -- un nouveau pod tirera toujours la derniere version disponible, jamais une version anterieure. Le PVC wp-content n'est de toute facon jamais impacte par ce mecanisme, quelle que soit la version du core.

Articles lies