ในปี 2018 ใครๆก็พูดถึง Kubernetes กัน หลายๆที่ ใช้กันเป็นปกติ เราก็ใช้ที่ DomeCloud แต่ในบางงาน ที่รันอยู่มานานแล้ว ไม่ได้ย้ายไปอยู่บน Kubernetes แต่ก็ยังรันบน Docker อยู่ดี มีหลายๆเซอร์วิส ที่ยังคงทำงานได้ดีอยู่ จนกระทั่งเจอพญานาค
ไม่ว่าพญานาคที่ว่า จะมาจากไหน ช่างมัน วันนี้ ผมจะมาเล่าถึงการทำ MariaDB Cluster ด้วย Galera
บล็อกนี้ ผมจะมาเล่าถึงการทำ MariaDB Galera Cluster เพื่อแบ่งปันกันครับ ใครมีข้อแนะนำอะไร ส่งข้อความมาให้ผมแก้ได้เลยที่ https://m.me/koonnarate
เริ่มเลยดีกว่า
ตัว MariaDB รองรับการทำ Galera Cluster ในตัว ตั้งแต่เวอร์ชั่น 10.1 (Changes & Improvements in MariaDB 10.1)
ในบล็อกนี้ ผมจะทำตัวอย่าง การทำ Cluster ด้วย MariaDB 10.2 บน Docker แต่จะเป็น Docker บนเครื่องเดียวกัน "ไม่สามารถ เอาไปใช้งานจริงได้" ถ้าจะเอาไปใช้งานจริง ต้องแยกให้แต่ละโหนด อยู่คนละเครื่องเซิฟเวอร์ จะใช้ผ่าน Docker network macvlan หรือ จะใช้ Default bridge network แล้วรันด้วยการ map port ก็ได้เช่นเดียวกัน แต่ ควรที่จะรันให้อยู่ภายใต้ Private Network
ถ้าใครจะใช้การ map port ต้อง map port ตามนี้
- 3306
- 4567
- 4568
- 4444
แต่ละ Port คืออะไรบ้าง ตามไปดู Galera Cluster FIREWALL SETTINGS
ย้ำ บล็อกนี้ เทสให้ดูบน Docker ที่รันอยู่บนเครื่องเดียวกัน
แรกสุด สร้าง Network สำหรับ Docker ใช้ Bridge Driver และใส่ subnet เป็น 192.168.10.0/16
ชื่อ db-net
docker network create -d bridge \
--subnet=192.168.10.0/16 \
db-net
จะสร้าง Database 3 containers ชื่อ node0, node1, node2
โดยจะมี Galera config แบบนี้ สำหรับ node0
[mysqld]
binlog_format=ROW
default-storage-engine=innodb
innodb_autoinc_lock_mode=2
bind-address=0.0.0.0
# Galera Provider Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
# Galera Cluster Configuration
wsrep_cluster_name="galera_cluster"
wsrep_cluster_address="gcomm://192.168.10.10,192.168.10.11,192.168.10.12"
# Galera Synchronization Configuration
wsrep_sst_method=rsync
# Galera Node Configuration
wsrep_node_address="192.168.10.10"
wsrep_node_name="node0"
สิ่งที่โหนดอื่นๆ จะต่างกัน จะมีส่วนนี้
# Galera Node Configuration
wsrep_node_address="192.168.10.10"
wsrep_node_name="node0"
นอกนั้นคอนฟิกเหมือนกัน
wsrep_cluster_address="gcomm://"
คือ IP ของโหนดที่จะเข้ามา Join Cluster คั่นด้วย commawsrep_node_address
คือ IP ของโหนดนั้นๆwsrep_node_name
คือ ชื่อของโหนด
ในคอนฟิก ผมมี IP 3 IP คือ
- 192.168.10.10
[node0]
- 192.168.10.11
[node1]
- 192.168.10.12
[node2]
รัน node0
ด้วย docker
docker run --rm -it --name node0 \
--net=db-net \
--ip=192.168.10.10 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node0/mysql:/var/lib/mysql \
mariadb:10.2 --wsrep-new-cluster
สังเกตว่า ผมใช้
--rm -it
เดี๋ยวจะมาเล่าให้ฟังทีหลัง
ที่ node0
รันขึ้นมาด้วย Image mariadb:10.2
และมี parameter --wsrep-new-cluster
คือการบอกว่า เครื่องนี้ จะเป็น Primary
หลังจากรันคำสั่งข้างบนไป จะได้
Initializing database
... # ข้าม
Database initialized
MySQL init process in progress...
... # ข้าม
... view(view_id(PRIM,c4673b0e,1) memb {
c4673b0e,0
} joined {
} left {
} partitioned {
})
... # ข้าม
WSREP: Quorum results:
version = 4,
component = PRIMARY,
conf_id = 0,
members = 1/1 (joined/total),
act_id = 0,
last_appl. = -1,
protocols = 0/8/3 (gcs/repl/appl),
group UUID = c468f36c-93da-11e8-9cba-678b3ddd89a6
... # ข้าม
[Note] mysqld: ready for connections.
...
รันเสร็จ เราจะได้ MariaDB Cluster 1 node ที่เป็น Primary ค้างหน้านี้ไว้ก่อน ให้รัน node1
ต่อ ก่อนรัน node1
อย่าลืมแก้ Galera config ให้เป็นของ node1
docker run -d --name node1 \
--restart=always \
--net=db-net \
--ip=192.168.10.11 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node1.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node1/mysql:/var/lib/mysql \
mariadb:10.2
ที่ Logs ของ node0
จะมี
... # ข้าม
...view(view_id(PRIM,79991dba,2) memb {
79991dba,0
d2d34f12,0
} joined {
} left {
} partitioned {
})
... # ข้าม
2018-07-30 9:32:28 139968731133696 [Note] WSREP: Quorum results:
version = 4,
component = PRIMARY,
conf_id = 1,
members = 1/2 (joined/total),
act_id = 7176,
last_appl. = 0,
protocols = 0/8/3 (gcs/repl/appl),
group UUID = c468f36c-93da-11e8-9cba-678b3ddd89a6
เมื่อรัน node1
เสร็จแล้ว เช็คจำนวนโหนดใน Cluster ที่ node0
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 2 |
+--------------------+-------+
1 row in set (0.00 sec)
ต่อไปก็รันโหนด node2
docker run -d --name node2 \
--restart=always \
--net=db-net \
--ip=192.168.10.12 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node2.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node2/mysql:/var/lib/mysql \
mariadb:10.2
หลังจากรันเสร็จแล้ว เช็คจำนวนโหนดใน Cluster อีกที
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
1 row in set (0.00 sec)
ตอนนี้ เราได้ Cluster ที่มี 3 node โดยเราสามารถเขียนลงที่โหนดไหนก็ได้
# สร้าง Database ชื่อ node0 ที่ node0
mysql -u root -p -h node0 -e "CREATE DATABASE node0;"
# แสดงรายชื่อ Database ที่ node2
mysql -u root -p -h node2 -e "SHOW DATABASES;"
# สร้าง และ แสดงชื่อ Database ที่โหนดต่างๆ
mysql -u root -p -h node1 -e "CREATE DATABASE node2;"
mysql -u root -p -h node1 -e "SHOW DATABASES;"
mysql -u root -p -h node2 -e "CREATE DATABASE node1;"
mysql -u root -p -h node0 -e "SHOW DATABASES;"
ต่อมา ลองลบ node0
container และข้อมูลที่ node0
มีอยู่ ออกทั้งหมด
docker rm -f node0
rm -rf data/node0
ถ้าดู Logs ที่โหนดอื่นๆ จะเห็น
WSREP: Quorum results:
version = 4,
component = PRIMARY,
conf_id = 5,
members = 2/2 (joined/total),
act_id = 7179,
last_appl. = 0,
protocols = 0/8/3 (gcs/repl/appl),
group UUID = c468f36c-93da-11e8-9cba-678b3ddd89a6
และก็ลบ node1
ทิ้งด้วย
docker rm -f node1
rm -rf data/node1
ตอนนี้ จะเหลือแต่ node2
เท่านั้น ที่ยังอยู่
MariaDB [(none)]> SHOW STATUS LIKE 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 1 |
+--------------------+-------+
1 row in set (0.00 sec)
Database ทั้งหมด
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| node0 |
| node1 |
| node2 |
| performance_schema |
+--------------------+
6 rows in set (0.01 sec)
ต่อมา รัน node0
กลับคืนมา
docker run -d --name node0 \
--restart=always \
--net=db-net \
--ip=192.168.10.10 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node0/mysql:/var/lib/mysql \
mariadb:10.2
สังเกตว่า ไม่ได้ใช้
--rm -it
แล้ว เดี๋ยวมาเล่า
และก็รัน node1
docker run -d --name node1 \
--restart=always \
--net=db-net \
--ip=192.168.10.11 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node1.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node1/mysql:/var/lib/mysql \
mariadb:10.2
ลองดู Database ที่มีอยู่ จะเท่ากับเท่าเดิม
MariaDB [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| node0 |
| node1 |
| node2 |
| performance_schema |
+--------------------+
6 rows in set (0.02 sec)
หลังจากที่ node0, node1
หายไป แต่ node2
ยังอยู่ ข้อมูลก็ยังอยู่ อันนี้ เรายังมีข้อมูลอยู่ที่ โหนดอื่นๆ ไม่ได้ดับ หรือ โหนดหายไปพร้อมกัน ทั้งหมด แล้ว ถ้ามันหายไปพร้อมกันทั้งหมด เช่น เซิฟเวอร์ถูก reboot จะเป็นยังไงน่ะ ไหน ลองซิ...
ผมจะลอง restart docker เพื่อจำลองการ reboot เซิฟเวอร์
ลุย...
Restart docker เสร็จแล้ว โหนดก็กลับมา แล้วข้อมูลยังอยู่ไหมน่ะ
ฉิบ หาย แล้ว
หลังจากที่ เซิฟเวอร์ถูก reboot แล้ว login ไม่ได้ ซวยแล้วๆๆๆๆๆ พอลองดูที่ /var/lib/mysql
ยังมีข้อมูลอยู่
ในรูปคือ ที่ node0
พอลองกับโหนดอื่นๆ ก็ได้ error เหมือนกัน login ไม่ได้
เช็ค Logs ที่ node0
ก็จะเห็นประมาณนี้
[Warning] WSREP: Quorum: No node with complete state:
Version : 4
Flags : 0x5
Protocols : 0 / 8 / 3
State : NON-PRIMARY
Desync count : 0
Prim state : NON-PRIMARY
Prim UUID : 00000000-0000-0000-0000-000000000000
Prim seqno : -1
First seqno : -1
Last seqno : -1
Prim JOINED : 0
State UUID : c4aaf165-93df-11e8-bfcf-d6db3193273e
Group UUID : 00000000-0000-0000-0000-000000000000
Name : 'node1'
Incoming addr: '192.168.10.11:3306'
Version : 4
Flags : 0x4
Protocols : 0 / 8 / 3
State : NON-PRIMARY
Desync count : 0
Prim state : NON-PRIMARY
Prim UUID : 00000000-0000-0000-0000-000000000000
Prim seqno : -1
First seqno : -1
Last seqno : -1
Prim JOINED : 0
State UUID : c4aaf165-93df-11e8-bfcf-d6db3193273e
Group UUID : 00000000-0000-0000-0000-000000000000
Name : 'node0'
Incoming addr: '192.168.10.10:3306'
Version : 4
Flags : 0x4
Protocols : 0 / 8 / 3
State : NON-PRIMARY
Desync count : 0
Prim state : NON-PRIMARY
Prim UUID : 00000000-0000-0000-0000-000000000000
Prim seqno : -1
First seqno : -1
Last seqno : -1
Prim JOINED : 0
State UUID : c4aaf165-93df-11e8-bfcf-d6db3193273e
Group UUID : 00000000-0000-0000-0000-000000000000
Name : 'node2'
Incoming addr: '192.168.10.12:3306'
[Warning] WSREP: No re-merged primary component found.
[Note] WSREP: Bootstrapped primary 00000000-0000-0000-0000-000000000000 found: 3.
[Note] WSREP: Quorum results:
version = 4,
component = PRIMARY,
conf_id = -1,
members = 3/3 (joined/total),
act_id = -1,
last_appl. = -1,
protocols = 0/8/3 (gcs/repl/appl),
group UUID = 00000000-0000-0000-0000-000000000000
ก็มี 3 members แล้วนี่หว่า แต่มันไม่เจอ Primary เราต้อง หาทางเอา Primary กลับมา ผมลองรัน --wsrep-new-cluster
ที่ node0
ใหม่ โดยลบ node0
ออกก่อน
docker rm -f node0
docker run --rm -it --name node0 \
--net=db-net \
--ip=192.168.10.10 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node0/mysql:/var/lib/mysql \
mariadb:10.2 --wsrep-new-cluster
ปรากฏว่า Container ตาย แต่ถ้าดู Logs ดีๆ จะเห็นว่า
[Note] WSREP: Start replication
[Note] WSREP: 'wsrep-new-cluster' option used, bootstrapping the cluster
[Note] WSREP: Setting initial position to 00000000-0000-0000-0000-000000000000:-1
[ERROR] WSREP: It may not be safe to bootstrap the cluster from this node. It was not the last one to leave the cluster and may not contain all the updates. To force cluster bootstrap with this node, edit the grastate.dat file manually and set safe_to_bootstrap to 1 .
อันนี้ edit the grastate.dat file manually and set safe_to_bootstrap to 1
ผมแก้ไฟล์ grastate.dat
จาก
# GALERA saved state
version: 2.1
uuid: c468f36c-93da-11e8-9cba-678b3ddd89a6
seqno: -1
safe_to_bootstrap: 0
แก้
safe_to_bootstrap: 0
เป็น
safe_to_bootstrap: 1
แล้วรันใหม่
docker run --rm -it --name node0 \
--net=db-net \
--ip=192.168.10.10 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v $(pwd)/conf/galera-node0.cnf:/etc/mysql/conf.d/galera.cnf \
-v $(pwd)/data/node0/mysql:/var/lib/mysql \
mariadb:10.2 --wsrep-new-cluster
แล้วก็เข้าไปดูใน Container จะเห็น Cluster size เป็น 1 อยู่ แต่ Database ยังอยู่ครบ
จากนั้นก็ restart node1, node2
docker restart node1 node2
จากนั้นก็เป็นการเสร็จพิธี
อ่านเพิ่มเติม Introducing the “Safe-To-Bootstrap” feature in Galera Cluster
จบแล้ว เท่านี้ ถ้าใครอ่านแล้วถูกใจ อยากบริจาคค่าเบียร์ ค่ากาแฟ กดบริจาคผ่าน PayPal ได้ที่ GHOST_URL/donate/ กดหลายๆแก้วก็ได้
Source code : https://github.com/narate/mariadb-galera-cluster