Puppet est un outil de gestion de systèmes open source permettant de centraliser et d'automatiser la gestion de la configuration. Les outils d'automatisation permettent de minimiser les tâches manuelles et répétitives et peuvent faire gagner beaucoup de temps.
Puppet fonctionne par défaut dans un modèle serveur/agent. Les agents récupèrent leur « catalogue » (état final souhaité) auprès du maître et l'appliquent localement. Ensuite, ils font rapport au serveur. Le catalogue est calculé en fonction des "faits" que la machine envoie au serveur, des entrées utilisateur (paramètres) et des modules (code source).
Dans ce blog, nous allons vous montrer comment déployer et gérer des instances MySQL/MariaDB via Puppet. Il existe un certain nombre de technologies autour de MySQL/MariaDB telles que la réplication (maître-esclave, Galera ou réplication de groupe pour MySQL), des équilibreurs de charge compatibles SQL comme ProxySQL et MariaDB MaxScale, des outils de sauvegarde et de récupération et bien d'autres que nous aborderons dans ce série de blogs. Il existe également de nombreux modules disponibles dans Puppet Forge construits et maintenus par la communauté qui peuvent nous aider à simplifier le code et à éviter de réinventer la roue. Dans ce blog, nous allons nous concentrer sur la réplication MySQL.
marionnettelabs/mysql
Il s'agit du module Puppet le plus populaire pour MySQL et MariaDB (et probablement le meilleur du marché) à l'heure actuelle. Ce module gère à la fois l'installation et la configuration de MySQL, ainsi que l'extension de Puppet pour permettre la gestion des ressources MySQL, telles que les bases de données, les utilisateurs et les subventions.
Le module est officiellement maintenu par l'équipe Puppet (via le référentiel puppetlabs Github) et prend en charge toutes les versions majeures de Puppet Enterprise 2019.1.x, 2019.0.x, 2018.1.x, Puppet>=5.5.10 <7.0.0 sur RedHat, Ubuntu, Plateformes Debian, SLES, Scientific, CentOS, OracleLinux. L'utilisateur a la possibilité d'installer MySQL, MariaDB et Percona Server en personnalisant le référentiel de packages
L'exemple suivant montre comment déployer un serveur MySQL. Sur le puppet master, installez le module MySQL et créez le fichier manifest :
(puppet-master)$ puppet module install puppetlabs/mysql
(puppet-master)$ vim /etc/puppetlabs/code/environments/production/manifests/mysql.pp
Ajoutez les lignes suivantes :
node "db1.local" {
class { '::mysql::server':
root_password => 't5[sb^D[+rt8bBYu',
remove_default_accounts => true,
override_options => {
'mysqld' => {
'log_error' => '/var/log/mysql.log',
'innodb_buffer_pool_size' => '512M'
}
'mysqld_safe' => {
'log_error' => '/var/log/mysql.log'
}
}
}
}
Ensuite, sur le nœud de l'agent marionnette, exécutez la commande suivante pour appliquer le catalogue de configuration :
(db1.local)$ puppet agent -t
Lors de la première exécution, vous pourriez obtenir l'erreur suivante :
Info: Certificate for db1.local has not been signed yet
Exécutez simplement la commande suivante sur le Puppet master pour signer le certificat :
(puppet-master)$ puppetserver ca sign --certname=db1.local
Successfully signed certificate request for db1.local
Réessayez avec la commande "puppet agent -t" pour réinitialiser la connexion avec le certificat signé.
La définition ci-dessus installera les packages standard liés à MySQL disponibles dans le référentiel de distribution du système d'exploitation. Par exemple, sur Ubuntu 18.04 (Bionic), vous obtiendrez les packages MySQL 5.7.26 installés :
(db1.local) $ dpkg --list | grep -i mysql
ii mysql-client-5.7 5.7.26-0ubuntu0.18.04.1 amd64 MySQL database client binaries
ii mysql-client-core-5.7 5.7.26-0ubuntu0.18.04.1 amd64 MySQL database core client binaries
ii mysql-common 5.8+1.0.4 all MySQL database common files, e.g. /etc/mysql/my.cnf
ii mysql-server 5.7.26-0ubuntu0.18.04.1 all MySQL database server (metapackage depending on the latest version)
ii mysql-server-5.7 5.7.26-0ubuntu0.18.04.1 amd64 MySQL database server binaries and system database setup
ii mysql-server-core-5.7 5.7.26-0ubuntu0.18.04.1 amd64 MySQL database server binaries
Vous pouvez opter pour d'autres fournisseurs comme Oracle, Percona ou MariaDB avec une configuration supplémentaire sur le référentiel (reportez-vous à la section README pour plus de détails). La définition suivante installera les packages MariaDB à partir du référentiel apt MariaDB (nécessite le module apt Puppet) :
$ puppet module install puppetlabs/apt
$ vim /etc/puppetlabs/code/environments/production/manifests/mariadb.pp
# include puppetlabs/apt module
include apt
# apt definition for MariaDB 10.3
apt::source { 'mariadb':
location => 'http://sgp1.mirrors.digitalocean.com/mariadb/repo/10.3/ubuntu/',
release => $::lsbdistcodename,
repos => 'main',
key => {
id => 'A6E773A1812E4B8FD94024AAC0F47944DE8F6914',
server => 'hkp://keyserver.ubuntu.com:80',
},
include => {
src => false,
deb => true,
},
}
# MariaDB configuration
class {'::mysql::server':
package_name => 'mariadb-server',
service_name => 'mysql',
root_password => 't5[sb^D[+rt8bBYu',
override_options => {
mysqld => {
'log-error' => '/var/log/mysql/mariadb.log',
'pid-file' => '/var/run/mysqld/mysqld.pid',
},
mysqld_safe => {
'log-error' => '/var/log/mysql/mariadb.log',
},
}
}
# Deploy on db2.local
node "db2.local" {
Apt::Source['mariadb'] ->
Class['apt::update'] ->
Class['::mysql::server']
}
Prenez note de la valeur key->id, où il existe un moyen spécial de récupérer l'identifiant à 40 caractères, comme indiqué dans cet article :
$ sudo apt-key adv --recv-keys --keyserver hkp://keyserver.ubuntu.com:80 0xF1656F24C74CD1D8
$ apt-key adv --list-public-keys --with-fingerprint --with-colons
uid:-::::1459359915::6DC53DD92B7A8C298D5E54F950371E2B8950D2F2::MariaDB Signing Key <[email protected]>::::::::::0:
sub:-:4096:1:C0F47944DE8F6914:1459359915::::::e::::::23:
fpr:::::::::A6E773A1812E4B8FD94024AAC0F47944DE8F6914:
Où la valeur de l'identifiant se trouve dans la ligne commençant par "fpr", qui est 'A6E773A1812E4B8FD94024AAC0F47944DE8F6914'.
Une fois le catalogue Puppet appliqué, vous pouvez accéder directement à la console MySQL en tant que root sans mot de passe explicite puisque le module configure et gère automatiquement ~/.my.cnf. Si nous voulons réinitialiser le mot de passe root à autre chose, changez simplement la valeur root_password dans la définition de Puppet et appliquez le catalogue sur le nœud de l'agent.
Déploiement de la réplication MySQL
Pour déployer une configuration de réplication MySQL, il faut créer au moins deux types de configuration pour séparer la configuration maître et esclave. Le maître aura la lecture seule désactivée pour permettre la lecture/écriture tandis que les esclaves seront configurés avec la lecture seule activée. Dans cet exemple, nous allons utiliser la réplication basée sur GTID pour simplifier la configuration (puisque la configuration de tous les nœuds serait très similaire). Nous voudrons initier le lien de réplication vers le maître juste après que l'esclave soit activé.
Supposons que nous ayons une réplication maître-esclave MySQL à 3 nœuds :
- db1.local - maître
- db2.local - esclave #1
- db3.local - esclave #2
Pour répondre aux exigences ci-dessus, nous pouvons écrire notre manifeste à quelque chose comme ceci :
# Puppet manifest for MySQL GTID-based replication MySQL 5.7 on Ubuntu 18.04 (Puppet v6.4.2)
# /etc/puppetlabs/code/environments/production/manifests/replication.pp
# node's configuration
class mysql {
class {'::mysql::server':
root_password => '[email protected]#',
create_root_my_cnf => true,
remove_default_accounts => true,
manage_config_file => true,
override_options => {
'mysqld' => {
'datadir' => '/var/lib/mysql',
'bind_address' => '0.0.0.0',
'server-id' => $mysql_server_id,
'read_only' => $mysql_read_only,
'gtid-mode' => 'ON',
'enforce_gtid_consistency'=> 'ON',
'log-slave-updates' => 'ON',
'sync_binlog' => 1,
'log-bin' => '/var/log/mysql-bin',
'read_only' => 'OFF',
'binlog-format' => 'ROW',
'log-error' => '/var/log/mysql/error.log',
'report_host' => ${fqdn},
'innodb_buffer_pool_size' => '512M'
},
'mysqld_safe' => {
'log-error' => '/var/log/mysql/error.log'
}
}
}
# create slave user
mysql_user { "${slave_user}@192.168.0.%":
ensure => 'present',
password_hash => mysql_password("${slave_password}")
}
# grant privileges for slave user
mysql_grant { "${slave_user}@192.168.0.%/*.*":
ensure => 'present',
privileges => ['REPLICATION SLAVE'],
table => '*.*',
user => "${slave_user}@192.168.0.%"
}
# /etc/hosts definition
host {
'db1.local': ip => '192.168.0.161';
'db2.local': ip => '192.169.0.162';
'db3.local': ip => '192.168.0.163';
}
# executes change master only if $master_host is defined
if $master_host {
exec { 'change master':
path => '/usr/bin:/usr/sbin:/bin',
command => "mysql --defaults-extra-file=/root/.my.cnf -e \"CHANGE MASTER TO MASTER_HOST = '$master_host', MASTER_USER = '$slave_user', MASTER_PASSWORD = '$slave_password', MASTER_AUTO_POSITION = 1; START SLAVE;\"",
unless => "mysql --defaults-extra-file=/root/.my.cnf -e 'SHOW SLAVE STATUS\G' | grep 'Slave_SQL_Running: Yes'"
}
}
}
## node assignment
# global vars
$master_host = undef
$slave_user = 'slave'
$slave_password = 'Replicas123'
# master
node "db1.local" {
$mysql_server_id = '1'
$mysql_read_only = 'OFF'
include mysql
}
# slave1
node "db2.local" {
$mysql_server_id = '2'
$mysql_read_only = 'ON'
$master_host = 'db1.local'
include mysql
}
# slave2
node "db3.local" {
$mysql_server_id = '3'
$mysql_read_only = 'ON'
$master_host = 'db1.local'
include mysql
}
Forcer l'agent à appliquer le catalogue :
(all-mysql-nodes)$ puppet agent -t
Sur le maître (db1.local), nous pouvons vérifier tous les esclaves connectés :
mysql> SHOW SLAVE HOSTS;
+-----------+-----------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID |
+-----------+-----------+------+-----------+--------------------------------------+
| 3 | db3.local | 3306 | 1 | 2d0b14b6-8174-11e9-8bac-0273c38be33b |
| 2 | db2.local | 3306 | 1 | a9dfa4c7-8172-11e9-8000-0273c38be33b |
+-----------+-----------+------+-----------+--------------------------------------+
Portez une attention particulière à la section "exec { 'change master' :", où cela signifie qu'une commande MySQL sera exécutée pour initier le lien de réplication si la condition est remplie. Toutes les ressources "exec" exécutées par Puppet doivent être idempotentes, c'est-à-dire l'opération qui aura le même effet que vous l'exécutiez une fois ou 10 001 fois. Il existe un certain nombre d'attributs de condition que vous pouvez utiliser comme "sauf si", "seulement si" et "créer" pour sauvegarder l'état correct et empêcher Puppet de gâcher votre configuration. Vous pouvez supprimer/commenter cette section si vous souhaitez lancer le lien de réplication manuellement.
Gestion MySQL
Ce module peut être utilisé pour effectuer un certain nombre de tâches de gestion MySQL :
- options de configuration (modifier, appliquer, configuration personnalisée)
- Ressources de la base de données (base de données, utilisateur, subventions)
- sauvegarde (création, planification, utilisateur de sauvegarde, stockage)
- restauration simple (mysqldump uniquement)
- installation/activation des plugins
Ressource de base de données
Comme vous pouvez le voir dans l'exemple de manifeste ci-dessus, nous avons défini deux ressources MySQL - mysql_user et mysql_grant - pour créer respectivement des privilèges d'utilisateur et d'octroi pour l'utilisateur. Nous pouvons également utiliser la classe mysql::db pour nous assurer qu'une base de données avec l'utilisateur et les privilèges associés sont présents, par exemple :
# make sure the database and user exist with proper grant
mysql::db { 'mynewdb':
user => 'mynewuser',
password => 'passw0rd',
host => '192.168.0.%',
grant => ['SELECT', 'UPDATE']
}
Notez que dans la réplication MySQL, toutes les écritures doivent être effectuées uniquement sur le maître. Assurez-vous donc que la ressource ci-dessus est affectée au maître. Sinon, une transaction erronée pourrait se produire.
Sauvegarde et restauration
Généralement, un seul hôte de sauvegarde est requis pour l'ensemble du cluster (sauf si vous répliquez un sous-ensemble de données). Nous pouvons utiliser la classe mysql::server::backup pour préparer les ressources de sauvegarde. Supposons que nous ayons la déclaration suivante dans notre manifeste :
# Prepare the backup script, /usr/local/sbin/mysqlbackup.sh
class { 'mysql::server::backup':
backupuser => 'backup',
backuppassword => 'passw0rd',
backupdir => '/home/backup',
backupdirowner => 'mysql',
backupdirgroup => 'mysql',
backupdirmode => '755',
backuprotate => 15,
time => ['23','30'], #backup starts at 11:30PM everyday
include_routines => true,
include_triggers => true,
ignore_events => false,
maxallowedpacket => '64M',
optional_args => ['--set-gtid-purged=OFF'] #extra argument if GTID is enabled
}
Puppet configurera toutes les conditions préalables avant d'exécuter une sauvegarde - création de l'utilisateur de sauvegarde, préparation du chemin de destination, attribution de la propriété et de l'autorisation, définition de la tâche cron et configuration des options de commande de sauvegarde à utiliser dans le script de sauvegarde fourni situé à /usr/local /sbin/mysqlbackup.sh. C'est alors à l'utilisateur d'exécuter ou de programmer le script. Pour faire une sauvegarde immédiate, invoquez simplement :
$ mysqlbackup.sh
Si nous extrayons la commande mysqldump réelle basée sur ce qui précède, voici à quoi cela ressemble :
$ mysqldump --defaults-extra-file=/tmp/backup.NYg0TR --opt --flush-logs --single-transaction --events --set-gtid-purged=OFF --all-databases
Pour ceux qui souhaitent utiliser d'autres outils de sauvegarde comme Percona Xtrabackup, MariaDB Backup (MariaDB uniquement) ou MySQL Enterprise Backup, le module fournit les classes privées suivantes :
- mysql::backup::xtrabackup (Percona Xtrabackup et MariaDB Backup)
- mysql::backup::mysqlbackup (Sauvegarde MySQL Entreprise)
Exemple de déclaration avec Percona Xtrabackup :
class { 'mysql::backup::xtrabackup':
xtrabackup_package_name => 'percona-xtrabackup',
backupuser => 'xtrabackup',
backuppassword => 'passw0rd',
backupdir => '/home/xtrabackup',
backupdirowner => 'mysql',
backupdirgroup => 'mysql',
backupdirmode => '755',
backupcompress => true,
backuprotate => 15,
include_routines => true,
time => ['23','30'], #backup starts at 11:30PM
include_triggers => true,
maxallowedpacket => '64M',
incremental_backups => true
}
Ce qui précède planifiera deux sauvegardes, une sauvegarde complète tous les dimanches à 23h30 et une sauvegarde incrémentielle tous les jours sauf le dimanche à la même heure, comme indiqué par la sortie de la tâche cron après l'application du manifeste ci-dessus :
(db1.local)$ crontab -l
# Puppet Name: xtrabackup-weekly
30 23 * * 0 /usr/local/sbin/xtrabackup.sh --target-dir=/home/backup/mysql/xtrabackup --backup
# Puppet Name: xtrabackup-daily
30 23 * * 1-6 /usr/local/sbin/xtrabackup.sh --incremental-basedir=/home/backup/mysql/xtrabackup --target-dir=/home/backup/mysql/xtrabackup/`date +%F_%H-%M-%S` --backup
Pour plus de détails et les options disponibles pour cette classe (et d'autres classes), consultez la référence des options ici.
Pour l'aspect restauration, le module ne prend en charge que la restauration avec la méthode de sauvegarde mysqldump, en important le fichier SQL directement dans la base de données à l'aide de la classe mysql::db, par exemple :
mysql::db { 'mydb':
user => 'myuser',
password => 'mypass',
host => 'localhost',
grant => ['ALL PRIVILEGES'],
sql => '/home/backup/mysql/mydb/backup.gz',
import_cat_cmd => 'zcat',
import_timeout => 900
}
Le fichier SQL ne sera chargé qu'une seule fois et non à chaque exécution, à moins que l'enforce_sql => true ne soit utilisé.
Options de configuration
Dans cet exemple, nous avons utilisé manage_config_file => true avec override_options pour structurer nos lignes de configuration qui seront ensuite poussées par Puppet. Toute modification du fichier manifeste ne reflétera que le contenu du fichier de configuration MySQL cible. Ce module ne chargera pas la configuration dans l'exécution ni ne redémarrera le service MySQL après avoir poussé les modifications dans le fichier de configuration. Il est de la responsabilité de l'administrateur système de redémarrer le service afin d'activer les modifications.
Pour ajouter une configuration MySQL personnalisée, nous pouvons placer des fichiers supplémentaires dans "includir", par défaut dans /etc/mysql/conf.d. Cela nous permet de remplacer les paramètres ou d'en ajouter d'autres, ce qui est utile si vous n'utilisez pas override_options dans mysql::server class. L'utilisation du modèle Puppet est fortement recommandée ici. Placez le fichier de configuration personnalisé sous le répertoire du modèle de module (par défaut, /etc/puppetlabs/code/environments/production/modules/mysql/templates) puis ajoutez les lignes suivantes dans le manifeste :
# Loads /etc/puppetlabs/code/environments/production/modules/mysql/templates/my-custom-config.cnf.erb into /etc/mysql/conf.d/my-custom-config.cnf
file { '/etc/mysql/conf.d/my-custom-config.cnf':
ensure => file,
content => template('mysql/my-custom-config.cnf.erb')
}
Pour implémenter des paramètres spécifiques à la version, utilisez la directive version, par exemple [mysqld-5.5]. Cela permet une configuration pour différentes versions de MySQL.
Marionnette vs ClusterControl
Saviez-vous que vous pouvez également automatiser le déploiement de la réplication MySQL ou MariaDB en utilisant ClusterControl ? Vous pouvez utiliser le module ClusterControl Puppet pour l'installer, ou simplement en le téléchargeant depuis notre site Web.
Par rapport à ClusterControl, vous pouvez vous attendre aux différences suivantes :
- Un peu de courbe d'apprentissage pour comprendre les syntaxes, le formatage et les structures de Puppet avant de pouvoir écrire des manifestes.
- Le manifeste doit être testé régulièrement. Il est très courant que vous obteniez une erreur de compilation sur le code, surtout si le catalogue est appliqué pour la première fois.
- Puppet suppose que les codes sont idempotents. La condition de test/vérification/vérification relève de la responsabilité de l'auteur pour éviter de gâcher un système en cours d'exécution.
- Puppet nécessite un agent sur le nœud géré.
- Incompatibilité descendante. Certains anciens modules ne fonctionnaient pas correctement sur la nouvelle version.
- La surveillance de la base de données/de l'hôte doit être configurée séparément.
L'assistant de déploiement de ClusterControl guide le processus de déploiement :
Vous pouvez également utiliser l'interface de ligne de commande ClusterControl appelée "s9s" pour obtenir des résultats similaires. La commande suivante crée un cluster de réplication MySQL à trois nœuds (fourni sans mot de passe à tous les nœuds a été configuré au préalable) :
$ s9s cluster --create \
--cluster-type=mysqlreplication \
--nodes=192.168.0.41?master;192.168.0.42?slave;192.168.0.43?slave;192.168.0.44?master; \
--vendor=oracle \
--cluster-name='MySQL Replication 8.0' \
--provider-version=8.0 \
--db-admin='root' \
--db-admin-passwd='$ecR3t^word' \
--log
Ressources associées Module Puppet pour ClusterControl - Ajout de la gestion et de la surveillance à vos clusters de bases de données existants Comment automatiser le déploiement de MySQL Galera Cluster à l'aide de la CLI s9s et de Chef Guide DevOps sur l'automatisation de l'infrastructure de base de données pour le commerce électronique - Relecture et diapositives Les configurations de réplication MySQL/MariaDB suivantes sont prises en charge :
- Réplication maître-esclave (basée sur le fichier/la position)
- Réplication maître-esclave avec GTID (MySQL/Percona)
- Réplication maître-esclave avec MariaDB GTID
- Réplication maître-maître (semi-synchronisée/asynchrone)
- Réplication de la chaîne maître-esclave (semi-sync/async)
Après le déploiement, les nœuds/clusters peuvent être surveillés et entièrement gérés par ClusterControl, y compris la détection automatique des pannes, le basculement maître, la promotion esclave, la récupération automatique, la gestion des sauvegardes, la gestion de la configuration, etc. Tous ces éléments sont regroupés dans un seul produit. L'édition communautaire (gratuite pour toujours !) propose le déploiement et la surveillance. En moyenne, votre cluster de base de données sera opérationnel en 30 minutes. Ce dont il a besoin, c'est uniquement d'un SSH sans mot de passe vers les nœuds cibles.
Dans la partie suivante, nous allons vous guider à travers le déploiement de Galera Cluster en utilisant le même module Puppet. Restez à l'écoute !