Blog de dada

DevOps, bidouilleur et routard plein de logiciels libres

kubernetes

MariaDB en Master/Slave via ProxySQL

Rédigé par dada / 01 décembre 2018 / Aucun commentaire



On y est. J'ai enfin réussi à faire tourner ProxySQL comme je l'entendais pour que mes conteneurs tapent dans des BDD loin de Kubernetes. Sortir ses bases de données du cluster est un choix. Si vous êtes courageux, vous pouvez les laisser dedans. Ceci-dit, faut être un tantinet croyant pour se lancer dans la seconde solution. Dans ces histoires de hautes dispo et de résistance aux pannes, la bonne solution semble être de gérer son cluster de BDD en dehors de son cluster k8s.

Installation des MariaDB

Là, je devrais vous passer une pile de commandes pour installer des serveurs MariaDB en master/slave, mais bon. Tout est très clair dans la documentation officielle.

Sachez simplement que le master porte l'IP 192.168.0.17 et que le slave, lui, là 192.168.0.77.

Installation de ProxySQL

Helm ? Non, pas ici. On va donc pondre un YAML avec le Deployment et le Service qui va bien :
dada@master:~/proxysql$ cat proxysql.yaml

apiVersion: v1
kind: Deployment
metadata:
  name: proxysql
  labels:
    app: proxysql
spec:
  replicas: 2
  selector:
    matchLabels:
      app: proxysql
      tier: frontend
  strategy:
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: proxysql
        tier: frontend
    spec:
      restartPolicy: Always
      containers:
      - image: severalnines/proxysql:1.4.12
        name: proxysql
        volumeMounts:
        - name: proxysql-config
          mountPath: /etc/proxysql.cnf
          subPath: proxysql.cnf
        ports:
        - containerPort: 6033
          name: proxysql-mysql
        - containerPort: 6032
          name: proxysql-admin
      volumes:
      - name: proxysql-config
        configMap:
          name: proxysql-configmap
---
apiVersion: v1
kind: Service
metadata:
  name: proxysql
  labels:
    app: proxysql
    tier: frontend
spec:
  type: NodePort
  ports:
  - nodePort: 30033
    port: 6033
    name: proxysql-mysql
  - nodePort: 30032
    port: 6032
    name: proxysql-admin
  selector:
    app: proxysql
    tier: frontend

A remarquer

Le Deployment contient la configuration de ce qu'on appelle un ReplicatSet. Ça permet à k8s de toujours maintenir le nombre de pods déclaré en fonction. Quand un pod se casse la figure, il revient à lui. Dans un RS, quand un pod de casse la figure, il va aussi revenir à lui. Super. Sauf que, par exemple, dans le cas d'une mise à jour, k8s ne va pas terminer les pods d'un coup, mais les gérer un par un.

Le YAML contient aussi un Service de type NodePort qui va nous permettre de mapper les ports 30032 et 30033 internes aux pods sur les ports 6032 et 6033 accessibles depuis l'extérieur. Voyez la suite du billet pour découvrir qu'au lieu de taper sur le port 3306 de vos serveurs MariaDB, vous allez taper sur le port 6033.

On fait gober tout ça au cluster :
dada@master:~/proxysql$ kubectl apply -f proxysql.yaml
Et voilà le travail :
dada@master:~/proxysql$ kubectl get pods --all-namespaces  | grep proxysql
default            proxysql-5c47fb85fb-fdh4g                              1/1     Running     1          39h
default            proxysql-5c47fb85fb-kvdfv                              1/1     Running     1          39h

Configuration de ProxySQL

Tout va se jouer dans le fichier proxysql.cnf. Tout, ou presque, mais j'en parlerai après. Voici déjà de quoi faire :
datadir="/var/lib/proxysql"
admin_variables=
{
        admin_credentials="proxysql-admin:adminpwd"
        mysql_ifaces="0.0.0.0:6032"
        refresh_interval=2000
}
mysql_variables=
{
        threads=4
        max_connections=2048
        default_query_delay=0
        default_query_timeout=36000000
        have_compress=true
        poll_timeout=2000
        interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
        default_schema="information_schema"
        stacksize=1048576
        server_version="5.1.30"
        connect_timeout_server=10000
        monitor_history=60000
        monitor_connect_interval=200000
        monitor_ping_interval=200000
        ping_interval_server_msec=10000
        ping_timeout_server=200
        commands_stats=true
        sessions_sort=true
        monitor_username="proxysql"
        monitor_password="proxysqlpwd"
}
mysql_replication_hostgroups =
(
        { writer_hostgroup=10, reader_hostgroup=20, comment="MariaDB Replication" }
)
mysql_servers =
(
        { address="192.168.0.17", port=3306, hostgroup=10, max_connections=100, max_replication_lag = 5 },
        { address="192.168.0.77", port=3306, hostgroup=20, max_connections=100, max_replication_lag = 5}
)
mysql_users =
(
        { username = "nextcloud" , password = "nextcloudpwd" , default_hostgroup = 10 , active = 1 }
)
mysql_query_rules =
(
        {
                rule_id=100
                active=1
                match_pattern="^SELECT .* FOR UPDATE"
                destination_hostgroup=10
                apply=1
        },
        {
                rule_id=200
                active=1
                match_pattern="^SELECT .*"
                destination_hostgroup=20
                apply=1
        },
        {
                rule_id=300
                active=1
                match_pattern=".*"
                destination_hostgroup=10
                apply=1
        }
)
Il est long alors on va le découper ensemble.

Global

{
        admin_credentials="proxysql-admin:adminpwd"
        mysql_ifaces="0.0.0.0:6032"
        refresh_interval=2000
}
Ici, on retrouve surtout les accès administrateur de ProxySQL et son port qui écoute sans limite d'IP.

Mysql_variables

mysql_variables=
{
        threads=4
        max_connections=2048
        default_query_delay=0
        default_query_timeout=36000000
        have_compress=true
        poll_timeout=2000
        interfaces="0.0.0.0:6033;/tmp/proxysql.sock"
        default_schema="information_schema"
        stacksize=1048576
        server_version="5.1.30"
        connect_timeout_server=10000
        monitor_history=60000
        monitor_connect_interval=200000
        monitor_ping_interval=200000
        ping_interval_server_msec=10000
        ping_timeout_server=200
        commands_stats=true
        sessions_sort=true
        monitor_username="proxysql"
        monitor_password="proxysqlpwd"
}
Là encore, je ne vais pas m'étendre sur toute la configuration (par défaut) mais sur deux points seulement : le port 6033 sera celui que vous allez rentrer dans les fichiers de configuration de vos services ayant besoin de MariaDB et l'utilisateur "monitor" est là pour vérifier l'état de vos serveurs MariaDB.

Mysql_replication_hostgroups

mysql_replication_hostgroups =
(
        { writer_hostgroup=10, reader_hostgroup=20, comment="MariaDB Replication" }
)
C'est à partir de là qu'on commence à s'amuser ! Ici, on déclare deux groupes de serveurs avec deux ID différents. Les writer, ceux qui ont le droit d'écrire et les reader, ceux qui n'auront que le droit de lire. Ça tombe bien, c'est plus ou moins ce qu'on veut.

Mysql_servers

mysql_servers =
(
        { address="192.168.0.17", port=3306, hostgroup=10, max_connections=100, max_replication_lag = 5 },
        { address="192.168.0.77", port=3306, hostgroup=20, max_connections=100, max_replication_lag = 5}
)
Si vous aviez suivi le blabla précédent, vous pouvez déchiffrer ces deux lignes en :

- Le serveur avec l'IP 192.168.0.17 fait partie du groupe des writers On lui demande de traiter 100 connexions actives maximum et d'avoir un lag avec son master de moins de 5 secondes.
- Le serveur avec l'IP 192.168.0.77 fait partie du groupe des reader. On lui demande de traiter 100 connexions actives maximum et d'avoir un lag avec son master de moins de 5 secondes.

Mysql_users

mysql_users =
(
        { username = "nextcloud" , password = "nextcloudpwd" , default_hostgroup = 10 , active = 1 }
)
Les utilisateurs MariaDB doivent être renseignés ici !
Dans mon cas, profitez de la configuration sécurisée de mon utilisateur nextcloud. Il sera assigné à un groupe d'utilisateur par défaut : le groupe des writer, et sera activé. Il est possible qu'un besoin particulier soit à l'origine de la création de la variable "active" mais j'ai du mal à comprendre pourquoi un compte MariaDB resterait renseigné avec active = 0 plutôt que dégagé. M'enfin.

Mysql_query_rules

mysql_query_rules =
(
        {
                rule_id=100
                active=1
                match_pattern="^SELECT .* FOR UPDATE"
                destination_hostgroup=10
                apply=1
        },
        {
                rule_id=200
                active=1
                match_pattern="^SELECT .*"
                destination_hostgroup=20
                apply=1
        },
        {
                rule_id=300
                active=1
                match_pattern=".*"
                destination_hostgroup=10
                apply=1
        }
Là, c'est la définition des règles qui seront automatiquement appliquées pour chaque serveur. La plus claire est la règle à l'ID 200 qui est assignée au hostgroup 20, celui des reader. C'est là que ProxySQL va faire un tri. Le hostgroup 20 n'ayant que la possibilité de faire des SELECT, on comprend bien qu'on parle de lecteurs uniquement.

ConfigMap

On charge tout ça dans une ConfigMap :
dada@master:~/proxysql$ kubectl create configmap proxysql-configmap --from-file=proxysql.cnf

Si on prend le temps de revenir sur le YAML pour comprendre la ConfigMap, on la repère ici :

      containers:
[...]
        volumeMounts:
        - name: proxysql-config
          mountPath: /etc/proxysql.cnf
          subPath: proxysql.cnf
[...]
      volumes:
      - name: proxysql-config
        configMap:
          name: proxysql-configmap

On comprend que les pods ProxySQL vont aller parcourir la liste des ConfigMaps disponibles pour repérer celle qui porte le nom "proxysql-config" et la monter dans /etc/proxysql.cnf.

Vérifier tout le système

Une commande que vous devriez connaître par cœur va nous prouver que tout fonctionne :

dada@master:~/proxysql$ kubectl logs proxysql-5c47fb85fb-fdh4g

Chez moi, elle sort quelque chose comme ça :

2018-12-01 08:30:19 [INFO] Dumping mysql_servers
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+-----------------+
| hostgroup_id | hostname     | port | weight | status | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment | mem_pointer     |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+-----------------+
| 10           | 192.168.0.17 | 3306 | 1      | 0      | 0           | 100             | 5                   | 0       | 0              |         | 140637072236416 |
| 20           | 192.168.0.17 | 3306 | 1      | 0      | 0           | 100             | 5                   | 0       | 0              |         | 140637022769408 |
| 20           | 192.168.0.77 | 3306 | 1      | 0      | 0           | 100             | 5                   | 0       | 0              |         | 140637085320960 |
+--------------+--------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+---------+-----------------+

On y retrouve la liste des serveurs et leurs rôles : mon master appartient aux groupes reader et writer. Normal puisqu'il doit écrire et lire. Mon slave, lui, n'appartient qu'au groupe des reader, comme convenu.

Le piège

Si j'ai pesté un certain temps contre ProxySQL sur Mastodon , c'est que ma configuration ne marchait pas alors que tout semblait normal. J'avais bien en tête ce que je viens de vous expliquer: Les reader, les writer, la configuration des users, etc. C'était clair.
Pourtant, quand je lancais mon conteneur Nextcloud pour qu'il s'installe comme un grand, il pété ma réplication en écrivant sur mon slave. Le con.
J'ai fini par ouvrir une issue pour crier à l'aide et on m'a expliqué ceci : ProxySQL vérifie la présence de la variable READ_ONLY sur tous les serveurs renseignés dans la rubrique mysql_replication_hostsgroups. Si la variable est a OFF sur votre slave, la réplication entre les deux serveurs MariaDB ne bronchera pas, mais ProxySQL ira taper dans votre slave, peu importe la configuration en place. Re le con.

Pour vous épargner des soucis, pensez bien à vérifier que votre slave affiche bien ceci :
MariaDB [(none)]> SHOW VARIABLES like 'read_only';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| read_only     | ON   |
+---------------+-------+
1 row in set (0.00 sec)
Vous êtes bons. Vous pouvez maintenant aller configurer vos pods pour aller taper sur vos MariaDB via proxysql:6033 en place et lieu de localhost:3306.

Prometheus et Graphana pour voir dans son cluster Kubernetes

Rédigé par dada / 23 novembre 2018 / 2 commentaires


Mes aventures avec Kubernetes ne se sont pas arrêtées, loin de là. J'ai actuellement 54 pods répartis sur 3 nodes et 1 master et 2 VM MariaDB en master/slave, le tout reparti sur mon laptop, mon PC fixe et un vieux PC portable datant de ma vie étudiante. Je pourrais aussi vous parler de mon Pi-Hole sur ma Raspberry qui voit passer toutes les requêtes. Bref.
Aujourd'hui, alors que je peste contre ProxySQL sur Mastodon, j'ai décidé de regarder du côté du monitoring de l'orchestrateur, histoire de prendre l'air. Si vous êtes un malheureux lecteur régulier, vous devriez savoir que j'adore Prometheus et que Grafana fait, à mes yeux, des dessins bien plus impressionnants que Klimt.

Note : ça n'a pas calmé ma frustration : c'est très rapide et simple à mettre en place.

Installer Prometheus

Avec pour base mes billets précédents, nous allons installer Prometheus, Grafana et l'AlertManager avec Helm.

Ajouter le dépôt dans helm

Là, à la manière d'APT, nous allons ajouter une source à Helm pour lui permettre d'installer ce qu'on lui demande.

helm repo add coreos https://s3-eu-west-1.amazonaws.com/coreos-charts/stable/

Installer l'operator

On enchaîne sur l'installation de l'operator, le grand patron qui va s'occuper pour nous de Prometheus dans k8s.

helm install coreos/prometheus-operator --name prometheus-operator --namespace monitoring
Une fois exécutée, vous devriez voir ça :
dada@master:~/prometheus$ kubectl get pods -n monitoring -o wide
NAME                                                   READY   STATUS    RESTARTS   AGE   IP             NODE     NOMINATED NODE
alertmanager-kube-prometheus-0                         2/2     Running   0          37m   10.244.3.140   node3    <none>
kube-prometheus-exporter-kube-state-65b6dbf6b4-b52jl   2/2     Running   0          33m   10.244.3.141   node3    <none>
kube-prometheus-exporter-node-5tnsl                    1/1     Running   0          37m   192.168.0.76   node1    <none>
kube-prometheus-exporter-node-fd7pt                    1/1     Running   0          37m   192.168.0.49   node3    <none>
kube-prometheus-exporter-node-mfdj2                    1/1     Running   0          37m   192.168.0.22   node2    <none>
kube-prometheus-exporter-node-rg5q6                    1/1     Running   0          37m   192.168.0.23   master   <none>
kube-prometheus-grafana-6f6c894c5b-2d6h4               2/2     Running   0          37m   10.244.2.165   node2    <none>
prometheus-kube-prometheus-0                           3/3     Running   1          37m   10.244.1.187   node1    <none>
prometheus-operator-87779759-wkpfz                     1/1     Running   0          49m   10.244.1.185   node1    <none>

Notez que si vous êtes, comme moi, sur une connexion ADSL classique, vous aller avoir le temps d'aller faire couler un grand café et d'aller le boire une clope au bec et au soleil. Votre cluster va télécharger beaucoup de pods et sur chaque nœud.

En y regardant bien, on retrouve :

  • L'AlertManager permettant de nous spammer en cas de souci,
  • Les exporter nodes placés logiquement sur chaque node et sur notre master,
  • Grafana, le copain de Prometheus qui vous fait des beaux dessins,
  • Le kube et l'operator.

Accéder à tout ça

L'installation est vraiment triviale. Le petit bonus de ce billet sera de vous passer une liste de commandes pour admirer le tout dans votre navigateur préféré : Firefox.

Pour accéder à l'interface de Prometheus

Commencez par ouvrir un tunnel SSH sur le port 9090 vers votre master :

ssh -L 9090:127.0.0.1:9090 dada@IPDuMaster

Puis lancez le port-foward :

kubectl port-forward -n monitoring prometheus-prometheus-operator-prometheus-0 9090

Pour accéder à l'interface de Grafana

Encore un tunnel SSH, sur le 3000 ce coup-ci :

ssh -L 3000:127.0.0.1:3000 dada@IPDuMaster

Et encore un port-forward :

kubectl port-forward $(kubectl get  pods --selector=app=grafana -n  monitoring --output=jsonpath="{.items..metadata.name}") -n monitoring  3000

Vous êtes bons ! Les dashboards sont maintenant accessibles en tapant http://localhost:PORT dans Firefox.

En image, ça devrait donner ça pour Grafana :

Et ça pour les alertes Prometheus :

Alors, oui. Vous avez aussi remarqué que des alertes étaient déjà levées ? Ce sont des outils/configurations que Prometheus attend de rencontrer dans votre cluster. Le mien n'a pas encore ces histoires de scheduler ou de controller manager. Ça va faire partie des découvertes à suivre dans les futurs billets.

Des bisous !

Et si on mettait PluXml dans un cluster k8s ?

Rédigé par dada / 11 novembre 2018 / Aucun commentaire



L'idée est saugrenue mais c'est exactement ce que j'ai fait ces derniers temps. Non sans souffrir, j'ai réussi à m'en sortir. Au programme : créer un conteneur qui va bien pour PluXml, mettre en place un déploiement pour le faire tourner dans k8s et l'utilisation de PersistentVolume.

Notez que je découvre toujours cet univers et que ce qui est présenté dans ce billet ne reflète que mes avancées.

La création du conteneur

Introduction

J'ai déjà pondu quelques conteneurs, principalement des exporters Prometheus en Python qui vont récupérer les statistiques de Mastodon et Pixelfed pour les afficher dans des dashboards Grafana (combo mots clés pour des recruteurs).

Pour PluXml, c'est différent. Il nous faut un conteneur avec :
- Les sources du CMS
- Du php7 et ses dépendances
- Un serveur web

Voyons comment y arriver en s'inspirant de ce qui a été fait pour Wordpress.

Le Dockerfile

C'est quoi ? C'est le fichier de configuration qui va nous permettre de créer le conteneur. En gros, les sources du conteneur.
FROM php:7.0-apache

WORKDIR /var/www/html

RUN apt update
RUN apt install -y wget unzip
RUN wget https://git.dadall.info/dada/pluxml/raw/master/pluxml-latest.zip
RUN mv pluxml-latest.zip /usr/src/

#VOLUME
VOLUME /var/www/html

RUN a2enmod rewrite
RUN service apache2 restart
RUN apt-get update && apt-get install -y \
        libfreetype6-dev \
        libjpeg62-turbo-dev \
        libpng-dev \
    && docker-php-ext-install -j$(nproc) iconv \
    && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \
    && docker-php-ext-install -j$(nproc) gd

# Expose
EXPOSE 80

COPY entrypoint.sh /usr/local/bin/

ENTRYPOINT ["entrypoint.sh"]
CMD ["apache2-foreground"]

Comprendre le Dockerfile

FROM : C'est ici qu'on indique clairement la solution de facilité. Pour ne pas se battre avec l'installation d'Apache et de PHP7, j'indique au Dockerfile de prendre comme base un conteneur ayant déjà PHP7, Apache et Debian configurés.

WORKDIR : Ici, on indique où vont s’exécuter les commandes qui vont suivre. Ce n'est pas pertinent dans mon cas, mais bon.

VOLUME : C'est une sorte de point de montage entre le conteneur et son hôte.

RUN : Simple : les commandes que nous allons exécuter à la création du conteneur. Ici, on va récupérer les sources de PluXml qui sont hébergées dans mon Gitlab perso, les extraire et les préparer. On va aussi activer le module Apache nécessaire au bon fonctionnement du CMS et installer le module PHP-GD, lui aussi obligatoire.

EXPOSE : C'est le port qui sera ouvert vers l'extérieur. On va taper sur le port 80 pour accéder au contenu du conteneur.

COPY : Commande permettant de copier des fichiers de votre ordinateur à l'intérieur du conteneur. ici, le script entrypoint.

CMD : La commande à exécuter une fois que tout est terminé.

Note : Oui, je sais, il y a des choses inutiles. C'est un exemple.

Entrypoint ?

Le script :
#!/bin/bash

if [ ! -e index.php ]; then
    unzip /usr/src/pluxml-latest.zip -d /var/www/html/
    mv /var/www/html/PluXml/* /var/www/html
    rm -rf /var/www/html/PluXml
    chown -R www-data: /var/www/html
fi

exec "$@"

Comprendre le entrypoint

On est face à un script bash tout simple mais il demande un peu d'explication quand même.
Nous voulons un conteneur mettant à disposition les sources de notre CMS adoré ET qui stockera les articles, commentaires, images, etc, loin de ce-dit conteneur. Sans ça, une fois le conteneur mort, les données mourront avec lui. Ces données, pour faire simple, seront dans le fameux VOLUME cité un peu plus haut.

Au sujet des sources, si notre Dockerfile s'amusait à les mettre directement dans /var/www/html, comme la norme le voudrait, elles disparaîtraient dans k8s.
Pourquoi ? Parce que ce VOLUME sera géré par k8s lui-même et sera vide :
- Dockerfile dépose les sources dans le conteneur
- k8s charge le conteneur
- k8s monte son persistentVolume dans /var/www/html
- Les sources du conteneur disparaissent. Échec.

Si vous regardez bien le Dockerfile et ses incroyablement nombreuses lignes de code : il déplace les sources dans /usr/src, bien au chaud, à l’abri du problème. À la création du conteneur, dans k8s, le script regardera s'il existe un fichier index.php (issu des sources du PluXml) et, le cas échéant, copiera les fichiers manquants dans /var/www/html qui sera un PersistentVolume dans k8s ! Et c'est bien ce que nous voulons !

La création de l'image

Pour construire une image Docker, placez-vous dans les répertoires où vous avez déposé les 2 fichiers détaillés précédemment et :
docker build -t pluxml-5.6 .
Vous allez voir des lignes s'afficher dans tous les sens : pas de panique, c'est normal. Une fois votre terminal calmé :
Successfully built d554d0753425
Successfully tagged pluxml-5.6:latest
Gagné !

Téléverser l'image dans un dépôt

On ne va pas s'attarder là-dessus. Sachez juste que pour que k8s se serve de l'image, il faut qu'elle soit téléchargeable. J'ai poussé mon image dans le Docker Hub, dans un dépôt public. Vous allez pouvoir jouer avec sans vous prendre la tête.

Préparer Kubernetes

Postulats de base :
- Vous avez configuré Rook.
- Vous avez dégagé ce qui concerne les pods Wordpress/Mysql.

Le deployment & co

On va reprendre le travail vu ces derniers jours et créer des YAML pour notre conteneur !
dada@k8smaster:~/pluxml$ cat pluxml.yaml 
apiVersion: v1
kind: Service
metadata:
  name: pluxml
  labels:
    app: pluxml
spec:
  ports:
  - port: 80
  selector:
    app: pluxml
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: plx-pv-claim
  labels:
    app: pluxml
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: pluxml
  labels:
    app: pluxml
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: pluxml
    spec:
      containers:
      - image: dadall/pluxml-5.6:latest
        imagePullPolicy: "Always"
        name: pluxml
        ports:
        - containerPort: 80
          name: pluxml
        volumeMounts:
        - name: pluxml-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: pluxml-persistent-storage
        persistentVolumeClaim:
          claimName: plx-pv-claim
Vous remarquez que je ne me suis pas foulé : c'est le YAML de Wordpress légèrement retravaillé. On y trouve le nom de mon image dans le Deployment et un Service pluxml.

L'ingress

dada@k8smaster:~/pluxml$ cat pluxml-ingress.yaml 
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: pluxml-ingress
spec:
 backend:
   serviceName: pluxml
   servicePort: 80
Là aussi, simple : l'ingress qui s'attache à l'application pluxml.

Vous créez tout ça :
dada@k8smaster:~/pluxml$ kubectl create -f pluxml.yaml
dada@k8smaster:~/pluxml$ kubectl create -f pluxml-ingress.yaml
Si tout va bien, vous avez un pod PluXml qui vient d'apparaitre et qui est joignable !

Le pod :
dada@k8smaster:~/pluxml$ kubectl get pods --all-namespaces -o wide | grep plux
default            pluxml-686f7d486-7p5sq                                    1/1     Running     0          82m     10.244.2.164   k8snode2    <none>
Le service :
dada@k8smaster:~/pluxml$ kubectl describe svc pluxml
Name:              pluxml
Namespace:         default
Labels:            app=pluxml
Annotations:       <none>
Selector:          app=pluxml
Type:              ClusterIP
IP:                10.100.177.201
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.31:80
Session Affinity:  None
Events:            <none>
L'Ingress :
dada@k8smaster:~/pluxml$ kubectl describe ingress pluxml
Name:             pluxml-ingress
Namespace:        default
Address:         
Default backend:  pluxml:80 (10.244.1.31:80,10.244.2.164:80)
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     pluxml:80 (10.244.1.31:80,10.244.2.164:80)
Annotations:
  kubernetes.io/ingress.class:  nginx
Events:                         <none>

Coucou PluXml !

Welcome to Nginx, puis Wordpress

Rédigé par dada / 09 novembre 2018 / Aucun commentaire

Perdu ?

On y arrive. Après des heures d'attente, de commandes bizarres, de pods dans tous les sens, je vais enfin vous montrer comment mettre en place un Wordpress, sa base de données et y accéder depuis le navigateur !

Wordpress

Il existe une configuration simple pour avoir un Wordpress fonctionnel : ne nous privons pas !

La partie MySQL

Son fichier de configuration :
apiVersion: v1
kind: Service
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  ports:
    - port: 3306
  selector:
    app: wordpress
    tier: mysql
  clusterIP: None
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: mysql-pv-claim2
  labels:
    app: wordpress
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi
---
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: wordpress-mysql
  labels:
    app: wordpress
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: mysql
    spec:
      containers:
      - image: mysql:5.6
        name: mysql
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: changeme
        ports:
        - containerPort: 3306
          name: mysql
        volumeMounts:
        - name: mysql-persistent-storage
          mountPath: /var/lib/mysql
      volumes:
      - name: mysql-persistent-storage
        persistentVolumeClaim:
          claimName: mysql-pv-claim2
Vous y trouverez les volumes persistant pour Rook, le point de montage, la configuration du port MySQL, le mot de passe, etc. Vous avez le temps de lire tout ça.

On lance la création du conteneur :
dada@k8smaster:~$ kubectl create -f mysql.yaml
Et le résultat :
default            wordpress-mysql-75477bf794-89mzw                          1/1     Running     1          5h38m   10.244.2.50    k8snode2    <none>

On peut enchaîner avec la suite. C'est vrai qu'une BDD de Wordpress sans le CMS pour l'utiliser, c'est moyen.

La partie PHP

Notez que je parle de la partie "PHP", mais il n'y a pas que PHP. Il me fallait un titre, alors voilà.

Cette partie, je l'ai modifié par rapport à ce que vous trouverez dans les documentations officielles.
apiVersion: v1
kind: Service
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  ports:
  - port: 80
  selector:
    app: wordpress
    tier: frontend
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: wp-pv-claim
  labels:
    app: wordpress
spec:
  storageClassName: rook-ceph-block
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 500Mi
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: wordpress
  labels:
    app: wordpress
spec:
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: wordpress
        tier: frontend
    spec:
      containers:
      - image: wordpress:4.6.1-apache
        name: wordpress
        env:
        - name: WORDPRESS_DB_HOST
          value: wordpress-mysql
        - name: WORDPRESS_DB_PASSWORD
          value: changeme
        ports:
        - containerPort: 80
          name: wordpress
        volumeMounts:
        - name: wordpress-persistent-storage
          mountPath: /var/www/html
      volumes:
      - name: wordpress-persistent-storage
        persistentVolumeClaim:
          claimName: wp-pv-claim
Vous pouvez lancer tout ça :
 kubectl create -f wordpress.yaml
Le pod wordpress devrait apparaître:
default            wordpress-796698694f-sxbhb                                1/1     Running     1          3h35m   10.244.2.51    k8snode2    <none>

Ce qu'il faut saisir

La présence des services de Wordpress :
dada@k8smaster:~/$ kubectl get services 
NAME                                     TYPE           CLUSTER-IP       EXTERNAL-IP    PORT(S)                      AGE
kubernetes                               ClusterIP      10.96.0.1        <none>         443/TCP                      4d8h
my-nginx-nginx-ingress-controller        LoadBalancer   10.100.45.147    192.168.0.50   80:32462/TCP,443:30337/TCP   3d5h
my-nginx-nginx-ingress-default-backend   ClusterIP      10.109.228.138   <none>         80/TCP                       3d5h
wordpress                                ClusterIP      10.102.55.203    <none>         80/TCP                       6m3s
wordpress-mysql                          ClusterIP      None             <none>         3306/TCP                     6h33m
La configuration du service wordpress-mysql, on ne va pas s'y attarder. Ça marche tout seul.
Le truc chouette, c'est la configuration du service wordpress.
dada@k8smaster:~/$ kubectl describe service wordpress
Name:              wordpress
Namespace:         default
Labels:            app=wordpress
Annotations:       <none>
Selector:          app=wordpress,tier=frontend
Type:              ClusterIP
IP:                10.102.55.203
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.2.61:80
Session Affinity:  None
Events:            <none>
On y retrouve les informations qu'il nous faut et surtout, surtout, le Endpoint ! C'est là qu'on peut savoir où vont se retrouver les requêtes ! Ici, on lit 10.244.2.61:80 : il s'agit de l'IP du pod qui fait tourner Wordpress et le port sur lequel l'attaquer.

On va pouvoir s'attaquer à la dernière configuration : le service Ingress

Le service Ingress

Ici, on va expliquer au videur du cluster qu'il peut laisser passer les requêtes vers Wordpress.
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
  name: nginx-ingress
spec:
 backend:
   serviceName: wordpress
   servicePort: 80
Créez tout ça :
kubectl create -f ingress.yaml
Une fois créé, vérifiez qu'il est bien là :
dada@k8smaster:~/$ kubectl get ingresses
NAME            HOSTS   ADDRESS   PORTS   AGE
nginx-ingress   *                 80      6s
Et vérifiez sa configuration :
dada@k8smaster:~/$ kubectl describe ingress nginx-ingress
Name:             nginx-ingress
Namespace:        default
Address:         
Default backend:  wordpress:80 (10.244.2.61:80)
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     wordpress:80 (10.244.2.61:80)
Annotations:
  kubernetes.io/ingress.class:  nginx
Events:
  Type    Reason  Age   From                      Message
  ----    ------  ----  ----                      -------
  Normal  CREATE  65s   nginx-ingress-controller  Ingress default/nginx-ingress
Normal  UPDATE  30s   nginx-ingress-controller  Ingress default/nginx-ingress

Dans les Rules, remarquez que le backend est bien présent. Ici, le backend est le service wordpress que nous avons mis en place un peu plus tôt. Tout va bien !

Maintenant ? Souvenez-vous de l'IP externe que vous avez réussi à récupérer dans mon précédent billet et allez voir !



Il existe des moyens bien plus simples pour arriver à ce résultat. Passer par un Ingress Controller n'est pas la plus rapide mais elle permet de mettre le nez dans cet outil qui, plus tard, vous permettra de rediriger le flux en fonction du host. Bref. C'est un choix qui invite à aller plus loin !

La suite ?

À voir...

Accéder au cluster k8s depuis l'extérieur

Rédigé par dada / 09 novembre 2018 / Aucun commentaire



Perdu ?

On vient de se battre pour mettre en place un cluster Ceph opérationnel, histoire de stocker ses données dans le cluster k8s et de les répliquer un peu. Maintenant que c'est bon, si on s'attaquait à des choses un peu plus visible, hors dashboard ?
Au programme de ce billet : Ingress, Service, Wordpress, des nouveaux pods.

Ingress Nginx

Cette chose-là va être le point d'entrée de notre cluster. Il va permettre à l'extérieur d'aller regarder à l'intérieur. Son rôle va être de comprendre la requête d'entrée et de l'orienter vers le pod qui y répondra. Dans le cas qui va suivre, aller sur l'IP du cluster pour y retrouver un Wordpress.

Installation via Helm

Comme on s'en sert déjà, on va continuer :
helm install stable/nginx-ingress --name my-nginx
Cette installation va faire apparaître les pods suivants :
dada@k8smaster:~$ kubectget pods -n default
NAME                                                      READY   STATUS    RESTARTS   AGE
my-nginx-nginx-ingress-controller-565bc9555b-pcqjz        1/1     Running   2          8h
my-nginx-nginx-ingress-default-backend-5bcb65f5f4-728tx   1/1     Running   2          8h
Et un service. On n'a pas encore parlé de services : je vous redirige vers la documentation officielle si vous voulez en savoir bien plus.
dada@k8smaster:~$ kubectl --namespace default get services -o wide -w my-nginx-nginx-ingress-controller
NAME                                TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                      AGE   SELECTOR
my-nginx-nginx-ingress-controller   LoadBalancer   10.100.45.147   <pending>     80:32462/TCP,443:30337/TCP   8s    app=nginx-ingress,component=controller,release=my-nginx
Ça veut dire que l'Ingress Controller est bien installé. Notez le <pending> : ça veut dire que notre controller n'a pas d'IP externe. En gros : il ne sert à rien. Il lui faut une IP pour que nous puissions l'attaquer.
Dans une installation chez des gros fournisseurs d'informatique dans les nuages, nous aurions une sorte de plugin nous fournissant déjà l'IP tant désirée. Avec notre infrastructure sous Virtualbox, il va falloir faire autrement. Et autrement, ça veut dire avec MetalLB.

MetalLB

MetalLB, ce sont des pods que nous allons installer dans notre cluster et qui nous serviront à palier le problème cité juste au dessus. Il ira interroger notre DHCP, la Freebox dans mon cas, pour récupérer une IP publique.

Installer MetalLB

Toujours avec Helm :
helm install --name metallb stable/metallb
Un peu d'attente et vous devriez voir de nouveaux pods :
dada@k8smaster:~$ kubectl get pods -n metallb-system
NAME                         READY   STATUS    RESTARTS   AGE
controller-765899887-ck6bv   1/1     Running   2          8h
speaker-jdqg9                1/1     Running   2          8h
speaker-t4vtk                1/1     Running   2          8h
Il lui manque un fichier de configuration pour nous offrir une adresse IP externe :
apiVersion: v1
kind: ConfigMap
metadata:
  namespace: default
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.0.50-192.168.0.60
Les adresses IP en bas de ce fichier de conf vont permettre à MetalLB d'aller titiller notre DHCP (Freebox pour moi) pour obtenir une adresse. Quelque chose entre 192.168.0.50 et 192.168.0.60 dans mon cas. Vérifiez bien la configuration de votre Freebox avant de choisir une plage d'IP.

On l'applique :
kubectl create -f config.yaml 

Retour à l'Ingress Nginx

Une fois après avoir configuré MetalLB, vous allez vérifier l'état du service :
dada@k8smaster:~/$ kubectl --namespace default get services -o wide -w my-nginx-nginx-ingress-controller
NAME                                TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)                      AGE    SELECTOR
my-nginx-nginx-ingress-controller   LoadBalancer   10.100.45.147   192.168.0.50   80:32462/TCP,443:30337/TCP   3d3h   app=nginx-ingress,component=controller,release=my-nginx
Vous avez vu ? Une EXTERNAL-IP ! On va pouvoir taper dessus !

La suite ?