LINUX

Cómo implementar MySQL en Kubernetes

implementar mysql en kubernetes

Este tutorial muestra los pasos detallados para implementar MySQL en Kubernetes. Usaré minikube aquí para demostrar ejemplos de MySQL de Kubernetes.

Todos conocemos la gran importancia de la persistencia de datos y casi todas nuestras aplicaciones dependen enormemente de algún tipo de sistema de administración de bases de datos (DBMS). La configuración de un DBMS en Kubernetes ayuda al equipo de DevOps y a los administradores de la base de datos a aprovechar y escalar la base de datos fácilmente.

Prepara el medio ambiente

Siga este tutorial que necesita para tener Minikube instalado en su Ubuntu Linux.

Puede verificar si el Minikube se ha puesto en marcha correctamente con el siguiente comando:

$ minikube status

Producción:

minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured

Crear secreto para MySQL

Usos de Kubernetes Secret para almacenar y administrar información confidencial como contraseñas, claves ssh y tokens OAuth. En este tutorial, usamos base64 codificado para almacenar ‘MYSQL_ROOT_PASSWORD’. Por ejemplo:

$ echo -n 'admin' | base64

Producción:

YWRtaW4=

Crear un mysql-secret.yaml archivo para MySQL que se asignará como una variable de entorno de la siguiente manera:

apiVersion: v1
kind: Secret
metadata:
  name: mysql-pass
type: Opaque
data:
  password: YWRtaW4=

Aplicar el manifiesto:

$ kubectl create -f mysql-secret.yaml

secret/mysql-pass created

Verifique que el Secret se acaba de crear con éxito:

$ kubectl get secrets

NAME                  TYPE                                  DATA   AGE
default-token-l7t7b   kubernetes.io/service-account-token   3      4h24m
mysql-pass            Opaque                                1      1m

Implementar MySQL

Crea el mysql-pod.yaml archivo para implementar el pod de MySQL en el clúster de 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: {}

Aplicar el archivo de manifiesto:

$ kubectl create -f mysql-pod.yaml

pod/k8s-mysql created

Verifique que el pod se esté ejecutando:

$ kubectl get pod

NAME        READY   STATUS    RESTARTS   AGE
k8s-mysql   1/1     Running   0          30s

Ahora, podemos conectarnos al k8s-mysql vaina:

$ 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> 

Uso de Kubernetes Service para exponer los pods a otros pods o sistemas externos. Usaremos el siguiente archivo de manifiesto mysql-service.yaml para hacer el k8s-mysql Pod ser accesible:

apiVersion: v1
kind: Service
metadata:
  name: mysql-service
  labels:
    name: lbl-k8s-mysql
spec:
  ports:
  - port: 3306
  selector:
    name: lbl-k8s-mysql
  type: ClusterIP

Aplica el manifiesto para crear el servicio:

$ kubectl create -f mysql-service.yaml

service/mysql-service created

Verifique que el servicio se haya creado correctamente:

$ 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

Creación de una API de NodeJS para acceder a mysql

Para poder conectarnos a mysql desde otro pod, necesitamos tener el IP dirección de nuestro pod que se puede hacer usando:

$ kubectl get pod k8s-mysql -o template --template={{.status.podIP}}

172.17.0.5

Muy bien, ahora voy a crear una muestra. NodeJS app, para almacenar un conjunto de mensajes en la tabla MESSAGES de la base de datos, la aplicación tendrá dos puntos finales:

  • ‘/ ping’: para verificar el estado del servidor
  • ‘/ msg-api / all’: para obtener todos los mensajes almacenados

Para simplificar las cosas … la tabla solo tendrá una columna llamada TEXTO.

Lo primero es lo primero, aplicación de nodo:

// api.js -> los puntos finales van aquí

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 -> servidor fire expressjs

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 -> empaquetar la imagen de la ventana acoplable para nuestra aplicación

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" ]

Ahora podemos construir nuestras imágenes de Docker a partir del 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
...

Y envíe la imagen construida a 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

Ahora podemos crear un pod de nuestra aplicación NodeJS, el archivo de especificaciones a continuación 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

Aplicar el manifiesto:

$ kubectl create -f msg-api-pod.yaml

pod/k8s-msg-api created

Asegúrese de que el pod se esté ejecutando comprobando el estado:

$ kubectl get pod

NAME          READY     STATUS    RESTARTS   AGE
k8s-msg-api   1/1       Running   0          22s
k8s-mysql     1/1       Running   0          1h

En este nivel, necesitamos exponer el pod creado para que pueda acceder a él desde el exterior. Esta vez lo haré usando solo la línea de comando en lugar de un archivo de especificaciones:

$ kubectl expose pod k8s-msg-api --port=8080 --name=k8s-srv-msg-api --type=NodePort

service/k8s-srv-msg-api exposed

Obtener datos de la base de datos mysql usando nodejs api

En este nivel, necesito señalar algunas cosas importantes, para entender todas las piezas, primero resumamos lo que hemos hecho hasta ahora, hasta ahora, hemos creado un pod MySQL y lo hemos expuesto a través de un servicio para hacer es accesible para otros pods, en segundo lugar, hemos creado una aplicación de muestra de nodejs, la llamamos api de mensajería, para que podamos usarla para acceder al pod de MySQL; De igual forma, para poder acceder a la API de mensajería necesitamos exponerla a través de un servicio, ¡espero que todo esté claro hasta aquí!

Ahora la pregunta es ¿cómo podemos llamar a nuestra API de mensajería desde fuera de nuestro clúster, principalmente minikube? Para hacerlo, necesitamos la dirección IP de nuestro nodo, ya que estoy usando minikube que crea solo un nodo, por lo que la dirección IP se resuelve, es la dirección IP del minikube en sí, simplemente ejecute:

$ minikube ip

192.168.99.100

¿Y el puerto? Bueno, buena pregunta! describamos nuestro servicio de api de mensajería para comprobarlo:

$ 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.

Entonces tenemos Port, que es el puerto de nuestro servicio API de mensajería. NodePort es el puerto en el que el servicio expuesto está disponible (accesible), es decir, el servicio está disponible en NodeIP:NodePort

Probemos eso:

$ curl 192.168.99.100:30887/ping

hello there! I m up and running!%

$ curl 192.168.99.100:30887/msg-api/all

[]%

Muy bien, hasta ahora podemos acceder a nuestra base de datos MySQL, insertemos algunos datos en nuestra base de datos usando la 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)

Consigamos estos datos a través de nuestra API de nodejs usando 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!"}]%

Conclusión

La contenedorización de la base de datos MySQL y la ejecución de DBMS en un clúster de Kubernetes aporta muchos beneficios al equipo de DevOps, como la portabilidad entre entornos, iniciar / detener y actualizar más fácilmente y tener mejor seguridad debido a que los servicios están aislados.

Gracias por leer y deje su sugerencia en la sección de comentarios a continuación.

Publicaciones relacionadas

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Botón volver arriba
Cerrar