How to Setup Mysql Service with Kubernetes

In this article we are going to follow our series of article about kubernetes (k8s) ecosystem. In the current article we are going to look at the persistent part with k8s mainly, how to setup and get mysql up and running in your k8s cluster. As like in my previews kubernetes secrets article, I’ll be using minikube to demonstrate all my steps.

What we will learn

  • Creating pods
  • Exposing pods via services
  • Connecting to mysql image based pod
  • Wiring pods together

This article is organized as follows

  • Introduction
  • Creating mysql pod and exposing as service
  • Creating a nodejs api to hit mysql, pod and service
  • Getting data from mysql database using nodejs api
  • Conclusion

Note: I’ll be using the below alias in my terminal and kubernetes-mysql will be my working folder:

alias ctl='kubectl'

alias ccre='ctl create -f '

alias cdels='ctl delete service '

alias cdelp='ctl delete pod '

alias clog='ctl logs '

alias cdess='ctl describe service '

alias cdesp='ctl describe pod '

alias cgets='ctl get service'

alias cgetp='ctl get pods'

alias cgete='ctl get endpoints'


We all know the big importance of data persistence and almost, all our applications rely hugely on some sort of database management systems (aka. DBMS). Therefore, setup a such DBMS in K8s is very important for a devops team, as I have mentioned in current article we are going to look close how to get mysql up and running in k8s from container, pod to service, and importantly I will go even further to illustrate how I can use my database mainly consume it through a simple API running in a separate pod. So, let’s get started!

I should mention that the current tutorial requires that you should have a basic understanding of the k8s' concepts mainly pods and services.

Creating mysql pod

First, I’m going to create a mysql based pod from the official latest mysql image, please note that I’m passing mysql_root_password as environment variable.

➜  kubernetes-mysql cat mysql-pod.yaml

apiVersion: v1

kind: Pod


name: "k8s-mysql"


name: "lbl-k8s-mysql"




image: mysql:latest

name:  "mysql"




value: "test"



containerPort: 3306

name:  "mysql"



name: "k8s-mysql-storage"

mountPath: "/var/lib/mysql"


- name: "k8s-mysql-storage"

emptyDir: {}

➜  kubernetes-mysql ccre -f mysql-pod.yaml

pod "k8s-mysql" created

Check if our pod is running:

➜  kubernetes-mysql cgetp


k8s-mysql   1/1       Running   0          5s

Check our pod’s logs:

➜  kubernetes-mysql clog k8s-mysql

Initializing database

2016-12-10T11:23:36.414301Z 0 [Warning] TIMESTAMP with implicit DEFAULT value is deprecated. Please...

MySQL init process done. Ready for start up.


2016-12-10T11:23:45.245972Z 0 [Note] mysqld: ready for connections.

Version: '5.7.16'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server (GPL)

Now we can connect to our pod to connect to our mysql server run exit twice to quit the mysql console and the bash:

➜  msg-api ctl exec -it k8s-mysql bash

root@k8s-mysql:/# echo $MYSQL_ROOT_PASSWORD


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 2

Server version: 5.7.16 MySQL Community Server (GPL)


Copyright (c) 2000, 2016, 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



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)




K8s comes with the service concept to expose pods to other pods or external systems, so that our pods will be reachable, more precise, with a service spec we can not only expose a pod but even define a set of policies/rules to access our pods. So, let’s create a service for our k8s-mysql pod created above:

➜  kubernetes-mysql ccre mysql-service.yaml

service "k8s-srv-mysql" created

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:

➜  msg-api ctl get -o template pod k8s-mysql --template={{.status.podIP}}

Alright, now I’m going to create a sample node js 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: '',
user: 'root',
password: 'test',
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('
+ '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,

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!");

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/

CMD [ "node", "server.js" ]

Now we can build our docker images from the Dockerfile

➜  msg-api docker build -t linoxide/msg-api:v0.0.3 . --no-cache=true

Sending build context to Docker daemon 9.728 kB

Step 1 : FROM node:latest


And push the built image to docker hub

➜  msg-api docker push linoxide/msg-api:v0.0.3

The push refers to a repository []

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 cat msg-api-pod.yaml

apiVersion: v1

kind: Pod


name: "k8s-msg-api"


name: "lbl-msg-api"




image: linoxide/msg-api:v0.0.1

name:  "msg-api"



containerPort: 8080

name:  "msg-api"

➜  msg-api ccre msg-api-pod.yaml

pod "k8s-msg-api" created

Make sure that the pod is running by checking the status:

➜  kubernetes-mysql cgetp


k8s-msg-api   1/1       Running   0          22s

k8s-mysql     1/1       Running   0          5h

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:

➜  msg-api 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 staff, 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:

➜  msg-api minikube ip

And what about the port? Well good question! let’s describe our messaging api service to check that out:

➜  msg-api cdess k8s-srv-msg-api

Name:           k8s-srv-msg-api

Namespace:      default

Labels:         name=lbl-msg-api

Selector:       name=lbl-msg-api

Type:           NodePort


Port:           <unset> 8080/TCP

NodePort:       <unset> 30887/TCP


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:

➜  msg-api curl

hello there! I m up and running!%

➜  msg-api curl


Very nice, so far we are able to hit our mysql database, let’s insert some data into our database using terminal.

➜  kubernetes-mysql kubectl exec -it k8s-mysql 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:

➜  msg-api curl

[{"id":1,"text":"this is the first msg!"},{"id":2,"text":"this is the second msg!"}]%


In this article we have seen the main step of working with a storage system, mainly mysql which still very used in our nowadays, we even built a small nodejs api to illustrate how a such mysql database can be consumed inside by another pod as well externally by hitting it from outside through the nodejs api. I guess the main steps shown in this article can be applied to build even a bigger system.

Mohamed Ez Ez 3:00 am


Your email address will not be published. Required fields are marked *


  1. thanks for the tutorial, but I think you should post code using a better code formatter. Its hard to read. maybe try using default alias when writing tutorials it will lessen the amount of confusion

  2. Your use of aliases makes it very difficult to learn from your tutorial. Listing them isn't helpful, and the names appear heavily optimized for speed, not readability. Consider just replacing your shortened commands with the full equivalent.