本教程展示了在 Kubernetes 上部署 MySQL 的詳細步驟。 我將在這裡使用 minikube 來演示 Kubernetes MySQL 示例。
我們都知道數據持久性的重要性,而且幾乎我們所有的應用程序都非常依賴某種數據庫管理系統 (DBMS)。 在 Kubernetes 上設置 DBMS 有助於 DevOps 團隊和數據庫管理員輕鬆利用和擴展數據庫。
準備環境
按照本教程,您需要在 Ubuntu Linux 上安裝 Minikube。
您可以通過以下命令驗證 Minikube 是否已成功啟動並運行:
$ minikube status
輸出:
minikube
type: Control Plane
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
為 MySQL 創建 Secret
Kubernetes 使用 Secret
存儲和管理敏感信息,例如密碼、ssh 密鑰和 OAuth 令牌。 在本教程中,我們使用 base64
編碼以存儲“MYSQL_ROOT_PASSWORD”。 為了 example:
$ 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
驗證 Secret
剛剛創建成功:
$ 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
在 Kubernetes 集群上部署 MySQL pod 的文件:
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
驗證 pod 是否正在運行:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8s-mysql 1/1 Running 0 30s
現在,我們可以連接到 k8s-mysql
在下面:
$ 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
將 pod 暴露給其他 pod 或外部系統。 我們將使用以下清單文件 mysql-service.yaml
使 k8s-mysql
pod 可達:
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
為了能夠從另一個 pod 連接到 mysql,我們需要有 IP
我們的 pod 的地址可以使用:
$ 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('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 -> 啟動 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 -> 為我們的應用程序捆綁 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 鏡像:
$ 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
...
並將構建的鏡像推送到 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
現在我們可以創建我們的 NodeJS 應用程序的 pod,下面的規範文件 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
應用清單:
$ kubectl create -f msg-api-pod.yaml
pod/k8s-msg-api created
通過檢查狀態確保 pod 正在運行:
$ kubectl get pod
NAME READY STATUS RESTARTS AGE
k8s-msg-api 1/1 Running 0 22s
k8s-mysql 1/1 Running 0 1h
在這個級別,我們需要公開創建的 pod,以便可以從外部訪問它。 這次我將只使用命令行而不是規範文件:
$ kubectl expose pod k8s-msg-api --port=8080 --name=k8s-srv-msg-api --type=NodePort
service/k8s-srv-msg-api exposed
使用nodejs api從mysql數據庫中獲取數據
在這個層面,我需要指出一些重要的東西,為了理解所有的部分,讓我們首先總結一下我們到目前為止所做的事情,到目前為止,我們已經創建了一個 MySQL pod,我們已經通過一個服務暴露了它其他 pod 可以訪問它,其次,我們創建了一個示例 nodejs 應用程序,我們將其稱為消息傳遞 api,以便我們可以使用它來訪問 MySQL pod; 同樣,為了能夠訪問消息傳遞 API,我們需要通過服務公開它,我希望一切都清楚,直到這裡!
現在的問題是我們如何從集群外部主要調用 minikube 的消息傳遞 API? 為此,我們需要我們節點的 IP 地址,因為我使用的 minikube 只創建一個節點,所以 IP 地址被解析,是 minikube ip 地址本身,只需運行:
$ 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.
所以我們有 Port,它是我們的消息 API 服務的端口。 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)
讓我們使用 nodejs API 獲取這些數據 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 集群上運行 DBMS 為 DevOps 團隊帶來了很多好處,例如跨環境的可移植性、更容易啟動/停止和更新以及由於服務被隔離而具有更好的安全性。
感謝您的閱讀,請在下面的評論部分留下您的建議。