How To Backup MySql Database To S3 On Kubernetes Using CronJon
ဒီနေ့မှာတော့ kubernetes ပေါ်မှာ statefulset နဲ့တင်ထားတဲ့ mysql db ကို cronjob ကိုသုံးပြီး aws s3 bucket ထဲကို backup လုပ်တာကို ရှင်းပြသွားမှာဖြစ်ပါတယ်။ Kubernetes ဆိုတာ containerized applications တွေအတွက် automate လုပ်ဖို့သုံးတာဖြစ်တယ်။ S3 ဆိုတာ က amazon က ထုတ်တဲ့ storage solution တစ်ခုဖြစ်တယ်။
cloud services တွေ popular မဖြစ်ခင်ကဆို ကျွန်တော်တို့ရဲ့ company တွေမှာ mysql ၊ mongodb ၊ postgresql အစရှိတဲ့ database တွေကို နေ့စဥ် crontab တွေ run ပြီး backup လုပ်ထားကြပါတယ်။ Backup ထားမှသာ တစ်ခုခု lose ဖြစ်သွားရင် restore ပြန်လုပ်ဖို့လွယ်ကူပါမယ်။ ဒီနေ့မှာဆိုရင်လည်း ကျွန်တော်က kubernetes ပေါ်မှာ mysql database ကို cronjob နဲ့ aws s3 ပေါ်မှာ backup လုပ်တာကိုရှင်းပြသွားပါမယ်။
Prequities
Basic Kubernetes Knowledge
AWS Account
MySql Basic
Deploy Mysql Statefulset On Kubernetes
ပထမဆုံးအနေနဲ့ mysql ကို statefulset တစ်ခုအနေနဲ့ kubernetes ပေါ်မှာ run ပေးရပါမယ်။ statefulset create မလုပ်ခင် statefulset အတွက်သုံးမယ့် volume ကိုစဥ်းစားရပါတော့မယ်။ volume provisioner တွေထဲမှာ on-prem အတွက်ဆို glusterfs ၊ nfs ၊ heketi ၊ openebs ၊ rook တို့ဟာလူသုံးများကြပါတယ်။ cloud kubernets services တွေဖြစ်တဲ့ AKS EKS GKE တို့အတွက်ဆို EBS ၊ AzureDisk ၊ GCE computeDisk တို့ကိုသုံးကြပါတယ်။
ဒီနေ့ မှာတော့ ကျွန်တော်က volume အကြောင်းကို ရေးမှာမဟုတ်တော့ အလွယ်တကူ သုံးနိုင်တဲ့ hostPath ကိုပဲ သုံးလိုက်ပါတယ်။ deployment အားလုံးကို default namespace မှာပဲ စမ်းမှာပါ။ အခု pv (persistent volume) တစ်ခုကို create လုပ်လိုက်ပါမယ်။ pv name ကိုတော့ mysql-pv လို့ပေးလိုက်ပါမယ်။ hostPath ဖြစ်လို့ class က manual ဖြစ်ပါတယ်။ RWO access mode ပေးထားပြီး 5GB ပေးသုံးထားပါတယ်။ PV နဲ့ PVC အကြောင်းကို volume အကြောင်းရေးတဲ့အခါ သေချာရေးပါဦီးမယ်။ ဒီ lab မှာ master တစ်ခုရယ် worker နှစ်ခုရယ် သုံးမှာဖြစ်ပါတယ်။
apiVersion: v1
kind: PersistentVolume
metadata:
name: mysql-pv
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/mnt/data"
kubectl apply နဲ့ pv ကိုအောက်ကအတိုင်း create လိုက်ပါ။ ပြီးရင် kubectl get pv နဲ့ ကြည့်လိုက်ပါ။
kubectl apply -f pv.yaml
kubectl get pv
pv တစ်ခု ရပြီဆိုရင် mysql statefulset ကို စပြီး create နိုင်ပါပြီ။ statefulset အကြောင်းကိုလည်း အခုအနည်းငယ်ပဲရေးလိုက်ပါတယ်။ statefulset တစ်ခုအတွက် headless service တစ်ခုဆောက်ပေးရပါတယ်။ ကျွန်တော်ကတော့ clusterIP အနေနဲ့တန်းပြီး create လုပ်လိုက်ပါတယ်။ sts ရဲ့ pod replica တစ်ခုချင်းစီဟာကိုယ်ပိုင် pvc တစ်ခုလိုပါတယ်။ ဒါကြောင့် volumeTemplate ကိုတစ်ခါထဲထည့်ဆောက်ပေးရပါတယ်။ အောက်က yaml file ကနေ mysql sts တစ်ခုကိုဆောက်မှာပါ။ ဒါဆို mysql sts ကို create လိုက်ကြရအောင်။ mysql pod ရဲ့ pvc ဟာ ခုဏက pv နဲ့ သွား bound ပါလိမ့်မယ်။
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
name: mysql
targetPort: 3306
selector:
app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: "mysql"
replicas: 1
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql
env:
# Use secret in real usage
- name: MYSQL_ROOT_PASSWORD
value: password
ports:
- containerPort: 3306
name: mysql
volumeMounts:
- name: pvc-hostpath
mountPath: /var/lib/mysql
volumeClaimTemplates:
- metadata:
name: mysql-pv
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
kubectl create နဲ့ sts တစ်ခုဆောက်လိုက်ပါမယ်။
kubectl create -f mysql.yaml
create ပြီးသွားရင် kubectl get နဲ့ ကြည့်လိုက်ရင် mysql sts run နေတာကို အောက်ကလို တွေ့ရမှာဖြစ်ပါတယ်။
oot@master:~# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/mysql-0 1/1 Running 0 2m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mysql ClusterIP 10.99.59.201 <pending> 3306/TCP 2m
NAME READY AGE
statefulset.apps/mysql 1/1 2m
mysql service ကို external access ပေးချင်ရင် service ကို NodePort (သို့) LoadBalancer အဖြစ် expose ပေးဖို့လိုပါတယ်။ kubectl edit svc နဲ့ NodePort အဖြစ် expose လိုက်ပါမယ်။ ClusterIP နေရာမှာ NodePort နဲ့အစားထိုးလိုက်ပါ။
kubectl edit svc mysql
ပြီးသွားရင် kubectl get svc နဲ့ services တွေကို ပြန်စစ်လိုက်ပါ။ mysql service ဟာ nodePort အနေနဲ့ ရလာပါပြီ။
oot@master:~# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mysql NodePort 10.99.59.201 <pending> 3306:32237/TCP 54m
ဒီ lab မှာ ကျွန်တော့် master node ip ဟာ 10.0.0.4 ဖြစ်ပြီး worker node က 10.0.0.5 ဖြစ်ပါတယ်။ NodePort ဆိုတော့ mysql service ဟာ node တွေရဲ့ port 32237 မှာ access ရနေမှာဖြစ်ပါတယ်။ 10.0.0.4 master node မှာပဲ access လုပ်ကြည့်ရအောင်။
root@master:~# mysql -h 10.0.0.4 -u root --port 32237 -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 119
Server version: 8.0.25 MySQL Community Server - GPL
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
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> ALTER USER 'root' IDENTIFIED WITH mysql_native_password BY 'Th@ungHt!ke00';
root password က statefulset yaml ထဲမှာထည့်ထားပါတယ်။ root password ပြန်ချိန်းပေးလိုက်ပါ။ sts yaml ထဲမှာ တစ်ခါထဲပြောင်းခဲ့လဲရပါတယ်။ ဒါဆိုရင်တော့ mysql အပိုင်းကပြီးပါပြီ။ backup လုပ်ဖို့အတွက် container တစ်ခု create ရပါမယ်။ ပြီးရင်တော့ s3 ထဲမှာ bucket တစ်ခုဆောက်ရပါမယ်။’mysql-cron-test’ ဆိုပြီး ကျွန်တော်ကတော့ bucket တစ်ခု ဆောက်ထားပါတယ်။ bucket ထဲမှာ mysql dump တွေကိုသိမ်းဖို့ folder တစ်ခု create ခဲ့ပါ။ အခု backup လုပ်ပေးမယ့် container တစ်ခုကို create ပါမယ်။
apiVersion: v1
kind: Secret
metadata:
name: my-database-backup
type: Opaque
data:
database_password: VGhAdW5nSHQha2UwMAo=
slack_webhook_url: cGFzc3dvcmQK
---
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: my-database-backup
spec:
schedule: "0 22 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: my-database-backup
image: ghcr.io/benjamin-maynard/kubernetes-cloud-mysql-backup:v2.5.0
imagePullPolicy: Always
env:
- name: AWS_ACCESS_KEY_ID
value: "<your_aws_access_key>"
- name: AWS_SECRET_ACCESS_KEY
value: "<your_aws_secret_key>"
- name: AWS_DEFAULT_REGION
value: "us-east-1"
- name: AWS_BUCKET_NAME
value: "mysql-cron-test"
- name: AWS_BUCKET_BACKUP_PATH
value: "/dump"
- name: TARGET_DATABASE_HOST
value: "10.0.0.4"
- name: TARGET_DATABASE_PORT
value: "32237"
- name: TARGET_DATABASE_NAMES
value: "mysql"
- name: TARGET_DATABASE_USER
value: "root"
- name: TARGET_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: my-database-backup
key: database_password
- name: BACKUP_TIMESTAMP
value: "_%Y_%m_%d"
- name: SLACK_ENABLED
value: "false"
- name: SLACK_CHANNEL
value: "#chatops"
- name: SLACK_WEBHOOK_URL
valueFrom:
secretKeyRef:
name: my-database-backup
key: slack_webhook_url
restartPolicy: Never
yaml file ထဲမှာ လိုအပ်တာတစ်ခုချင်းထည့်ပေးရပါမယ်။ database_password က Th@ungHtikeOo ကို base64 နဲ့ encode လုပ်ထားတာဖြစ်ပါတယ်။ aws access key နဲ့ secret key ကိုလည်းမိမိတို့ရဲ့ key တွေကိုထည့်ပေးပါ။ bucket region က bucket create ခဲ့တဲ့ region ၊ db host က k8s node ip တစ်ခုထည့်ရမယ်၊ db port က node port ဖြစ်တဲ့ 32237 ၊ target db name က backup လုပ်ချင်တဲ့ db name ၊ slack ကို disabled ခဲ့တယ်။
production ဆို slack channel နဲ့ connect လုပ်ပေးလိုက်ပါ။ cron ရဲ့ value အရ နေ့တိုင်း ည (၁၀) နာရီမှာ database ကို backup မှာဖြစ်ပါတယ်။ အားလုံးသတ်မှတ်ပြီးသွားရင် job ကို create လုပ်လိုက်ပါတော့မယ်။
kubectl create -f mysql_cronjob.yaml
ပြီးသွားရင်တော့ ည (၁၀) နာရီရောက်တဲ့အခါကျွန်တော်တို့ရဲ့ db ကို စပြီး backup မယ့် job တစ်ခု run နေတာကိုတွေ့ရပါလိမ့်မယ်။ backup ပြီးတဲ့အခါ job က completed ဖြစ်ပြီး terminated သွားပါမယ်။ နောက်ရက် ည (၁၀)နာရီ ရောက်တဲ့အခါ နောက် job တစ်ခုကို run နေပါလိမ့်မယ်။
root@master:~# kubectl get all
NAME READY STATUS RESTARTS AGE
pod/my-database-backup-1625918040-678nz 0/1 Completed 0 30s
pod/mysql-0 1/1 Running 0 54m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mysql LoadBalancer 10.99.59.201 <pending> 3306:32237/TCP 54m
NAME READY AGE
statefulset.apps/mysql 1/1 54m
NAME COMPLETIONS DURATION AGE
job.batch/my-database-backup-1625918040 1/1 11s 30s
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
cronjob.batch/my-database-backup * * * * * False 0 32s 38s
aws s3 ထဲမှာလည်း အောက်ကပုံအတိုင်း sql dump တစ်ခုကို တွေ့ရပါလိမ့်မယ်။

ဒါဆိုရင်တော့ ဒီနေ့lab က ဒီလောက်ပါပဲ။ ဒီ lab လေးကို လုပ်ကြည့်ကြဖို့လည်း တိုက်တွန်းပါတယ်။ အားလုံးကိုကျေးဇူးတင်ပါတယ်။
Thanks for reading …