导读:
kubernetes集群搭建其实仅仅是第一步,不是为了部署而部署,当然是需要实战使用啦。在实际的生产活动中和测试活动中,kubernetes的使用是不相同的,本文试图从一个贴近实际的生产活动来讲述如何优雅的,正确的使用kubernetes。
部署前的相关知识简介:
由于是单实例的MySQL,因此,本文暂时不使用StatefulSet.。
StatefulSet的简介
在本文中,我们将在Kubernetes中部署数据库,因此我们必须了解什么是StatefulSet。是用于管理有状态应用程序的工作负载。它管理一组Pod的实现和扩展,并保证这些Pod的顺序和唯一性。
像Deployment一样,StatefulSet也管理具有相同容器规范的一组Pod。由StatefulSets维护的Pod具有唯一的,持久的身份和稳定的主机名,而不用管它们位于哪个节点上。如果我们想要一个跨存储的持久性,我们可以创建一个持久性卷并将StatefulSet用作解决方案的一部分。即使StatefulSet中的Pod容易发生故障,存储卷与新Pod进行匹配也很容易。
StatefulSet对于需要以下一项或多项功能的应用程序很有价值:
-
稳定的唯一网络标识符。
-
稳定,持久的存储。
-
有序,顺畅的部署和扩展。
-
有序的自动滚动更新。
在Kubernetes上部署数据库时,我们需要使用StatefulSet,但是使用StatefulSet有一些局限性:
-
需要使用持久性存储卷为Pod提供存储。
-
删除副本或按比例缩小副本将不会删除附加到StatefulSet的存储卷。存储卷确保数据的安全性。
-
StatefulSet当前需要Headless Service 来负责Pod的网络标识。
-
与Deployment 不同,StatefulSet不保证删除StatefulSet资源时删除所有Pod,而Deployment在被删除时会删除与Deployment关联的所有Pod。在删除StateFul之前,你必须将pod副本数量缩小到0 。
实践正文:
一,
部署一个使用本地存储卷的单机MySQL
MySQL版本是5.7.23----通常用在测试环境,生产是不允许这样做的哦
这个部署示例将使用最为简单的本地存储,MySQL的相关配置文件和相关账号密码等敏感信息将使用明文存储在资源清单文件内,部署方式为deployment。
(1)StorageClass---存储类
这个可以不使用,因为我们下面将使用的是静态存储:
[root@master mysql]# cat mysql-sc.yaml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner # no-provisioner代表不会自动创建PV,创建PV步骤不可省略
volumeBindingMode: immutable #立刻绑定
(2)pv---存储卷定义
在node2节点,新建目录,mkdir -p /mnt/mysql-data,因为在此文件内做了亲和度调度,指定是在node2存储数据卷。
- volumeMode: Filesystem 这个其实是默认的,可以不写,但写了也没错。
- persistentVolumeReclaimPolicy: Delete 数据卷回收策略,这里是删除,也就是pvc删除了,数据卷跟随删除,是默认策略,但不建议使用这个,建议使用Retain。
- persistentVolumeReclaimPolicy: Recycle:不保留数据。经测试pvc删除后,在nfs服务端的数据也会随机删除。只有hostPath和NFS支持这种方式
- Retain:不清理保留数据。即删除pvc或者pv后,在插件上的数据(nfs服务端)不会被删除。这种方式是最常用的,可以避免误删pvc或者pv而造成数据的丢失。
- 本例使用了节点亲和度,这里比较容易出错的地方是,如果有亲和度策略,该pv无法更新,只能删除重新生成。说人话就是,比如,上面的删除策略想要调整,OK,必须删除pv,不能patch它,然后重新执行清单文件才可以,否则报错报到你怀疑人生。
cat mysql-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
spec:
capacity:
storage: 15Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
# storageClassName: local-storage
local: # 指定它是一个 Local Persistent Volume
path: /mnt/mysql-data # PV对应的本地磁盘路径
nodeAffinity: # 亲和性标志
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- k8s-node1 # 必须部署在node1上
(3)pvc---存储卷需求清单
没什么好说的了,只需要记住这个name: mysql-pv-claim,一会部署的时候引用这个名字即可。
[root@master mysql]# cat mysql-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mysql-pv-claim
spec:
# storageClassName: local-storage
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
(4)deployment方式部署
数据库密码是password
strategy:
type: Recreate是一种部署方式,停止旧版本部署新版本,说人话就是,旧的去了,新的就来。当然,本例无所谓了。
strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 -->一次可以添加多少个实例 maxUnavailable: 0 -->能够容忍多少个实例无法使用,0表示不容忍
rollingupdae是另一种部署方式--见名思意,滚动更新通过逐个替换实例来逐步部署新版本的应用,直到所有实例都被替换完成为止。它通常遵循以下过程:在负载均衡器后面使用版本 A 的实例池,然后部署版本 B 的一个实例,当服务准备好接收流量时(Readiness Probe 正常),将该实例添加到实例池中,然后从实例池中删除一个版本 A 的实例并关闭。
通常生产环境是使用滚动更新或者蓝绿更新策略。
- image: mysql:5.7.23
imagePullPolicy: IfNotPresent镜像拉取策略是本地有就用本地的,如果没有该镜像就去网络上拉取,通常生产环境也是用这个的,毕竟很多项目是在内网中的,获取镜像是有问题的。
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim这个name写了两次,保证一致就可以,但可以随便定义, claimName: mysql-pv-claim这个是引用的上面的pvc的名字,不能乱写。
[root@master mysql]# cat deploy-mysql.yaml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: mysql
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- image: mysql:5.7.23
imagePullPolicy: IfNotPresent
name: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql-pv-claim
(5)编写服务清单
这个service的作用只是一会测试的时候使用,端口暴露3306,集群内部使用。
[root@master mysql]# cat mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
应用这些文件(这个顺序是建立顺序,注意,StorageClass文件不需要执行,因为,我已经将关于它的地方注释掉了):
k apply -f mysql-pv.yaml -f mysql-pvc.yaml -f deploy-mysql.yaml
单元测试:
客户端连接MySQL,可以看到连接成功,密码测试成功:
[root@master mysql]# kubectl run -it --rm --image=mysql:5.7.23 --restart=Never mysql-client -- mysql -h mysql -ppassword
If you don't see a command prompt, try pressing enter.
mysql> status;
--------------
mysql Ver 14.14 Distrib 5.7.23, for Linux (x86_64) using EditLine wrapper
Connection id: 4
Current database:
Current user: root@10.244.1.25
SSL: Cipher in use is DHE-RSA-AES256-SHA
Current pager: stdout
Using outfile: ''
Using delimiter: ;
Server version: 5.7.23 MySQL Community Server (GPL)
Protocol version: 10
Connection: mysql via TCP/IP
Server characterset: latin1
Db characterset: latin1
Client characterset: latin1
Conn. characterset: latin1
TCP port: 3306
Uptime: 10 min 19 sec
Threads: 1 Questions: 12 Slow queries: 0 Opens: 105 Flush tables: 1 Open tables: 98 Queries per second avg: 0.019
--------------
在node2节点,查看一下此数据库的数据目录是否存在:
[root@slave2 ~]# ll /mnt/mysql-data/
total 188484
-rw-r----- 1 polkitd ssh_keys 56 Sep 7 13:44 auto.cnf
-rw------- 1 polkitd ssh_keys 1679 Sep 7 13:45 ca-key.pem
-rw-r--r-- 1 polkitd ssh_keys 1107 Sep 7 13:45 ca.pem
-rw-r--r-- 1 polkitd ssh_keys 1107 Sep 7 13:45 client-cert.pem
-rw------- 1 polkitd ssh_keys 1679 Sep 7 13:45 client-key.pem
-rw-r----- 1 polkitd ssh_keys 407 Sep 7 15:28 ib_buffer_pool
-rw-r----- 1 polkitd ssh_keys 79691776 Sep 7 15:28 ibdata1
-rw-r----- 1 polkitd ssh_keys 50331648 Sep 7 15:28 ib_logfile0
-rw-r----- 1 polkitd ssh_keys 50331648 Sep 7 13:44 ib_logfile1
-rw-r----- 1 polkitd ssh_keys 12582912 Sep 7 15:28 ibtmp1
drwxr-x--- 2 polkitd ssh_keys 4096 Sep 7 13:44 mysql
drwxr-x--- 2 polkitd ssh_keys 8192 Sep 7 13:44 performance_schema
-rw------- 1 polkitd ssh_keys 1679 Sep 7 13:45 private_key.pem
-rw-r--r-- 1 polkitd ssh_keys 451 Sep 7 13:45 public_key.pem
-rw-r--r-- 1 polkitd ssh_keys 1107 Sep 7 13:45 server-cert.pem
-rw------- 1 polkitd ssh_keys 1679 Sep 7 13:45 server-key.pem
drwxr-x--- 2 polkitd ssh_keys 8192 Sep 7 13:44 sys
看一哈pv和pvc是否正常(其实也是扯淡了,已经看到数据库的数据了,当然没问题啦);
[root@master mysql]# k get pv,pvc,svc
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
persistentvolume/mysql-pv 15Gi RWO Delete Bound default/mysql-pv-claim 14m
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
persistentvolumeclaim/mysql-pv-claim Bound mysql-pv 15Gi RWO 14m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 11d
service/mysql ClusterIP None <none> 3306/TCP 10m
小结:
OK,现在一个最为简单的MySQL就部署好了, 那么,部署的这么一个数据库有什么问题吗?当然有啦,第一,数据库的密码以明文的形式存在pv建立的文件里,毫无秘密可言,第二,此数据库使用的是默认3306端口,也是毫无安全性,所有的配置都是默认,此数据库也可以说是一个没有任何定制的数据库,比如,慢查询,binlog也没有开启等等个性化的设置都没有全部默认,第三,现在仅仅是一个数据库服务,如果有N个服务,比如redis单或者集群实例,nginx实例等等其他服务,那么,持久化存储的管理就会给我们带来一个巨大的挑战(这个例子使用的是静态存储,需要一个动态存储来解放自己啦)。
因此,可以说,这样创建的数据库是完全不能使用在生产环境的,只能是作为一个测试或者学习的环境,是一个快速出环境的手段啦。
二,部署一个生产可用的MySQL
生产环境下的可用的MySQL所要求的东西更多一点,比如,安全性,可定制化性,可扩展性,这些虽然简简单单的就这么几句话,但,实际做起来你就会发现不是那么好搞的哦,下面我就详细讲讲如何实现一个可用于生产环境的MySQL单机版部署:
首先,需要一个动态存储类,也就是StorageClass,还需要一个secret,以保存敏感信息,主要是MySQL的密码,其次,需要一个configmap,此文件保存MySQL的配置文件。
(1)
nfs的StorageClass类创建
(2)
存放密码的secret文件:
secret文件内的密码是加密了,type: Opaque表示模糊模式,没什么太多意义,需要和pvc一个namespace。
root用户名使用base64加密,密码也是同样的加密方式,这里,我的密码就不演示加密了:
[root@master wordpress]# echo -n root |base64
cm9vdA==
[root@master mysql1]# cat secret.yaml
kind: Secret
apiVersion: v1
metadata:
name: user-and-password
namespace: database
type: Opaque
data:
username: cm9vdA==
password: c2hpZ3VhbmczMg==
(3)
config文件
MySQL的主要配置文件,根据此配置文件,可定制MySQL:
这里的MySQL配置文件部分就可以实现端口号更改等等各种各样的需求,我写的这个是比较全的所有配置,但,基本都是默认的东西。
cm需要和secret以及pvc是一个namespace。
[root@master mysql1]# cat mysql-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
namespace: database
data:
mysqld.cnf: |-
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
log-error = /var/log/mysql/error.log #haha
symbolic-links=0
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
lock_wait_timeout = 3600
open_files_limit = 65535
back_log = 1024
max_connections = 512
max_connect_errors = 1000000
table_open_cache = 1024
table_definition_cache = 1024
thread_stack = 512K
sort_buffer_size = 4M
join_buffer_size = 4M
read_buffer_size = 8M
read_rnd_buffer_size = 4M
bulk_insert_buffer_size = 64M
thread_cache_size = 768
interactive_timeout = 600
wait_timeout = 600
tmp_table_size = 32M
max_heap_table_size = 32M
(3)
存储清单文件---pvc
[root@master mysql1]# cat mysql-pvc.yaml
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mysql
namespace: database
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1.5Gi
storageClassName: managed-nfs-storage
(4)
服务文件
端口暴露的作用,为了后面的单元测试环节,和cm,secret,等一样的namespace。
[root@master mysql1]# cat mysql-svc.yaml
apiVersion: v1
kind: Service
metadata:
name: mysql
namespace: database
spec:
ports:
- port: 3306
selector:
app: mysql
clusterIP: None
(5)
部署文件
该文件仍然是指定了namespace为database,并且挂载了cm和secret
[root@master mysql1]# cat deploy-mysql.yaml
apiVersion: apps/v1 # for versions before 1.9.0 use apps/v1beta2
kind: Deployment
metadata:
name: mysql
namespace: database
spec:
selector:
matchLabels:
app: mysql
strategy:
type: Recreate
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7.23
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: user-and-password
key: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: mysql-persistent-storage
mountPath: /var/lib/mysql
- name: config-volume1
mountPath: "/etc/mysql/mysql.conf.d/"
volumes:
- name: mysql-persistent-storage
persistentVolumeClaim:
claimName: mysql
- name: config-volume1
configMap:
name: mysql-config
(6)
部署命令
[root@master mysql1]# k apply -f mysql-pvc.yaml -f mysql-svc.yaml -f secret.yaml -f mysql-cm.yaml -f deploy-mysql.yaml
persistentvolumeclaim/mysql configured
service/mysql unchanged
secret/user-and-password unchanged
configmap/mysql-config unchanged
deployment.apps/mysql unchanged
(7)
单元测试
密码是设定在secret内的,现在密码校验通过,证明secret文件生效了。
[root@master mysql1]# kubectl run -it --rm --image=mysql:5.7.23 --restart=Never -n database mysql-client -- mysql -h mysql -psecret
If you don't see a command prompt, try pressing enter.
mysql> select version();
+-----------+
| version() |
+-----------+
| 5.7.23 |
+-----------+
1 row in set (0.00 sec)
mysql> status;
--------------
mysql Ver 14.14 Distrib 5.7.23, for Linux (x86_64) using EditLine wrapper
configmap的测试:
配置文件的查看(先进入容器,然后查看文件),和前面定义的配置文件匹配了,证明cm没有问题。
[root@master mysql1]# k exec -it mysql-bd8c75548-pk552 -n database -- /bin/bash
root@mysql-bd8c75548-pk552:/# cat /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
pid-file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock
datadir = /var/lib/mysql
log-error = /var/log/mysql/error.log #haha
symbolic-links=0
sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
lock_wait_timeout = 3600
open_files_limit = 65535
back_log = 1024
max_connections = 512
max_connect_errors = 1000000
table_open_cache = 1024
table_definition_cache = 1024
thread_stack = 512K
sort_buffer_size = 4M
join_buffer_size = 4M
read_buffer_size = 8M
read_rnd_buffer_size = 4M
bulk_insert_buffer_size = 64M
thread_cache_size = 768
interactive_timeout = 600
wait_timeout = 600
tmp_table_size = 32M
max_heap_table_size = 32Mroot
至此,一个自己可控的,具有一定安全性的单实例MySQL就部署完了。