How to Deploy MySQL on Kubernetes
source link: https://linoxide.com/deploy-mysql-on-kubernetes/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
How to Deploy MySQL on Kubernetes
This tutorial shows detailed steps of deploying MySQL on Kubernetes. I’ll be using minikube here to demonstrate Kubernetes MySQL examples.
We all know the big importance of data persistence and almost, all our applications rely hugely on some sort of Database Management Systems (DBMS). Setting up a DBMS on Kubernetes helps the DevOps team and Database administrators leverage and scale the Database easily.
Prepare the environment
Follow this tutorial you need to have Minikube installed on your Ubuntu Linux.
You can verify whether the Minikube has been successfully up and running by the following command:
$ minikube status
Output:
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
Create Secret for MySQL
Kubernetes uses Secret
to store and manage sensitive information such as passwords, ssh keys and OAuth tokens. In this tutorial, we use base64
encoded to store 'MYSQL_ROOT_PASSWORD'. For example:
$ echo -n 'admin' | base64
Output:
YWRtaW4=
Create a mysql-secret.yaml
file for MySQL that will be mapped as an environment variable as follows:
apiVersion: v1
kind: Secret
metadata:
name: mysql-pass
type: Opaque
data:
password: YWRtaW4=
Apply the manifest:
$ kubectl create -f mysql-secret.yaml
secret/mysql-pass created
Verify that the Secret
has just been created successfully:
$ kubectl get secrets
NAME TYPE DATA AGE
default-token-l7t7b kubernetes.io/service-account-token 3 4h24m
mysql-pass Opaque 1 1m
Deploy MySQL
Create the mysql-pod.yaml
file to deploy MySQL pod on Kubernetes cluster:
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: {}
Apply the manifest file:
$ kubectl create -f mysql-pod.yaml
pod/k8s-mysql created
Verify that the pod is running:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8s-mysql 1/1 Running 0 30s
Now, we can connect to the k8s-mysql
pod:
$ 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 use Service
to expose pods to other pods or external systems. We will use the following manifest file mysql-service.yaml
to make the k8s-mysql
pod be reachable:
apiVersion: v1
kind: Service
metadata:
name: mysql-service
labels:
name: lbl-k8s-mysql
spec:
ports:
- port: 3306
selector:
name: lbl-k8s-mysql
type: ClusterIP
Apply the manifest to create the service:
$ kubectl create -f mysql-service.yaml
service/mysql-service created
Verify that the service has been successfully 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
Creating a NodeJS Api to hit mysql
In order to be able to connect to mysql from an other pod we need to have the IP
address of our pod which can be done using:
$ kubectl get pod k8s-mysql -o template --template={{.status.podIP}}
172.17.0.5
Alright, now I’m going to create a sample NodeJS
app, to store a set of messages in database MESSAGES table, the app will have two endpoints:
- '/ping': to check the server health
- '/msg-api/all': to get all the stored messages
To keep things simple... the table will have only one column called TEXT.
First thing first, node app:
// api.js -> endpoints goes here
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 -> 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('\n\t ==== Message API listening on 8080! ====')
})
// Dockerfile -> bundle docker image for our app
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" ]
Now we can build our docker images from the 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
...
And push the built image to 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
Now we can create a pod of our NodeJS app, the below spec file 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
Apply the manifest:
$ kubectl create -f msg-api-pod.yaml
pod/k8s-msg-api created
Make sure that the pod is running by checking the status:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8s-msg-api 1/1 Running 0 22s
k8s-mysql 1/1 Running 0 1h
At this level we need to expose the created pod so that can access it from outside. This time I will do it using only command line rather a spec file:
$ kubectl expose pod k8s-msg-api --port=8080 --name=k8s-srv-msg-api --type=NodePort
service/k8s-srv-msg-api exposed
Getting data from mysql database using nodejs api
At this level, I need to point out some important stuff, in order understand all the pieces, let’s first, summarize what we have done until now, so far, we have created a MySQL pod and we have exposed it through a service to make it accessible for other pods, second, we have created a sample nodejs app, we called it a messaging api, so that we can use it to hit the MySQL pod; similarly, to be able to access the messaging API we need to expose it through a service, I hope everything is clear until here!
Now the question is how can we call our messaging api from outside of our cluster mainly minikube? To do so, we need the IP address of our node, as I'm using minikube which create only one node so, the IP address is resolved, is the minikube ip address itself, just run:
$ minikube ip
192.168.99.100
And what about the port? Well good question! let’s describe our messaging api service to check that out:
$ 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.
So we have Port which is the port of our messaging API service. NodePort is the port on which the exposed service is available (accessible) i.e., the service is available on NodeIP:NodePort
Let’s try that out:
$ curl 192.168.99.100:30887/ping
hello there! I m up and running!%
$ curl 192.168.99.100:30887/msg-api/all
[]%
Very nice, so far we are able to hit our MySQL database, let’s insert some data into our database using 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)
Let’s get this data through our nodejs API using 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
Containerizing the MySQL database and running DBMS on a Kubernetes cluster brings a lot of benefits to DevOps team, such as portability across environments, start/stop and update easier and having better security due to the services are isolated.
Thanks for reading and please leave your suggestion in the below comment section.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK