Trong một vài blog trước, chúng tôi đã đề cập đến cách chạy Cụm Galera trên Docker, cho dù trên Docker độc lập hay trên Docker Swarm nhiều máy chủ với mạng lớp phủ. Trong bài đăng blog này, chúng ta sẽ xem xét việc chạy Galera Cluster trên Kubernetes, một công cụ điều phối để chạy các vùng chứa trên quy mô lớn. Một số phần khác nhau, chẳng hạn như cách ứng dụng sẽ kết nối với cụm, cách Kubernetes xử lý chuyển đổi dự phòng và cách cân bằng tải hoạt động trong Kubernetes.
Kubernetes vs Docker Swarm
Mục tiêu cuối cùng của chúng tôi là đảm bảo Galera Cluster chạy đáng tin cậy trong môi trường vùng chứa. Trước đây chúng tôi đã đề cập đến Docker Swarm và hóa ra là chạy Galera Cluster trên đó có một số trình chặn, ngăn không cho nó sẵn sàng sản xuất. Hành trình của chúng tôi hiện tiếp tục với Kubernetes, một công cụ điều phối vùng chứa cấp sản xuất. Hãy xem nó có thể hỗ trợ mức độ “sẵn sàng sản xuất” nào khi chạy một dịch vụ trạng thái như Galera Cluster.
Trước khi tiến xa hơn, chúng ta hãy làm nổi bật một số điểm khác biệt chính giữa Kubernetes (1.6) và Docker Swarm (17.03) khi chạy Galera Cluster trên các vùng chứa:
- Kubernetes hỗ trợ hai đầu dò kiểm tra sức khỏe - tình trạng sống và sự sẵn sàng. Điều này rất quan trọng khi chạy một Cụm Galera trên các vùng chứa, vì một vùng chứa Galera đang hoạt động không có nghĩa là nó đã sẵn sàng để phân phát và phải được đưa vào bộ cân bằng tải (hãy nghĩ đến trạng thái kết hợp / nhà tài trợ). Docker Swarm chỉ hỗ trợ một đầu dò kiểm tra sức khỏe tương tự như tính năng sống của Kubernetes, một thùng chứa khỏe mạnh và tiếp tục chạy hoặc không khỏe mạnh và được lên lịch lại. Đọc tại đây để biết chi tiết.
- Kubernetes có trang tổng quan về giao diện người dùng có thể truy cập được qua “proxy kubectl”.
- Docker Swarm chỉ hỗ trợ cân bằng tải vòng tròn (xâm nhập), trong khi Kubernetes sử dụng ít kết nối nhất.
- Docker Swarm hỗ trợ lưới định tuyến để xuất bản dịch vụ ra mạng bên ngoài, trong khi Kubernetes hỗ trợ một thứ tương tự được gọi là NodePort, cũng như bộ cân bằng tải bên ngoài (GCE GLB / AWS ELB) và tên DNS bên ngoài (như đối với v1.7)
Cài đặt Kubernetes bằng Kubeadm
Chúng tôi sẽ sử dụng kubeadm để cài đặt một cụm Kubernetes 3 nút trên CentOS 7. Nó bao gồm 1 nút chính và 2 nút (tay sai). Kiến trúc vật lý của chúng tôi trông như thế này:
1. Cài đặt kubelet và Docker trên tất cả các nút:
$ 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. Trên trang cái, khởi tạo cái chính, sao chép tệp cấu hình, thiết lập mạng Pod bằng Weave và cài đặt Bảng điều khiển 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. Sau đó trên các nút còn lại khác:
$ kubeadm join --token 091d2a.e4862a6224454fd6 192.168.55.140:6443
4. Xác minh các nút đã sẵn sàng:
$ 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
Hiện chúng tôi có một cụm Kubernetes để triển khai Galera Cluster.
Cụm Galera trên Kubernetes
Trong ví dụ này, chúng tôi sẽ triển khai MariaDB Galera Cluster 10.1 bằng cách sử dụng hình ảnh Docker được lấy từ kho lưu trữ DockerHub của chúng tôi. Các tệp định nghĩa YAML được sử dụng trong triển khai này có thể được tìm thấy trong thư mục example-kubernetes trong kho lưu trữ Github.
Kubernetes hỗ trợ một số bộ điều khiển triển khai. Để triển khai Cụm Galera, người ta có thể sử dụng:
- ReplicaSet
- StatefulSet
Mỗi người trong số họ có ưu và khuyết điểm riêng. Chúng ta sẽ xem xét từng thứ và xem đâu là điểm khác biệt.
Điều kiện tiên quyết
Hình ảnh mà chúng tôi đã tạo yêu cầu một etcd (độc lập hoặc cụm) để khám phá dịch vụ. Để chạy một cụm etcd yêu cầu mỗi phiên bản etcd phải chạy với các lệnh khác nhau, vì vậy chúng tôi sẽ sử dụng bộ điều khiển Pods thay vì Triển khai và tạo một dịch vụ có tên “etcd-client” làm điểm cuối cho etcd Pods. Tệp định nghĩa etcd-cluster.yaml nói lên tất cả.
Để triển khai cụm etcd 3-pod, chỉ cần chạy:
$ kubectl create -f etcd-cluster.yaml
Xác minh xem cụm etcd đã sẵn sàng chưa:
$ 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
Kiến trúc của chúng tôi bây giờ trông giống như sau:
Somenines MySQL trên Docker:Cách chứa cơ sở dữ liệu của bạn Khám phá tất cả những gì bạn cần hiểu khi xem xét chạy dịch vụ MySQL trên đầu ảo hóa vùng chứa DockerTải xuống Sách trắngSử dụng ReplicaSet
Một ReplicaSet đảm bảo rằng một số lượng "bản sao" nhóm được chỉ định đang chạy tại bất kỳ thời điểm nào. Tuy nhiên, Triển khai là một khái niệm cấp cao hơn quản lý các ReplicaSets và cung cấp các cập nhật khai báo cho các nhóm cùng với rất nhiều tính năng hữu ích khác. Do đó, bạn nên sử dụng Deployments thay vì trực tiếp sử dụng ReplicaSets, trừ khi bạn yêu cầu điều phối cập nhật tùy chỉnh hoặc hoàn toàn không yêu cầu cập nhật. Khi bạn sử dụng Deployments, bạn không phải lo lắng về việc quản lý ReplicaSets mà họ tạo. Các triển khai sở hữu và quản lý các ReplicaSets của chúng.
Trong trường hợp của chúng tôi, chúng tôi sẽ sử dụng Deployment làm bộ điều khiển khối lượng công việc, như được hiển thị trong định nghĩa YAML này. Chúng tôi có thể trực tiếp tạo Galera Cluster ReplicaSet và Service bằng cách chạy lệnh sau:
$ kubectl create -f mariadb-rs.yml
Xác minh xem cụm đã sẵn sàng chưa bằng cách xem ReplicaSet (rs), pods (po) và dịch vụ (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
Từ kết quả đầu ra ở trên, chúng tôi có thể minh họa các Nhóm và Dịch vụ của chúng tôi như dưới đây:
Chạy Galera Cluster trên ReplicaSet tương tự như việc coi nó như một ứng dụng không trạng thái. Nó sắp xếp việc tạo, xóa và cập nhật nhóm và có thể được nhắm mục tiêu cho Tính năng tự động định vị theo chiều ngang (HPA), tức là một ReplicaSet có thể được tự động điều chỉnh tỷ lệ nếu nó đáp ứng các ngưỡng hoặc mục tiêu nhất định (sử dụng CPU, gói mỗi giây, yêu cầu mỗi giây vv).
Nếu một trong các nút Kubernetes gặp sự cố, các Pod mới sẽ được lên lịch trên một nút có sẵn để đáp ứng các bản sao mong muốn. Các tập được liên kết với Pod sẽ bị xóa, nếu Pod bị xóa hoặc lên lịch lại. Tên máy chủ Pod sẽ được tạo ngẫu nhiên, khiến việc theo dõi nơi chứa vùng chứa sẽ khó hơn bằng cách chỉ cần nhìn vào tên máy chủ.
Tất cả điều này hoạt động khá tốt trong môi trường thử nghiệm và dàn dựng, nơi bạn có thể thực hiện toàn bộ vòng đời của vùng chứa như triển khai, mở rộng, cập nhật và phá hủy mà không có bất kỳ sự phụ thuộc nào. Chia tỷ lệ lên và xuống rất đơn giản, bằng cách cập nhật tệp YAML và đăng nó lên cụm Kubernetes hoặc bằng cách sử dụng lệnh tỷ lệ:
$ kubectl scale replicaset galera-rs --replicas=5
Sử dụng StatefulSet
Được gọi là PetSet trên phiên bản trước 1.6, StatefulSet là cách tốt nhất để triển khai Galera Cluster trong sản xuất, bởi vì:
- Xóa và / hoặc thu nhỏ StatefulSet sẽ không xóa các ổ được liên kết với StatefulSet. Điều này được thực hiện để đảm bảo an toàn dữ liệu, thường có giá trị hơn so với việc tự động xóa tất cả các tài nguyên StatefulSet có liên quan.
- Đối với StatefulSet có N bản sao, khi các Nhóm đang được triển khai, chúng được tạo tuần tự, theo thứ tự từ {0 .. N-1 }.
- Khi các Nhóm đang bị xóa, chúng được kết thúc theo thứ tự ngược lại, từ { N-1 .. 0}.
- Trước khi áp dụng thao tác điều chỉnh tỷ lệ cho một Pod, tất cả các thiết bị trước đó phải Đang chạy và Sẵn sàng.
- Trước khi một Pod bị chấm dứt, tất cả các thiết bị kế nhiệm của nó phải hoàn toàn ngừng hoạt động.
StatefulSet cung cấp hỗ trợ hạng nhất cho các vùng chứa trạng thái. Nó cung cấp một đảm bảo triển khai và mở rộng. Khi một Cụm Galera ba nút được tạo, ba Nhóm sẽ được triển khai theo thứ tự db-0, db-1, db-2. db-1 sẽ không được triển khai trước khi db-0 là “Đang chạy và sẵn sàng”, và db-2 sẽ không được triển khai cho đến khi db-1 là “Đang chạy và sẵn sàng”. Nếu db-0 không thành công, sau khi db-1 là "Đang chạy và sẵn sàng", nhưng trước khi db-2 được khởi chạy, db-2 sẽ không được khởi chạy cho đến khi db-0 được khởi chạy lại thành công và trở thành "Đang chạy và sẵn sàng".
Chúng tôi sẽ sử dụng triển khai Kubernetes của lưu trữ liên tục được gọi là PersentlyVolume và PersentlyVolumeClaim. Điều này để đảm bảo tính ổn định của dữ liệu nếu nhóm được lên lịch lại cho nút khác. Mặc dù Galera Cluster cung cấp bản sao chính xác của dữ liệu trên mỗi bản sao, nhưng việc có dữ liệu liên tục trong mỗi nhóm sẽ tốt cho mục đích khắc phục sự cố và phục hồi.
Để tạo một bộ lưu trữ liên tục, trước tiên chúng ta phải tạo PersentlyVolume cho mỗi nhóm. PV là các plugin khối lượng giống như Volumes trong Docker, nhưng có vòng đời độc lập với bất kỳ nhóm riêng lẻ nào sử dụng PV. Vì chúng ta sẽ triển khai Cụm Galera 3 nút, chúng ta cần tạo 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
Định nghĩa trên cho thấy chúng ta sẽ tạo 3 PV, được ánh xạ tới đường dẫn vật lý của các nút Kubernetes với 10GB dung lượng lưu trữ. Chúng tôi đã định nghĩa ReadWriteOnce, có nghĩa là ổ đĩa có thể được gắn kết dưới dạng đọc-ghi chỉ bởi một nút duy nhất. Lưu các dòng trên vào mariadb-pv.yml và đăng nó lên Kubernetes:
$ kubectl create -f mariadb-pv.yml
persistentvolume "datadir-galera-0" created
persistentvolume "datadir-galera-1" created
persistentvolume "datadir-galera-2" created
Tiếp theo, xác định các tài nguyên PersentlyVolumeClaim:
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"
Định nghĩa trên cho thấy rằng chúng tôi muốn xác nhận tài nguyên PV và sử dụng spec.selector.matchLabels để tìm kiếm PV của chúng tôi ( metadata.labels.app:galera-ss ) dựa trên chỉ mục nhóm tương ứng ( metadata.labels.podindex ) do Kubernetes chỉ định. metadata.name tài nguyên phải sử dụng định dạng “{volumeMounts.name} - {pod} - {ordinal index}” được xác định trong spec.templates.containers để Kubernetes biết điểm gắn kết nào để ánh xạ xác nhận quyền sở hữu vào nhóm.
Lưu các dòng trên vào mariadb-pvc.yml và đăng nó lên 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
Bộ nhớ liên tục của chúng tôi hiện đã sẵn sàng. Sau đó, chúng tôi có thể bắt đầu triển khai Galera Cluster bằng cách tạo tài nguyên StatefulSet cùng với tài nguyên dịch vụ Headless như được hiển thị trong mariadb-ss.yml:
$ kubectl create -f mariadb-ss.yml
service "galera-ss" created
statefulset "galera-ss" created
Bây giờ, hãy truy xuất bản tóm tắt về việc triển khai StatefulSet của chúng tôi:
$ 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
Tại thời điểm này, Cụm Galera của chúng tôi đang chạy trên StatefulSet có thể được minh họa như trong sơ đồ sau:
Chạy trên StatefulSet đảm bảo các số nhận dạng nhất quán như tên máy chủ, địa chỉ IP, ID mạng, miền cụm, Pod DNS và bộ nhớ. Điều này cho phép Pod dễ dàng phân biệt chính nó với những người khác trong một nhóm Pod. Khối lượng sẽ được giữ lại trên máy chủ lưu trữ và sẽ không bị xóa nếu Pod bị xóa hoặc lên lịch lại vào một nút khác. Điều này cho phép khôi phục dữ liệu và giảm nguy cơ mất toàn bộ dữ liệu.
Về mặt tiêu cực, thời gian triển khai sẽ là N-1 thời gian (N =bản sao) lâu hơn vì Kubernetes sẽ tuân theo trình tự thứ tự khi triển khai, lên lịch lại hoặc xóa tài nguyên. Sẽ hơi phức tạp khi chuẩn bị PV và yêu cầu trước khi nghĩ đến việc mở rộng cụm của bạn. Lưu ý rằng việc cập nhật StatefulSet hiện có hiện là một quy trình thủ công, nơi bạn chỉ có thể cập nhật spec.replicas vào lúc này.
Kết nối với Dịch vụ và nhóm Galera
Có một số cách bạn có thể kết nối với cụm cơ sở dữ liệu. Bạn có thể kết nối trực tiếp với cổng. Trong ví dụ về dịch vụ “galera-rs”, chúng tôi sử dụng NodePort, hiển thị dịch vụ trên IP của mỗi Node tại một cổng tĩnh (NodePort). Dịch vụ ClusterIP, mà dịch vụ NodePort sẽ định tuyến, được tạo tự động. Bạn sẽ có thể liên hệ với dịch vụ NodePort, từ bên ngoài cụm, bằng cách yêu cầu {NodeIP}:{NodePort} .
Ví dụ để kết nối với Cụm Galera bên ngoài:
(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
Trong không gian mạng Kubernetes, Pods có thể kết nối qua IP cụm hoặc tên dịch vụ nội bộ có thể truy xuất được bằng cách sử dụng lệnh sau:
$ 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>
Từ danh sách dịch vụ, chúng ta có thể thấy rằng Galera Cluster ReplicaSet Cluster-IP là 10.107.89.109. Trong nội bộ, một nhóm khác có thể truy cập cơ sở dữ liệu thông qua địa chỉ IP này hoặc tên dịch vụ bằng cách sử dụng cổng tiếp xúc, 3306:
(etcd0 pod)$ mysql -udb_user -ppassword -hgalera-rs -P3306 -e 'select @@hostname'
+------------------------+
| @@hostname |
+------------------------+
| galera-251551564-z4sgx |
+------------------------+
Bạn cũng có thể kết nối với NodePort bên ngoài từ bên trong bất kỳ nhóm nào trên cổng 30000:
(etcd0 pod)$ mysql -udb_user -ppassword -h192.168.55.143 -P30000 -e 'select @@hostname'
+------------------------+
| @@hostname |
+------------------------+
| galera-251551564-z4sgx |
+------------------------+
Kết nối với các Nhóm phụ trợ sẽ được cân bằng tải cho phù hợp dựa trên thuật toán kết nối ít nhất.
Tóm tắt
Tại thời điểm này, chạy Galera Cluster trên Kubernetes trong sản xuất có vẻ hứa hẹn hơn nhiều so với Docker Swarm. Như đã thảo luận trong bài đăng blog trước, những lo ngại được nêu ra được giải quyết khác với cách Kubernetes sắp xếp các vùng chứa trong StatefulSet, (mặc dù nó vẫn là một tính năng beta trong v1.6). Chúng tôi hy vọng rằng phương pháp được đề xuất sẽ giúp chạy Galera Cluster trên các thùng chứa ở quy mô sản xuất.