Introduction
Haute disponibilité de stockage: NAS, SAN, RAID
RAID
/Note: le RAID n'est pas un backup!!!/
RAID signifie /Redundant Array of Independent Disks/ - on dit aussi parfois /Redundant Array of Inexpensive Disks/ :p
RAID0 (lol)
Alors, si vous avez des données dont vous n'avez rien à faire, que vous pouvez facilement retélécharger par exemple, et que vous avez besoin de performances, RAID-0 est tout indiqué (par exemple: jeux vidéos). Mais c'est à peu près tout . RAID-0 "découpe" les données entre les disques, en lecture comme en écriture, en stripes dont la taille est prédéfinie lors de la création de l'agrégat - en général des stripes de 64Ko. Dès lors, on peut facilement écrire nos données sur 2 (ou plus) disques en même temps. idem pour la lecture. On peut considérer, grossièrement, qu'un agrégat RAID-0 de deux disques identiques permet de quasiment doubler les performances en écriture et en lecture. Remplacez les disques à plateau par des SSD et on peut obtenir des performances plus que sympathiques
Par contre, si un disque de l'agrégat rencontre un problème, tout l'agrégat est perdu, et donc les données qui se trouvaient dessus
RAID1
Ici, nos deux (ou plus) disques sont en miroir, c'est à dire que les deux (ou plus) disques contiennent exactement les mêmes données. On peut dès lors perdre tous les disques sauf un avant de perdre nos données, mais bien sûr les coûts sont plus élevés, vu qu'il faut, au minimum, doubler chaque disque pour chaque agrégat RAID-1.
RAID4 et RAID-DP
Ici, un disque (dans le cas de RAID-4) est dédié aux informations de parité. Deux disques sont réservés aux informations de parité dans le cas de RAID-DP. On rencontre souvent RAID-DP sur les baies Netapp: on a un bon ratio performance/résilience (on peut perdre deux disques par agrégat sans perdre de données dans le cas de RAID-DP, un seul dans le cas de RAID-4)
RAID5
Ici, on n'utilise pas un disque réservé pour les informations de parité, mais ces informations sont réparties sur chaque disque de l'agrégat, de manière séquentielle. Il nécessite, tout comme RAID-4, au minimum 3 disques. On obtient les meilleures performances sur un agrégat de 6/7 disques. Par exemple ici, une grappe RAID-5 sur une carte Perc H700, avec 6 disques SATA moisis:
lidstah@polaris:~$ sudo pveperf
CPU BOGOMIPS: 76612.16
REGEX/SECOND: 1511699
HD SIZE: 97.93 GB (/dev/mapper/pve-root)
BUFFERED READS: 467.61 MB/sec
AVERAGE SEEK TIME: 7.67 ms
FSYNCS/SECOND: 1697.46
DNS EXT: 32.21 ms
DNS INT: 47.76 ms
On voit qu'on obtient des performances plus que décentes niveau reads et fsync.
Par contre, on ne peut perdre qu'un seul disque.
RAID6
RAID6 est une évolution de RAID5 permettant d'utiliser /n/ informations redondantes de parité au lieu d'une seule. La grande majorité des implémentations de RAID6 se limitent à une valeur de /n = 2/ du fait de la complexité des fondements mathématiques à la base de ce système.
Ses avantages principaux sont de pouvoir résister à la perte de /n/ disques (en pratique, souvent deux disques), mais ses défauts principaux sont des temps d'écriture plus longs du fait des calculs plus complexes, et un temps de reconstruction du RAID en cas de perte de 2 disques très, très, trèèèès long
Cependant il est de plus en plus utilisé du fait de l'augmentation des performances du matériel dédié, comme des CPUs, permettant de l'implémenter de façon logicielle (ajouté au kernel Linux 2.6, donc ça remonte).
RAID-Z (ZFS)
Similaire à RAID-5, le RAID-Z, utilisé par ZFS de Sun Microsystems, utilise un système de /copy-on-write/ afin de limiter les effets de /trous d'écriture/ (baisse des performances en écriture de nombreux petits fichiers sur un RAID-5). Néanmoins il est moins performant que RAID-5, sauf si on alloue de la RAM ou des flashpools SSD pour mettre en cache les opérations de lecture/écriture (L2ARC et ZIL).
RAIDs rigolos
RAID01
RAID10
RAID05
RAID15
RAID50
RAID51
RAID Matériel et RAID Logiciel
RAID Matériel
Le RAID Matériel implique l'utilisation de cartes dédiées (en général PCIe), utilisant souvent des firmwares propriétaires. La carte RAID s'occupe de la gestion des agrégats RAID et présente au système d'exploitation un seul /block device/. L'OS ne "voit" alors qu'un seul disque par agrégat RAID, sans nécessairement connaître l'état des disques utilisés - cependant des drivers existent permettant de vérifier l'état - notamment SMART - des disques utilisés par l'agrégat au niveau du système d'exploitation.
Ces cartes RAID disposent souvent de mémoire cache, permettant d'augmenter notablement les performances. De plus, elles disposent en général d'ASICs dédiés à la gestion des différents niveau de RAID, permettant de libérer le CPU lors, par exemple, de la reconstruction d'un RAID dont un disque a été remplacé.
RAID Logiciel
Ici, c'est l'OS qui gère directement les agrégats RAID, ce qui permet une meilleure souplesse d'utilisation, mais impacte - légèrement - les performances.
Sous Windows, le Gestionnaire de disques permet de facilement créer des agrégats RAID (0,1,5,6 iirc).
Sous GNU/Linux, on utilisera l'utilitaire /mdadm/. Par exemple pour créer un RAID-1 entre nos disques sdc et sdd (de taille identique):
- Tout d'abord on partitionne nos disques:
# dans les deux cas:
# type de table de partitions GPT
# taille identique de partition
# type de partition: "fd", RAID Linux Autodetect
$ sudo cfdisk /dev/sdc
$ sudo cfdisk /dev/sdd
Puis on crée notre agrégat RAID-1 avec la commande mdadm:
$ sudo mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdc1 /dev/sdd1
# puis on "daemonise" notre volume RAID pour qu'il soit chargé à chaque démarrage:
$ sudo mdadm --daemonise /dev/md0
# en cas de fail:
$ sudo mdadm --monitor --daemonise /dev/md0
On peut maintenant vérifier les détails de notre aggrégat RAID-1:
$ sudo fdisk -l
$ sudo mdadm --detail /dev/md0
Et enfin, le formater et le monter:
$ sudo mkfs.ext4 /dev/md0
$ sudo mount /dev/md0 /mnt
$ df -h
$ sudo umount /mnt
Pour finir, on peut le déclarer dans fstab. Admettons qu'on veuille le monter dans le répertoire raid1 du répertoire home:
$ sudo mkdir /home/raid1
$ sudo vim /etc/fstab
/dev/md0 /home/raid1 ext4 defaults 0 1
:wq
Si un des disques vient à flancher, on va d'abord le retirer de l'agrégat avant d'ajouter un nouveau disque:
# on le marque "faulty"
$ sudo mdadm --manage /dev/md0 --set-faulty /dev/sdc1
# on le retire de la grappe
$ sudo mdadm --manage /dev/md0 --remove /dev/sdc1
# une fois le disque remplacé par un nouveau disque (partitionné, etc)
# on remet le nouveau disque en prod
# on va partir du principe que la partition est /dev/sdc1:
$ sudo mdadm --manage /dev/md0 --add /dev/sdc1
On peut aussi vouloir stopper complètement une grappe et la supprimer:
$ sudo mdadm --detail /dev/md0
$ sudo mdadm --stop /dev/md0
$ sudo mdadm --zero-superblock /dev/sdc1
$ sudo mdadm --zero-superblock /dev/sdd1
# Puis on modifie la conf' de mdadm si nécessaire
NAS
Définition
Un NAS (/Network Attached Storage/) fournit des services similaires à ceux d'un serveur de fichiers. Il expose des partages de fichiers NFS (/Network File System/), SMB/CIFS (euuurk), et parfois du FTP (euurk) ou WebDAV. Il se différencie d'un SAN dans le sens où il gère aussi le système de fichiers (ext4, WAFL chez Netapp) exposé via les protocoles sus-cités, là où un SAN expose des /block devices/ via des /LUNs/ (atchoum, on y revient plus bas) exposés en général sur un réseau dédié (mais pas obligatoirement).
Quelques fabricants de NAS bien connus:
- Netapp
- EMC (maintenant Dell-EMC)
On peut tout à fait s'en servir pour héberger des images disques (vmdk, qcow2, raw, etc) auxquelles les VMs d'un cluster d'hypervision accèdent, notamment si le NAS dispose de plusieurs interfaces réseau rapides (10Gbps et +, mais c'est utilisable à partir d'1Gbps). petit exemple de migration live avec proxmox
Ils sont aussi très utilisés comme stockage de sauvegarde: ils implémentent en effet pour la plupart plusieurs niveaux de RAID.
NFS /Network File System/
NFS a été créé par Sun MicroSystems en 1984. Il utilise le port 2049, en UDP (oui oui) ou en TCP. Il a été formalisé dans la RFC1094 pour sa v1.
Les versions du protocole en cours d'utilisation sont principalement la v3 (2003, RFC 3530) et la v4 (2010, RFC 5661), qui sont incompatibles entre elles, pour plusieurs raisons:
- NFSv3 est stateless, et propose des mécanismes de sécurité basiques. les données transitent "en clair" - il est donc impératif de l'utiliser sur un réseau de confiance.
- NFSv4 est stateful, propose la reprise après incident, la réplication sur d'autres serveurs NFS (haute dispo), et des mécanismes d'authentifications plus costauds (kerberos) ainsi que le chiffrement des données transitant sur le réseau, qu'on préfèrera toujours de confiance
SMB/CIFS (lol)
Beaucoup utilisé en environnement Microsoft (comme c'est étrange). /Server Message Block/, anciennement CIFS /Common Internet File System/, descend de LAN Manager, créé par IBM (comme quoi, le jour où Microsoft créera quelque chose de lui-même n'est pas encore arrivé ) en 1985 pour OS/2. Il en est actuellement à sa version 3 (Windows 8 et Server 2012).
Historiquement, SMB se basait sur la couche session, NetBIOS, connu pour générer du broadcast et donc pour gaspiller inutilement de la bande passante. On le remplace désormais par un serveur WINS.
En environnement non-Windows il est notablement moins performant que NFS (qu'on préfèrera donc comme /datastore/ pour nos hyperviseurs).
Une implémentation libre de SMB est Samba (qui ne fait pas que ça).
SAN
Définition
Un SAN /Storage Area Network/ est un réseau spécialisé mutualisant les ressources de stockage. À la différence d'un NAS, il propose un accès bas niveau aux disques. Son trafic réseau est dès lors très similaire aux principes utilisés par les disques internes de nos machines (SATA, SCSI, SAS).
Il permet de mutualiser, tout comme le NAS, les ressources de stockage. Mais à la différence d'un NAS, les baies de stockage n'apparaissent pas comme des volumes partagés sur le réseau: elles exposent des /block devices/, et donc chaque serveur voit les LUN /Logical Unit Number/ auxquels il a accès comme son propre disque dur.
On dispose de plusieurs protocoles pour le partage de ces disques: iSCSI, qui utilise le réseau (de préférence dédié) classique, et Fibre Channel, qui nécessite des cartes FC et des commutateurs FC dédiés, avec les prix qui vont avec, et les performances qui vont avec (jusqu'à 128Gbps, mais plus généralement 8/16Gbps).
iSCSI /Internet Small Computer System Interface/
C'est un protocole de stockage en réseau basé sur le protocole IP. Il transporte les commandes SCSI sur les réseaux IP (d'où son nom :p). On peut l'utiliser aussi bien sur un LAN que sur un WAN (et notamment Internet), et dès lors il permet d'être indépendant de l'emplacement physique du stockage. Il ne nécessite pas d'infrastructure dédiée, contrairement à Fibre Channel.
Il est composé de deux éléments:
- Un /Initiator/ qui est un composant logiciel du serveur hôte qui gère et transporte les données sur le réseau. Chaque hôte est identifié par un nom unique, IQN
- Une cible (/Target/) qui identifie le périphérique de stockage.
Il existe des passerelles iSCSI <-> Fibre Channel.
iSCSI est un protocole de l'IETF (RFC3720, 3721, 3722).
Fibre Channel
Fibre Channel est un protocole dont la norme est ANSI X3T11 (atchoum). Il a à la base été créé pour les super-ordinateurs - son but étant de permettre une connexion très, très haut débit à une solution de stockage - mais il est devenu un des protocoles standard des SAN.
C'est un protocole série, qui peut fonctionner aussi bien sur des câbles Ethernet, coaxiaux ou de la fibre optique. On utilise des /Global Link Modules/ GLM pour l'interconnexion entre tout ce beau monde
Il existe trois topologies différentes pour Fibre Channel:
- /Fabric/: il s'agit d'une topologie nécessitant un commutateur Fibre Channel. Dès lors sa topologie est de type étoile
- /Fibre Channel Arbitrated Loop/ - FC-AL: c'est une topologie en anneau. On peut doubler la boucle, etc
- et notre bon vieux Point à point, où les périphériques sont reliés directement entre eux. Elle est donc plus limitée que les précédentes
On peut encapsuler ce protocole dans des trames Ethernet: FCoE, /Fibre Channel over Ethernet/.
Les débits sont assez impressionnants:
| Génération | Année | Débit en Mo/s |
|---|---|---|
| FC 1Gb | 1997 | 125 |
| FC 2Gb | 2001 | 250 |
| FC 4Gb | 2004 | 500 |
| FC 8Gb | 2005 | 1000 |
| FC 10Gb | 2008 | 1250 |
| FC 16Gb | 2011 | 2000 |
| FC 32Gb | 2016 | 4000 |
| FC 128Gb | 2016 | 16000 |
Fibre channel utilise aussi plusieurs types de ports:
- E_port: relie les commutateurs FC entre eux. On appelle le lien entre deux commutateurs un /Inter Switch Link/, ISL. On peut les agréger entre deux commutateurs, ce qui permet d'augmenter la bande passante de communication entre deux switches. 2 ISL à 128Gbps permettent donc d'envoyer du pâté à 256Gbps
(/trunking/).
- EX_port: le nom du port reliant un routeur FC à un switch FC.
- TE_port: /Trunking Expansion Port/ - groupement de E_ports.
- N_port: /Node Port/ port du serveur ou du périphérique de stockage dans une topologie point à point ou Fabric
- NL_port: idem mais dans une topologie boucle
- F_port: /Fabric Port/ port du switch auquel se raccorde un serveur ou périphérique de stockage.
- FL_port: la même mais dans une topologie boucle
- G_port: /generic port/ port qui se configure automatiquement en E_port ou F_port
- L_port: port générique d'un port en boucle.
Concepts de haute disponibilité des systèmes et services
Load-Balancing et Haute Disponibilité
Concepts
Imaginons une infrastructure Web classique. En phase de développement, 3 VMs étaient utilisées: un serveur Web+CGI, un serveur de base de données, et un serveur de stockage, soit autant de SPOFs.
Une première solution pour la production serait d'ajouter de la redondance:
- Plusieurs serveurs Web
- base de données répliquées
- Solution de stockage "clusterisée" (minio, etc)
Et pour la distribution des requêtes entre nos serveurs web et de BDD, on pourrait ajouter une machine de /load-balancing/ qui va répartir la charge entre nos différentes machines. Ce faisant, on ajoute cependant un SPOF supplémentaire: le load-balancer. Dès lors il va nous en falloir plusieurs, et il va aussi falloir un mécanisme pour pouvoir assigner et déplacer l'adresse IP (ou les adresses...) de notre plateforme d'un LB à l'autre. C'est ce qu'on appelle une IP flottante ou /IP Failover/.
Pour cela, on peut utiliser différents logiciels, par exemple /keepalived/
keepalived
Tout d'abord, il faut activer la possibilité de pouvoir se /bind/ sur une adresse IP qui ne soit pas locale dans notre fichier:
/etc/sysctl.d/10-nonlocalbind.conf
net.ipv4.ip_nonlocal_bind = 1
sudo systemctl restart systemd-sysctl.service
Sinon HAProxy nous jettera sur un restart
Autres préparatifs:
sudo apt install keepalived
On peut maintenant attaquer la configuration de keepalived sur notre MASTER:
# vim /etc/keepalived/keepalived.conf
global_defs {
# process identifier
lvs_id haproxy_DH
enable_script_security
script_user root root
}
# on verif que haproxy tourne bien
vrrp_script check_haproxy {
script "systemctl status haproxy"
interval 2
weight 2
}
# virtual interface
vrrp_instance VI_01 {
state MASTER
interface ens18
virtual_router_id 51
priority 101
# ip fo partagee entre les serveurs
virtual_ipaddress {
192.168.1.70
}
track_script {
check_haproxy
}
}
/Note: en vrai on utilisera un autre user que root hein/
Et sur notre replica, les seules différences concernant le paramètre state (BACKUP) et la priorité plus faible:
global_defs {
# process identifier
lvs_id haproxy_DH
enable_script_security
script_user root root
}
# on verif que haproxy tourne bien
vrrp_script check_haproxy {
script "systemctl status haproxy"
interval 2
weight 2
}
# virtual interface
vrrp_instance VI_01 {
state BACKUP
interface ens18
virtual_router_id 51
priority 100
# ip fo partagee entre les serveurs
virtual_ipaddress {
192.168.1.70
}
track_script {
check_haproxy
}
}
Maintenant, on peut tester la fonctionnalité en tuant brutalement haproxy sur la VM master. Ou en tuant la VM master tout court
Haproxy
Haproxy est un reverse-proxy/Load Balancer très performant. Il va nous permettre de distribuer les requêtes reçues sur notre IP FO, soit en HTTP vers nos serveurs Web, soit en TCP vers nos serveurs de base de données. On va l'installer:
sudo apt install haproxy hatop
Puis utiliser la configuration suivante (adaptez aux IPs de vos VMs):
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# Default ciphers to use on SSL-enabled listening sockets.
#ssl-default-bind-ciphers ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS
#ssl-default-bind-ciphers kEECDH+aRSA+AES256:kRSA+AES256:+AES256:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL
#ssl-default-bind-ciphers kEECDH+aRSA+AES256:kRSA+AES256:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL
ssl-default-bind-ciphers kEECDH+aRSA+AES256:!kEDH:!LOW:!EXP:!MD5:!aNULL:!eNULL
ssl-default-bind-ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256
tune.ssl.default-dh-param 2048
ssl-default-bind-options ssl-min-ver TLSv1.2
maxconn 2000
defaults
log global
mode http
maxconn 2000
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend B3-front
bind 192.168.1.70:80
default_backend B3-backend
backend B3-backend
balance leastconn
option httpclose
option forwardfor
compression algo gzip
server web-1 192.168.1.73:80 check
server web-2 192.168.1.74:80 check
listen SQLbackend
balance leastconn
bind 192.168.1.70:3306
mode tcp
option tcplog
server sql1 192.168.1.75:3306 check
server sql2 192.168.1.76:3306 check
Bases de données
ACID (Jim Gray)
Les propriétés ACID (/Atomicité, Cohérence, Isolation, Durabilité/) sont un ensemble de propriétés qui garantissent qu'une transaction est effectuée de manière fiable.
Ces propriétés et les technologies liées ont été définies et développées par Jim Gray à la fin des années 70 - note "Les mystères de l'Histoire de l'Informatique": Jim Gray a disparu en 2008 avec son voilier. On n'a jamais retrouvé ni l'un ni l'autre, et il a été déclaré mort /in absentia/ en 2012. À la base défini pour les systèmes transactionnels bancaires (TPF & cie), ce modèle s'applique particulièrement bien aux bases de données relationnelles.
Réplication active
Ici, chaque membre du cluster de réplication effectue les mêmes calculs que la machine maître.
Réplication passive
Ici, seul le primary exécute les calculs et ne propage que les modifications.
Réplication primary/replica (actif/passif), en cascade (postgreSQL), schéma primaire
Ici, une base de données maître gère les écritures et transmet les requêtes à sa(ses) base(s) de données replica. On peut ensuite répartir la charge des requêtes en lecture sur le primary et les replicas, mais seul le primary peut écrire des données. On peut y adjoindre un mécanisme de failover (postgreSQL notamment) permettant de /switcher/ un replica en primary si le primary ne répond plus.
On peut distinguer deux modes de fonctionnement:
- Le primary envoie les données à tous ses replicas
- Le primary envoie les données à un ou plusieurs replicas qui se comportent eux-mêmes comme des "primaries" pour leurs propres replicas. C'est ce qu'on appelle la réplication en cascade, /streaming replication/, qu'on utilise beaucoup dans un environnement PostgreSQL.
Réplication Master/Master, multimaster (actif/actif)
Ici, les deux (ou plus) bases de données sont toutes primary. Dans le cas de MySQL/MariaDB, elles sont effectivement replicas les unes des autres (inception anyone? ).
Cependant cela peut poser des problèmes de concurrence dans certaines situations particulières, la prévention des conflits entre plusieurs transactions simultanées pouvant être particulièrement complexe.
On peut citer, pêle-mêle:
- Galera, backend MariaDB
- CockroachDB, backend PostgreSQL (qui fait plus de choses que du "simple" master/master)
comme solutions de réplication multimaîtres.
Exemple: Setup MariaDB/Galera
MariaDB, fork de MySQL (suite au rachat de Sun par Oracle), est une des BDDs les plus utilisées. Elle propose, via un module nommé Galera, une solution de /clusterisation/ (désolé pour l'anglicisme/néologisme) de type master/master/master.../master. En situation réelle, on utilisera à minima 3 instances de Galera pour obtenir un bon quorum. Cependant, au vu de la situation actuelle, on utilisera 2 VMs pour "simuler" notre Cluster Galera.
On va commencer par installer mariadb sur les VMs de notre "cluster":
$ sudo apt update && sudo apt install mariadb-server mariadb-client
On va ensuite configurer Galera sur notre première VM (attention, il faudra repasser sur la config une fois le cluster en place). Dans cet exemple, mes deux VMs ont pour IP 192.168.1.43 et 192.168.1.27:
$ sudo vim /etc/mysql/conf.d/galera.cnf
[mysqld]
#####################
#mysql settings
#####################
log_bin=1
log_slave_updates=1
sync_binlog=1
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
query_cache_size=1M
query_cache_type=OFF
innodb_flush_log_at_trx_commit=0
innodb_buffer_pool_size=256M
bind-address=0.0.0.0
#####################
# Galera settings
#####################
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name="dynacluster"
# ici, on ne précise rien pour le moment.
# On renseignera ensuite les IPs des membres
# du cluster. Mais pour l'init du cluster
# on laisse "gcomm://" vide
wsrep_cluster_address="gcomm://"
wsrep_sst_auth=sst_backup:supermotdepasse
wsrep_sst_method=rsync
wsrep_on=ON
wsrep_node_address="192.168.1.43"
wsrep_node_name="dyna-sql1.lidstah.info"
# on sauvegarde,quitte et on relance le service
$ sudo systemctl restart mysql
Sur notre client, on procède quasiment de même:
[mysqld]
#####################
#mysql settings
#####################
log_bin=1
log_slave_updates=1
sync_binlog=1
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
query_cache_size=1M
query_cache_type=OFF
innodb_flush_log_at_trx_commit=0
innodb_buffer_pool_size=256M
bind-address=0.0.0.0
#####################
# Galera settings
#####################
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name="dynacluster"
# cette fois ci on renseigne bien
# toutes les IPs des membres du cluster
# dès le début
wsrep_cluster_address="gcomm://192.168.1.43,192.168.1.27"
wsrep_sst_auth=sst_backup:supermotdepasse
wsrep_sst_method=rsync
wsrep_on=ON
wsrep_node_address="192.168.1.27"
wsrep_node_name="dyna-sql2.lidstah.info"
# on save/quit puis restart mysql
$ sudo systemctl restart mysql
Il ne nous reste plus qu'à terminer la configuration de notre premier serveur, en modifiant la ligne:
$ sudo vim /etc/mysql/conf.d/galera.cnf
wsrep_cluster_address="gcomm://"
# on remplace par:
wsrep_cluster_address="gcomm://192.168.1.43,192.168.1.27"
# save/quit/restart ;)
$ sudo systemctl restart mysql
On va maintenant tester que la réplication est fonctionnelle entre nos deux machines, par exemple en créant une DB sur la première machine, en vérifiant qu'elle est bien créée aussi sur la seconde, puis en la supprimant sur la seconde machine et en regardant si elle a bien été supprimée du premier:
sql1$ sudo mysql
CREATE DATABASE test;
sql2$ sudo mysql
SHOW DATABASES;
DROP DATABASE test;
sql1$ sudo mysql
SHOW DATABASES;
Génial, non? Maintenant, on placera en général 2 LBs (+keepalive ) en mode TCP "devant" notre cluster de BDD. Bonus, on obtient ainsi de la répartition de charge entre les machines de notre cluster de BDD
Mon cluster est pété! comment faire?
Sur une des machines du cluster:
$ sudo galera_recovery
WSREP: Recovered position 849806a9-3ec1-11eb-bf1b-f71b6f48588f:4
--wsrep_start_position=849806a9-3ec1-11eb-bf1b-f71b6f48588f:4
Ici, la partie qui nous intéresse est 849806a9-3ec1-11eb-bf1b-f71b6f48588f:4
Ensuite, on édite le fichier suivant:
$ sudo vim /var/lib/mysql/grastate.dat
version: 2.1
uuid: 849806a9-3ec1-11eb-bf1b-f71b6f48588f
seqno: -1
safe_to_bootstrap: 0
Et on remplace UUID par l'UID obtenue dans la commande galera_recovery - c'est l'UID avant les deux points et le numéro de séquence.
On remplace enfin la valeur de seqno par la valeur après les deux points (dans notre exemple, 4) et on modifie le paramètre safe_to_bootstrap à 1:
version: 2.1
uuid: 849806a9-3ec1-11eb-bf1b-f71b6f48588f
seqno: 4
safe_to_bootstrap: 1
On relance enfin mariadb, puis on fait de même sur les autres membres du cluster.
Tolérance de panne réseau
Comme on a vu précédemment avec le stockage et avec les bases de données, notre but est d'éliminer la plus grande partie des SPOF de notre infrastructure. Mais pour le moment il nous reste un gros SPOF: le réseau
Pour cela, on va en général redonder notre équipement réseau (routeurs, switches) et utiliser des mécanismes d'agrégation de lien (/bonding/) pour conserver la connectivité et ce même en cas de défaillance d'un routeur, d'un switch, ou d'une interface réseau.
/bonding/ d'interfaces réseau sous GNU/Linux
Il est possible d'agréger plusieurs interfaces réseau afin qu'elles semblent se comporter comme une seule interface, avec sa propre adresse MAC. C'est ce qu'on appelle le /bonding/ d'interfaces. Suivant les situations, notamment le type de bond et le matériel utilisé il peut être nécessaire d'effectuer une configuration similaire côté commutateur (LACP notamment).
Pour déclarer un bond, on va d'abord choisir les interfaces réseau appartenant à ce bond et le déclarer dans la configuration (ici eno1 et ens1f0 vont être /bondées/ dans l'interface bond007 (joke inside©)):
sudo apt install ifenslave
vim /etc/network/interfaces
iface eno1 inet manual
bond-master bond007
iface ens1f0 inet manual
bond-master bond007
On déclare ensuite notre interface bond007:
auto bond007
iface bond007 inet static # manual si bridgé
bond-mode 1 # active-backup
bond-slaves eno1 ens1f0
bond-miimon 100 # monitor (ms)
bond-downdelay 250 # timeout (ms)
bond-updelay 250 # up (ms)
address ...
gateway ...
mtu ...
Les différents modes de bonding sous GNU/Linux sont:
- mode 1: active-backup. le second lien n'est utilisé que si le premier est down
- mode 2: XOR, en fonction de l'adresse MAC source et celle de destination
- mode 4: LACP, aggrégation de lien 802.3ad. toutes les interfaces sont utilisées. Par exemple 2 interfaces 1Gbps permettront de transférer deux flux à 1Gbps (un par interface), mais ne double pas la bande passante contrairement à la croyance populaire
- mode 5: adaptative load-balancing. répartit la charge entre chaque interface en fonction du load de chaque interface et passe en mode backup si un des liens tombe (en théorie :p)
Gestion du stockage
Réplication synchrone
Une réplication synchrone du stockage implique que les deux espaces de stockage de la réplication sont identiques au bloc près à tout moment.
Réplication asynchrone
La réplication asynchrone diffère de la réplication synchrone du stockage car elle permet un léger décalage entre la source et la cible.
Exemple: DRBD sous GNU/Linux (/Distributed Replicated Block Device/)
DRBD est un système de stockage distribué pour GNU/Linux fonctionnant en mode bloc. Il permet la réplication des /block devices/ entre plusieurs serveurs. Il utilise un module du kernel, et un ensemble d'utilitaires dans l'espace utilisateur permettant son administration.
Il effectue la réplication des données:
- en temps réel
- de façon transparente (les appicatifs ne savent pas que les données sont répliquées)
- de façon synchrone ou asynchrone. En mode synchrone, si un applicatif écrit une donnée sur le périphérique, il sera notifié de l'acquittement de l'entrée/sortie une fois que tous les serveurs auront effectué la transaction. En mode asynchrone, l'acquittement est effectué dès que la donnée est écrite sur le disque "local", avant que la donnée ait été propagée.
DRBD ne permet la réplication qu'entre deux noeuds jusqu'à la version 9.
Il peut être utilisé pour répliquer:
- Un FS classique
- un système de fichiers partagé (GlusterFS & cie)
- un block device logique (LVM)
Je vous encourage chaudement à parcourir la documentation des commandes suivantes :
drbdadm (administration de DRBD)
drbdsetup (configuration du module DRBD)
drbdmeta (gestion des métadonnées)
Hyperviseurs et haute disponibilité
La plupart des clusters de virtualisation (Proxmox, VMWare, Nutanix, XCP-NG/Xen orchestra, Ganeti/Xen, etc) permettent de migrer à chaud des VMs répliquées en DRBD ou dont le disque virtuel est sur un stockage distant (NAS ou SAN par exemple), quasi instantanément (minus le temps de copie de la RAM de la VM d'un hyperviseur à un autre).
De même, ces solutions permettent de relancer automatiquement une machine virtuelle dont l'hyperviseur d'origine rencontrerait une défaillance. Il faut alors penser à implémenter un mécanisme de /fencing/ afin d'éviter une situation de ce type:
- L'hyperviseur A subit une panne d'alimentation sur ses deux alimentations redondantes, et la VM "Web" dont le disque virtuel est sur un NAS se retrouve donc down pour le cluster
- L'hyperviseur B relance la VM "Web", assigne le disque virtuel de la VM, tout va bien
- L'hyperviseur A est réparé et remis en prod dans le cluster, redémarre et... relance la VM "Web".
Sur du /stateless/ ce n'est pas embêtant outre mesure, sur du /stateful/ c'est très, très problématique
Orchestrateurs de containers et haute disponibilité
Ici, les seuls SPOFs seraient les VMs control-planes de notre orchestrateur, les autres noeuds du cluster (VMs ou physiques) étant interchangeables et remplaçables à volonté tant qu'on dispose des ressources nécessaires. Dès lors il est important:
- d'avoir un système de réplication/sauvegarde (snapshots etcd dans le cas de Kubernetes) de notre control-plane
- d'avoir plusieurs control-planes
de notre solution d'orchestration.
Il faut ensuite définir les stratégies que l'orchestrateur aura à suivre en cas de défaillance d'un noeud du cluster, par exemple relancer les services (pods, déploiements, replica sets dans le cas de kubernetes) sur d'autres noeuds.
Mise en application : "lightweight" Kubernetes, K3S
Ici, nous allons mettre en place un mini-cluster K3S. K3S est une distribution de Kubernetes conçue pour s'exécuter sur des machines aux performances restreintes, raspberry Pi et clones, petites VMs, etc.
Nous allons d'abord créer deux machines virtuelles:
- Une VM "control-plane", 2 coeurs, 2Go RAM
- Un /control-plane/ Kubernetes est une machine exécutant les applicatifs nécessaires au fonctionnement d'un cluster Kubernetes: serveur api, ordonnanceur (scheduler), stockage de l'état du cluster (etcd), etc
- Normalement un control-plane n'exécute jamais de charge de travail autre. Dans le cas de K3S, cependant, le control-plane peut aussi se comporter comme un noeud de travail.
- Une VM "worker", 2 coeurs, 2Go RAM aussi.
- Un /worker/ Kubernetes exécute généralement les charges applicatives destinées aux utilisateurs et utilisatrices: par exemple un service wordpress, un dépot git (gitea, gogs, gitlab...), un ERP, etc. Ces charges de travail sont distribuées entre les noeuds de travail par l'ordonnanceur s'exécutant sur le(s) control-plane(s).
Idéalement, en production, on exécutera plusieurs control-planes (pour des raisons de haute-disponibilité, minimum 3 pour des raisons de quorum) et bien évidemment plusieurs workers. Si l'un de nos /workers/ rencontre une indisponibilité, ses charges de travail seront réparties entre les /workers/ restants - dans la limite de leur capacité.
Donc, notre cluster est capable de "s'auto-soigner". Cette solution permet d'assurer la haute-disponibilité de nos services, dont les différents éléments constitutifs seront en général déjà répliqués (par exemple plusieurs containers wordpress).
Fonctionnement d'un cluster Kubernetes
Séparation de nos charges de travail, isolation: Namespaces
On organisera nos charges de travail à l'intérieur d'espaces de noms (/namespaces/) qui, s'ils sont assez permissifs par défaut, permettent d'isoler les charges de travail entre elles avec des paramétrages assez fins, notamment réseau (/NetworkPolicies/). On peut aussi limiter l'utilisation CPU ou mémoire des applicatifs s'exécutant à l'intérieur d'un namespace. On peut tout à fait imaginer créer des namespaces du type:
- client1: ERP, GLPI
- client2: gitlab, airflow, API maison
- client3: site web corpo, base de données du site web
- client4: environnement de développement, preprod
- network: serveurs DNS, serveurs VPN (wireguard), terraform de notre entreprise
- wiki: /knowledge base/ (wiki par exemple) de nos devs
- etc
C'est un concept très malléable qui permet d'organiser et isoler les différentes charges de travail que notre cluster kubernetes exécute
Quelques commandes /kubectl/ pratiques:
#lister les namespaces:
kubectl get ns
#créer un namespace
kubectl create ns mon-namespace
#Supprimer un Namespace
#Et tout ce qu'il contient (FEAR)
kubectl delete ns mon-namespace
#Décrire un namespace
kubectl describe ns mon-namespace
De plus, Kubernetes propose des mécanismes d'auto-dimensionnement des applicatifs, notamment l'/Horizontal Pods Autoscaler/ qui permet de créer de nouvelles instances d'un applicatif si celui-ci subit une charge importante, puis de détruire les instances non-utiles une fois la charge supplémentaire absorbée.
/NOTE: attention au tarif sur une solution Kubernetes "cloud" si vous le combinez avec du Vertical Autoscaling (qui va vous créer des VMs!)!!/
L'unité de base: le Pod
Un pod est très similaire à une /stack/ docker-compose. En effet, bien que la majorité des pods n'exécutent qu'un container, il est possible de créer des pods contenant plusieurs containers. Imaginons que la base de notre microservice API soit composée d'un petit serveur web et d'un cache Redis (par exemple pour stocker les résultats des requetes de notre API vers la base de données, afin de limiter la charge sur la BDD). On a plusieurs choix (plus ou moins pertinents):
- Créer une image de container contenant notre applicatif node ET Redis. C'est ce qu'on appelle un /anti-pattern/, en effet, en règle générale un container doit exécuter un applicatif et un seul.
- Créer un pod pour notre applicatif node, et un pod pour notre redis, ainsi que les Services (cf plus bas) permettant aux deux pods de communiquer. C'est une solution valable, mais pas forcément optimale si on veut dimensionner facilement notre stack.
- Créer un pod contenant un container de notre applicatif ET un container Redis. C'est la solution la plus adaptée pour dimensionner facilement notre stack, notre applicatif node discutant directement dans le pod avec "son" instance redis.
Exemple: créer un pod avec l'image nginx dans l'espace de noms par défaut 'default', exécuter un shell dans le pod, et détruire le pod:
kubectl run nginx --image=nginx
kubectl exec -it nginx -- /bin/bash
root@nginx:/# ls -al; exit
kubectl delete pod nginx
C'est assez pratique pour tester rapidement qu'une image se lance ou pas, etc. Mais en général on préfèrera utiliser un déploiement (ou un daemonset, ou un statefulset) pour créer et gérer nos ressources.
Création/Suppression de nos pods: Déploiements, Daemonsets, StatefulSets et ReplicaSet
En effet, créer nos pods "à la main" ne serait pas pratique à l'échelle (si on doit en gérer plusieurs centaines par exemple), ni, non plus, d'un point de vue reproducibilité de notre infrastructure, sans compter les éventuelles erreurs humaines.
Au lieu de créer manuellement nos pods, nous allons définir des ressources de façon programmatique, dans des fichiers YAML, expliquant au cluster quel(s) pods nous voulons créer, dans quel(s) namespaces, avec quel(s) droits, quel(s) volumes de stockage, etc. Il en existe plusieurs suivant le besoin à couvrir:
- Déploiement / Deployment: C'est la ressource la plus courante. Elle permet de faire créer au cluster le ou les pods nécessaires à notre micro-service, en indiquant la configuration voulue pour nos pods (points de montage, secrets, variables d'environnement, image de container, options de sécurité, limites CPU/RAM et requests CPU/RAM, ports réseau exposés de chaque container de notre pod, nombre de /replicas/ de notre pod, etc. Les pods sont placés automatiquement sur les workers disposant des ressources nécessaires à leur exécution. Plusieurs pods du meme micro-service peuvent "tourner" sur le meme worker.
- DaemonSet: Identique au déploiement sauf qu'un daemonset créera une instance de notre pod sur chaque worker. Notamment utilisé pour les pods de collecte de données de supervision, les pods manipulant la configuration réseau des workers, etc.
- StatefulSets: Similaire au déploiement, mais pour les charges de travail dites Stateful comme les bases de données. Dans le cas d'une base de données, permet de dimensionner notre service de bases de données en modifiant le nombre de replica, ce qu'on ne pourrait pas faire avec un déploiement (ou du moins, nettement moins simplement).
De plus, nous pouvons définir des variables d'environnement dans la /spec/ de nos pods, qui seront passées à l'environnement du pod: par exemple une /envvar/ contenant l'adresse de notre base de données, une autre l'utilisateur, une autre son mot de passe (on utilisera généralement un secret Kubernetes pour passer ce genre de données sensibles à nos pods afin que ces informations ne risquent pas de se retrouver dans notre solution de versionnage (git via github par exemple...).
Comme nous décrivons nos ressources de façon programmatique, dans des fichiers YAML, il faut donc appliquer ces fichiers yaml via kubectl (ou des solutions de déploiement continu comme FluxCD, ArgoCD, etc):
kubectl apply -f mon-deploiement.yml
De façon identique à la manipulation des pods vue précedemment, on peut manipuler ces ressources via kubectl (ici un déploiement):
kubectl get deploy -n mon-namespace
kubectl describe deploy -n mon-namespace mon-deploy
kubectl delete deploy -n mon-namespace mon-deploy
Bien évidemment, je vous invite fortement à consulter la documentation en ligne de kubernetes et kubectl, très fournie (et parfois un peu absconce).
Réseau: Services, Load-Balancers et Ingresses
Si nos déploiements, damonsets, et statefulsets nous permettent de créer les différents pods composant notre service, ils ne nous permettent pas d'exposer ces services à d'autres pods du cluster (ClusterIP), ni à l'extérieur du cluster (c'est à dire à nos utilisateurs et utilisatrices, soit via une LoadBalancerIP, soit via un Ingress (qui sera exposé via un service de type LoadBalancer et une IP "publique")).
- Service: un service permet d'exposer le(s) pod(s) de notre applicatif soit à l'intérieur du cluster (service de type ClusterIP) soit à l'extérieur via un LoadBalancer (MetalLB par exemple) qui lui assignera une IP "publique" de type LoadBalancerIP.
- LoadBalancer: permet d'assigner une IP "publique" à un service. Il me faudra donc autant d'IPs publiques que de services à exposer à l'extérieur.
- Ingress: Si je ne dispose que d'une IP publique, je vais utiliser un Ingress, dont le service sera exposé par notre load-balancer, et qui va distribuer les requetes (principalement http/https) entre les différents services en les différenciant soit via leur /headers/ Host (www.monsite.com, admin.monsite.com, www.autresite.com, etc), soit via leurs URLs respectives (www.monsite.com/login, www.monsite.com/shop, etc).
L'avantage d'un Ingress étant ici de n'utiliser qu'une seule IP publique pour distribuer entre plusieurs applicatifs. Cependant, il est plus complexe d'exposer d'autres services notamment en TCP pur ou en UDP (DNS) que directement avec un LoadBalancer. C'est la solution la plus simple, par contre, pour exposer des services depuis un cluster kubernetes /managé/ (GKS, Scaleway Kapsule, Azure Kubernetes, etc).
Stockage: Volumes Persistants (PV), Réclamation de PV (PersistentVolumeClaim) et montage dans nos pods
Il faut savoir que les systèmes de fichiers de nos pods sont immutables. Toute modification manuelle du contenu du système de fichiers des containers de nos pods ne survivra pas à un redéploiement (par exemple, une mise à jour) et ne se propagera pas entre les pods d'un meme service.
Afin de pouvoir faire persister et partager nos données entre nos pods et leurs futures versions, il est nécessaire de définir des Volumes Persistants (Persistent Volume) qui seront liés à nos pods via une réclamation de volume persistant (PersistentVolumeClaim), et assignés à nos pods via les directives /volumes/ et /volumeMounts/ dans nos déploiements, daemonsets et statefulsets.
Afin que les données soient accessibles depuis tous nos workers, on utilisera généralement du stockage distant, comme NFS, iSCSI, MiniIO, Netapp Trident, etc, ou des solutions de réplication des données entre nos noeuds comme LongHorn (iSCSI), Rook (Ceph), etc.
Dans notre cas nous utiliserons NFS avec provisionnement automatique des volumes persistants via l'utilitaire nfs-subdir-external-provisioner.
ConfigMaps et Secrets
On peut aussi vouloir passer un fichier de configuration (ex: /etc/haproxy/haproxy.cfg dans un pod HAProxy) à nos pods. Pour cela, on peut utiliser une ConfigMap, qui sera montée comme un fichier dans nos pods.
De plus, on peut aussi avoir besoin de "cacher" des informations sensibles (logins, mots de passes, certificats HTTPS et notamment la clef privée) et ne pas vouloir les /commit/ dans notre solution de gestion de versions (git, fossil, mercurial...). Pour celà, Kubernetes propose des ressources de type Secrets, qu'on pourra assigner à des variables d'environnements ou monter dans des fichiers à l'intérieur de nos pods afin qu'ils soient lus par nos applicatifs. On sort un peu du sujet du cours et du TP mais gardez le bien à l'esprit
RBAC: Role Based Access Control et comptes de service (serviceAccount)
Pour le moment on utilise kubectl et kubernetes avec le role RBAC cluster-admin. Bien que ce soit suffisant pour nos besoins limités, gardez à l'esprit que ce role a les pleins pouvoirs sur le cluster. En mixant droits RBAC, serviceAccounts et Tokens d'accès, on peut définir des droits très fins limitant ce qu'un utilisateur peut faire dans le cluster.
Par exemple, on peut imaginer un role RBAC donnant tous les pouvoirs dans un namespace donné - mais juste ce namespace, lié à un serviceAccount /michel-dev/. Michel pourra accéder et modifier les ressources de ce namespace en se connectant avec le Token lié à son serviceAccount.
Là aussi on sort un peu du "scope" de ce TP, mais je vous recommande plus que chaudement la lecture de la documentation de Kubernetes relative à RBAC, surtout si vous voulez utiliser Kubernetes en production
####* Conclusion
Maintenant qu'on a abordé la théorie, passons à la pratique. Nous utiliserons k3s du fait de sa légèreté et simplicité d'utilisation qui nous permettra de nous concentrer sur nos recettes et nos applicatifs, mais il existe beaucoup d'autres distributions de kubernetes comme:
- Talos Linux
- Ubuntu + microk8s
- Flatcar Linux
- CoreOS
- n'importe quelle distribution linux et installation manuelle de Kubernetes - la maintenance pique
Installation du control-plane
Sur notre VM debian, il suffit simplement d'un (horrible curl | shell, mais bon...):
curl -fsL https://get.k3s.io | sh -
Pour installer notre control plane.
On va noter/copier/etc le contenu du fichier:
/var/lib/rancher/k3s/server/node-token
qui va nous permettre de joindre notre ou nos workers à notre cluster fraichement créé
Installation des workers
Là aussi, ça va etre extremement simple! Si notre control-plane a pour IP 192.168.10.13, et que notre fichier node-token contenait abcdefgh12345678:
curl -fsL https://get.k3s.io | \
K3S_URL=https://192.168.10.13:6443 \
K3S_TOKEN=abcdefgh12345678 \
sh -
et voilà!
On va maintenant vérifier que nos nodes sont bien joints au cluster avec la commande:
kubectl get nodes
qui devrait nous lister nos différents nodes (normalement juste un control-plane et un worker)
Installation de nfs-subdir-external-provisionner
On va simplement copier le fichier suivant dans un fichier nfs.yaml sur notre master, dans le chemin:
/var/lib/rancher/k3s/server/manifests/nfs.yaml
Ce fichier doit contenir (adaptez la conf aux données de votre serveur NFS si vous avez dévié des choix proposés durant le TP:
apiVersion: helm.cattle.io/v1
kind: HelmChart
metadata:
name: nfs
namespace: default
spec:
chart: nfs-subdir-external-provisioner
repo: https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner
targetNamespace: default
set:
nfs.server: 192.168.10.17
nfs.path: /srv/nfs/k3s
storageClass.name: nfs
Notre premier service: wordpress (lol)
Ici, nous allons créer notre premier service, wordpress, qui utilisera le cluster galera comme base de données:
apiVersion: v1
kind: Namespace
metadata:
name: web
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: wp-pvc
namespace: web
spec:
storageClassName: "nfs"
accessModes:
- ReadWriteMany
resources:
requests:
storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: wp
name: wp
namespace: web
spec:
replicas: 1
selector:
matchLabels:
app: wp
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
template:
metadata:
labels:
app: wp
spec:
volumes:
- name: wp-data
persistentVolumeClaim:
claimName: wp-pvc
containers:
- name: wp
image: wordpress
env:
- name: WORDPRESS_DB_HOST
value: "192.168.10.10"
- name: WORDPRESS_DB_USER
value: "wp"
- name: WORDPRESS_DB_PASSWORD
value: "epsiepsi"
- name: WORDPRESS_DB_NAME
value: "wordpress"
securityContext:
allowPrivilegeEscalation: false
resources:
requests:
memory: "256Mi"
cpu: "100m"
ports:
- containerPort: 80
protocol: TCP
volumeMounts:
- name: wp-data
mountPath: /var/www/html
restartPolicy: Always
---
apiVersion: v1
kind: Service
metadata:
name: wp-svc
namespace: web
spec:
type: ClusterIP
ports:
- port: 80
targetPort: 80
protocol: TCP
name: web
selector:
app: wp
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
annotations:
kubernetes.io/ingress.class: traefik
traefik.ingress.kubernetes.io/frontend-entry-points: http
#HTTPS setup with automatic redirect
#traefik.ingress.kubernetes.io/frontend-entry-points: http,https
#traefik.ingress.kubernetes.io/redirect-entry-point: https
#traefik.ingress.kubernetes.io/redirect-permanent: "true"
name: web-ingress
namespace: web
spec:
rules:
- host: wordpress.lidstah.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: wp-svc
port:
number: 80