В последните няколко блога разгледахме как да стартирате Galera Cluster на Docker, независимо дали на самостоятелен Docker или на Docker Swarm с множество хостове с мрежа за наслагване. В тази публикация в блога ще разгледаме стартирането на Galera Cluster на Kubernetes, инструмент за оркестрация за стартиране на контейнери в мащаб. Някои части са различни, като например как приложението трябва да се свърже с клъстера, как Kubernetes обработва отказ и как работи балансирането на натоварването в Kubernetes.
Kubernetes срещу Docker Swarm
Нашата крайна цел е да гарантираме, че Galera Cluster работи надеждно в контейнерна среда. По-рано покрихме Docker Swarm и се оказа, че стартирането на Galera Cluster върху него има редица блокери, които пречат да бъде готов за производство. Нашето пътуване сега продължава с Kubernetes, инструмент за оркестриране на контейнери от производствен клас. Нека да видим кое ниво на „готовност за производство“ може да поддържа, когато изпълнява услуга за състояние, като Galera Cluster.
Преди да продължим по-нататък, нека подчертаем някои от ключовите разлики между Kubernetes (1.6) и Docker Swarm (17.03) при стартиране на Galera Cluster на контейнери:
- Kubernetes поддържа две сонди за проверка на здравето – жизненост и готовност. Това е важно при стартиране на Galera Cluster върху контейнери, тъй като живият контейнер Galera не означава, че е готов за обслужване и трябва да бъде включен в комплекта за балансиране на натоварването (помислете за състояние на свързване/донор). Docker Swarm поддържа само една сонда за проверка на здравето, подобна на жизнеността на Kubernetes, контейнерът е или здрав и продължава да работи, или нездравословен и се пренасрочва. Прочетете тук за подробности.
- Kubernetes има табло за управление на потребителския интерфейс, достъпно чрез „kubectl proxy“.
- Docker Swarm поддържа само кръгово балансиране на натоварването (влизане), докато Kubernetes използва най-малко връзка.
- Docker Swarm поддържа мрежа за маршрутизиране за публикуване на услуга във външната мрежа, докато Kubernetes поддържа нещо подобно, наречено NodePort, както и външни балансатори на натоварване (GCE GLB/AWS ELB) и външни DNS имена (както за v1.7)
Инсталиране на Kubernetes с помощта на Kubeadm
Ще използваме kubeadm за инсталиране на Kubernetes клъстер с 3 възли на CentOS 7. Състои се от 1 главен и 2 възела (миньони). Нашата физическа архитектура изглежда така:
1. Инсталирайте kubelet и Docker на всички възли:
$ ARCH=x86_64
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-${ARCH}
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg
https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
EOF
$ setenforce 0
$ yum install -y docker kubelet kubeadm kubectl kubernetes-cni
$ systemctl enable docker && systemctl start docker
$ systemctl enable kubelet && systemctl start kubelet
2. На главния, инициализирайте главния, копирайте конфигурационния файл, настройте Pod мрежата с помощта на Weave и инсталирайте Kubernetes Dashboard:
$ kubeadm init
$ cp /etc/kubernetes/admin.conf $HOME/
$ export KUBECONFIG=$HOME/admin.conf
$ kubectl apply -f https://git.io/weave-kube-1.6
$ kubectl create -f https://git.io/kube-dashboard
3. След това на останалите останали възли:
$ kubeadm join --token 091d2a.e4862a6224454fd6 192.168.55.140:6443
4. Проверете дали възлите са готови:
$ kubectl get nodes
NAME STATUS AGE VERSION
kube1.local Ready 1h v1.6.3
kube2.local Ready 1h v1.6.3
kube3.local Ready 1h v1.6.3
Вече имаме клъстер Kubernetes за внедряване на клъстер Galera.
Galera Cluster на Kubernetes
В този пример ще разположим MariaDB Galera Cluster 10.1, използвайки Docker изображение, изтеглено от нашето хранилище на DockerHub. Файловете с дефиниции на YAML, използвани в това разгръщане, могат да бъдат намерени в директорията example-kubernetes в хранилището на Github.
Kubernetes поддържа редица контролери за внедряване. За да разгърнете клъстер Galera, можете да използвате:
- Набор реплики
- StatefulSet
Всеки от тях има своите плюсове и минуси. Ще разгледаме всеки един от тях и ще видим каква е разликата.
Предварителни условия
Изображението, което изградихме, изисква etcd (самостоятелен или клъстер) за откриване на услуга. За да стартирате etcd клъстер, изисква всеки etcd екземпляр да се изпълнява с различни команди, така че ще използваме Pods контролер вместо Deployment и ще създадем услуга, наречена „etcd-client“ като крайна точка за etcd Pods. Файлът с дефиниция etcd-cluster.yaml разказва всичко.
За да разположите 3-pod etcd клъстер, просто изпълнете:
$ kubectl create -f etcd-cluster.yaml
Проверете дали etcd клъстерът е готов:
$ kubectl get po,svc
NAME READY STATUS RESTARTS AGE
po/etcd0 1/1 Running 0 1d
po/etcd1 1/1 Running 0 1d
po/etcd2 1/1 Running 0 1d
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/etcd-client 10.104.244.200 <none> 2379/TCP 1d
svc/etcd0 10.100.24.171 <none> 2379/TCP,2380/TCP 1d
svc/etcd1 10.108.207.7 <none> 2379/TCP,2380/TCP 1d
svc/etcd2 10.101.9.115 <none> 2379/TCP,2380/TCP 1d
Нашата архитектура сега изглежда така:
Няколко MySQL в Docker:Как да контейнеризирате вашата база данни Открийте всичко, което трябва да разберете, когато обмисляте да стартирате услуга на MySQL горната част на виртуализацията на контейнера Docker Изтеглете Бялата книгаИзползване на ReplicaSet
ReplicaSet гарантира, че определен брой „реплика“ на модула се изпълняват по всяко време. Въпреки това, внедряването е концепция от по-високо ниво, която управлява ReplicaSets и предоставя декларативни актуализации на pods заедно с много други полезни функции. Ето защо се препоръчва да използвате разгръщания, вместо директно да използвате ReplicaSets, освен ако не се нуждаете от персонализирана оркестрация на актуализации или изобщо не изисквате актуализации. Когато използвате Deployments, не е нужно да се притеснявате за управлението на ReplicaSets, които те създават. Разгръщанията притежават и управляват своите ReplicaSets.
В нашия случай ще използваме Deployment като контролер на натоварването, както е показано в тази дефиниция на YAML. Можем директно да създадем Galera Cluster ReplicaSet и Service, като изпълним следната команда:
$ kubectl create -f mariadb-rs.yml
Проверете дали клъстерът е готов, като погледнете ReplicaSet (rs), pods (po) и услуги (svc):
$ kubectl get rs,po,svc
NAME DESIRED CURRENT READY AGE
rs/galera-251551564 3 3 3 5h
NAME READY STATUS RESTARTS AGE
po/etcd0 1/1 Running 0 1d
po/etcd1 1/1 Running 0 1d
po/etcd2 1/1 Running 0 1d
po/galera-251551564-8c238 1/1 Running 0 5h
po/galera-251551564-swjjl 1/1 Running 1 5h
po/galera-251551564-z4sgx 1/1 Running 1 5h
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc/etcd-client 10.104.244.200 <none> 2379/TCP 1d
svc/etcd0 10.100.24.171 <none> 2379/TCP,2380/TCP 1d
svc/etcd1 10.108.207.7 <none> 2379/TCP,2380/TCP 1d
svc/etcd2 10.101.9.115 <none> 2379/TCP,2380/TCP 1d
svc/galera-rs 10.107.89.109 <nodes> 3306:30000/TCP 5h
svc/kubernetes 10.96.0.1 <none> 443/TCP 1d
От изхода по-горе можем да илюстрираме нашите модули и услуги, както следва:
Изпълнението на Galera Cluster на ReplicaSet е подобно на третирането му като приложение без състояние. Той организира създаването, изтриването и актуализациите на капсули и може да бъде насочен към хоризонтални модули за автоматично мащабиране (HPA), т.е. ReplicaSet може да бъде автоматично мащабиран, ако отговаря на определени прагове или цели (използване на процесора, пакети в секунда, заявка за секунда и др.).
Ако един от възлите на Kubernetes се повреди, новите Pods ще бъдат насрочени на наличен възел, за да отговарят на желаните реплики. Томовете, свързани с Pod, ще бъдат изтрити, ако Pod бъде изтрит или пренасрочен. Името на хост на Pod ще бъде генерирано на случаен принцип, което затруднява проследяването на мястото на контейнера, като просто погледнете името на хоста.
Всичко това работи доста добре в тестови и сценични среди, където можете да изпълнявате пълен жизнен цикъл на контейнера като разгръщане, мащабиране, актуализиране и унищожаване без никакви зависимости. Мащабирането нагоре и надолу е лесно, като актуализирате YAML файла и го публикувате в клъстер Kubernetes или с помощта на командата scale:
$ kubectl scale replicaset galera-rs --replicas=5
Използване на StatefulSet
Известен като PetSet във версия преди 1.6, StatefulSet е най-добрият начин за внедряване на Galera Cluster в производството, защото:
- Изтриването и/или намаляването на StatefulSet няма да изтрие томовете, свързани с StatefulSet. Това се прави, за да се гарантира безопасността на данните, която обикновено е по-ценна от автоматичното изчистване на всички свързани ресурси на StatefulSet.
- За StatefulSet с N реплики, когато Pods се разгръщат, те се създават последователно, в ред от {0 .. N-1 }.
- Когато модулите се изтриват, те се прекратяват в обратен ред, от {N-1 .. 0}.
- Преди операция за мащабиране да бъде приложена към Pod, всичките му предшественици трябва да са Running и Ready.
- Преди да бъде прекратен Pod, всички негови наследници трябва да бъдат напълно изключени.
StatefulSet осигурява първокласна поддръжка за контейнери с поддържане на състоянието. Той осигурява гаранция за разгръщане и мащабиране. Когато се създаде клъстер Galera с три възли, три модула ще бъдат разгърнати в реда db-0, db-1, db-2. db-1 няма да бъде разгърнат, преди db-0 да е „Работащ и готов“, а db-2 няма да бъде разгърнат, докато db-1 не е „Работащ и готов“. Ако db-0 се провали, след като db-1 е „Работащ и готов“, но преди db-2 да бъде стартиран, db-2 няма да бъде стартиран, докато db-0 не се стартира успешно и не стане „Работащ и готов“.
Ще използваме Kubernetes реализация на постоянно хранилище, наречено PersistentVolume и PersistentVolumeClaim. Това, за да се гарантира устойчивост на данните, ако модулът е пренасрочен към другия възел. Въпреки че Galera Cluster предоставя точното копие на данните за всяка реплика, запазването на данните във всяка капсула е добро за отстраняване на неизправности и цели за възстановяване.
За да създадем постоянно хранилище, първо трябва да създадем PersistentVolume за всяка капсула. PV са обемни плъгини като Volumes в Docker, но имат жизнен цикъл, независим от всеки отделен модул, който използва PV. Тъй като ще внедрим клъстер Galera с 3 възела, трябва да създадем 3 PV:
apiVersion: v1
kind: PersistentVolume
metadata:
name: datadir-galera-0
labels:
app: galera-ss
podindex: "0"
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
hostPath:
path: /data/pods/galera-0/datadir
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: datadir-galera-1
labels:
app: galera-ss
podindex: "1"
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
hostPath:
path: /data/pods/galera-1/datadir
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: datadir-galera-2
labels:
app: galera-ss
podindex: "2"
spec:
accessModes:
- ReadWriteOnce
capacity:
storage: 10Gi
hostPath:
path: /data/pods/galera-2/datadir
Горната дефиниция показва, че ще създадем 3 PV, съпоставени с физическия път на възлите на Kubernetes с 10 GB пространство за съхранение. Ние дефинирахме ReadWriteOnce, което означава, че томът може да бъде монтиран като четене-запис само от един възел. Запазете горните редове в mariadb-pv.yml и го публикувайте в Kubernetes:
$ kubectl create -f mariadb-pv.yml
persistentvolume "datadir-galera-0" created
persistentvolume "datadir-galera-1" created
persistentvolume "datadir-galera-2" created
След това дефинирайте ресурсите PersistentVolumeClaim:
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mysql-datadir-galera-ss-0
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
selector:
matchLabels:
app: galera-ss
podindex: "0"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mysql-datadir-galera-ss-1
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
selector:
matchLabels:
app: galera-ss
podindex: "1"
---
kind: PersistentVolumeClaim
apiVersion: v1
metadata:
name: mysql-datadir-galera-ss-2
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
selector:
matchLabels:
app: galera-ss
podindex: "2"
Горната дефиниция показва, че бихме искали да поискаме PV ресурсите и да използваме spec.selector.matchLabels за да потърсите нашата PV (metadata.labels.app:galera-ss ) въз основа на съответния индекс на капсула (metadata.labels.podindex ) присвоен от Kubernetes. име на метаданни ресурсът трябва да използва формата „{volumeMounts.name}-{pod}-{ordinal index}“, дефиниран под spec.templates.containers така че Kubernetes знае коя точка на монтиране да преобразува искането в модула.
Запазете горните редове в mariadb-pvc.yml и го публикувайте в Kubernetes:
$ kubectl create -f mariadb-pvc.yml
persistentvolumeclaim "mysql-datadir-galera-ss-0" created
persistentvolumeclaim "mysql-datadir-galera-ss-1" created
persistentvolumeclaim "mysql-datadir-galera-ss-2" created
Нашето постоянно хранилище вече е готово. След това можем да започнем внедряването на Galera Cluster, като създадем ресурс StatefulSet заедно с ресурс за услуга без глава, както е показано в mariadb-ss.yml:
$ kubectl create -f mariadb-ss.yml
service "galera-ss" created
statefulset "galera-ss" created
Сега извлечете обобщението на нашето внедряване на StatefulSet:
$ kubectl get statefulsets,po,pv,pvc -o wide
NAME DESIRED CURRENT AGE
statefulsets/galera-ss 3 3 1d galera severalnines/mariadb:10.1 app=galera-ss
NAME READY STATUS RESTARTS AGE IP NODE
po/etcd0 1/1 Running 0 7d 10.36.0.1 kube3.local
po/etcd1 1/1 Running 0 7d 10.44.0.2 kube2.local
po/etcd2 1/1 Running 0 7d 10.36.0.2 kube3.local
po/galera-ss-0 1/1 Running 0 1d 10.44.0.4 kube2.local
po/galera-ss-1 1/1 Running 1 1d 10.36.0.5 kube3.local
po/galera-ss-2 1/1 Running 0 1d 10.44.0.5 kube2.local
NAME CAPACITY ACCESSMODES RECLAIMPOLICY STATUS CLAIM STORAGECLASS REASON AGE
pv/datadir-galera-0 10Gi RWO Retain Bound default/mysql-datadir-galera-ss-0 4d
pv/datadir-galera-1 10Gi RWO Retain Bound default/mysql-datadir-galera-ss-1 4d
pv/datadir-galera-2 10Gi RWO Retain Bound default/mysql-datadir-galera-ss-2 4d
NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE
pvc/mysql-datadir-galera-ss-0 Bound datadir-galera-0 10Gi RWO 4d
pvc/mysql-datadir-galera-ss-1 Bound datadir-galera-1 10Gi RWO 4d
pvc/mysql-datadir-galera-ss-2 Bound datadir-galera-2 10Gi RWO 4d
В този момент нашият Galera Cluster, работещ на StatefulSet, може да бъде илюстриран както на следната диаграма:
Изпълнението на StatefulSet гарантира последователни идентификатори като име на хост, IP адрес, мрежов идентификатор, клъстерен домейн, Pod DNS и съхранение. Това позволява на шушулката лесно да се отличава от другите в група шушулки. Томът ще бъде запазен на хоста и няма да бъде изтрит, ако Pod бъде изтрит или пренасрочен на друг възел. Това позволява възстановяване на данни и намалява риска от пълна загуба на данни.
От отрицателна страна, времето за внедряване ще бъде N-1 пъти (N =реплики) по-дълго, защото Kubernetes ще се подчинява на порядковата последователност при разполагане, разсрочване или изтриване на ресурсите. Би било малко главоболие да подготвите PV и претенциите, преди да помислите за мащабиране на вашия клъстер. Обърнете внимание, че актуализирането на съществуващ StatefulSet понастоящем е ръчен процес, при който можете само актуализирате spec.replicas в момента.
Свързване с Galera Cluster Service и Pods
Има няколко начина, по които можете да се свържете с клъстера на базата данни. Можете да се свържете директно към порта. В примера за услуга „galera-rs“, ние използваме NodePort, излагайки услугата на IP на всеки възел на статичен порт (NodePort). Автоматично се създава ClusterIP услуга, към която услугата NodePort ще маршрутизира. Ще можете да се свържете с услугата NodePort извън клъстера, като поискате {NodeIP}:{NodePort} .
Пример за външно свързване към клъстера Galera:
(external)$ mysql -udb_user -ppassword -h192.168.55.141 -P30000
(external)$ mysql -udb_user -ppassword -h192.168.55.142 -P30000
(external)$ mysql -udb_user -ppassword -h192.168.55.143 -P30000
В мрежовото пространство на Kubernetes Pods могат да се свързват чрез клъстер IP или име на услуга вътрешно, което може да бъде извлечено чрез следната команда:
$ kubectl get services -o wide
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
etcd-client 10.104.244.200 <none> 2379/TCP 1d app=etcd
etcd0 10.100.24.171 <none> 2379/TCP,2380/TCP 1d etcd_node=etcd0
etcd1 10.108.207.7 <none> 2379/TCP,2380/TCP 1d etcd_node=etcd1
etcd2 10.101.9.115 <none> 2379/TCP,2380/TCP 1d etcd_node=etcd2
galera-rs 10.107.89.109 <nodes> 3306:30000/TCP 4h app=galera-rs
galera-ss None <none> 3306/TCP 3m app=galera-ss
kubernetes 10.96.0.1 <none> 443/TCP 1d <none>
От списъка с услуги можем да видим, че Galera Cluster ReplicaSet Cluster-IP е 10.107.89.109. Вътрешно друг модул може да осъществява достъп до базата данни чрез този IP адрес или име на услугата, използвайки открития порт 3306:
(etcd0 pod)$ mysql -udb_user -ppassword -hgalera-rs -P3306 -e 'select @@hostname'
+------------------------+
| @@hostname |
+------------------------+
| galera-251551564-z4sgx |
+------------------------+
Можете също така да се свържете с външния NodePort от вътрешността на всеки модул на порт 30000:
(etcd0 pod)$ mysql -udb_user -ppassword -h192.168.55.143 -P30000 -e 'select @@hostname'
+------------------------+
| @@hostname |
+------------------------+
| galera-251551564-z4sgx |
+------------------------+
Връзката към задните модули ще бъде съответно балансирана на натоварване въз основа на алгоритъм за най-малка връзка.
Резюме
В този момент стартирането на Galera Cluster на Kubernetes в производство изглежда много по-обещаващо в сравнение с Docker Swarm. Както беше обсъдено в последната публикация в блога, повдигнатите опасения се решават по различен начин с начина, по който Kubernetes организира контейнерите в StatefulSet (въпреки че все още е бета функция във v1.6). Надяваме се, че предложеният подход ще помогне за стартирането на Galera Cluster на контейнери в мащабно производство.