Mẫu MySQL replication bằng StatefulSet trong tài liệu Kubernetes là cách dễ nhất để thấy một database có trạng thái chạy trên cluster: một pod primary, các pod replica, và một sidecar sao chép dữ liệu bằng xtrabackup. Mẫu gốc dùng MySQL 5.7. Khi thay image MySQL của mẫu bằng bản 8.x và bật xác thực, pod replica báo Init:CrashLoopBackOff, nhưng nguyên nhân nằm ở log của sidecar bên pod primary. Kỹ năng chính cần có ở đây là lần ngược lỗi dây chuyền giữa các pod.
Trong MySQL replication kiểu này, phần volume của từng pod dựa trên kiến thức ở bài chẩn đoán ContainerCreating và bài PVC ReadWriteOnce. Về mô hình database riêng ngoài cluster, xem MySQL đa tenant trên VPS.
Mẫu MySQL replication bằng StatefulSet: ConfigMap, Service headless và Service đọc
Mẫu theo hướng dẫn chạy ứng dụng có trạng thái nhân bản gồm ba phần:
- Một ConfigMap có hai khoá:
primary.cnf(bậtlog-bin) vàreplica.cnf(bậtsuper-read-only). - Một Service headless (
clusterIP: None) để mỗi pod có DNS ổn định nhưmysql-0.mysql. Pod 0 là primary, nên ghi luôn đi vào địa chỉ này. - Một Service thường tên
mysql-readđể cân bằng tải các truy vấn đọc trên mọi pod.
data:
primary.cnf: |
[mysqld]
log-bin
replica.cnf: |
[mysqld]
super-read-onlyStatefulSet dùng volumeClaimTemplates với ReadWriteOnce, nên mỗi pod có một PVC riêng, phù hợp với database vì dữ liệu của mỗi bản sao là độc lập.
Bốn container trong pod MySQL replication: init-mysql, clone-mysql, mysql và sidecar xtrabackup
Mỗi pod trong mô hình MySQL replication này có hai initContainer và hai container chạy thường trực:
init-mysql: tínhserver-idtừ số thứ tự của pod (100 + ordinal, tránh giá trị 0), rồi copyprimary.cnfhoặcreplica.cnfvào mộtemptyDirdùng chung.clone-mysql: chạy trên replica. Nếu datadir đã có dữ liệu thì thoát; nếu chưa, nó kéo bản sao từ pod đứng trước quancat --recv-only, giải nén bằngxbstream, rồixtrabackup --prepare.mysql: container chính.xtrabackup: sidecar. Nó chạyncat --listenở cổng 3307, và mỗi khi có kết nối đến thì chạyxtrabackup --backup --stream=xbstreamđể phục vụ pod mới. Nếu pod vừa clone xong, sidecar còn chạy lệnh thiết lập replication.
Hãy chú ý hướng đi của dữ liệu. Pod 1 (init) kết nối vào cổng 3307 của pod 0 (sidecar). Nghĩa là khi clone ở pod 1 thất bại, bạn phải nhìn cả hai phía.
MySQL replication bản không xác thực chạy được và cách kiểm tra đồng bộ
Với MYSQL_ALLOW_EMPTY_PASSWORD=1, cả ba pod lên 2/2 Running. Kiểm tra đồng bộ bằng cách tạo dữ liệu ở primary rồi đọc ở replica:
kubectl exec -it mysql-0 -- mysql -u root -e "CREATE DATABASE d1; CREATE TABLE d1.posts (id INT PRIMARY KEY, title VARCHAR(50)); INSERT INTO d1.posts VALUES (1,'a');"
kubectl exec -it mysql-1 -- mysql -u root -e "SELECT * FROM d1.posts;"Hàng dữ liệu xuất hiện ở pod 1 thì MySQL replication hoạt động. Để kiểm tra chính xác hơn, chạy trên replica lệnh sau (từ MySQL 8.0.22 là tên mới; SHOW SLAVE STATUS đã lỗi thời, và các câu lệnh tên cũ bị gỡ ở 8.4) rồi xem Replica_IO_Running, Replica_SQL_Running đều là Yes:
kubectl exec mysql-1 -c mysql -- mysql -u root -e 'SHOW REPLICA STATUS\G'Cấu hình root không mật khẩu chỉ nên dùng để thử nghiệm: bất kỳ pod nào trong cluster cũng kết nối được.
MySQL replication bản có xác thực: replica kẹt Init:CrashLoopBackOff
Thêm Secret chứa mật khẩu root, dùng MYSQL_ROOT_PASSWORD cho container mysql và truyền --password cho xtrabackup. Lần này pod 0 vẫn 2/2, còn pod 1 là:
NAME READY STATUS RESTARTS
mysql-0 2/2 Running 0
mysql-1 0/2 Init:CrashLoopBackOff 4Log init clone-mysql của pod 1 báo xtrabackup: Error: cannot open ./xtrabackup_checkpoints và This target seems not to have correct metadata. Đây chỉ là triệu chứng: không có luồng dữ liệu nào được nhận về nên không có file metadata. Đừng sửa ở đây. Một event FailedMount ... Unable to find device path cũng xuất hiện nhưng là lỗi thoáng qua, đừng nhầm đó là nguyên nhân.
Hãy lần ngược sang pod primary, nơi sidecar phải tạo luồng backup:
kubectl logs mysql-0 -c xtrabackup
Failed to connect to MySQL server: Authentication plugin 'caching_sha2_password' cannot be loadedThông báo trong log còn kèm một dòng khác về việc không mở được file thư viện plugin; ở đây chỉ trích dòng chính (rút gọn).
Một điểm cần sửa trong manifest mẫu: kubelet thay $(MYSQL_ROOT_PASSWORD) thành giá trị thật ngay trong command, nên mật khẩu xuất hiện trong pod spec, trong tiến trình và trong log khi bật set -x. Nên đọc mật khẩu từ biến môi trường bên trong script (MYSQL_PWD hoặc --defaults-extra-file) và tránh set -x quanh các dòng đó.
Nguyên nhân tầng 1 của lỗi MySQL replication: xtrabackup 2.4 thiếu plugin caching_sha2_password
Image mẫu của tài liệu Kubernetes (đã ngừng phát triển) đóng gói Percona XtraBackup 2.4, dựng trên thư viện client MySQL 5.7. MySQL 8.0 đặt caching_sha2_password làm plugin xác thực mặc định. XtraBackup 2.4.4 trong image mẫu dựng trên thư viện client MySQL 5.7.13, cũ hơn các bản 5.7 có plugin client caching_sha2_password (từ 5.7.23). Vì vậy sidecar không đăng nhập được vào server 8.x ngay cả khi mật khẩu đúng, và luồng backup không bao giờ khởi động.
Cần cẩn trọng với kết luận. Bản không xác thực vẫn đồng bộ được với cùng cặp image, và chưa có giải thích đã kiểm chứng cho điều này. Hai phát hiện (bản có xác thực gặp Unsupported server version, bản không xác thực đồng bộ chạy được) chưa khớp nhau; có thể do cách kiểm tra đồng bộ hoặc đường đi mã khi không có mật khẩu, nhưng chưa xác nhận. Cách tự kiểm chứng: ở bản không xác thực, chạy kubectl logs mysql-0 -c xtrabackup và kubectl exec mysql-1 -c mysql -- mysql -e 'SHOW REPLICA STATUS\G', rồi xem log sidecar có dòng Unsupported server version hay không. Vì vậy không nên nói "xtrabackup 2.4 không sao lưu được MySQL 8"; điều chắc chắn hẹp hơn là nó không đăng nhập được bằng caching_sha2_password.
Tầng 2 của MySQL replication: đổi sang mysql_native_password và lỗi Unsupported server version
Hướng sửa hiển nhiên là ép server dùng plugin cũ. Trong MySQL 8.0 và 8.2, dòng sau trong cả hai file cấu hình làm user mới dùng mysql_native_password:
[mysqld]
default_authentication_plugin=mysql_native_passwordKết quả: sidecar đăng nhập được (Connected to MySQL server), nhưng ngay sau đó:
Error: Unsupported server version: '8.2.0'.Đây là rào cản thứ hai ở bản có xác thực: mỗi dòng XtraBackup nhắm tới một dòng MySQL. 2.4 dành cho 5.7 trở xuống; XtraBackup 8.0 dành cho MySQL 8.0.x. XtraBackup 2.4 không nằm trong danh sách phiên bản được hỗ trợ cho MySQL 8.x; thông báo này xuất hiện ngay sau khi đăng nhập được.
Hai cảnh báo khi chép cấu hình này. Thứ nhất, default_authentication_plugin bị gỡ khỏi MySQL 8.4 (thay bằng authentication_policy), và mysql_native_password bị tắt theo mặc định ở 8.4, chỉ bật lại bằng --mysql-native-password=ON. Hãy kiểm tra tài liệu MySQL 8.4 trước khi dùng. Thứ hai, nếu StatefulSet được tạo lại mà PVC vẫn còn, user đã tồn tại sẽ giữ plugin cũ, vì cấu hình chỉ áp cho user tạo sau đó.
Thử image Percona: lỗi kéo image rồi thiếu ncat
Để cứu mô hình MySQL replication này, thay sang percona/percona-xtrabackup:8.0 gặp hai lỗi liên tiếp. Đầu tiên là ImagePullBackOff do ghi tên image thiếu tiền tố percona/ trong manifest, một lỗi gõ chứ không liên quan tới Kubernetes. Sau khi kéo được, sidecar báo exec: ncat: not found. Image này không đi kèm ncat. Việc cài thêm gói cung cấp ncat (ví dụ nmap hoặc nmap-ncat) không thành công.
Giả thuyết rằng image phụ thuộc vào percona-server chưa được chứng minh; một khả năng khác là việc cài gói thất bại vì container chạy không phải root. Chưa kiểm chứng tới bước xtrabackup --prepare với 8.0, nên không rõ cặp XtraBackup 8.0 và MySQL 8.2 có chạy được không (XtraBackup 8.0 chỉ hỗ trợ MySQL 8.0.x).
Phần MySQL replication chưa giải quyết và lựa chọn thay thế
Điều chưa chốt của MySQL replication trong mẫu này: cặp image xtrabackup nào chạy được với MySQL 8.2 trong mẫu này, và vì sao bản không xác thực đồng bộ được. Ngoài ra, mẫu dùng CHANGE MASTER TO và START SLAVE, đã lỗi thời từ 8.0.22 và có tên mới CHANGE REPLICATION SOURCE TO, START REPLICA (các câu lệnh tên cũ bị gỡ ở 8.4). Muốn dùng mẫu này với MySQL 8, hãy ghim MySQL 8.0.x và dùng image XtraBackup 8.0, rồi kiểm chứng đủ ba bước backup, prepare, START REPLICA; việc này chưa được kiểm chứng trong bài. Mật khẩu root đi qua dòng lệnh cũng là điểm yếu; chart có tài khoản replication riêng tốt hơn.
Về lựa chọn thay thế, chưa có phép so sánh nào giữa tự dựng, chart và operator. Một ghi nhận liên quan: chart MySQL kiểu architecture: replication của Bitnami dùng tài khoản replication riêng. Với chart này, replica từng CrashLoopBackOff vì probe thất bại và container bị kill; nới failureThreshold của startupProbe thì ổn định. Nguyên nhân khởi động chậm (có thể do volume) là giả thuyết, chưa xác nhận. YAML dưới đặt dưới khoá secondary.startupProbe của chart Bitnami mysql:
secondary:
startupProbe:
enabled: true
initialDelaySeconds: 45
periodSeconds: 10
failureThreshold: 25Theo tài liệu về probe, startupProbe cho phép chờ tối đa khoảng initialDelaySeconds + failureThreshold × periodSeconds trước khi liveness probe bắt đầu, tức 45 + 25 × 10 = 295 giây trong ví dụ trên.
Checklist khi dựng MySQL replication trong StatefulSet
- Với MySQL replication, chọn công cụ sao lưu cùng dòng phiên bản với MySQL: 2.4 với 5.7, 8.0 với 8.0.x.
- Khi init của replica lỗi, đọc log sidecar của pod đứng trước.
- Không để mật khẩu nằm trong
commandcủa pod hay bậtset -xquanh dòng dùng mật khẩu. - Không dùng root không mật khẩu cho MySQL replication ngoài thử nghiệm; dùng tài khoản replication riêng.
- Cấu hình MySQL replication bằng
CHANGE REPLICATION SOURCE TOvàSTART REPLICAở MySQL 8.0.22 trở lên. - Không chép
default_authentication_pluginsang 8.4. - Kiểm tra trạng thái MySQL replication bằng
SHOW REPLICA STATUS, không chỉSELECT. - Nếu volume chậm, nới
startupProbethay vì liveness.
Bài học
- Khi MySQL replication lỗi, lỗi hiện ở init của pod sau, nguyên nhân nằm ở sidecar của pod trước.
- Tách lỗi xác thực (
caching_sha2_password) khỏi lỗi phiên bản (Unsupported server version); sửa một lỗi chỉ để lộ lỗi kế tiếp. - Phân biệt điều đã kiểm chứng với giả thuyết trước khi kết luận về tương thích phiên bản.
Bài tiếp theo
Chạy artisan trên Kubernetes: migrate bằng Job hay initContainer, scheduler bằng CronJob. Series quay lại ứng dụng Laravel với cách chạy migrate, scheduler và queue.



