Cómo 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.