Ce tutoriel montre les étapes détaillées du déploiement de MySQL sur Kubernetes. J’utiliserai ici minikube pour montrer des exemples de Kubernetes MySQL.
Nous connaissons tous la grande importance de la persistance des données et presque toutes nos applications reposent énormément sur une sorte de système de gestion de base de données (SGBD). La configuration d’un SGBD sur Kubernetes aide l’équipe DevOps et les administrateurs de base de données à exploiter et à faire évoluer facilement la base de données.
Préparer l’environnement
Suivez ce tutoriel, vous devez avoir Minikube installé sur votre Linux Ubuntu.
Vous pouvez vérifier si le Minikube a été correctement installé et exécuté par la commande suivante :
$ minikube status
Sortir:
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
Créer un secret pour MySQL
Kubernetes utilise Secret
pour stocker et gérer des informations sensibles telles que les mots de passe, les clés ssh et les jetons OAuth. Dans ce tutoriel, nous utilisons base64
encodé pour stocker ‘MYSQL_ROOT_PASSWORD’. Pour example:
$ echo -n 'admin' | base64
Sortir:
YWRtaW4=
Créer un mysql-secret.yaml
pour MySQL qui sera mappé en tant que variable d’environnement comme suit :
apiVersion: v1
kind: Secret
metadata:
name: mysql-pass
type: Opaque
data:
password: YWRtaW4=
Appliquez le manifeste :
$ kubectl create -f mysql-secret.yaml
secret/mysql-pass created
Vérifiez que le Secret
vient d’être créé avec succès :
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-l7t7b kubernetes.io/service-account-token 3 4h24m
mysql-pass Opaque 1 1m
Déployer MySQL
Créer le mysql-pod.yaml
fichier pour déployer le pod MySQL sur le cluster Kubernetes :
apiVersion: v1
kind: Pod
metadata:
name: k8s-mysql
labels:
name: lbl-k8s-mysql
spec:
containers:
- name: mysql
image: mysql:latest
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-pass
key: password
ports:
- name: mysql
containerPort: 3306
protocol: TCP
volumeMounts:
- name: k8s-mysql-storage
mountPath: /var/lib/mysql
volumes:
- name: k8s-mysql-storage
emptyDir: {}
Appliquez le fichier manifeste :
$ kubectl create -f mysql-pod.yaml
pod/k8s-mysql created
Vérifiez que le pod est en cours d’exécution :
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8s-mysql 1/1 Running 0 30s
Maintenant, nous pouvons nous connecter au k8s-mysql
en dessous de:
$ kubectl exec k8s-mysql -it -- bash
root@k8s-mysql:/# echo $MYSQL_ROOT_PASSWORD
admin
root@k8s-mysql:/# mysql --user=root --password=$MYSQL_ROOT_PASSWORD
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or g.
Your MySQL connection id is 11
Server version: 8.0.22 MySQL Community Server - GPL
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or 'h' for help. Type 'c' to clear the current input statement.
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
mysql>
Utilisation de Kubernetes Service
pour exposer les pods à d’autres pods ou systèmes externes. Nous utiliserons le fichier manifeste suivant mysql-service.yaml
pour faire le k8s-mysql
le pod soit accessible :
apiVersion: v1
kind: Service
metadata:
name: mysql-service
labels:
name: lbl-k8s-mysql
spec:
ports:
- port: 3306
selector:
name: lbl-k8s-mysql
type: ClusterIP
Appliquez le manifeste pour créer le service :
$ kubectl create -f mysql-service.yaml
service/mysql-service created
Vérifiez que le service a été créé avec succès :
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 5h4m
mysql-service ClusterIP 10.110.22.182 3306/TCP 30s
Création d’une API NodeJS pour frapper mysql
Afin de pouvoir se connecter à mysql à partir d’un autre pod, nous devons avoir le IP
adresse de notre pod qui peut être fait en utilisant:
$ kubectl get pod k8s-mysql -o template --template={{.status.podIP}}
172.17.0.5
D’accord, maintenant je vais créer un échantillon NodeJS
app, pour stocker un ensemble de messages dans la table MESSAGES de la base de données, l’application aura deux points de terminaison :
- ‘/ping’ : pour vérifier la santé du serveur
- ‘/msg-api/all’ : pour obtenir tous les messages stockés
Pour simplifier les choses… le tableau n’aura qu’une seule colonne appelée TEXTE.
Tout d’abord, application de nœud :
// api.js -> les points de terminaison vont ici
var express = require('express')
var mysql = require('mysql')
var Router = express.Router();
var ConnPool = mysql.createPool({
host: '172.17.0.5',
user: 'root',
password: 'admin',
database: 'k8smysqldb'
})
// create database and MESSAGE table if not exist
ConnPool.query('CREATE DATABASE IF NOT EXISTS k8smysqldb', function (err) {
if (err) throw Error('nt **** error creating database **** ' + err)
console.log('nt ==== database k8smysqldb created !! ====')
ConnPool.query('USE k8smysqldb', function (err) {
if (err) throw Error('nt **** error using database **** ' + err);
console.log('nt ==== database k8smysqldb switched !! ====')
ConnPool.query('CREATE TABLE IF NOT EXISTS messages('
+ 'id INT NOT NULL AUTO_INCREMENT,'
+ 'PRIMARY KEY(id),'
+ 'text VARCHAR(100)'
+ ')', function (err) {
if (err) throw Error('nt **** error creating table **** ' + err);
})
})
})
/**
* /all
*/
Router.get('/all', function (req, res) {
ConnPool.getConnection(function (errConn, conn) {
if (errConn) throw Error('error get connection : ' + errConn)
conn.query('SELECT * FROM messages', function (errSelect, rows) {
if (errSelect) throw Error('error selecting messages : ' + errSelect)
res.writeHead(200, {
'Content-Type': 'application/json'
});
var result = {
success: true,
rows: rows.length,
}
res.write(JSON.stringify(rows));
res.end();
})
})
})
module.exports = Router
// server.js -> fire expressjs server
var express = require('express')
var msgApi = require('./api')
var app = express()
app.use('/msg-api', msgApi)
app.get('/ping', function (req, res) {
res.write("hello there! I m up and running!");
res.end();
})
app.listen(8080, function () {
console.log('nt ==== Message API listening on 8080! ====')
})
// Dockerfile -> bundle image docker pour notre application
FROM node:latest
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/package.json
RUN npm i
COPY . /usr/src/app/
EXPOSE 8080
CMD [ "node", "server.js" ]
Nous pouvons maintenant créer nos images Docker à partir du Dockerfile :
$ docker build -t linoxide/msg-api:v0.0.3 . --no-cache=true
Sending build context to Docker daemon 5.12kB
Step 1/8 : FROM node:latest
---> 2d840844f8e7
Step 2/8 : RUN mkdir -p /usr/src/app
---> Using cache
---> 1c29cda3dcd8
Step 3/8 : WORKDIR /usr/src/app
...
Et poussez l’image intégrée vers Docker Hub :
$ docker push linoxide/msg-api:v0.0.3
The push refers to a repository [docker.io/linoxide/msg-api]
c4477a160652: Pushed
32c1bac97782: Pushed
3d629e3d2e5a: Pushed
...
v1: digest: sha256:dba64e7ff64561f4af866fbbb657555cad7621688c7f312975943f5baf89efa2 size: 2628
Nous pouvons maintenant créer un pod de notre application NodeJS, le fichier de spécifications ci-dessous msg-api-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: k8s-msg-api
labels:
name: lbl-msg-api
spec:
containers:
- name: msg-api
image: linoxide/msg-api:v0.0.1
ports:
- name: msg-api
Appliquez le manifeste :
$ kubectl create -f msg-api-pod.yaml
pod/k8s-msg-api created
Assurez-vous que le pod est en cours d’exécution en vérifiant l’état :
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8s-msg-api 1/1 Running 0 22s
k8s-mysql 1/1 Running 0 1h
À ce niveau, nous devons exposer le pod créé afin qu’il puisse y accéder de l’extérieur. Cette fois, je le ferai en utilisant uniquement la ligne de commande plutôt qu’un fichier de spécifications :
$ kubectl expose pod k8s-msg-api --port=8080 --name=k8s-srv-msg-api --type=NodePort
service/k8s-srv-msg-api exposed
Obtenir des données de la base de données mysql à l’aide de nodejs api
A ce niveau, je dois souligner quelques trucs importants, afin de comprendre toutes les pièces, résumons d’abord ce que nous avons fait jusqu’à présent, jusqu’à présent, nous avons créé un pod MySQL et nous l’avons exposé via un service pour faire il est accessible pour d’autres pods, deuxièmement, nous avons créé un exemple d’application nodejs, nous l’avons appelé une API de messagerie, afin que nous puissions l’utiliser pour accéder au pod MySQL ; de même, pour pouvoir accéder à l’API de messagerie nous devons l’exposer via un service, j’espère que tout est clair jusqu’ici !
Maintenant, la question est de savoir comment pouvons-nous appeler notre API de messagerie de l’extérieur de notre cluster principalement minikube ? Pour ce faire, nous avons besoin de l’adresse IP de notre nœud, car j’utilise minikube qui ne crée qu’un seul nœud, donc l’adresse IP est résolue, c’est l’adresse ip minikube elle-même, exécutez simplement :
$ minikube ip
192.168.99.100
Et qu’en est-il du port ? Eh bien bonne question ! décrivons notre service d’API de messagerie pour vérifier cela :
$ kubectl describe service k8s-srv-msg-api
Name: k8s-srv-msg-api
Namespace: default
Labels: name=lbl-msg-api
Selector: name=lbl-msg-api
Type: NodePort
IP: 10.0.0.170
Port: <unset> 8080/TCP
NodePort: <unset> 30887/TCP
Endpoints: 172.17.0.6:8080
Session Affinity: None
No events.
Nous avons donc Port qui est le port de notre service API de messagerie. NodePort est le port sur lequel le service exposé est disponible (accessible), c’est-à-dire que le service est disponible sur NodeIP:NodePort
Essayons ça :
$ curl 192.168.99.100:30887/ping
hello there! I m up and running!%
$ curl 192.168.99.100:30887/msg-api/all
[]%
Très bien, jusqu’à présent, nous pouvons accéder à notre base de données MySQL, insérons des données dans notre base de données à l’aide d’un terminal.
$ kubectl exec k8s-mysql -it -- bash
root@k8s-mysql:/# mysql --user=root --password=$MYSQL_ROOT_PASSWORD
mysql: [Warning] Using a password on the command line interface can be insecure.
...
mysql> use k8smysqldb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+----------------------+
| Tables_in_k8smysqldb |
+----------------------+
| messages |
+----------------------+
1 row in set (0.01 sec)
mysql> insert into messages(text) values ('this is the first msg!');
Query OK, 1 row affected (0.01 sec)
mysql> insert into messages(text) values ('this is the second msg!');
Query OK, 1 row affected (0.01 sec)
Obtenons ces données via notre API nodejs en utilisant curl:
$ curl 192.168.99.100:30887/msg-api/all
[{"id":1,"text":"this is the first msg!"},{"id":2,"text":"this is the second msg!"}]%
Conclusion
La conteneurisation de la base de données MySQL et l’exécution du SGBD sur un cluster Kubernetes apportent de nombreux avantages à l’équipe DevOps, tels que la portabilité entre les environnements, le démarrage/l’arrêt et la mise à jour plus faciles et une meilleure sécurité car les services sont isolés.
Merci d’avoir lu et s’il vous plaît laissez votre suggestion dans la section commentaire ci-dessous.