By mysql-operator deployment of highly available mysql statefulset in kubernetes cluster.
Preparing the Environment
As used herein, the open source project operator mysql-operator with death only to support more than mysql 8.0.11 version, changed under the code, support for 5.7.0 or later, the project addresses , this deployment is mysql-5.7.26, the use of dockerhub mirror mysql / mysql-server: 5.7.26.
Code compilation
git clone download the program proceeds to code directory, execution sh hack/build.sh
, the compiled code to obtain binary mysql-agent and mysql-operator, into the binary bin/linux_amd64
, performing docker build -f docker/mysql-agent/Dockerfile -t $IMAGE_NAME_AGENT .
, docker build -f docker/mysql-operator/Dockerfile -t $IMAGE_NAME_OPERATOR .
construct the mirrors, mysql-operator operator generated image of a mirror image, MySQL- generating agent is a mirror image, when creating mysql service, and as sidecar mysql-server in the same container from the pod.
Deployment operator
First according to the document deployment Deployment mysql-operator of the document is to use the helm installation, do not want to install helm and tiller, you can only install a helm client, enter the code directory, then execute helm template --name mysql-operator mysql-operator
yaml file generation deployment need, and then directly execution kubectl apply -f mysql-operator.yaml
create operator. The CRD yaml created the desired type of operator, the operator of the required Deployment and operator RBAC permissions.
# change directory into mysql-operator
cd mysql-operator
# generate mysql-operator.yaml
helm template --name mysql-operator mysql-operator > mysql-operator.yaml
# deploy on kubernetes
kubectl apply -f mysql-operator.yaml
# deployed.
[root@localhost]$ kubectl get deploy -n mysql-operator
NAME READY UP-TO-DATE AVAILABLE AGE
mysql-operator 1/1 1 1 2d5h
Creating a cluster mysql
Herein cluster created for mysql 3 node, a node for the master, two for the slave, master node may read and write, slave node is read only, for use kubernetes Local PV persistent store.
First, create a node for each PV, Local PV needs to be defined nodeAffinity
, node constraints created.
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv0
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: mysql-storage
local:
path: /data/mysql-data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- 192.168.0.1
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv1
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: mysql-storage
local:
path: /data/mysql-data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- 192.168.0.2
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: mypv2
spec:
capacity:
storage: 1Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Delete
storageClassName: mysql-storage
local:
path: /data/mysql-data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- 192.168.0.3
# create pv
kubectl create -f pv.yaml
# get presistence volume
[root@localhost]$ kubectl get pv
mypv-0 1Gi RWO Delete Available mysql-storage 4s
mypv-1 1Gi RWO Delete Available mysql-storage 4s
mypv-2 1Gi RWO Delete Available mysql-storage 4s
Next, we need to create in the namespace mysql to create corresponding RBAC permissions for mysql to create.
apiVersion: v1
kind: ServiceAccount
metadata:
name: mysql-agent
namespace: mysql2
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: mysql-agent
namespace: mysql2
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: mysql-agent
subjects:
- kind: ServiceAccount
name: mysql-agent
namespace: mysql2
If you need to customize the mysql password, you need to create a secret, password is required base64 encryption. the next linux echo -n 'password' | base64
as password encryption.
apiVersion: v1
data:
password: cm9vdA==
kind: Secret
metadata:
labels:
v1alpha1.mysql.oracle.com/cluster: mysql
name: mysql-pv-root-password
namespace: mysql2
kubectl apply -f rbac.yaml
kubectl apply -f secret.yaml
In the creation of the operator, we have created the following crd type of deployment is mysqlclusters types of resources needed to create a mysql cluster.
[root@localhost]$ kubectl get crd | grep mysql
mysqlbackups.mysql.oracle.com 2019-05-14T02:51:11Z
mysqlbackupschedules.mysql.oracle.com 2019-05-14T02:51:11Z
mysqlclusters.mysql.oracle.com 2019-05-14T02:51:11Z
mysqlrestores.mysql.oracle.com 2019-05-14T02:51:11Z
The next operator to start creating a custom resource types (CRD) instances mysqlclusters.
apiVersion: mysql.oracle.com/v1alpha1
kind: Cluster
metadata:
name: mysql
namespace: mysql2
spec:
# 和mysql-server镜像版本的tag一直
version: 5.7.26
repository: 20.26.28.56/dcos/mysql-server
# 节点数量
members: 3
# 指定 mysql 密码,和之前创建的secret名称一致
rootPasswordSecret:
name: mysql-pv-root-password
resources:
agent:
limits:
cpu: 500m
memory: 200Mi
requests:
cpu: 300m
memory: 100Mi
server:
limits:
cpu: 1000m
memory: 1000Mi
requests:
cpu: 500m
memory: 500Mi
volumeClaimTemplate:
metadata:
name: mysql-pv
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: "mysql-storage"
resources:
requests:
storage: 1Gi
kubectl apply -f mysql.yaml
After you do, you will see kubernetes in this namespace start pulling up statefulset mysql and will create a headless service.
[root@localhost]$ kubectl get all -n mysql2
NAME READY STATUS RESTARTS AGE
pod/mysql-0 2/2 Running 0 8h
pod/mysql-1 2/2 Running 0 8h
pod/mysql-2 2/2 Running 0 8h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mysql ClusterIP None <none> 3306/TCP 21h
NAME READY AGE
statefulset.apps/mysql 1/1 21h
Run the hack/cluster-status.sh
script, the cluster will get the following information:
{
"clusterName": "Cluster",
"defaultReplicaSet": {
"name": "default",
"primary": "mysql-0.mysql:3306",
"ssl": "DISABLED",
"status": "OK_NO_TOLERANCE",
"statusText": "Cluster is NOT tolerant to any failures. 2 members are not active",
"topology": {
"mysql-0.mysql:3306": {
"address": "mysql-0.mysql:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"mysql-1.mysql:3306": {
"address": "mysql-1.mysql:3306",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"mysql-2.mysql:3306": {
"address": "mysql-2.mysql:3306",
"mode": "n/a",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
},
"topologyMode": "Single-Primary"
},
"groupInformationSourceMember": "mysql-0.mysql:3306"
}
By DNS address mysql-0.mysql.mysql2.svc.cluster.local:3306
can connect to the database to read and write. At this time, a multi-node cluster mysql have been deployed, but, outside of the cluster service can not access the database.
External access via haproxy-ingress
First, headless service only through the cluster DNS service access to external access, you also need to create an additional Service. In order to allow external access to the mysql-0 service, we create a ClusterIP type of service for mysql-0.
kind: Service
apiVersion: v1
metadata:
name: mysql-0
namespace: mysql2
spec:
selector:
# 通过 selector 将 pod 约束到 mysql-0
statefulset.kubernetes.io/pod-name: mysql-0
ports:
- protocol: TCP
port: 3306
targetPort: 3306
Next, we need to create an ingress-controller, paper choices are HAProxy-Ingress .
Since the mysql service over the TCP protocol communication, kubernetes ingress default port supports only http and https, haproxy-ingress provides port by means of configmap, configuring TCP service, you need to create a configmap, configmap the data in, key to HAProxy listening , value for the service to be forwarded services and ports.
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-tcp
namespace: mysql2
data:
"3306": "mysql2/mysql-0:3306"
kubectl apply -f mysql-0.yaml
kubectl apply -f tcp-svc.yaml
Next, create ingress-controller,
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
labels:
run: haproxy-ingress
name: haproxy-ingress-192.168.0.1-30080
namespace: mysql2
spec:
replicas: 1
selector:
matchLabels:
run: haproxy-ingress
strategy:
type: RollingUpdate
template:
metadata:
labels:
run: haproxy-ingress
spec:
tolerations:
- key: app
operator: Equal
value: haproxy
effect: NoSchedule
serviceAccount: ingress-controller
nodeSelector:
kubernetes.io/hostname: 192.168.0.1
containers:
- args:
- --tcp-services-configmap=$(POD_NAMESPACE)/mysql-tcp
- --default-backend-service=$(POD_NAMESPACE)/mysql
- --default-ssl-certificate=$(POD_NAMESPACE)/tls-secret
- --ingress-class=ha-mysql
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
image: jcmoraisjr/haproxy-ingress
name: haproxy-ingress
ports:
# 和 configmap 中定义的端口对应
- containerPort: 3306
hostPort: 3306
name: http
protocol: TCP
- containerPort: 443
name: https
protocol: TCP
- containerPort: 1936
hostPort: 30081
name: stat
protocol: TCP
apiVersion: v1
kind: ServiceAccount
metadata:
name: ingress-controller
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ingress-controller
rules:
- apiGroups:
- ""
resources:
- configmaps
- pods
- secrets
- namespaces
verbs:
- get
- apiGroups:
- ""
resources:
- configmaps
verbs:
- get
- update
- apiGroups:
- ""
resources:
- configmaps
verbs:
- create
- apiGroups:
- ""
resources:
- endpoints
verbs:
- get
- create
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ingress-controller
subjects:
- kind: ServiceAccount
name: ingress-controller
- apiGroup: rbac.authorization.k8s.io
kind: User
name: ingress-controller
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: ingress-controller
rules:
- apiGroups:
- ""
resources:
- configmaps
- endpoints
- nodes
- pods
- secrets
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- apiGroups:
- ""
resources:
- services
verbs:
- get
- list
- watch
- apiGroups:
- "extensions"
resources:
- ingresses
verbs:
- get
- list
- watch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- patch
- apiGroups:
- "extensions"
resources:
- ingresses/status
verbs:
- update
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: ingress-controller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: ingress-controller
subjects:
- kind: ServiceAccount
name: ingress-controller
namespace: mysql2
- apiGroup: rbac.authorization.k8s.io
kind: User
name: ingress-controller
kubectl apply -f ingress-controller.yaml
kubectl apply -f ingress-rbac.yaml -n mysql2
Finally, create ingress rules:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
annotations:
ingress.kubernetes.io/ssl-redirect: "false"
kubernetes.io/ingress.class: ha-mysql
name: ha-mysql
spec:
rules:
- http:
paths:
- backend:
serviceName: mysql-0
servicePort: 3306
path: /
+ Can now be mapped port access to mysql cluster by haproxy of IP.
annex
The following is yaml file used above: