☸️ Как развернуть MySQL на Kubernetes

by itisgood

В этом руководстве показаны подробные шаги по развертыванию MySQL в Kubernetes.

Я буду использовать minikube, чтобы продемонстрировать примеры работы на Kubernetes СУБД MySQL.

Все мы знаем о большой важности сохранения данных, и почти все наши приложения во многом полагаются на какие-то системы управления базами данных (СУБД).

Настройка СУБД на Kubernetes помогает команде DevOps и администраторам баз данных легко использовать и масштабировать базу данных.

Подготовьте окружающую среду следуя этому руководству, вам необходимо установить Minikube на Ubuntu Linux.

Вы можете проверить, успешно ли был запущен Minikube, с помощью следующей команды:

$ minikube status

Вывод:

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

Создадим секрет для MySQL

Kubernetes использует Secret для хранения и управления конфиденциальной информацией, такой как пароли, ключи ssh и токены OAuth.

В этом руководстве мы используем кодировку base64 для хранения MYSQL_ROOT_PASSWORD.

Например:

$ echo -n 'admin' | base64

Вывод:

YWRtaW4=

Создайте файл mysql-secret.yaml для MySQL, который будет отображаться как переменная среды следующим образом:

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

Примените манифест:

$ kubectl create -f mysql-secret.yaml

secret/mysql-pass created

Убедитесь, что секрет только что успешно создан:

$ kubectl get secrets

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

Развертка MySQL

Создайте файл mysql-pod.yaml для развертывания модуля MySQL в кластере 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: {}

Применим файл манифеста:

$ kubectl create -f mysql-pod.yaml

pod/k8s-mysql created
Убедитесь, что под запущен:
$ kubectl get pod

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

Теперь мы можем подключиться к поду k8s-mysql: 2

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

Kubernetes использует Service для предоставления доступа к подам другим подам или внешним системам.

Мы будем использовать следующий файл манифеста mysql-service.yaml, чтобы сделать pod k8s-mysql доступным:

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

Примените манифест для создания сервиса:

$ kubectl create -f mysql-service.yaml

service/mysql-service created

Убедитесь, что служба была успешно создана:

$ 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

Создание NodeJS Api для обращения к mysql

Чтобы иметь возможность подключиться к mysql из другого подая, нам необходимо иметь IP-адрес нашего пода, что можно сделать с помощью:

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

172.17.0.5

Хорошо, теперь я собираюсь создать образец приложения NodeJS, чтобы хранить набор сообщений в таблице MESSAGES базы данных, приложение будет иметь две конечные точки:

  • ‘/ping’: проверить работоспособность сервера
  • ‘/msg-api/all’: получить все сохраненные сообщения

Для простоты … в таблице будет только один столбец с именем TEXT.

Первым делом приложение узла: //api.js -> сюда идут конечные точки

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('\n\t **** error creating database **** ' + err)

console.log('\n\t ==== database k8smysqldb created !! ====')

ConnPool.query('USE k8smysqldb', function (err) {
if (err) throw Error('\n\t **** error using database **** ' + err);

console.log('\n\t ==== 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('\n\t **** 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 -> запустить сервер 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('\n\t ==== Message API listening on 8080! ====')
})
//Dockerfile -> объединить образ Docker для нашего приложения
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" ]
Теперь мы можем создавать наши образы из Dockerfile:
$ docker build -t itisgood/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
...
И пуллим собранный образ в Docker Hub:
$ docker push itisgood/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

Теперь мы можем создать под нашего приложения NodeJS, приведенный ниже файл спецификации msg-api-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: k8s-msg-api
  labels:
    name: lbl-msg-api
spec:
  containers:
  - name: msg-api
    image: itisgood/msg-api:v0.0.1
    ports:
    - name: msg-api

Примените манифест:

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

pod/k8s-msg-api created
Убедитесь, что под запущен, проверив статус:
$ kubectl get pod

NAME          READY     STATUS    RESTARTS   AGE
k8s-msg-api   1/1       Running   0          22s
k8s-mysql     1/1       Running   0          1h
На этом уровне нам нужно открыть созданный под, чтобы получить к нему доступ извне.
На этот раз я сделаю это, используя только командную строку, а не файл спецификации:
$ kubectl expose pod k8s-msg-api --port=8080 --name=k8s-srv-msg-api --type=NodePort

service/k8s-srv-msg-api exposed

Получение данных из базы данных mysql с использованием nodejs api

На этом уровне мне нужно указать на некоторые важные вещи, чтобы понять все части, давайте сначала подведем итоги того, что мы уже сделали.

Пока мы создали под MySQL и представили его через службу, чтобы сделать его доступным для других подов.

Во-вторых, мы создали образец приложения nodejs, чтобы использовать его для обращения к поду MySQL; аналогично, чтобы получить доступ к API обмена сообщениями, нам нужно предоставить его через службу, я надеюсь, что до этого все ясно!

Теперь вопрос в том, как мы можем вызвать наш API обмена сообщениями извне нашего кластера в основном minikube?

Для этого нам нужен IP-адрес нашего узла, поскольку я использую minikube, который создает только один узел, поэтому IP-адрес разрешен, это сам IP-адрес minikube, просто запустите:

$ minikube ip

192.168.99.100

А что с портом?

Давайте опишем нашу службу API обмена сообщениями, чтобы убедиться в этом:

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

Итак, у нас есть порт, который является портом нашей службы обмена сообщениями.

NodePort – это порт, на котором предоставленная служба доступна, то есть служба доступна на NodeIP:NodePort

Давайте попробуем:

$ curl 192.168.99.100:30887/ping

hello there! I m up and running!%

$ curl 192.168.99.100:30887/msg-api/all

[]%

Очень хорошо, пока мы можем попасть в нашу базу данных MySQL, давайте вставим некоторые данные в нашу базу данных с помощью терминала.

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

Давайте получим эти данные через наш API nodejs с помощью 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!"}]%

Заключение

Контейнеризация базы данных MySQL и запуск СУБД в кластере Kubernetes дает команде DevOps множество преимуществ, таких как переносимость между средами, упрощение запуска/остановки и обновления, а также лучшая безопасность благодаря изолированности служб.

 

Loading

You may also like

Leave a Comment