Béjean Développement

Google Cloud Platform : Déployer Magento 2 dans Kubernetes

Cet article est une base de travail qui vous permettra de déployer Magento 2 au sein de Kubernetes et de Google Kubernetes Engine. L'objectif de cet article et de vous permettre d'avoir une base pour le déploiement de Magento 2 avec sa base de données managée sous MySQL 8.0, Elasticsearch, PHP-FPM et Nginx. A partir de cet article, il sera possible de déployer RabbitMQ, Redis...

Premiers pas

Avant de commencer, il faut avoir installer Gcloud, Kubectl, Terraform et Kustomize sur son poste.

Je recommande l'utilisation d'un système Linux (VM, natif ou WSL) et d'installer :

Préparation du projet

Se connecter à GCloud

Lancer gcloud init

Il vous sera demandé de se connecter, pour cela, il faut ouvrir le lien dans votre navigateur, choisir votre compte Google. Terminer en sélectionnant ou en crééant un nouveau projet

Vérifier que vous êtes bien connecté en lançant la commande : gcloud auth list.

Création d'un projet GCloud

Pour créer un projet, vous pouvez lancer la commande : gcloud projects create magento2-testing --name="Magento 2 Testing". Puis vous devez sélectionner le projet, en lançant la commande : gcloud config set project magento2-testing. Vous pouvez ensuite contrôler par le biais de la commande : gcloud info.

Création d'un Service Account

Pour utiliser GCP avec Terraform, il faut créer un Service Account dans Google Cloud au sein de votre projet : gcloud iam service-accounts create terraform --description="Service Account for Terraform" --display-name="Terraform SA".

Récupérer la clé JSON en lançant la commande : gcloud iam service-accounts keys create ~./sa-private-key.json --iam-account="terraform@magento2-testing.iam.gserviceaccount.com".

Associer un compte de facturation

Afin de pouvoir déployer un service dans GCloud, il est impératif d'associer un compte de facturation au projet que nous avons créé. Pour cela, je vous invite à consulter cette documentation.

Création de l'environnement local

Sur votre ordinateur, créer le dossier magento2-testing et son dossier enfant terraform. Ce dernier servira à stocker l'ensemble des fichiers qui nous permettront de déployer une boutique Magento 2 dans un Cluster Kubernetes.

Au sein de ce dossier, vous pouvez créer 3 fichiers :

  • main.tf
  • outputs.tf
  • variables.tf

Le fichier main.tf contiendra l'ensemble de nos directives ou de notre configuration nécessaire au déploiement de l'infrastructure.

Le fichier outputs.tf contiendra les déclarations permettant d'afficher des données qui pourront être réutilisées, notamment lorsque l'on utilise des modules.

Le fichier variables.tf contiendra les variables qui personnaliseront l'infrastructure.

Création d'un workspace

Si vous le souhaitez, sachez qu'il est possible de créer des environnements de travail au sein du dossier. Voici quelques commandes qui vous seront utiles :

  • Lister les environnements : terraform workspace list
  • Sélectionner un environnement : terraform workspace select name
  • Créer un nouvel environnement : terraform workspace new name

Initialisation du projet

Importation du provider

Pour déployer notre application, nous avons besoin du provider Google. Pour cela, nous allons ajouter dans le fichier main.tf le contenu ci-dessous :

terraform {
  required_providers {
    google = {
      source = "hashicorp/google"
      version = "3.72.0"
    }
  }
}

provider "google" {
  credentials = file(~./sa-private-key.json)
  project     = "magento2-testing"
  region      = "europe-west1"
}

Le premier bloc permet d'indiquer à Terraform que l'on souhaite utiliser le provider Google. Puis le second bloc permet de s'authentifier auprès de Google. Les paramètres credentials, project correspondent aux valeurs que nous avons renseignés lors de l'utilisation des commandes gcloud.

J'ai rencontré quelques problèmes de droits en utilisant le compte de service. J'ai pu le contourner, en lançant la commande : gcloud auth application-default login et en remplaçant, dans le fichier main.tf le provider par data "google_client_config" "provider" {}.

Une fois renseigné, il faut lancer la commande terraform init qui va procéder au téléchargement du provider Google dans le dossier .terraform situé à la racine de notre projet. Un fichier nommé .terraform.lock.hcl est également créé.

Lien vers la documentation du provider Google.

Déploiement de l'infrastructure

Réservation d'une IP globale

Lors de la création d'un load balancer, Google va lui assigner une adresse IP externe éphémère. Nous allons préférer utiliser une adresse IP statique, pour cela, la commande GCloud est gcloud compute addresses create magento-frontend --project=magento2-testing --global.

Dans Terraform, il faut utiliser la ressource google_compute_global_address :

### Reserve Static IP
resource "google_compute_global_address" "magento_static_ip" {
  name    = "magento-frontend"
  project = var.project_id
}

Vous pouvez ajouter, dans le fichier outputs.tf, la valeur de l'adresse IP qui sera réservée avec la directive :

output "magento_frontend_static_ip" {
  value = google_compute_global_address.magento_static_ip.address
}

Avant d'appliquer nos changements, nous pouvons vérifier les actions qui seront réalisées par Terraform, pour ce faire, il faut lancer la commande terraform plan. A ce moment, Terraform nous indique qu'il procèdera à la création d'une ressource. Pour appliquer nos changements, il faut exécuter la commande terraform apply.

Pour réaliser la création de l'IP statique via Terraform, le compte de service, ou Service Account, doit être autorisé. Pour cela, il faut vous rendre dans la page IAM de votre projet et ajouter le rôle Administrateur d'adresse IP publique Compute au compte de service Terraform.

Création des variables

Avant de procéder à la création du cluster, nous allons définir quelques variables dans le fichier variables.tf :

variable "project_id" {
  type    = string
  default = "magento2-testing"
}
variable "region" {
  type    = string
  default = "europe-west1"
}
variable "cluster_name" {
  type    = string
  default = "gke-europe-west1"
}

Puis, nous allons remplacer, dans le fichier main.tf, les valeurs project et region par var.project_id et var.region. Votre fichier main.tf doit avoir le résultat suivant :

terraform {
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "3.72.0"
    }
  }
}

provider "google" {
  credentials = file(~./sa-private-key.json)
  project     = var.project_id
}

### Reserve Static IP
resource "google_compute_global_address" "magento_static_ip" {
  name    = "magento-frontend"
  project = var.project_id
}

Création du cluster Kubernetes

Avant de procéder à la création du cluster, il est nécessaire d'activer le service Kubernetes Engine API via la commande gcloud services enable container.googleapis.com. De plus les rôles Administrateur de Compute et Administrateur de Kubernetes Engine doivent être assignés à votre compte de service.

La resource google_container_cluster permet de créer un cluster dans Google Cloud Platform. Ajouter le bloc ci-dessous dans le fichier main.tf :

### Create Kubernetes Cluster
resource "google_container_cluster" "magento_cluster" {
  name                     = var.cluster_name
  project                  = var.project_id
  location                 = var.region
  remove_default_node_pool = true
  initial_node_count       = 1
}

Dans Google Cloud, un cluster Kubernetes ne peut pas être créé sans un ensemble de noeuds, ou node pool. Pour ce faire, nous utilisons la directive remove_default_node_pool. La directive initial_node_count permet de définir le nombre de noeuds à créer. Appliquer les modifications et patientez quelques minutes car la création d'un cluster Kubernetes n'est pas instantanée.

Création d'un ensemble de noeuds

La resource google_container_node_pool va nous permettre de créer un ensemble de noeuds au sein de notre cluster. Ajouter le bloc ci-dessous dans le fichier main.tf :

### Create Kubernetes Node Pool
resource "google_container_node_pool" "magento_preemptible_nodes" {
  name       = "magento-preemptible-nodes"
  project    = var.project_id
  location   = var.region
  cluster    = google_container_cluster.magento_cluster.name
  node_count = 1

  node_config {
    preemptible   = true
    machine_type  = "e2-standard-4"
    disk_size_gb  = "100"
    disk_type     = "pd-ssd"
    image_type    = "cos"

    oauth_scopes  = [
      "https://www.googleapis.com/auth/cloud-platform"
    ]
  }
}

En appliquant les changements ci-dessus, Terraform va demander la création de 3 noeuds, répartis dans chaque zone de la région europe-west1. Nous indiquons également que l'ensemble de noeuds doit être créé dans le cluster que nous avons créé précédemment. Nous récupérons le nom de la ressource magento_cluster par le biais de la directive google_container_cluster.magento_cluster.name.

Chaque noeud sera composé de machine e2-standard-4, soit 4 vCPUs et 16 Go de RAM par machine et avec des disques SSD de 100 Go chacun.

Déploiement du projet

Pour utiliser Kustomize et kubectl, il faut être authentifié auprès du cluster, pour ce faire, lancer la commande gcloud container clusters get-credentials gke-europe-west1 --region europe-west1 --project magento2-testing.

Installation de Nginx

Avant de déployer Magento 2, nous allons contrôler le bon fonctionnement de notre infrastructure en déployant un conteneur Nginx.

Pour ce faire, nous allons utiliserKustomize. Nous allons créer 2 dossiers en exécutant la commande mkdir -p manifests/base. Dans le dossier base nous allons créer un premier fichier nommé kustomization.yaml contenant le code suivant :

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - namespace.yaml
  - statefulset.yaml
  - service.yaml

Kustomize va nous permettre de concaténer l'ensemble des fichiers présents dans le bloc ressources lorsque l'on utilisera la commande kustomize build ./manifests/base.

Continuons, en crééant chaque fichier ci-dessous avec leur contenu.

namespace.yaml :

---
apiVersion: v1
kind: Namespace
metadata:
  name: magento

statefulset.yaml :

---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: base-nginx
  namespace: magento
spec:
  replicas: 3
  selector:
    matchLabels:
      app: base-nginx
  serviceName: "base-nginx"
  template:
    metadata:
      labels:
        app: base-nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.21
          ports:
            - containerPort: 80
      volumes:
        - name: base-nginx-persistent-storage
          persistentVolumeClaim:
            claimName: base-nginx-volumeclaim
  volumeClaimTemplates:
    - metadata:
        name: base-nginx-persistent-storage
        namespace: magento
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 1Gi

service.yaml :

---
apiVersion: v1
kind: Service
metadata:
  name: base-nginx
  namespace: magento
spec:
  type: NodePort
  selector:
    app: base-nginx
  ports:
    - port: 80

Vous pouvez lancer la commande kustomize build ./manifests/base qui va concaténer l'ensemble des fichiers définis ci-dessus. Puis, vous pouvez exécuter la commande kustomize build ./manifests/base | kubectl apply -f - pour créer les différentes ressources.

Nous pouvons tester le fonctionnement en utiliser le transfert de port, ou port-forward, pour ce faire, lancer la commande : kubectl port-forward --namespace magento $(kubectl get pod --namespace magento --selector="app=base-nginx" --output jsonpath='{.items[0].metadata.name}') 8080:80 puis, dans un autre terminal, lancer un curl : curl 127.0.0.1:8080. Vous devriez avoir, en retour, le contenu de la page HTML par défaut de Nginx.

Déploiement d'un cluster Elasticsearch

Pour l'installation d'Elasticsearch, j'ai suivi le mode d'emploi proposé par Elastic.

Je vous invite à lire la documentation si vous souhaitez des informations complémentaires sur l'installation et le paramétrage. Tout d'abord, commençons par installer les ressources personnalisées, ou CRD :

kubectl apply -f https://download.elastic.co/downloads/eck/1.6.0/all-in-one.yaml

J'ai également organisé mes manifests en créant un nouveau dossier nommé elasticsearch et j'y ai ajouté 2 fichiers YAML :

kustomization.yaml :

---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ./../base/
  - cluster.yaml

cluster.yaml :

---
apiVersion: elasticsearch.k8s.elastic.co/v1
kind: Elasticsearch
metadata:
  name: elasticsearch
  namespace: magento
spec:
  version: 7.13.2
  nodeSets:
    - name: default
      count: 3
      config:
        node.master: true
        node.data: true
        node.ingest: true
        node.store.allow_mmap: false
        xpack.security.authc:
          anonymous:
            username: anonymous
            roles: superuser
            authz_exception: false
      podTemplate:
        spec:
          initContainers:
            - name: install-plugins
              command:
                - sh
                - -c
                - |
                  bin/elasticsearch-plugin install --batch repository-gcs
  http:
    tls:
      selfSignedCertificate:
        disabled: true

Dans le fichier cluster.yaml, j'ai précisé que je ne souhaitais aucune authentification pour se connecter au cluster et j'ai également désactivé le certificat auto-signé.

Lancer la commande kustomize build ./manifests/elasticsearch/ | kubectl apply -f - pour procéder au déploiement du cluster Elasticsearch.

Création d'une instance CloudSQL

Le déploiement de l'instance CloudSQL sera réalisée par Terraform. Ajouter le bloc d'instructions, ce-dessous, à la fin du fichier main.tf. Exécuter la commande terraform apply -auto-approve pour créer :

  • un réseau privé,
  • une instance CloudSQL MySQL 8,
  • une base de données,
  • et l'utilisateur SQL.

Dans le cas où vous travaillez dans un nouveau projet, ou un projet n'ayant jamais eu d'instance Cloud SQL vous devriez avoir l'erreur ci-dessous :

Error, failed to create instance magento: googleapi: Error 400: Invalid request: Incorrect Service Networking config for instance: magento2-testing:magento:SERVICE_NETWORKING_NOT_ENABLED., invalid

Pour activer le service networking, il faut se connecter en tant que propriétaire avec la commande gcloud config set account <votre-email-google> et exécuter la commande gcloud services enable servicenetworking.googleapis.com --project=magento2-testing.

Connectez-vous, ensuite, avec votre compte de service : gcloud auth activate-service-account terraform@magento2-testing.iam.gserviceaccount.com --key-file=~./sa-private-key.json.

main.tf :

### Create Cloud SQL
data "google_compute_network" "private_network" {
  provider = google
  project  = var.project_id
  name     = "default"
}

resource "google_compute_global_address" "private_ip_address" {
  provider      = google
  name          = "private-ip-address"
  project       = var.project_id
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = 16
  network       = data.google_compute_network.private_network.id
}

resource "google_service_networking_connection" "private_vpc_connection" {
  provider                = google
  network                 = data.google_compute_network.private_network.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.private_ip_address.name]
}

resource "google_sql_database_instance" "magento" {
  name                = "magento"
  project             = var.project_id
  region              = var.region
  database_version    = "MYSQL_8_0"
  deletion_protection = true

  settings {
    tier                 = "db-custom-2-3840" # 2vCPU - 3.75 Go
    availability_type    = "REGIONAL"

    backup_configuration {
      enabled            = true
      binary_log_enabled = true
    }

    ip_configuration {
      ipv4_enabled       = true
      private_network    = data.google_compute_network.private_network.id
    }
  }

  depends_on = [google_service_networking_connection.private_vpc_connection]
}

resource "google_sql_database" "database" {
  project  = var.project_id
  name     = "magento"
  instance = google_sql_database_instance.magento.name
}

resource "google_sql_user" "users" {
  project  = var.project_id
  name     = var.sql_user
  instance = google_sql_database_instance.magento.name
  host     = "%"
  password = var.sql_password
}

variables.tf :

Ajouter également ce bloc dans le fichier variables.tf :

variable "sql_user" {
  type        = string
  description = "MySQL User"
  sensitive   = true
}
variable "sql_password" {
  type        = string
  description = "MySQL User Password"
  sensitive   = true
}

outputs.tf :

Terminons par l'affichage d'un output dans le fichier outputs.tf :

output "magento_cloudsql_instance_connection_name" {
  value = google_sql_database_instance.magento.connection_name
}

Création des secrets

Avant de commencer le déploiement de Magento 2, nous allons créer 2 secrets, un premier pour la connexion au CloudSQL et un second pour se connecter à la base de données du CloudSQL. Ces secrets contiennent les données du compte de service ainsi que les valeurs des variables Terraform sql_user et sql_password.

Dans votre terminal, créer le premier secret en lançant la commande :

kubectl create secret generic cloudsql-instance-credentials -n magento --from-file ~./sa-private-key.json

Pour utiliser le premier secret, votre service de compte doit avoir le droit Client Cloud SQL pour se connecter à votre instance.

Créer le dernier secret pour permettre la connexion à l'instance CloudSQL depuis Kubernetes :

kubectl create secret generic cloudsql-db-credentials -n magento \
    --from-literal username=<sql-user> \
    --from-literal dbname=<sql-dbname> \
    --from-literal password=<sql-password>

Activer le service API

Pour se connecter à la base de données CloudSQL, nous allons utiliser un Cloud Proxy mais avant, nous avons besoin d'activer le service sqladmin, pour ce faire, connectez-vous en tant que propriétaire avec la commande gcloud config set account <votre-email-google> et exécutez la commande gcloud services enable sqladmin.googleapis.com --project=magento2-testing.

Vous pouvez vérifier en ouvrant la page web du service. Vous devriez avoir un texte API activée avec une icône verte.

Installation de Magento

Nous allons créer un nouveau dossier nommé magento qui sera enfant du dossier manifests. Ce dossier contiendra 5 fichiers, voici le contenu de chaque fichier :

kustomization.yaml :

---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ./../elasticsearch/
  - configmap-nginx.yaml
  - configmap-phpfpm.yaml
  - statefulset.yaml
  - service.yaml

configmap-nginx.yaml :

Certains paramètres ci-dessous sont spécifique à mon utilisation. Le cluster Magento 2 que nous déployons est paramétré pour un environnement de développement et non de production. Je vous invite à modifier les paramètres ci-dessous.

Pensez à modifier la valeur <votre-nom-de-domaine> pour la remplacer par votre nom de domaine associé à l'adresse IP statique que nous avons réservée au début de l'article.

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: nginx
  namespace: magento
data:
  default: |-
    upstream fastcgi_backend {
      server 127.0.0.1:9000;
    }

    server {
      listen       8000;
      listen  [::]:8000;
      server_name m2-testing.bejean.ovh magento2-testing.bejean.ovh;

      set $MAGE_ROOT /var/www/html/magento;
      set $MAGE_DEBUG_SHOW_ARGS 0;

      include /var/www/html/magento/nginx.conf.sample;
    }
  nginx: |-
    # let's assume dual-core machine
    worker_processes 2;

    error_log /var/log/nginx/error.log debug;
    pid /var/run/nginx.pid;

    events {
      # this should be equal to value of "ulimit -n"
      # reference: https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration
      worker_connections 1024;
    }

    http {
      sendfile on;
      tcp_nopush on;
      tcp_nodelay on;
      types_hash_max_size 2048;

      map $scheme $fastcgi_https { ## Detect when HTTPS is used
        default off;
        https on;
      }

      server_names_hash_bucket_size 64;

      include /etc/nginx/mime.types;
      default_type application/octet-stream;

      rewrite_log on;
      log_format main ‘$remote_addr - $remote_user [$time_local] $request ‘ ‘”$status” $body_bytes_sent “$http_referer” ‘ ‘”$http_user_agent” “$http_x_forwarded_for”’

      gzip on;
      gzip_disable “msie6”;
      gzip_vary on;
      gzip_proxied any;
      gzip_comp_level 6;
      gzip_min_length 1100;
      gzip_buffers 16 8k;
      gzip_http_version 1.1;
      gzip_types
        text/plain
        text/css
        text/js
        text/xml
        text/javascript
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/xml+rss
        image/svg+xml;

      client_body_buffer_size 10K;
      client_header_buffer_size 1k;
      client_max_body_size 8m;
      large_client_header_buffers 4 16k;

      client_body_timeout 12;
      client_header_timeout 12;
      # Tune nginx keepalives to work with the GCP HTTP(S) Load Balancer:
      keepalive_timeout 650;
      keepalive_requests 10000;
      send_timeout 10;

      include /etc/nginx/conf.d/*.conf;
    }

configmap-phpfpm.yaml :

Certains paramètres ci-dessous sont spécifique à mon utilisation. Le cluster Magento 2 que nous déployons est paramétré pour un environnement de développement et non de production. Je vous invite à modifier les paramètres ci-dessous.

Pensez à modifier la valeur <username> et <password> pour la remplacer par les identifiants que vous possédez.

---
apiVersion: v1
kind: ConfigMap
metadata:
  name: phpfpm
  namespace: magento
data:
  auth: |-
    {
        "http-basic": {
            "repo.magento.com": {
                "username": "<username>",
                "password": "<password>"
            }
        }
    }
  php: |-
    ; -*- coding: utf-8 -*-

    memory_limit = 4G
    max_execution_time = 1800
    zlib.output_compression = On
    cgi.fix_pathinfo = 1
    date.timezone = "Europe/Paris"
    opcache.enable_cli = 1
    opcache.memory_consumption = 512
    opcache.max_accelerated_files = 100000
    opcache.save_comments = 1
    realpath_cache_size = 10M
    realpath_cache_ttl = 7200

    ; Default Value
    expose_php = On
    display_errors = On ; Production Value: Off
    display_startup_errors = On ; Production Value: Off
    error_reporting = E_ALL ; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT
    log_level = debug
    ignore_repeated_errors = Off
    ignore_repeated_source = Off

    ; Specific Value
    html_errors = On ; Default value: Off
    log_errors_max_len = 1024 ; Default value: 1024
    upload_max_filesize = 8M ; Default value: 1024
    post_max_size = 8M ; Default value: 1024
  phpfpm: |-
    ; -*- coding: utf-8 -*-

    [global]
    error_log = /proc/self/fd/2
    daemonize = no

    [www]
    access.log = /proc/self/fd/2

    user = app
    group = app

    listen = 9000
    listen.owner = app
    listen.group = app
    listen.mode = 0660

    pm = dynamic
    pm.max_children = 40
    pm.start_servers = 16
    pm.min_spare_servers = 8
    pm.max_spare_servers = 24

    clear_env = no

    ; Ensure worker stdout and stderr are sent to the main error log.
    catch_workers_output = yes

service.yaml :

---
apiVersion: v1
kind: Service
metadata:
  name: frontend
  namespace: magento
spec:
  type: NodePort
  selector:
    app: frontend
  ports:
    - port: 8000
      protocol: TCP
      targetPort: 8000
      name: http

statefulset.yaml :

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: frontend
  namespace: magento
spec:
  replicas: 1
  selector:
    matchLabels:
      app: frontend
  serviceName: "frontend"
  template:
    metadata:
      labels:
        app: frontend
    spec:
      securityContext:
        runAsUser: 1000
        runAsGroup: 1000
        fsGroup: 1000
      terminationGracePeriodSeconds: 10
      containers:
        - name: nginx
          image: lab.frogg.it:5050/bejean-developpement/kubernetes/magento2/kubenginx:1.0.5
          ports:
            - containerPort: 8000
              name: nginx
          volumeMounts:
            - name: frontend-persistent-storage
              mountPath: /var/www/html
            - name: configmap-nginx
              mountPath: /etc/nginx/nginx.conf
              subPath: nginx
              readOnly: true
            - name: configmap-nginx
              mountPath: /etc/nginx/conf.d/default.conf
              subPath: default
              readOnly: true
          livenessProbe:
            httpGet:
              port: 8000
              path: /healthcheck.html
            initialDelaySeconds: 3
            periodSeconds: 3
          readinessProbe:
            httpGet:
              port: 8000
              path: /healthcheck.html
            initialDelaySeconds: 3
            periodSeconds: 3
        - name: phpfpm
          image: lab.frogg.it:5050/bejean-developpement/kubernetes/magento2/kubephpfpm:1.0.6
          ports:
            - containerPort: 9000
              name: phpfpm
          env:
            - name: MAGENTO_DB_HOST
              value: 127.0.0.1:3306
            - name: MAGENTO_DB_NAME
              valueFrom:
                secretKeyRef:
                  name: cloudsql-db-credentials
                  key: dbname
            - name: MAGENTO_DB_USER
              valueFrom:
                secretKeyRef:
                  name: cloudsql-db-credentials
                  key: username
            - name: MAGENTO_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: cloudsql-db-credentials
                  key: password
          volumeMounts:
            - name: frontend-persistent-storage
              mountPath: /var/www/html
            - name: configmap-phpfpm
              mountPath: /var/www/.composer/auth.json
              subPath: auth
              readOnly: true
            - name: configmap-phpfpm
              mountPath: /usr/local/etc/php/php.ini
              subPath: php
              readOnly: true
            - name: configmap-phpfpm
              mountPath: /usr/local/etc/php-fpm.conf
              subPath: phpfpm
              readOnly: true
        - name: cloudsql-proxy
          image: gcr.io/cloudsql-docker/gce-proxy:1.11
          command: ["/cloud_sql_proxy",
                    "-instances=magento2-testing:europe-west1:magento=tcp:3306",
                    "-credential_file=/secrets/cloudsql/sa-private-key.json" ]
          securityContext:
            runAsUser: 2  # non-root user
            allowPrivilegeEscalation: false
          volumeMounts:
            - name: cloudsql-instance-credentials
              mountPath: /secrets/cloudsql
              readOnly: true
      volumes:
        - name: frontend-persistent-storage
          persistentVolumeClaim:
            claimName: frontend-volumeclaim
        - name: cloudsql-instance-credentials
          secret:
            secretName: cloudsql-instance-credentials
        - name: configmap-nginx
          configMap:
            name: nginx
            items:
              - key: default
                path: default
              - key: nginx
                path: nginx
        - name: configmap-phpfpm
          configMap:
            name: phpfpm
            items:
              - key: auth
                path: auth
              - key: php
                path: php
              - key: phpfpm
                path: phpfpm
  volumeClaimTemplates:
    - metadata:
        name: frontend-persistent-storage
        namespace: magento
      spec:
        accessModes: [ "ReadWriteOnce" ]
        resources:
          requests:
            storage: 50Gi

Lorsque vous lancerez la commande kustomize build ./manifests/magento | kubectl apply -f - tous les conteneurs seront opérationnelles sauf nginx. Pour avoir accès aux logs, vous pouvez lancer la commande : kubectl logs frontend-0 -n magento -c nginx. Ceci vous indiquera que le conteneur nginx a besoin du fichier nginx.conf.sample. Ce dernier n'est pas présent dans le disque persistant car nous n'avons pas installé les fichiers sources de Magento.

Pour plus de simplicité, les commandes ci-dessous vous permettront d'installer, au sein de votre projet, les fichiers sources de Magento ainsi que la base de données. Dans un environnement professionnel, je vous recommande de créer un projet Git contenant les fichiers sources de Magento. Un fichier Dockerfile sera nécessaire pour vous permettre de compiler des images PHP-FPM et Nginx avec les fichiers sources de votre projet Git. Ces images devront être chargées au sein d'une registry Docker et récupérées lors du déploiement du StetefulSet.

Reprenons le déploiement en lançant la création du projet Magento 2 via Composer :

kubectl exec -it frontend-0 -n magento -c phpfpm -- composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition magento

Si toutefois, vous préférez réaliser cette action directement dans le conteneur vous avez la possibilité d'exécuter la commande : kubectl exec -it frontend-0 -n magento -c phpfpm -- bash.

Avant d'installer Magento, nous allons créer un fichier healthcheck.html dans le dossier pub. POur ce faire, nous allons utiliser la commande précédente pour se rendre dans le conteneur phpfpm. Puis, nous lançons la commande cd magento && touch pub/healthcheck.html.

Le fichier healthcheck.html est très important car il va permettre aux internautes d'accéder à la boutique Magento 2 en passant par load balancing. Sachez qu'il est possible de personnaliser selon vos envies en vérifiant, par exemple, la connexion à la base de données, au serveur Elasticsearch...

Une fois le fichier crée, nous allons installer les tables dans la base de données depuis le pod phpfpm. Connectez-vous au pod et lancez la commande ci-dessous, à la racine du projet Magento :

bin/magento setup:install \
  --db-host=$MAGENTO_DB_HOST \
  --db-name=$MAGENTO_DB_NAME \
  --db-user=$MAGENTO_DB_USER \
  --db-password=$MAGENTO_DB_PASSWORD \
  --base-url=https://<votre-nom-de-domaine>/ \
  --backend-frontname=admin \
  --admin-firstname=<prenom> \
  --admin-lastname=<nom> \
  --admin-email=<votre-email>> \
  --admin-user=admin \
  --admin-password=<votre-mot-de-passe> \
  --language=fr_FR \
  --currency=EUR \
  --timezone=Europe/Paris \
  --use-rewrites=1 \
  --search-engine=elasticsearch7 \
  --elasticsearch-host=elasticsearch-es-http \
  --elasticsearch-port=9200 \
  --elasticsearch-index-prefix=magento

Déploiement du Load Balancer

Nous allons utiliser le Load Balancer proposé par Google avec un certificat auto-généré. Commençons par créer un nouveau dossier nommé loadbalancer dans le dossier manifests. Au sein de ce dossier, nous allons créer 4 fichiers dont le contenu est le suivant :

kustomization.yaml :

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
  - ./../magento/
  - certificate.yaml
  - config.yaml
  - ingress.yaml

certificate.yaml :

---
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: certificate
  namespace: magento
spec:
  domains:
    - <votre-nom-de-domaine>

config.yaml :

---
apiVersion: networking.gke.io/v1beta1
kind: FrontendConfig
metadata:
  name: redirect-to-https-config
  namespace: magento
spec:
  redirectToHttps:
    enabled: true
    responseCodeName: MOVED_PERMANENTLY_DEFAULT

ingress.yaml :

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: frontend-ingress
  namespace: magento
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "magento-frontend"
    networking.gke.io/managed-certificates: "certificate"
    kubernetes.io/ingress.class: "gce"
    networking.gke.io/v1beta1.FrontendConfig: "redirect-to-https-config"
spec:
  defaultBackend:
    service:
      name: frontend
      port:
        number: 8000

Pour lancer la création du load balancer , il est nécessaire de modifier la commande kustomize en spécifiant le dossier loadbalancer. Exécuter la commande kustomize build ./manifests/loadbalancer | kubectl apply -f - pour créer les différentes ressources. Le temps de création des Ingress prend un peu de temps. Il faut patienter quelques minutes avant de pouvoir afficher la page HTML du conteneur nginx.

Vous pouvez tester, en lançant un curl sur votre nom de domaine en http et en https où vous devriez avoir une 301 en http et une 200 avec le contenu HTML par défaut de Nginx en https.

Vérification du projet

Désormais, vous pouvez ouvrir votre navigateur préféré et accéder à votre boutique Magento 2.

Formation vidéo

Pour mieux appréhender Terraform et Kubernetes, j'ai suivi les formations vidéos de Xavki :

Liens utiles