Dalam beberapa blog terakhir, kami membahas cara menjalankan Galera Cluster di Docker, baik di Docker mandiri atau di multi-host Docker Swarm dengan jaringan overlay. Dalam posting blog ini, kita akan melihat cara menjalankan Galera Cluster di Kubernetes, alat orkestrasi untuk menjalankan container dalam skala besar. Beberapa bagian berbeda, seperti bagaimana aplikasi harus terhubung ke cluster, bagaimana Kubernetes menangani failover dan bagaimana load balancing bekerja di Kubernetes.
Kubernetes vs Docker Swarm
Target utama kami adalah memastikan Galera Cluster berjalan dengan andal di lingkungan container. Kami sebelumnya membahas Docker Swarm, dan ternyata menjalankan Galera Cluster di dalamnya memiliki sejumlah pemblokir, yang mencegahnya untuk siap produksi. Perjalanan kami sekarang berlanjut dengan Kubernetes, alat orkestrasi container tingkat produksi. Mari kita lihat tingkat "kesiapan produksi" mana yang dapat didukungnya saat menjalankan layanan stateful seperti Galera Cluster.
Sebelum kita melangkah lebih jauh, mari kita soroti beberapa perbedaan utama antara Kubernetes (1.6) dan Docker Swarm (17.03) saat menjalankan Galera Cluster pada container:
- Kubernetes mendukung dua pemeriksaan kesehatan - keaktifan dan kesiapan. Hal ini penting ketika menjalankan Galera Cluster pada container, karena container Galera yang hidup tidak berarti siap untuk disajikan dan harus disertakan dalam set load balancing (pikirkan status joiner/donor). Docker Swarm hanya mendukung satu pemeriksaan pemeriksaan kesehatan yang mirip dengan keaktifan Kubernetes, sebuah wadah sehat dan terus berjalan atau tidak sehat dan dijadwalkan ulang. Baca di sini untuk detailnya.
- Kubernetes memiliki dasbor UI yang dapat diakses melalui “kubectl proxy”.
- Docker Swarm hanya mendukung load balancing round-robin (ingress), sedangkan Kubernetes menggunakan koneksi paling sedikit.
- Docker Swarm mendukung mesh perutean untuk memublikasikan layanan ke jaringan eksternal, sementara Kubernetes mendukung sesuatu yang serupa yang disebut NodePort, serta penyeimbang beban eksternal (GCE GLB/AWS ELB) dan nama DNS eksternal (seperti untuk v1.7)
Menginstal Kubernetes Menggunakan Kubeadm
Kita akan menggunakan kubeadm untuk menginstal sebuah cluster Kubernetes 3-node pada CentOS 7. Ini terdiri dari 1 master dan 2 node (minion). Arsitektur fisik kami terlihat seperti ini:
1. Instal kubelet dan Docker di semua node:
$ 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. Pada master, inisialisasi master, salin file konfigurasi, atur jaringan Pod menggunakan Weave dan instal Dasbor Kubernetes:
$ 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. Kemudian pada node lain yang tersisa:
$ kubeadm join --token 091d2a.e4862a6224454fd6 192.168.55.140:6443
4. Pastikan node sudah siap:
$ 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
Kami sekarang memiliki cluster Kubernetes untuk penerapan Galera Cluster.
Cluster Galera di Kubernetes
Dalam contoh ini, kita akan men-deploy MariaDB Galera Cluster 10.1 menggunakan image Docker yang diambil dari repositori DockerHub kita. File definisi YAML yang digunakan dalam penerapan ini dapat ditemukan di direktori example-kubernetes di repositori Github.
Kubernetes mendukung sejumlah pengontrol penerapan. Untuk menyebarkan Cluster Galera, seseorang dapat menggunakan:
- Set Replika
- StatefulSet
Masing-masing dari mereka memiliki pro dan kontra sendiri. Kita akan melihat masing-masing dari mereka dan melihat apa perbedaannya.
Prasyarat
Gambar yang kami buat memerlukan etcd (standalone atau cluster) untuk penemuan layanan. Untuk menjalankan cluster etcd, setiap instance etcd harus dijalankan dengan perintah yang berbeda, jadi kita akan menggunakan pengontrol Pods alih-alih Deployment dan membuat layanan yang disebut "etcd-client" sebagai titik akhir untuk Pod etcd. File definisi etcd-cluster.yaml menjelaskan semuanya.
Untuk men-deploy cluster 3-pod etcd, cukup jalankan:
$ kubectl create -f etcd-cluster.yaml
Verifikasi apakah cluster etcd sudah siap:
$ 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
Arsitektur kami sekarang terlihat seperti ini:
Beberapa MySQL di Docker:Cara Menampung Basis Data AndaTemukan semua yang perlu Anda pahami saat mempertimbangkan untuk menjalankan layanan MySQL di atas virtualisasi wadah DockerUnduh White PaperMenggunakan ReplicaSet
Sebuah ReplicaSet memastikan bahwa sejumlah pod "replika" tertentu berjalan pada waktu tertentu. Namun, Deployment adalah konsep tingkat tinggi yang mengelola ReplicaSet dan menyediakan pembaruan deklaratif untuk pod bersama dengan banyak fitur berguna lainnya. Oleh karena itu, disarankan untuk menggunakan Deployment daripada langsung menggunakan ReplicaSets, kecuali jika Anda memerlukan orkestrasi pembaruan khusus atau tidak memerlukan pembaruan sama sekali. Saat Anda menggunakan Deployments, Anda tidak perlu khawatir tentang mengelola ReplicaSet yang mereka buat. Deployments memiliki dan mengelola ReplicaSet mereka.
Dalam kasus kami, kami akan menggunakan Deployment sebagai pengontrol beban kerja, seperti yang ditunjukkan dalam definisi YAML ini. Kita bisa langsung membuat ReplicaSet dan Service Galera Cluster dengan menjalankan perintah berikut:
$ kubectl create -f mariadb-rs.yml
Verifikasi apakah cluster sudah siap dengan melihat ReplicaSet (rs), pod (po) dan services (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
Dari output di atas, kita dapat menggambarkan Pod dan Service kita sebagai berikut:
Menjalankan Galera Cluster di ReplicaSet mirip dengan memperlakukannya sebagai aplikasi stateless. Ini mengatur pembuatan, penghapusan, dan pembaruan pod dan dapat ditargetkan untuk Horizontal Pod Autoscales (HPA), yaitu ReplicaSet dapat diskalakan secara otomatis jika memenuhi ambang atau target tertentu (penggunaan CPU, paket per detik, permintaan per detik dll).
Jika salah satu node Kubernetes down, Pod baru akan dijadwalkan pada node yang tersedia untuk memenuhi replika yang diinginkan. Volume yang terkait dengan Pod akan dihapus, jika Pod dihapus atau dijadwalkan ulang. Nama host Pod akan dibuat secara acak, sehingga lebih sulit untuk melacak di mana wadah itu berada hanya dengan melihat nama hostnya.
Semua ini bekerja dengan cukup baik di lingkungan pengujian dan staging, di mana Anda dapat melakukan siklus hidup container penuh seperti penerapan, penskalaan, pembaruan, dan penghancuran tanpa ketergantungan apa pun. Penskalaan naik dan turun sangat mudah, dengan memperbarui file YAML dan mempostingnya ke cluster Kubernetes atau dengan menggunakan perintah skala:
$ kubectl scale replicaset galera-rs --replicas=5
Menggunakan StatefulSet
Dikenal sebagai PetSet pada versi pra 1.6, StatefulSet adalah cara terbaik untuk menyebarkan Galera Cluster dalam produksi, karena:
- Menghapus dan/atau memperkecil StatefulSet tidak akan menghapus volume yang terkait dengan StatefulSet. Hal ini dilakukan untuk memastikan keamanan data, yang umumnya lebih berharga daripada pembersihan otomatis semua resource StatefulSet terkait.
- Untuk StatefulSet dengan N replika, ketika Pod di-deploy, Pod akan dibuat secara berurutan, dari {0 .. N-1 }.
- Saat Pod dihapus, Pod akan dihentikan dalam urutan terbalik, dari {N-1 .. 0}.
- Sebelum operasi penskalaan diterapkan ke Pod, semua pendahulunya harus Berjalan dan Siap.
- Sebelum sebuah Pod dihentikan, semua penerusnya harus dimatikan sepenuhnya.
StatefulSet menyediakan dukungan kelas satu untuk container stateful. Ini memberikan jaminan penyebaran dan penskalaan. Ketika Cluster Galera dengan tiga node dibuat, tiga Pod akan di-deploy dengan urutan db-0, db-1, db-2. db-1 tidak akan di-deploy sebelum db-0 "Berjalan dan Siap", dan db-2 tidak akan di-deploy hingga db-1 "Berjalan dan Siap". Jika db-0 gagal, setelah db-1 "Berjalan dan Siap", tetapi sebelum db-2 diluncurkan, db-2 tidak akan diluncurkan hingga db-0 berhasil diluncurkan kembali dan menjadi "Berjalan dan Siap".
Kita akan menggunakan implementasi Kubernetes dari penyimpanan persisten yang disebut PersistentVolume dan PersistentVolumeClaim. Ini untuk memastikan persistensi data jika pod dijadwal ulang ke node lain. Meskipun Galera Cluster menyediakan salinan data yang tepat pada setiap replika, memiliki data yang persisten di setiap pod bagus untuk tujuan pemecahan masalah dan pemulihan.
Untuk membuat penyimpanan persisten, pertama-tama kita harus membuat PersistentVolume untuk setiap pod. PV adalah plugin volume seperti Volume di Docker, tetapi memiliki siklus hidup yang independen dari setiap pod individu yang menggunakan PV. Karena kita akan men-deploy Cluster Galera 3-node, kita perlu membuat 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
Definisi di atas menunjukkan bahwa kita akan membuat 3 PV, yang dipetakan ke jalur fisik node Kubernetes dengan ruang penyimpanan 10GB. Kami mendefinisikan ReadWriteOnce, yang berarti volume dapat dipasang sebagai baca-tulis hanya dengan satu node. Simpan baris di atas ke mariadb-pv.yml dan kirimkan ke Kubernetes:
$ kubectl create -f mariadb-pv.yml
persistentvolume "datadir-galera-0" created
persistentvolume "datadir-galera-1" created
persistentvolume "datadir-galera-2" created
Selanjutnya, tentukan sumber daya 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"
Definisi di atas menunjukkan bahwa kami ingin mengklaim sumber daya PV dan menggunakan spec.selector.matchLabels untuk mencari PV kami (metadata.labels.app:galera-ss ) berdasarkan indeks pod masing-masing (metadata.labels.podindex ) ditugaskan oleh Kubernetes. metadata.name resource harus menggunakan format “{volumeMounts.name}-{pod}-{ordinal index}” yang ditentukan di bawah spec.templates.containers jadi Kubernetes tahu titik mount mana yang akan memetakan klaim ke dalam pod.
Simpan baris di atas ke mariadb-pvc.yml dan kirimkan ke 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
Penyimpanan persisten kami sekarang sudah siap. Kemudian kita dapat memulai penerapan Galera Cluster dengan membuat sumber daya StatefulSet bersama dengan sumber daya layanan Headless seperti yang ditunjukkan di mariadb-ss.yml:
$ kubectl create -f mariadb-ss.yml
service "galera-ss" created
statefulset "galera-ss" created
Sekarang, ambil ringkasan penerapan StatefulSet kami:
$ 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
Pada titik ini, Galera Cluster kami yang berjalan di StatefulSet dapat diilustrasikan seperti pada diagram berikut:
Berjalan di StatefulSet menjamin pengidentifikasi yang konsisten seperti nama host, alamat IP, ID jaringan, domain cluster, DNS Pod, dan penyimpanan. Hal ini memungkinkan Pod untuk dengan mudah membedakan dirinya dari yang lain dalam kelompok Pod. Volume akan disimpan di host dan tidak akan dihapus jika Pod dihapus atau dijadwal ulang ke node lain. Hal ini memungkinkan pemulihan data dan mengurangi risiko kehilangan data total.
Sisi negatifnya, waktu penerapan adalah N-1 kali (N =replika) lebih lama karena Kubernetes akan mematuhi urutan ordinal saat menerapkan, menjadwal ulang, atau menghapus sumber daya. Akan sedikit merepotkan untuk menyiapkan PV dan klaim sebelum berpikir untuk menskalakan klaster Anda. Perhatikan bahwa memperbarui StatefulSet yang ada saat ini merupakan proses manual, di mana Anda hanya dapat memperbarui spec.replicas saat ini.
Menghubungkan ke Layanan dan Pod Galera Cluster
Ada beberapa cara Anda dapat terhubung ke cluster database. Anda dapat terhubung langsung ke port. Dalam contoh layanan "galera-rs", kami menggunakan NodePort, mengekspos layanan pada setiap IP Node di port statis (NodePort). Layanan ClusterIP, yang akan dirutekan oleh layanan NodePort, dibuat secara otomatis. Anda dapat menghubungi layanan NodePort, dari luar cluster, dengan meminta {NodeIP}:{NodePort} .
Contoh untuk terhubung ke Galera Cluster secara eksternal:
(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
Dalam ruang jaringan Kubernetes, Pod dapat terhubung melalui IP cluster atau nama layanan secara internal yang dapat diambil dengan menggunakan perintah berikut:
$ 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>
Dari daftar layanan, kita dapat melihat bahwa Galera Cluster ReplicaSet Cluster-IP adalah 10.107.89.109. Secara internal, pod lain dapat mengakses database melalui alamat IP atau nama layanan ini menggunakan port yang terbuka, 3306:
(etcd0 pod)$ mysql -udb_user -ppassword -hgalera-rs -P3306 -e 'select @@hostname'
+------------------------+
| @@hostname |
+------------------------+
| galera-251551564-z4sgx |
+------------------------+
Anda juga dapat terhubung ke NodePort eksternal dari dalam pod mana pun di port 30000:
(etcd0 pod)$ mysql -udb_user -ppassword -h192.168.55.143 -P30000 -e 'select @@hostname'
+------------------------+
| @@hostname |
+------------------------+
| galera-251551564-z4sgx |
+------------------------+
Koneksi ke Pod backend akan seimbang bebannya berdasarkan algoritma koneksi terkecil.
Ringkasan
Pada titik ini, menjalankan Galera Cluster di Kubernetes dalam produksi tampaknya jauh lebih menjanjikan dibandingkan dengan Docker Swarm. Seperti yang dibahas dalam posting blog terakhir, masalah yang diangkat ditangani secara berbeda dengan cara Kubernetes mengatur container di StatefulSet, (walaupun masih fitur beta di v1.6). Kami berharap pendekatan yang disarankan akan membantu menjalankan Galera Cluster pada container dalam skala besar dalam produksi.