บทความนี้ครอบคลุม: สถาปัตยกรรม Distributed SQL, การติดตั้ง CockroachDB บน Docker Swarm แบบ Secure, Security PKI, Monitoring ด้วย Prometheus/Grafana และแนวทาง Production-ready ทั้งหมด
ฐานข้อมูลเชิงสัมพันธ์แบบดั้งเดิม (Traditional RDBMS) อย่าง PostgreSQL, MySQL, หรือ Oracle ถูกออกแบบมาในยุคที่ Application ทำงานบนเครื่องเดียว (Single Machine) และมีผู้ใช้งานในระดับหลักร้อยถึงหลักพัน ระบบเหล่านี้ยึดหลัก ACID (Atomicity, Consistency, Isolation, Durability) อย่างเคร่งครัด ซึ่งเป็นสิ่งที่ดี แต่กลับกลายเป็น Bottleneck เมื่อ Scale ขึ้น
ปัญหาหลักของ Vertical Scaling:
┌─────────────────────────────────────────────────┐
│ Vertical Scaling (Scale Up) │
│ │
│ [Small Server] → [Big Server] → [LIMIT!] │
│ 4 CPU, 16GB 32 CPU, 256GB 128 CPU? │
│ │
│ ✗ ราคาแพงแบบ Exponential │
│ ✗ Single Point of Failure ยังอยู่ │
│ ✗ มี Physical Ceiling │
└─────────────────────────────────────────────────┘
ตัวอย่างการคำนวณต้นทุน Vertical Scaling:
| ขนาดเซิร์ฟเวอร์ | ราคาโดยประมาณ/เดือน (Cloud) | Throughput ที่ได้ |
|---|---|---|
| 4 vCPU, 16 GB RAM | $150 | ~1,000 TPS |
| 16 vCPU, 64 GB RAM | $600 | ~3,500 TPS |
| 64 vCPU, 256 GB RAM | $2,400 | ~10,000 TPS |
| 128 vCPU, 512 GB RAM | $9,600 | ~18,000 TPS |
สังเกตว่าราคาเพิ่ม 64 เท่า แต่ Throughput เพิ่มเพียง 18 เท่า — นี่คือปัญหา Diminishing Returns ของ Vertical Scaling
NoSQL (Not Only SQL) เกิดขึ้นในช่วงปี 2000s เพื่อแก้ปัญหา Scale ของ Web Scale Companies อย่าง Facebook, Amazon, Google ระบบเหล่านี้สามารถ Scale แนวนอน (Horizontal Scaling) ได้ดีมาก แต่แลกมาด้วยต้นทุนที่สำคัญ
ประเภทของ NoSQL และข้อจำกัด:
CAP Theorem และการ Trade-off:
ทฤษฎีบท CAP (CAP Theorem) ระบุว่าระบบ Distributed ไม่สามารถรับประกันได้ทั้ง 3 อย่างพร้อมกัน:
โดยที่:
NoSQL ส่วนใหญ่เลือก AP (Available + Partition Tolerant) จึงยอมเสีย Consistency โดยใช้ Eventual Consistency ซึ่งหมายความว่า:
[Node A] เขียน: balance = 1000
[Node B] อ่าน: balance = 800 ← อาจยังไม่ sync!
[Node B] อ่านอีกครั้ง: balance = 1000 ← sync แล้ว
สำหรับ Application ทางการเงิน การที่ยอดเงินไม่ถูกต้อง แม้เพียงชั่วคราว ถือเป็นปัญหาร้ายแรงมาก
NewSQL คือแนวคิดที่ต้องการ "เค้กทั้งชิ้น" — ความสามารถในการ Scale แนวนอนของ NoSQL รวมกับ ACID Guarantees ของ RDBMS แบบดั้งเดิม
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#d5c4a1', 'secondaryColor': '#3c3836', 'tertiaryColor': '#32302f', 'background': '#282828', 'mainBkg': '#282828', 'nodeBorder': '#504945', 'clusterBkg': '#3c3836', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836', 'attributeBackgroundColorEven': '#282828', 'attributeBackgroundColorOdd': '#3c3836'}}}%%
graph LR
subgraph Traditional["Traditional RDBMS (CP)"]
T1["✓ Strong Consistency\n✓ ACID Transaction\n✗ Horizontal Scale\n✗ Single Point of Failure"]
end
subgraph NoSQL["NoSQL (AP)"]
N1["✓ Horizontal Scale\n✓ High Availability\n✗ Eventual Consistency\n✗ Limited Transactions"]
end
subgraph NewSQL["NewSQL (CP + Scale)"]
NS1["✓ Strong Consistency\n✓ ACID Transaction\n✓ Horizontal Scale\n✓ Fault Tolerant"]
end
Traditional -->|"Scale Problem"| NewSQL
NoSQL -->|"Consistency Problem"| NewSQL
style Traditional fill:#cc241d,color:#ebdbb2
style NoSQL fill:#d79921,color:#282828
style NewSQL fill:#98971a,color:#ebdbb2
ตัวแทนของ NewSQL:
| ระบบ | ต้นกำเนิด | ฐานเทคโนโลยี |
|---|---|---|
| CockroachDB | Cockroach Labs | Raft Consensus + RocksDB/Pebble |
| Google Spanner | TrueTime API + Paxos | |
| YugabyteDB | Yugabyte Inc. | Raft + DocDB |
| TiDB | PingCAP | Raft + TiKV |
| Vitess | YouTube/CNCF | MySQL Sharding Layer |
CockroachDB ถูกสร้างขึ้นโดย Spencer Kimball, Peter Mattis และ Ben Darnell ในปี 2015 ทั้งสามคนเป็นอดีตวิศวกรจาก Google โดยตรงได้รับแรงบันดาลใจจากเอกสารวิจัย "Spanner: Google's Globally-Distributed Database" ที่ตีพิมพ์ในปี 2012
สิ่งที่ Google Spanner ทำได้:
ความท้าทายที่ CockroachDB ต้องแก้: Google มี Hardware พิเศษ (GPS + Atomic Clock) ที่ทั่วไปไม่มี CockroachDB จึงต้องคิดค้น Hybrid Logical Clock (HLC) เพื่อแทนที่ TrueTime โดยไม่ต้องพึ่ง Hardware พิเศษ
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#d5c4a1', 'secondaryColor': '#3c3836', 'tertiaryColor': '#32302f', 'background': '#282828', 'mainBkg': '#282828', 'nodeBorder': '#504945', 'clusterBkg': '#3c3836', 'titleColor': '#ebdbb2', 'edgeLabelBackground': '#3c3836'}}}%%
timeline
title วิวัฒนาการของ Distributed Database
2012 : Google Spanner Paper ตีพิมพ์
: โลกรู้ว่า Global Strong Consistency เป็นไปได้
2015 : Cockroach Labs ก่อตั้ง
: CockroachDB v1 เริ่มพัฒนา
: ใช้ Raft แทน Paxos
2017 : CockroachDB v1.0 Released
: Production-ready ครั้งแรก
2019 : v19.1 - Multi-region Partitioning
: Geo-partitioning feature
2021 : v21.1 - Multi-region Abstractions
: Survival Goals concept
2022 : v22.1 - License เปลี่ยนเป็น BSL
: ข้อจำกัดการใช้งานเชิงพาณิชย์
2023 : v23.1 - Logical Data Replication
: Physical Cluster Replication
2024 : v24.x - Performance improvements
: Enhanced observability
ชื่อ "CockroachDB" มาจากแมลงสาบ (Cockroach) — สัตว์ที่ขึ้นชื่อเรื่องความอึด ทนทาน และรอดชีวิตได้ในสภาวะที่เลวร้าย ปรัชญาการออกแบบมีหลักการสำคัญ:
Cloud-native Design Principles:
| คุณสมบัติ | CockroachDB | YugabyteDB | Vitess | Patroni+PG |
|---|---|---|---|---|
| PostgreSQL Compatible | บางส่วน (Wire Protocol) | สูง (YQL) | ต่ำ (Sharding Layer) | 100% |
| Distributed Transaction | ✓ Native | ✓ Native | ✗ Limited | ✗ ไม่มี |
| Auto Rebalancing | ✓ | ✓ | ✗ | ✗ |
| Multi-region Native | ✓ | ✓ | ✗ | ✗ |
| License | BSL (v22.1+) | Apache 2.0 | Apache 2.0 | Apache 2.0 |
| Operational Complexity | ปานกลาง | ปานกลาง | สูง | สูงมาก |
| OLTP Performance | ดี | ดี | ดีมาก (Single shard) | ดีมาก |
| Community | ใหญ่ | กลาง | ใหญ่ | ใหญ่มาก |
เมื่อไหร่ควรเลือกอะไร:
ตั้งแต่ CockroachDB v22.1 (พฤษภาคม 2022) Cockroach Labs ได้เปลี่ยน License จาก Apache 2.0 เป็น Business Source License (BSL) 1.1 ซึ่งมีผลกระทบที่ต้องเข้าใจ
ข้อจำกัดหลักของ BSL:
BSL → Apache 2.0 อัตโนมัติ:
Source Code จะถูก Convert เป็น Apache 2.0 โดยอัตโนมัติหลังจาก 4 ปี นับจากวันที่ Release
v22.1 (2022) → Apache 2.0 ในปี 2026
v23.1 (2023) → Apache 2.0 ในปี 2027
| Edition | ราคา | Feature หลัก | ข้อจำกัด |
|---|---|---|---|
| Core (Self-hosted) | ฟรี | SQL, Replication, HA, Backup | ไม่มี Geo-partitioning, ไม่มี Encrypted Backup |
| CockroachDB Cloud | Pay-as-you-go | Managed, Auto-scaling | Cloud vendor lock-in |
| Enterprise | License fee | Geo-partitioning, Encrypted Backup, SAML SSO | ราคาสูง |
หากต้องการ Open Source อย่างแท้จริง พิจารณา:
สถาปัตยกรรมของ CockroachDB เป็นแบบ Symmetric Peer-to-Peer — ทุกโหนดเท่าเทียมกัน ไม่มีโหนดพิเศษที่เป็น "Master" ซึ่งแตกต่างจาก MySQL Replication หรือ Elasticsearch ที่มี Master Node
ข้อดีของ Peer-to-Peer:
Client → [Node 1] ──── [Node 2]
│ │
└──── [Node 3] ┘
ทุกโหนดสามารถรับ SQL Query ได้
โหนดที่รับ Query จะเป็น "Gateway" สำหรับ Transaction นั้น
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#d5c4a1', 'secondaryColor': '#3c3836', 'tertiaryColor': '#32302f', 'background': '#282828', 'mainBkg': '#3c3836', 'nodeBorder': '#689d6a', 'clusterBkg': '#3c3836', 'titleColor': '#fabd2f', 'edgeLabelBackground': '#3c3836'}}}%%
graph TB
subgraph "CockroachDB Internal Layers"
SQL["SQL Layer\n(Parser, Planner, Executor)\nรับ SQL จาก Client แปลงเป็น KV Operations"]
KV["Transactional KV Layer\n(MVCC, Transaction Coordinator)\nจัดการ Multi-key Transactions"]
DIST["Distribution Layer\n(Range Cache, DistSender)\nหา Range ที่ถูกต้องสำหรับ Key"]
REPL["Replication Layer\n(Raft Consensus)\nแน่ใจว่าข้อมูลถูก Replicate ครบ Quorum"]
STORE["Storage Layer\n(Pebble - LSM Tree)\nเขียนข้อมูลลง Disk จริงๆ"]
end
CLIENT["SQL Client\n(Application)"] -->|"SQL Query\nSELECT * FROM orders"| SQL
SQL -->|"KV Operations\nGet/Put/Scan"| KV
KV -->|"Route to Range\nKey → Range → Node"| DIST
DIST -->|"Raft Propose\nWrite to Leader"| REPL
REPL -->|"Write WAL + SST\nCommit to Disk"| STORE
style SQL fill:#458588,color:#ebdbb2
style KV fill:#689d6a,color:#ebdbb2
style DIST fill:#d79921,color:#282828
style REPL fill:#cc241d,color:#ebdbb2
style STORE fill:#8f3f71,color:#ebdbb2
style CLIENT fill:#504945,color:#ebdbb2
อธิบายแต่ละ Layer:
Raft คือ Consensus Algorithm ที่ออกแบบให้เข้าใจง่ายกว่า Paxos โดยมีแนวคิดหลักคือ "Replicated State Machine" — ทุก Node ต้อง Execute ลำดับคำสั่งเดียวกัน เพื่อให้ State เหมือนกัน
กระบวนการเลือก Leader (Leader Election):
RequestVote ไปยัง Follower ทุกตัวสูตร Quorum:
โดยที่ N = จำนวน Replica ทั้งหมด
ตัวอย่างการคำนวณ:
Write Path (การเขียนข้อมูล):
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#d5c4a1', 'secondaryColor': '#3c3836', 'tertiaryColor': '#32302f', 'background': '#282828', 'mainBkg': '#282828', 'nodeBorder': '#504945', 'clusterBkg': '#3c3836', 'titleColor': '#fabd2f', 'edgeLabelBackground': '#3c3836'}}}%%
sequenceDiagram
participant Client as SQL Client
participant Gateway as Gateway Node
participant Leader as Raft Leader
participant F1 as Follower 1
participant F2 as Follower 2
Client->>Gateway: INSERT INTO orders VALUES (...)
Gateway->>Leader: KV Put (Route to Leaseholder)
Note over Leader: 1. AppendEntries to Log
Leader->>F1: AppendEntries RPC
Leader->>F2: AppendEntries RPC
F1-->>Leader: ACK (Log written)
F2-->>Leader: ACK (Log written)
Note over Leader: 2. Quorum reached (2/2 Followers)
Note over Leader: 3. Commit entry to State Machine
Leader-->>Gateway: Write Committed
Gateway-->>Client: SUCCESS
Note over F1,F2: 4. Apply entry asynchronously
Read Path (การอ่านข้อมูล):
Strong Read (Default):
Client → Gateway → Leaseholder → ตอบกลับทันที
(ไม่ต้องรอ Quorum เพราะ Leaseholder รับประกัน Fresh Data)
Follower Read (Stale, ถ้าเปิดใช้):
Client → Gateway → Follower → ตอบกลับ (อาจช้า ~500ms)
(ข้อมูลอาจช้ากว่า Reality เล็กน้อย แต่ Latency ต่ำกว่า)
CockroachDB รับประกัน Serializable Isolation ซึ่งเป็น Level สูงสุดของ ACID Isolation โดยใช้ MVCC + Write Intent + Timestamp Ordering
กลไก MVCC (Multi-Version Concurrency Control):
-- Transaction 1 (เริ่มที่ T=100)
BEGIN;
SELECT balance FROM accounts WHERE id = 1;
-- อ่าน Snapshot ณ T=100 → balance = 1000
-- Transaction 2 (เริ่มที่ T=101)
BEGIN;
UPDATE accounts SET balance = 500 WHERE id = 1;
COMMIT; -- Commit ที่ T=102
-- Transaction 1 ยังคงเห็น balance = 1000
-- เพราะมัน Snapshot ที่ T=100
SELECT balance FROM accounts WHERE id = 1;
-- ยังคงเห็น 1000 (ไม่ใช่ 500)
COMMIT;
Range คือหน่วยพื้นฐานของการจัดการข้อมูลใน CockroachDB โดย:
Table: orders (1.5 GB ข้อมูล)
Range 1: id [1 ... 50,000] → 500 MB → บน Node 1
Range 2: id [50,001 ... 100,000] → 500 MB → บน Node 2
Range 3: id [100,001 ... 150,000] → 500 MB → บน Node 3
Replication Factor = จำนวน Copy ของแต่ละ Range (Default = 3)
Range 1: id [1 ... 50,000]
Replica 1 (Leader): Node 1 ←── Raft Leader
Replica 2 (Follower): Node 2
Replica 3 (Follower): Node 3
Zone Configuration สำหรับ Replication Factor:
-- ดู Replication Zone ปัจจุบัน
SHOW ZONE CONFIGURATION FOR RANGE default;
-- เปลี่ยน Default Replication Factor เป็น 5
ALTER RANGE default CONFIGURE ZONE USING num_replicas = 5;
-- กำหนด Replication สำหรับ Table เฉพาะ
ALTER TABLE orders CONFIGURE ZONE USING
num_replicas = 5,
constraints = '{"+region=th-central": 3, "+region=th-east": 2}';
Leaseholder คือ Replica ที่ได้รับ "สิทธิ์ชั่วคราว" ในการให้บริการ Strong Read โดยไม่ต้องรอ Quorum
ผลต่อ Latency:
ถ้า Application อยู่ในประเทศไทย แต่ Leaseholder อยู่ที่ US:
→ Latency สูงมาก (200ms+ ต่อ Query)
ถ้า Leaseholder ถูก Move มาไว้ใกล้ Application:
→ Latency ลดลงอย่างมาก (< 5ms)
-- Follow-the-Workload: ย้าย Leaseholder ตามการใช้งาน (Enterprise)
ALTER TABLE orders CONFIGURE ZONE USING
lease_preferences = '[[+region=th-central]]';
HLC (Hybrid Logical Clock) รวม 2 แนวคิดเข้าด้วยกัน:
โดยที่:
กฎการอัปเดต HLC:
l = max(l_local, time_now), c = c + 1l = max(l_local, l_message, time_now), ปรับ c ตามกฎCockroachDB ปฏิเสธที่จะทำงาน หากนาฬิกาของโหนดใดโหนดหนึ่งต่างจากโหนดอื่นเกิน 500ms
ทำไมถึง Fatal:
สมมติ Node A มีเวลา T=1000ms
Node B มีเวลา T=600ms (drift 400ms)
Transaction ที่ Commit บน Node A ที่ T=900ms
จะดู "เหมือนเกิดในอนาคต" จากมุมมองของ Node B
→ ลำดับ Transaction ผิดพลาด
→ ข้อมูลอาจไม่ Consistent!
เมื่อ Clock Drift เกิน Threshold จะเห็น Error:
clock synchronization error: this node is more than 500ms ahead of the connected nodes
# ติดตั้ง Chrony บน Ubuntu/Debian
apt-get install -y chrony
# ตรวจสอบ Configuration
cat /etc/chrony.conf
# ต้องมี Server ที่ดี เช่น
# server time.google.com iburst
# server time1.google.com iburst
# ตรวจสอบ Synchronization Status
chronyc tracking
# ดู System time offset — ต้องน้อยกว่า 500ms
chronyc sources -v
# ดู NTP Server ที่ใช้อยู่
# ตรวจสอบว่า Sync แล้ว
timedatectl status
# ต้องเห็น: System clock synchronized: yes
ตัวอย่าง Output ที่ถูกต้อง:
Reference ID : 8.8.4.4 (time.google.com)
Stratum : 2
Ref time (UTC) : Wed Mar 18 10:00:00 2026
System time : 0.000012345 seconds slow of NTP time ← ต้องน้อยกว่า 0.5
Last offset : -0.000012345 seconds
RMS offset : 0.000023456 seconds
Frequency : -2.345 ppm fast
| จำนวน Node | Replication Factor | Quorum ต้องการ | ทนการสูญเสียได้ | ต้นทุน |
|---|---|---|---|---|
| 3 | 3 | 2 | 1 node | ต่ำ |
| 5 | 5 | 3 | 2 nodes | ปานกลาง |
| 7 | 7 | 4 | 3 nodes | สูง |
| 9 | 9 | 5 | 4 nodes | สูงมาก |
โดยที่ N = จำนวน Replica (= จำนวน Node ในกรณี Replication Factor = N)
ตัวอย่างการคำนวณ:
ทำไม Production ควรใช้ 5 Node:
Production Best Practice: กระจายโหนดข้าม Failure Domain
Zone/Rack 1 Zone/Rack 2 Zone/Rack 3
[Node 1] [Node 2] [Node 3]
[Node 4] [Node 5]
→ แม้ Rack 1 ไฟดับทั้ง Rack → เหลือ Node 2,3,4,5 → Quorum ยังได้ (4 จาก 5)
# กำหนด Locality เมื่อ Start CockroachDB
cockroach start \
--locality=region=th-central,zone=dc1,rack=rack-01 \
...
%%{init: {'theme': 'base', 'themeVariables': {'primaryColor': '#282828', 'primaryTextColor': '#ebdbb2', 'primaryBorderColor': '#504945', 'lineColor': '#d5c4a1', 'secondaryColor': '#3c3836', 'tertiaryColor': '#32302f', 'background': '#282828', 'mainBkg': '#282828', 'nodeBorder': '#504945', 'clusterBkg': '#3c3836', 'titleColor': '#fabd2f', 'edgeLabelBackground': '#3c3836'}}}%%
graph TB
subgraph Internet["Internet / Internal Network"]
APP["Application Servers\n(microservices)"]
end
subgraph DMZ["Load Balancer Layer"]
LB1["HAProxy 1\n(Active)"]
LB2["HAProxy 2\n(Standby)"]
VIP["Virtual IP\n(Keepalived)"]
end
subgraph SwarmCluster["Docker Swarm Cluster"]
subgraph Managers["Swarm Managers (3 nodes)"]
MGR1["Manager 1\nCockroachDB Node 1\n10.0.1.1"]
MGR2["Manager 2\nCockroachDB Node 2\n10.0.1.2"]
MGR3["Manager 3\nCockroachDB Node 3\n10.0.1.3"]
end
subgraph Workers["Swarm Workers (2 nodes)"]
WRK1["Worker 1\nCockroachDB Node 4\n10.0.1.4"]
WRK2["Worker 2\nCockroachDB Node 5\n10.0.1.5"]
end
end
subgraph Storage["Persistent Storage"]
VOL["Local SSD Volumes\n/cockroach-data"]
end
APP -->|"Port 5432/26257"| VIP
VIP --> LB1
VIP -.->|"Failover"| LB2
LB1 -->|"Round Robin\nHealth Check"| MGR1
LB1 --> MGR2
LB1 --> MGR3
LB1 --> WRK1
LB1 --> WRK2
MGR1 <-->|"Raft + Gossip\nOverlay Network"| MGR2
MGR2 <-->|":26257"| MGR3
MGR3 <-->|"Encrypted"| WRK1
WRK1 <-->|"VXLAN"| WRK2
MGR1 <-->|""| WRK1
MGR2 <-->|""| WRK2
MGR1 --- VOL
MGR2 --- VOL
MGR3 --- VOL
WRK1 --- VOL
WRK2 --- VOL
style Internet fill:#1d2021,color:#ebdbb2
style DMZ fill:#3c3836,color:#ebdbb2
style SwarmCluster fill:#282828,color:#ebdbb2
style Managers fill:#1d2021,color:#ebdbb2
style Workers fill:#32302f,color:#ebdbb2
style Storage fill:#3c3836,color:#ebdbb2
แนะนำให้ใช้ Manager Node เป็น CockroachDB Node ด้วย ในระบบขนาดกลาง เพราะ:
| Node | Swarm Role | CockroachDB Role | IP |
|---|---|---|---|
| server-01 | Manager | CockroachDB Node 1 | 10.0.1.1 |
| server-02 | Manager | CockroachDB Node 2 | 10.0.1.2 |
| server-03 | Manager | CockroachDB Node 3 | 10.0.1.3 |
| server-04 | Worker | CockroachDB Node 4 | 10.0.1.4 |
| server-05 | Worker | CockroachDB Node 5 | 10.0.1.5 |
# สร้าง Network สำหรับ CockroachDB Cluster (Encrypted)
docker network create \
--driver overlay \
--opt encrypted \
--subnet 10.10.0.0/24 \
cockroach-net
# สร้าง Network สำหรับ Application Tier
docker network create \
--driver overlay \
--subnet 10.20.0.0/24 \
app-net
# CockroachDB อยู่ใน cockroach-net
# Application อยู่ใน app-net + cockroach-net (เชื่อมทั้งสอง)
# ทำให้ DB Node ไม่ expose ตรงไปยัง External
| ระดับ | CPU | RAM | Disk | Network |
|---|---|---|---|---|
| Development | 2 vCPU | 4 GB | 50 GB SSD | 1 Gbps |
| Small Production | 8 vCPU | 32 GB | 500 GB NVMe | 10 Gbps |
| Medium Production | 16 vCPU | 64 GB | 1 TB NVMe | 10 Gbps |
| Large Production | 32 vCPU | 128 GB | 2 TB NVMe RAID | 25 Gbps |
หมายเหตุ: CockroachDB ไวต่อ Disk Latency มากกว่า Throughput — ใช้ NVMe SSD แทน SATA SSD หรือ HDD เสมอ
# ===== File Descriptors =====
# CockroachDB ต้องการ File Descriptors จำนวนมาก
echo "* soft nofile 1048576" >> /etc/security/limits.conf
echo "* hard nofile 1048576" >> /etc/security/limits.conf
echo "root soft nofile 1048576" >> /etc/security/limits.conf
echo "root hard nofile 1048576" >> /etc/security/limits.conf
# ตรวจสอบหลัง Relogin
ulimit -n
# ต้องเห็น: 1048576
# ===== Kernel Parameters =====
cat >> /etc/sysctl.conf << 'EOF'
# สำหรับ CockroachDB / Docker Swarm
vm.max_map_count = 1048576
vm.swappiness = 1
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.core.netdev_max_backlog = 65536
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 10
net.ipv4.tcp_keepalive_probes = 6
# สำหรับ Docker Overlay Network
net.ipv4.ip_forward = 1
EOF
sysctl -p
# ===== Transparent HugePages (ปิด — เป็นปัญหากับ Pebble/RocksDB) =====
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
# ให้ Persistent หลัง Reboot
cat >> /etc/rc.local << 'EOF'
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/transparent_hugepage/defrag
EOF
chmod +x /etc/rc.local
# ติดตั้ง Chrony
apt-get update && apt-get install -y chrony
# สร้าง Configuration ที่เหมาะสม
cat > /etc/chrony.conf << 'EOF'
# ใช้ Google Public NTP Servers
server time1.google.com iburst minpoll 4 maxpoll 6
server time2.google.com iburst minpoll 4 maxpoll 6
server time3.google.com iburst minpoll 4 maxpoll 6
server time4.google.com iburst minpoll 4 maxpoll 6
# Fallback ถ้า Google ไม่ได้
pool pool.ntp.org iburst
# Log file
logdir /var/log/chrony
log tracking measurements statistics
# Allow step on first three clock updates only
makestep 1.0 3
# ป้องกัน Clock ถอยหลัง (ห้าม Step ถ้า Drift เกิน 1 วินาที)
maxdistance 1.5
rtcsync
EOF
systemctl enable chrony
systemctl restart chrony
# ตรวจสอบสถานะ
chronyc tracking
chronyc sources -v
# Script ตรวจสอบทุกวัน
cat > /usr/local/bin/check-clock-sync.sh << 'EOF'
#!/bin/bash
OFFSET=$(chronyc tracking | grep "System time" | awk '{print $4}')
THRESHOLD=0.4 # 400ms (ต่ำกว่า 500ms limit ของ CockroachDB)
if (( $(echo "$OFFSET > $THRESHOLD" | bc -l) )); then
echo "WARNING: Clock offset ${OFFSET}s exceeds ${THRESHOLD}s threshold!"
logger -t clock-check "CRITICAL: Clock drift ${OFFSET}s on $(hostname)"
fi
EOF
chmod +x /usr/local/bin/check-clock-sync.sh
# เพิ่มใน Crontab
echo "*/5 * * * * /usr/local/bin/check-clock-sync.sh" | crontab -
# ===== บน server-01 (First Manager) =====
docker swarm init \
--advertise-addr 10.0.1.1 \
--listen-addr 10.0.1.1:2377
# บันทึก Token ที่ได้ ทั้ง Manager Token และ Worker Token
docker swarm join-token manager
docker swarm join-token worker
# ===== บน server-02, server-03 (Additional Managers) =====
docker swarm join \
--token SWMTKN-1-xxxxx-manager-token \
10.0.1.1:2377
# ===== บน server-04, server-05 (Workers) =====
docker swarm join \
--token SWMTKN-1-xxxxx-worker-token \
10.0.1.1:2377
# ===== ตรวจสอบบน Manager =====
docker node ls
# ต้องเห็นทุก Node และ Status = Ready
# เพิ่ม Label เพื่อใช้ใน Placement Constraints
# บน server-01
docker node update --label-add role=cockroachdb server-01
docker node update --label-add zone=dc1 server-01
docker node update --label-add rack=rack-01 server-01
# บน server-02
docker node update --label-add role=cockroachdb server-02
docker node update --label-add zone=dc1 server-02
docker node update --label-add rack=rack-02 server-02
# บน server-03
docker node update --label-add role=cockroachdb server-03
docker node update --label-add zone=dc2 server-03
docker node update --label-add rack=rack-01 server-03
# บน server-04
docker node update --label-add role=cockroachdb server-04
docker node update --label-add zone=dc2 server-04
docker node update --label-add rack=rack-02 server-04
# บน server-05
docker node update --label-add role=cockroachdb server-05
docker node update --label-add zone=dc3 server-05
docker node update --label-add rack=rack-01 server-05
# ตรวจสอบ Labels
docker node inspect server-01 --format '{{json .Spec.Labels}}'
# สร้าง Overlay Network แบบ Encrypted สำหรับ CockroachDB
docker network create \
--driver overlay \
--opt encrypted \
--attachable \
--subnet 10.10.0.0/24 \
--gateway 10.10.0.1 \
cockroach-net
# สร้าง Network สำหรับ App Tier
docker network create \
--driver overlay \
--attachable \
--subnet 10.20.0.0/24 \
app-net
# ตรวจสอบ Networks
docker network ls | grep overlay
docker network inspect cockroach-net
# ===== UFW Rules (Ubuntu) =====
# Allow Swarm Management Port (เฉพาะระหว่าง Swarm Nodes)
ufw allow from 10.0.1.0/24 to any port 2377 proto tcp comment "Docker Swarm Manager"
# Allow Container Network Discovery
ufw allow from 10.0.1.0/24 to any port 7946 proto tcp comment "Swarm Container Discovery TCP"
ufw allow from 10.0.1.0/24 to any port 7946 proto udp comment "Swarm Container Discovery UDP"
# Allow Overlay Network (VXLAN)
ufw allow from 10.0.1.0/24 to any port 4789 proto udp comment "Docker Overlay Network VXLAN"
# Allow CockroachDB SQL / Inter-node
ufw allow from 10.0.1.0/24 to any port 26257 proto tcp comment "CockroachDB SQL + Inter-node"
# Allow CockroachDB Admin UI (เฉพาะ Internal หรือ VPN)
ufw allow from 10.0.0.0/8 to any port 8080 proto tcp comment "CockroachDB Admin UI"
# Allow HAProxy Health Check Port
ufw allow from 10.0.1.0/24 to any port 8081 proto tcp comment "HAProxy Stats"
# ===== iptables Rules (CentOS/RHEL) =====
# iptables -A INPUT -s 10.0.1.0/24 -p tcp --dport 26257 -j ACCEPT
# iptables -A INPUT -s 10.0.1.0/24 -p tcp --dport 8080 -j ACCEPT
สรุปตาราง Port:
| Port | Protocol | วัตถุประสงค์ | Source |
|---|---|---|---|
| 26257 | TCP | SQL Client + Inter-node Communication | All Nodes + Apps |
| 8080 | TCP | Admin Web UI | Internal/VPN Only |
| 2377 | TCP | Docker Swarm Manager | Swarm Nodes |
| 7946 | TCP/UDP | Container Network Discovery | Swarm Nodes |
| 4789 | UDP | Overlay Network (VXLAN) | Swarm Nodes |
| คุณสมบัติ | Local SSD/NVMe | Network Storage (NFS/Ceph) |
|---|---|---|
| Latency | < 0.1ms | 1–5ms |
| IOPS | 100K–1M+ | 10K–100K |
| Availability | ต่ำ (ผูกกับ Host) | สูง (HA built-in) |
| Data Safety | ต้องพึ่ง CockroachDB Replication | มี Storage Level Replication |
| แนะนำ | ✓ Production CockroachDB | ✗ ไม่แนะนำสำหรับ DB |
CockroachDB มี Replication ในตัว จึงไม่จำเป็นต้องใช้ Storage ที่มี HA → ใช้ Local SSD ตรงๆ ดีกว่า เพราะ Latency ต่ำกว่ามาก
# สร้างไดเรกทอรีบน Host ทุกโหนด
# ทำบนทุก Node ที่จะรัน CockroachDB
mkdir -p /data/cockroach/node1
chmod 755 /data/cockroach/node1
chown 1000:1000 /data/cockroach/node1 # CockroachDB User ID
# ใน docker-compose.yml ใช้ bind mount
# volumes:
# - type: bind
# source: /data/cockroach/node1
# target: /cockroach/cockroach-data
Minimum IOPS per CockroachDB Node:
- 500 IOPS สำหรับ Small workload
- 2,000 IOPS สำหรับ Medium workload
- 10,000+ IOPS สำหรับ High workload
ทดสอบ IOPS ก่อน Deploy:
fio --name=iops-test \
--filename=/data/cockroach/test \
--rw=randwrite \
--bs=4k \
--iodepth=16 \
--numjobs=4 \
--direct=1 \
--size=1G \
--runtime=60 \
--time_based
# เป้าหมาย: Write IOPS > 2000 สำหรับ Production
# ===== ขั้นตอนการสร้าง PKI =====
# 1. สร้างไดเรกทอรีสำหรับ Certificates
mkdir -p /cockroach-certs
chmod 700 /cockroach-certs
cd /cockroach-certs
# 2. ดึง CockroachDB Binary (ถ้ายังไม่มี)
# Docker-based approach
docker run --rm -v /cockroach-certs:/certs \
cockroachdb/cockroach:v24.1.0 \
cert create-ca \
--certs-dir=/certs \
--ca-key=/certs/ca.key \
--lifetime=87600h # 10 ปี
# หรือใช้ Binary โดยตรง
cockroach cert create-ca \
--certs-dir=/cockroach-certs \
--ca-key=/cockroach-certs/ca.key \
--lifetime=87600h
# ไฟล์ที่ได้:
# /cockroach-certs/ca.crt ← Public CA Certificate (แจกได้)
# /cockroach-certs/ca.key ← Private CA Key (เก็บเป็นความลับ!)
/cockroach-certs/
├── ca.crt ← CA Certificate (Public)
├── ca.key ← CA Private Key (เก็บเป็นความลับ!)
├── node.crt ← Node Certificate
├── node.key ← Node Private Key
├── client.root.crt ← Client Certificate สำหรับ root user
├── client.root.key ← Client Private Key สำหรับ root
├── client.app.crt ← Client Certificate สำหรับ app user
└── client.app.key ← Client Private Key สำหรับ app
# ตรวจสอบอายุ Certificate
openssl x509 -in /cockroach-certs/ca.crt -noout -dates
# notBefore=Mar 18 00:00:00 2026 GMT
# notAfter=Mar 18 00:00:00 2036 GMT
# ตรวจสอบ SAN ใน Certificate
openssl x509 -in /cockroach-certs/node.crt -noout -text | grep -A1 "Subject Alternative Name"
# Script ตรวจสอบ Expiry (ตั้งเตือน 30 วันก่อนหมด)
cat > /usr/local/bin/check-cert-expiry.sh << 'EOF'
#!/bin/bash
CERT_DIR="/cockroach-certs"
WARN_DAYS=30
for cert in $CERT_DIR/*.crt; do
EXPIRY=$(openssl x509 -in "$cert" -noout -enddate 2>/dev/null | cut -d= -f2)
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s)
NOW_EPOCH=$(date +%s)
DAYS_LEFT=$(( (EXPIRY_EPOCH - NOW_EPOCH) / 86400 ))
if [ $DAYS_LEFT -lt $WARN_DAYS ]; then
echo "WARNING: $cert expires in $DAYS_LEFT days ($EXPIRY)"
fi
done
EOF
chmod +x /usr/local/bin/check-cert-expiry.sh
# สร้าง Node Certificate สำหรับแต่ละโหนด
# ต้องระบุ IP Address และ Hostname ทั้งหมดที่โหนดนี้ตอบสนอง
# Node 1
cockroach cert create-node \
10.0.1.1 \
server-01 \
server-01.internal \
localhost \
127.0.0.1 \
cockroach1 \
cockroach1.cockroach-net \
--certs-dir=/cockroach-certs \
--ca-key=/cockroach-certs/ca.key \
--lifetime=8760h \
--overwrite
# เปลี่ยนชื่อเพื่อจัดเก็บแยก Node
mv /cockroach-certs/node.crt /cockroach-certs/node1.crt
mv /cockroach-certs/node.key /cockroach-certs/node1.key
# Node 2
cockroach cert create-node \
10.0.1.2 \
server-02 \
server-02.internal \
localhost \
127.0.0.1 \
cockroach2 \
cockroach2.cockroach-net \
--certs-dir=/cockroach-certs \
--ca-key=/cockroach-certs/ca.key \
--lifetime=8760h \
--overwrite
mv /cockroach-certs/node.crt /cockroach-certs/node2.crt
mv /cockroach-certs/node.key /cockroach-certs/node2.key
# ทำแบบเดียวกันสำหรับ Node 3, 4, 5
# ...
# ตรวจสอบ SAN ใน Certificate
openssl x509 -in /cockroach-certs/node1.crt -noout -text | grep -A5 "Subject Alternative Name"
# สร้าง Client Certificate สำหรับ root (ใช้สำหรับ Init และ Admin)
cockroach cert create-client root \
--certs-dir=/cockroach-certs \
--ca-key=/cockroach-certs/ca.key \
--lifetime=8760h
# สร้าง Client Certificate สำหรับ Application User
cockroach cert create-client app_user \
--certs-dir=/cockroach-certs \
--ca-key=/cockroach-certs/ca.key \
--lifetime=8760h
# สร้าง Client Certificate สำหรับ Read-only User
cockroach cert create-client readonly_user \
--certs-dir=/cockroach-certs \
--ca-key=/cockroach-certs/ca.key \
--lifetime=8760h
# ตรวจสอบทุก Certificate ที่สร้าง
cockroach cert list --certs-dir=/cockroach-certs
Role Hierarchy:
root → DBA operations, CREATE/DROP DATABASE, GRANT
admin → Database Admin, CREATE TABLE, GRANT ในฐานะของตน
app_user → Application: INSERT/UPDATE/DELETE/SELECT ใน Database ที่กำหนด
readonly_user → Reporting: SELECT เท่านั้น
# สร้าง Docker Secrets จาก Certificate Files
# CA Certificate (Public — แชร์ได้)
docker secret create cockroach-ca-crt /cockroach-certs/ca.crt
# Node 1 Certificates
docker secret create cockroach-node1-crt /cockroach-certs/node1.crt
docker secret create cockroach-node1-key /cockroach-certs/node1.key
# Node 2 Certificates
docker secret create cockroach-node2-crt /cockroach-certs/node2.crt
docker secret create cockroach-node2-key /cockroach-certs/node2.key
# Node 3 Certificates
docker secret create cockroach-node3-crt /cockroach-certs/node3.crt
docker secret create cockroach-node3-key /cockroach-certs/node3.key
# Node 4 Certificates
docker secret create cockroach-node4-crt /cockroach-certs/node4.crt
docker secret create cockroach-node4-key /cockroach-certs/node4.key
# Node 5 Certificates
docker secret create cockroach-node5-crt /cockroach-certs/node5.crt
docker secret create cockroach-node5-key /cockroach-certs/node5.key
# Client Certificate สำหรับ root
docker secret create cockroach-client-root-crt /cockroach-certs/client.root.crt
docker secret create cockroach-client-root-key /cockroach-certs/client.root.key
# ตรวจสอบ Secrets ที่สร้าง
docker secret ls
# ใน docker-compose.yml
secrets:
cockroach-ca-crt:
external: true
cockroach-node1-crt:
external: true
cockroach-node1-key:
external: true
services:
cockroach1:
secrets:
- source: cockroach-ca-crt
target: /cockroach/certs/ca.crt
mode: 0444 # Read-only for all
uid: "1000"
gid: "1000"
- source: cockroach-node1-crt
target: /cockroach/certs/node.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node1-key
target: /cockroach/certs/node.key
mode: 0400 # Read-only for owner only
uid: "1000"
gid: "1000"
TLS Configuration:
- CockroachDB ใช้ TLS 1.2+ สำหรับทุก Connection
- Client → Node: TLS (ใช้ Client Certificate สำหรับ Authentication)
- Node → Node: TLS (ใช้ Node Certificate)
- Cipher Suite: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384
-- สร้าง Role แยกตามหน้าที่
CREATE ROLE readonly;
CREATE ROLE readwrite;
CREATE ROLE admin_role;
-- Assign Privileges
GRANT SELECT ON DATABASE myapp TO readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE myapp TO readwrite;
GRANT ALL ON DATABASE myapp TO admin_role;
-- สร้าง User และ Assign Role
CREATE USER reporting_user WITH PASSWORD 'strong-password';
GRANT readonly TO reporting_user;
CREATE USER app_service WITH PASSWORD 'another-strong-password';
GRANT readwrite TO app_service;
-- ตรวจสอบ Privileges
SHOW GRANTS ON DATABASE myapp;
# cockroach.yml — Production Docker Swarm Stack
# ใช้: docker stack deploy -c cockroach.yml crdb
version: "3.8"
# ===== SECRETS =====
secrets:
cockroach-ca-crt:
external: true
cockroach-node1-crt:
external: true
cockroach-node1-key:
external: true
cockroach-node2-crt:
external: true
cockroach-node2-key:
external: true
cockroach-node3-crt:
external: true
cockroach-node3-key:
external: true
cockroach-node4-crt:
external: true
cockroach-node4-key:
external: true
cockroach-node5-crt:
external: true
cockroach-node5-key:
external: true
cockroach-client-root-crt:
external: true
cockroach-client-root-key:
external: true
# ===== NETWORKS =====
networks:
cockroach-net:
external: true
app-net:
external: true
# ===== VOLUMES =====
volumes:
cockroach1-data:
driver: local
driver_opts:
type: none
o: bind
device: /data/cockroach/node1
cockroach2-data:
driver: local
driver_opts:
type: none
o: bind
device: /data/cockroach/node2
cockroach3-data:
driver: local
driver_opts:
type: none
o: bind
device: /data/cockroach/node3
cockroach4-data:
driver: local
driver_opts:
type: none
o: bind
device: /data/cockroach/node4
cockroach5-data:
driver: local
driver_opts:
type: none
o: bind
device: /data/cockroach/node5
# ===== SERVICES =====
services:
# ─────────── CockroachDB Node 1 ───────────
cockroach1:
image: cockroachdb/cockroach:v24.1.0
hostname: cockroach1
command: >
start
--certs-dir=/cockroach/certs
--store=/cockroach/cockroach-data
--listen-addr=0.0.0.0:26257
--advertise-addr=cockroach1:26257
--http-addr=0.0.0.0:8080
--join=cockroach1:26257,cockroach2:26257,cockroach3:26257,cockroach4:26257,cockroach5:26257
--locality=region=th-central,zone=dc1,rack=rack-01
--cache=8GiB
--max-sql-memory=8GiB
--logtostderr=WARNING
--log-config-file=/cockroach/log.yaml
networks:
- cockroach-net
ports:
- target: 8080
published: 8081
protocol: tcp
mode: host
volumes:
- cockroach1-data:/cockroach/cockroach-data
secrets:
- source: cockroach-ca-crt
target: /cockroach/certs/ca.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node1-crt
target: /cockroach/certs/node.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node1-key
target: /cockroach/certs/node.key
mode: 0400
uid: "1000"
gid: "1000"
deploy:
replicas: 1
placement:
constraints:
- node.hostname == server-01
- node.labels.role == cockroachdb
resources:
limits:
cpus: "14"
memory: 28G
reservations:
cpus: "4"
memory: 16G
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 5
window: 120s
healthcheck:
test: ["CMD", "curl", "-f", "--insecure", "https://localhost:8080/health?ready=1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# ─────────── CockroachDB Node 2 ───────────
cockroach2:
image: cockroachdb/cockroach:v24.1.0
hostname: cockroach2
command: >
start
--certs-dir=/cockroach/certs
--store=/cockroach/cockroach-data
--listen-addr=0.0.0.0:26257
--advertise-addr=cockroach2:26257
--http-addr=0.0.0.0:8080
--join=cockroach1:26257,cockroach2:26257,cockroach3:26257,cockroach4:26257,cockroach5:26257
--locality=region=th-central,zone=dc1,rack=rack-02
--cache=8GiB
--max-sql-memory=8GiB
--logtostderr=WARNING
networks:
- cockroach-net
volumes:
- cockroach2-data:/cockroach/cockroach-data
secrets:
- source: cockroach-ca-crt
target: /cockroach/certs/ca.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node2-crt
target: /cockroach/certs/node.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node2-key
target: /cockroach/certs/node.key
mode: 0400
uid: "1000"
gid: "1000"
deploy:
replicas: 1
placement:
constraints:
- node.hostname == server-02
- node.labels.role == cockroachdb
resources:
limits:
cpus: "14"
memory: 28G
reservations:
cpus: "4"
memory: 16G
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 5
healthcheck:
test: ["CMD", "curl", "-f", "--insecure", "https://localhost:8080/health?ready=1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# Node 3, 4, 5 มีโครงสร้างเหมือนกัน เปลี่ยนเพียง:
# hostname, advertise-addr, placement constraints,
# secrets (node3/4/5), locality (zone/rack), volumes
# ─────────── CockroachDB Node 3 ───────────
cockroach3:
image: cockroachdb/cockroach:v24.1.0
hostname: cockroach3
command: >
start
--certs-dir=/cockroach/certs
--store=/cockroach/cockroach-data
--listen-addr=0.0.0.0:26257
--advertise-addr=cockroach3:26257
--http-addr=0.0.0.0:8080
--join=cockroach1:26257,cockroach2:26257,cockroach3:26257,cockroach4:26257,cockroach5:26257
--locality=region=th-central,zone=dc2,rack=rack-01
--cache=8GiB
--max-sql-memory=8GiB
--logtostderr=WARNING
networks:
- cockroach-net
volumes:
- cockroach3-data:/cockroach/cockroach-data
secrets:
- source: cockroach-ca-crt
target: /cockroach/certs/ca.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node3-crt
target: /cockroach/certs/node.crt
mode: 0444
uid: "1000"
gid: "1000"
- source: cockroach-node3-key
target: /cockroach/certs/node.key
mode: 0400
uid: "1000"
gid: "1000"
deploy:
replicas: 1
placement:
constraints:
- node.hostname == server-03
- node.labels.role == cockroachdb
resources:
limits:
cpus: "14"
memory: 28G
reservations:
cpus: "4"
memory: 16G
restart_policy:
condition: on-failure
delay: 10s
max_attempts: 5
healthcheck:
test: ["CMD", "curl", "-f", "--insecure", "https://localhost:8080/health?ready=1"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
# ─────────── HAProxy Load Balancer ───────────
haproxy:
image: haproxy:2.8-alpine
networks:
- cockroach-net
- app-net
ports:
- target: 26257
published: 26257
protocol: tcp
mode: ingress
- target: 8404
published: 8404
protocol: tcp
mode: ingress
configs:
- source: haproxy-config
target: /usr/local/etc/haproxy/haproxy.cfg
deploy:
replicas: 2
placement:
constraints:
- node.role == manager
update_config:
parallelism: 1
delay: 30s
restart_policy:
condition: on-failure
healthcheck:
test: ["CMD", "haproxy", "-c", "-f", "/usr/local/etc/haproxy/haproxy.cfg"]
interval: 30s
timeout: 10s
retries: 3
configs:
haproxy-config:
external: true
# ===== ขั้นตอนการ Deploy =====
# 1. ตรวจสอบว่า Swarm พร้อม
docker node ls
# 2. ตรวจสอบว่า Secrets และ Networks พร้อม
docker secret ls
docker network ls | grep overlay
# 3. สร้างไดเรกทอรีบน Host แต่ละ Node
# ทำบนทุก Node ที่จะรัน CockroachDB
for node in 1 2 3 4 5; do
mkdir -p /data/cockroach/node${node}
chown 1000:1000 /data/cockroach/node${node}
done
# 4. Deploy Stack
docker stack deploy -c cockroach.yml crdb
# 5. ตรวจสอบ Services
docker stack services crdb
# ต้องเห็น REPLICAS = 1/1 สำหรับทุก Service
# 6. ตรวจสอบ Container Status
docker service ls
docker service ps crdb_cockroach1
docker service ps crdb_cockroach2
# ... และ Service อื่นๆ
# 7. ดู Logs เพื่อตรวจสอบการ Start
docker service logs crdb_cockroach1 --tail 50 -f
# รอจนเห็น: "node starting"
Common Errors และวิธีแก้:
# Error 1: "no such file or directory" สำหรับ Data Directory
# สาเหตุ: ไดเรกทอรีบน Host ยังไม่ได้สร้าง
# แก้: mkdir -p /data/cockroach/node1
# Error 2: Secret "cockroach-node1-crt" not found
# สาเหตุ: ยังไม่ได้ Create Docker Secret
# แก้: docker secret create cockroach-node1-crt /path/to/cert
# Error 3: Network "cockroach-net" not found
# สาเหตุ: ยังไม่ได้สร้าง Overlay Network
# แก้: docker network create --driver overlay --opt encrypted cockroach-net
# Error 4: Certificate SAN mismatch
# สาเหตุ: hostname ใน advertise-addr ไม่ตรงกับ SAN ใน Certificate
# แก้: Regenerate Certificate พร้อม hostname ที่ถูกต้อง
# ===== Init Cluster — ทำครั้งเดียวเท่านั้น! =====
# รอจนทุก Node Start แล้ว (ตรวจสอบจาก Logs)
# จากนั้น Exec เข้าไปใน Container ของ Node ใดก็ได้
docker exec -it $(docker ps -q -f name=crdb_cockroach1) \
cockroach init \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257
# ผลลัพธ์ที่ถูกต้อง:
# Cluster successfully initialized
# ===== ตรวจสอบสถานะ =====
# ดู Node Status
docker exec -it $(docker ps -q -f name=crdb_cockroach1) \
cockroach node status \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257
# ต้องเห็น 5 Nodes ทั้งหมด และ is_live = true
# ดู Cluster Status ผ่าน SQL
docker exec -it $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257 \
--execute="SELECT node_id, address, is_live FROM crdb_internal.gossip_liveness;"
# haproxy.cfg สำหรับ CockroachDB
global
log stdout format raw local0
maxconn 10000
tune.ssl.default-dh-param 2048
defaults
mode tcp
log global
option tcplog
option dontlognull
option tcp-check
timeout connect 5s
timeout client 30s
timeout server 30s
retries 3
# ===== Frontend สำหรับ SQL Client =====
frontend cockroachdb_sql
bind *:26257
default_backend cockroachdb_nodes
# ===== Backend CockroachDB Nodes =====
backend cockroachdb_nodes
balance roundrobin
option tcp-check
# Health Check: ใช้ HTTP endpoint /health?ready=1
# โหนดที่ยัง Bootstrapping หรือ Unhealthy จะถูก Remove ออก
option httpchk GET /health?ready=1
http-check expect status 200
server cockroach1 cockroach1:26257 check port 8080 inter 2000 rise 3 fall 2
server cockroach2 cockroach2:26257 check port 8080 inter 2000 rise 3 fall 2
server cockroach3 cockroach3:26257 check port 8080 inter 2000 rise 3 fall 2
server cockroach4 cockroach4:26257 check port 8080 inter 2000 rise 3 fall 2
server cockroach5 cockroach5:26257 check port 8080 inter 2000 rise 3 fall 2
# ===== Stats Page =====
frontend stats
bind *:8404
stats enable
stats uri /stats
stats refresh 30s
stats auth admin:securepassword
# สร้าง Docker Config สำหรับ HAProxy
docker config create haproxy-config haproxy.cfg
# ตรวจสอบ HAProxy Logs
docker service logs crdb_haproxy -f
# ทดสอบ Connection ผ่าน HAProxy
cockroach sql \
--certs-dir=/cockroach-certs \
--host=localhost:26257 \
--execute="SELECT 1;"
-- เชื่อมต่อด้วย Root Certificate
-- cockroach sql --certs-dir=/cockroach/certs --host=localhost:26257
-- ===== สร้าง Database =====
CREATE DATABASE myapp
ENCODING = 'UTF8';
-- ตรวจสอบ Database
SHOW DATABASES;
-- ===== สร้าง Schema =====
USE myapp;
CREATE SCHEMA app;
CREATE SCHEMA audit;
CREATE SCHEMA reporting;
-- ===== ตัวอย่างการสร้างตาราง =====
CREATE TABLE app.orders (
order_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
customer_id UUID NOT NULL,
status STRING NOT NULL DEFAULT 'pending',
total_amount DECIMAL(15,2) NOT NULL,
currency STRING(3) NOT NULL DEFAULT 'THB',
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
INDEX idx_customer_id (customer_id),
INDEX idx_status (status),
INDEX idx_created_at (created_at DESC)
);
CREATE TABLE app.customers (
customer_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
email STRING NOT NULL UNIQUE,
name STRING NOT NULL,
phone STRING,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- ===== ตรวจสอบ Table Schema =====
SHOW TABLES FROM app;
SHOW CREATE TABLE app.orders;
-- สร้าง Role ตามหน้าที่
CREATE ROLE app_readonly;
CREATE ROLE app_readwrite;
CREATE ROLE app_admin;
-- Grant Privileges ให้ Role
GRANT SELECT ON DATABASE myapp TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA reporting TO app_readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON DATABASE myapp TO app_readwrite;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA app TO app_readwrite;
GRANT ALL ON DATABASE myapp TO app_admin;
-- สร้าง Users
CREATE USER service_account WITH PASSWORD 'Str0ng!P@ssw0rd#2026';
GRANT app_readwrite TO service_account;
CREATE USER reporting_bot WITH PASSWORD 'R3p0rt!ng@2026';
GRANT app_readonly TO reporting_bot;
CREATE USER dba_user WITH PASSWORD 'DB@Adm1n!2026';
GRANT app_admin TO dba_user;
-- ตรวจสอบ
SHOW GRANTS ON DATABASE myapp;
SHOW ROLES;
# Format มาตรฐาน
postgresql://{user}:{password}@{host}:{port}/{database}?sslmode=verify-full&sslrootcert={ca.crt}&sslcert={client.crt}&sslkey={client.key}
# ตัวอย่าง — Secure Mode (แนะนำ)
postgresql://service_account@haproxy:26257/myapp?sslmode=verify-full&sslrootcert=/certs/ca.crt
# ตัวอย่าง — ใช้ Client Certificate Authentication
postgresql://root@localhost:26257/myapp?sslmode=verify-full&sslrootcert=/certs/ca.crt&sslcert=/certs/client.root.crt&sslkey=/certs/client.root.key
# Secure Connection พร้อม Certificate
cockroach sql \
--certs-dir=/cockroach-certs \
--host=haproxy:26257 \
--user=root \
--database=myapp
# Insecure (Development Only — ไม่แนะนำ Production)
cockroach sql \
--insecure \
--host=localhost:26257
ทำไมต้องใช้ Connection Pooling:
# pgbouncer.ini
[databases]
myapp = host=haproxy port=26257 dbname=myapp sslmode=verify-full \
sslrootcert=/etc/pgbouncer/ca.crt \
sslcert=/etc/pgbouncer/client.crt \
sslkey=/etc/pgbouncer/client.key
[pgbouncer]
listen_port = 5432
listen_addr = 0.0.0.0
auth_type = scram-sha-256
auth_file = /etc/pgbouncer/userlist.txt
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 25
min_pool_size = 5
reserve_pool_size = 5
server_lifetime = 300
server_idle_timeout = 30
log_connections = 1
log_disconnections = 1
# สร้าง userlist.txt
echo '"service_account" "md5hash_of_password"' > /etc/pgbouncer/userlist.txt
# หรือใช้ scram-sha-256 format
# Admin UI อยู่ที่ port 8080 ของทุก Node
# เข้าผ่าน HTTPS
https://server-01:8080
# หรือผ่าน Port ที่ Published
https://localhost:8081
# Login ด้วย:
# Username: root (หรือ User ที่มี admin Role)
# Certificate หรือ Password ที่ตั้งไว้
Overview Dashboard:
Metrics ที่ต้อง Monitor:
| Metric | ค่าปกติ | ค่าที่ต้อง Alert |
|---|---|---|
| Under-replicated ranges | 0 | > 0 นาน > 5 นาที |
| Node liveness | All alive | Any node dead |
| P99 SQL Latency | < 100ms | > 500ms |
| QPS | ตาม Workload | Drop > 50% |
| Clock offset | < 100ms | > 400ms |
| Disk usage | < 70% | > 85% |
# ===== Test 1: Drain โหนดออก (Graceful) =====
# จำลองการทำ Maintenance
# Mark Node เป็น Drain
docker node update --availability drain server-05
# สังเกตใน Admin UI:
# - Container บน Node 5 จะถูก Reschedule
# - CockroachDB Node 5 จะรายงานว่า Suspect แล้ว Dead
# - Range Re-replication จะเริ่มทำงาน
# ===== Test 2: Kill Container โดยตรง (Abrupt) =====
docker service scale crdb_cockroach5=0
# สังเกตใน Terminal:
watch -n2 'docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach node status \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257'
# ===== Test 3: Network Partition Simulation =====
# บน server-05: Block ทราฟฟิก CockroachDB
iptables -A INPUT -p tcp --dport 26257 -j DROP
iptables -A OUTPUT -p tcp --dport 26257 -j DROP
# สังเกต: Node 5 กลายเป็น Suspect ใน ~5 วินาที
# สังเกต: Cluster ยังทำงานปกติด้วย 4 Node
# คืนสภาพ
iptables -D INPUT -p tcp --dport 26257 -j DROP
iptables -D OUTPUT -p tcp --dport 26257 -j DROP
# นำโหนดกลับมา
docker service scale crdb_cockroach5=1
# สังเกตการ Rejoin:
docker service logs crdb_cockroach5 --tail 20 -f
# เห็น: "node joined cluster"
# ตรวจสอบ Replication Recovery
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach node status \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257
# คืน Availability
docker node update --availability active server-05
-- Backup Database ทั้งหมดไปยัง Local Storage
BACKUP DATABASE myapp
INTO 'nodelocal://1/backup/myapp'
AS OF SYSTEM TIME '-10s';
-- AS OF SYSTEM TIME ช่วยให้ Backup ไม่กระทบ Performance มาก
-- Backup ทั้ง Cluster
BACKUP INTO 'nodelocal://1/backup/full-cluster'
AS OF SYSTEM TIME '-10s';
-- Incremental Backup (ต้องมี Full Backup ก่อน)
BACKUP DATABASE myapp
INTO LATEST IN 'nodelocal://1/backup/myapp'
AS OF SYSTEM TIME '-10s';
-- ดู Backup ที่มีอยู่
SHOW BACKUPS IN 'nodelocal://1/backup/myapp';
-- Backup ไปยัง S3
BACKUP DATABASE myapp
INTO 's3://my-bucket/cockroachdb/myapp?AUTH=specified&AWS_ACCESS_KEY_ID=xxx&AWS_SECRET_ACCESS_KEY=yyy'
AS OF SYSTEM TIME '-10s';
-- หรือใช้ IAM Role (ถ้ารันบน AWS EC2)
BACKUP DATABASE myapp
INTO 's3://my-bucket/cockroachdb/myapp?AUTH=implicit'
AS OF SYSTEM TIME '-10s';
-- MinIO (Self-hosted S3-compatible)
BACKUP DATABASE myapp
INTO 's3://my-bucket/cockroachdb/myapp?AWS_ENDPOINT=https://minio:9000&AUTH=specified&AWS_ACCESS_KEY_ID=minioadmin&AWS_SECRET_ACCESS_KEY=minioadmin&AWS_REGION=us-east-1'
AS OF SYSTEM TIME '-10s';
-- ตรวจสอบ Backup ก่อน Restore
SHOW BACKUP 's3://my-bucket/cockroachdb/myapp/2026/03/18-000000';
-- Restore Database
RESTORE DATABASE myapp
FROM LATEST IN 's3://my-bucket/cockroachdb/myapp'
WITH new_db_name = 'myapp_restored';
-- Restore Table เดียว
RESTORE TABLE myapp.app.orders
FROM LATEST IN 's3://my-bucket/cockroachdb/myapp'
WITH into_db = 'myapp_restored';
-- Restore ณ เวลาที่กำหนด (Point-in-time Recovery)
RESTORE DATABASE myapp
FROM 's3://my-bucket/cockroachdb/myapp'
AS OF SYSTEM TIME '2026-03-18 10:00:00';
#!/bin/bash
# /usr/local/bin/cockroach-backup.sh
COCKROACH_HOST="localhost"
COCKROACH_PORT="26257"
CERTS_DIR="/cockroach-certs"
S3_BUCKET="s3://my-backup-bucket/cockroachdb"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/cockroach-backup.log"
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a $LOG_FILE; }
log "Starting CockroachDB backup..."
# Full Backup ทุก Sunday
if [ "$(date +%u)" -eq 7 ]; then
log "Running FULL backup..."
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql \
--certs-dir=$CERTS_DIR \
--host=$COCKROACH_HOST:$COCKROACH_PORT \
--execute="BACKUP INTO '${S3_BUCKET}/full-${DATE}' AS OF SYSTEM TIME '-30s';"
log "Full backup completed: ${S3_BUCKET}/full-${DATE}"
else
# Incremental Backup ทุกวัน
log "Running INCREMENTAL backup..."
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql \
--certs-dir=$CERTS_DIR \
--host=$COCKROACH_HOST:$COCKROACH_PORT \
--execute="BACKUP INTO LATEST IN '${S3_BUCKET}' AS OF SYSTEM TIME '-30s';"
log "Incremental backup completed."
fi
log "Backup process finished."
# Crontab: รันทุกวัน 02:00
# 0 2 * * * /usr/local/bin/cockroach-backup.sh
# ===== ก่อน Upgrade =====
# 1. ตรวจสอบ Version ปัจจุบัน
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach version \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257
# 2. Backup ก่อน Upgrade!
# (ทำ Backup เหมือนหัวข้อ 7.3)
# 3. Set Downgrade Option (ป้องกัน Auto-finalize)
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257 \
--execute="SET CLUSTER SETTING cluster.preserve_downgrade_option = '24.1';"
# ===== Upgrade ทีละโหนด =====
# 4. อัปเดต Image ใน docker-compose.yml
# เปลี่ยน: cockroachdb/cockroach:v24.1.0
# เป็น: cockroachdb/cockroach:v24.2.0
# 5. Update Service ทีละ Service
docker service update \
--image cockroachdb/cockroach:v24.2.0 \
--update-parallelism 1 \
--update-delay 30s \
crdb_cockroach1
# รอ Service อัปเดตเสร็จและ Healthy
watch -n5 'docker service ps crdb_cockroach1'
# 6. ทำซ้ำสำหรับ Node 2, 3, 4, 5
docker service update --image cockroachdb/cockroach:v24.2.0 crdb_cockroach2
# รอ...
docker service update --image cockroachdb/cockroach:v24.2.0 crdb_cockroach3
# รอ...
# ทำจนครบทุก Node
# ===== หลัง Upgrade =====
# 7. Finalize Version (หลัง Upgrade ครบทุกโหนด)
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257 \
--execute="RESET CLUSTER SETTING cluster.preserve_downgrade_option;"
# 8. ตรวจสอบ Version
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257 \
--execute="SELECT version();"
| อาการ | สาเหตุที่พบบ่อย | วิธีแก้ |
|---|---|---|
clock synchronization error |
NTP ไม่ Sync | chronyc tracking, ตรวจสอบ Firewall ไปยัง NTP Server |
| Under-replicated ranges ค้างนาน | โหนดล่มเกิน Quorum | ตรวจ cockroach node status, Restart Node ที่ Dead |
| OOM Killed | --max-sql-memory สูงเกิน |
ลด --max-sql-memory, เพิ่ม RAM, ตรวจ Long-running Queries |
| Certificate error / TLS handshake | SAN ไม่ตรง Hostname | openssl x509 -text -in node.crt, Regenerate Certificate |
| Node ไม่ Join Cluster | Network ไม่ถึงกัน | ตรวจ Firewall Port 26257, Docker Overlay Network |
| Split brain | Network Partition | ตรวจ Overlay Network, รอ Network กลับมา |
| High P99 Latency | Hotspot หรือ Index ขาด | ตรวจ EXPLAIN ANALYZE, เพิ่ม Index |
| Connection refused | HAProxy ไม่ Health check ผ่าน | ตรวจ /health?ready=1 ของแต่ละ Node |
# Debug Commands ที่มีประโยชน์
# ตรวจสอบ Range Distribution
cockroach sql --certs-dir=/cockroach/certs --host=cockroach1:26257 \
--execute="SHOW RANGES FROM TABLE app.orders;"
# ตรวจสอบ Active Queries
cockroach sql --certs-dir=/cockroach/certs --host=cockroach1:26257 \
--execute="SELECT * FROM [SHOW CLUSTER STATEMENTS] WHERE application_name != '§ internal' ORDER BY start DESC LIMIT 10;"
# ตรวจสอบ Slow Queries
cockroach sql --certs-dir=/cockroach/certs --host=cockroach1:26257 \
--execute="SELECT query, elapsed, rows FROM crdb_internal.cluster_queries ORDER BY elapsed DESC LIMIT 10;"
# ดู Event Log
cockroach sql --certs-dir=/cockroach/certs --host=cockroach1:26257 \
--execute="SELECT timestamp, event_type, info FROM system.eventlog ORDER BY timestamp DESC LIMIT 20;"
ตัวอย่างการคำนวณสำหรับ Server 32GB RAM:
Total RAM = 32 GB
cache = 32 × 0.25 = 8 GB
max-sql-memory = 32 × 0.25 = 8 GB
Total Allocated = 8 + 8 = 16 GB = 50% (✓ ต่ำกว่า 75%)
OS + Docker overhead ≈ 4 GB
CockroachDB Process + Go Runtime ≈ 4 GB
เหลือ Buffer: 32 - 16 - 4 - 4 = 8 GB
Command:
--cache=8GiB
--max-sql-memory=8GiB
ระวัง: ถ้าตั้ง --max-sql-memory สูงเกินไปและมี Query ที่ใช้ Memory มาก (เช่น ORDER BY บน Dataset ใหญ่) อาจทำให้ Container ถูก OOM Kill
# ตรวจสอบ Memory Usage ปัจจุบัน
docker stats $(docker ps -q -f name=crdb_cockroach)
# ดู Memory Breakdown ใน CockroachDB
docker exec $(docker ps -q -f name=crdb_cockroach1) \
cockroach sql --certs-dir=/cockroach/certs --host=cockroach1:26257 \
--execute="SELECT * FROM crdb_internal.node_memory_metrics;"
# ตัวอย่าง Locality สำหรับ Single Region Multi-Zone
--locality=region=th-central,zone=bkk-az1,rack=rack-01
# ตัวอย่าง Locality สำหรับ Multi-Region
--locality=region=ap-southeast-1,zone=ap-southeast-1a # Singapore
--locality=region=ap-southeast-7,zone=ap-southeast-7a # Thailand
--locality=region=ap-east-1,zone=ap-east-1a # Hong Kong
-- ดู Survival Goal ของ Database
SHOW SURVIVAL GOAL FOR DATABASE myapp;
-- ตั้งค่า Zone Survival (ทน Zone ล่มได้)
-- ต้องมี 3 Zone ใน Cluster
ALTER DATABASE myapp SURVIVE ZONE FAILURE;
-- ตั้งค่า Region Survival (ทน Region ล่มได้)
-- ต้องมี 3 Region ใน Cluster
ALTER DATABASE myapp SURVIVE REGION FAILURE;
-- Regional Table: ข้อมูลอยู่ใน Region เดียว (Low Latency Read/Write)
ALTER TABLE app.orders SET LOCALITY REGIONAL BY TABLE IN "ap-southeast-7";
-- Regional By Row: แต่ละ Row อยู่ใน Region ของมัน
ALTER TABLE app.customers ADD COLUMN region crdb_internal_region
NOT NULL DEFAULT default_to_database_primary_region(gateway_region())::crdb_internal_region;
ALTER TABLE app.customers SET LOCALITY REGIONAL BY ROW AS region;
-- Global Table: ทุก Row ถูก Replicate ไปทุก Region (Read ที่ไหนก็ Fast)
ALTER TABLE app.config SET LOCALITY GLOBAL;
# log.yaml — วางใน /cockroach/log.yaml
sinks:
stderr:
channels: [DEV, OPS, HEALTH]
filter: WARNING
format: crdb-v2
file-groups:
sql:
channels: [SQL_SCHEMA, SQL_EXEC, SQL_PERF]
dir: /cockroach/logs
max-file-size: 100MiB
max-group-size: 1GiB
filter: INFO
format: json
ops:
channels: [OPS, HEALTH, STORAGE]
dir: /cockroach/logs
max-file-size: 50MiB
max-group-size: 500MiB
filter: WARNING
format: json
dev:
channels: [DEV]
dir: /cockroach/logs
max-file-size: 50MiB
filter: WARNING
format: crdb-v2
# promtail-config.yml สำหรับส่ง Log ไปยัง Loki
server:
http_listen_port: 9080
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
- job_name: cockroachdb
static_configs:
- targets:
- localhost
labels:
job: cockroachdb
service: crdb
__path__: /cockroach/logs/*.json
pipeline_stages:
- json:
expressions:
timestamp: timestamp
severity: sev
channel: channel
message: message
node_id: node_id
- labels:
severity:
channel:
node_id:
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: cockroachdb-prod
environment: production
rule_files:
- /etc/prometheus/rules/*.yml
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
scrape_configs:
# ===== CockroachDB Metrics =====
- job_name: 'cockroachdb'
scheme: https
tls_config:
ca_file: /etc/prometheus/certs/ca.crt
cert_file: /etc/prometheus/certs/client.prometheus.crt
key_file: /etc/prometheus/certs/client.prometheus.key
insecure_skip_verify: false
static_configs:
- targets:
- 'cockroach1:8080'
- 'cockroach2:8080'
- 'cockroach3:8080'
- 'cockroach4:8080'
- 'cockroach5:8080'
labels:
cluster: cockroachdb-prod
metrics_path: '/_status/vars'
relabel_configs:
- source_labels: [__address__]
target_label: instance
regex: '([^:]+).*'
replacement: '${1}'
# ===== HAProxy Metrics =====
- job_name: 'haproxy'
static_configs:
- targets: ['haproxy:8404']
metrics_path: '/stats;csv'
Import Dashboard จาก Grafana.com:
Metrics ที่ต้องมีใน Dashboard:
# Key Metrics Queries
# Node Liveness
cockroachdb_liveness_livenesscount{job="cockroachdb"}
# Under-replicated Ranges
cockroachdb_ranges_underreplicated{job="cockroachdb"}
# SQL QPS
rate(cockroachdb_sql_select_count[5m]) + rate(cockroachdb_sql_insert_count[5m])
# P99 Query Latency
histogram_quantile(0.99, rate(cockroachdb_sql_service_latency_bucket[5m])) * 1e-9
# Clock Offset
cockroachdb_clock_offset_meannanos / 1e9
# Storage Used
cockroachdb_capacity_used{job="cockroachdb"} / cockroachdb_capacity{job="cockroachdb"}
# cockroachdb-alerts.yml
groups:
- name: cockroachdb.critical
rules:
# Node Down
- alert: CockroachDBNodeDown
expr: absent(cockroachdb_liveness_livenesscount) or cockroachdb_liveness_livenesscount < 5
for: 1m
labels:
severity: critical
team: database
annotations:
summary: "CockroachDB node is down"
description: "One or more CockroachDB nodes are not responding. Current live nodes: {{ $value }}"
runbook: "https://wiki.internal/runbooks/cockroachdb-node-down"
# Under-replicated Ranges
- alert: CockroachDBUnderReplicatedRanges
expr: cockroachdb_ranges_underreplicated > 0
for: 5m
labels:
severity: warning
team: database
annotations:
summary: "CockroachDB has under-replicated ranges"
description: "{{ $value }} ranges are under-replicated on {{ $labels.instance }}"
runbook: "https://wiki.internal/runbooks/cockroachdb-under-replicated"
# Clock Skew
- alert: CockroachDBClockSkew
expr: abs(cockroachdb_clock_offset_meannanos) > 400000000
for: 1m
labels:
severity: critical
team: database
annotations:
summary: "CockroachDB clock skew is high"
description: "Clock offset on {{ $labels.instance }} is {{ $value | humanizeDuration }}"
runbook: "https://wiki.internal/runbooks/cockroachdb-clock-skew"
- name: cockroachdb.performance
rules:
# High P99 Latency
- alert: CockroachDBHighLatency
expr: |
histogram_quantile(0.99,
rate(cockroachdb_sql_service_latency_bucket[5m])
) > 0.5
for: 10m
labels:
severity: warning
team: database
annotations:
summary: "CockroachDB P99 latency is high"
description: "P99 SQL latency on {{ $labels.instance }} is {{ $value | humanizeDuration }}"
# Low QPS (possible outage)
- alert: CockroachDBLowQPS
expr: |
sum(rate(cockroachdb_sql_query_started_count[5m])) < 10
for: 5m
labels:
severity: warning
team: database
annotations:
summary: "CockroachDB QPS is suspiciously low"
description: "QPS is {{ $value }}, which may indicate an outage or traffic drop"
- name: cockroachdb.storage
rules:
# Disk Usage High
- alert: CockroachDBDiskUsageHigh
expr: |
(cockroachdb_capacity_used / cockroachdb_capacity) * 100 > 85
for: 10m
labels:
severity: warning
team: database
annotations:
summary: "CockroachDB disk usage is high"
description: "Disk usage on {{ $labels.instance }} is {{ $value | humanize }}%"
# Low Available Disk
- alert: CockroachDBDiskAlmostFull
expr: |
(cockroachdb_capacity_used / cockroachdb_capacity) * 100 > 95
for: 5m
labels:
severity: critical
team: database
annotations:
summary: "CockroachDB disk is almost full!"
description: "Disk usage on {{ $labels.instance }} is {{ $value | humanize }}%"
# alertmanager.yml
global:
resolve_timeout: 5m
slack_api_url: 'https://hooks.slack.com/services/xxx/yyy/zzz'
route:
group_by: ['alertname', 'cluster']
group_wait: 30s
group_interval: 5m
repeat_interval: 12h
receiver: 'database-team'
routes:
- match:
severity: critical
receiver: 'database-oncall'
repeat_interval: 1h
- match:
severity: warning
receiver: 'database-team'
receivers:
- name: 'database-oncall'
pagerduty_configs:
- routing_key: 'xxx'
slack_configs:
- channel: '#db-alerts-critical'
title: '🚨 CRITICAL: {{ .CommonAnnotations.summary }}'
text: '{{ .CommonAnnotations.description }}'
- name: 'database-team'
slack_configs:
- channel: '#db-alerts'
title: '⚠️ WARNING: {{ .CommonAnnotations.summary }}'
text: '{{ .CommonAnnotations.description }}\nRunbook: {{ .CommonAnnotations.runbook }}'
✅ เหมาะมาก:
ตัวอย่าง Use Case จริง:
-- Financial Transfer — ต้องการ ACID Transaction
BEGIN;
UPDATE accounts SET balance = balance - 1000
WHERE account_id = 'acc-001' AND balance >= 1000;
UPDATE accounts SET balance = balance + 1000
WHERE account_id = 'acc-002';
INSERT INTO transactions (from_account, to_account, amount, timestamp)
VALUES ('acc-001', 'acc-002', 1000, now());
COMMIT;
-- ถ้า Node ล่มระหว่าง Transaction → Auto-rollback → ข้อมูลสมบูรณ์เสมอ
❌ อาจไม่คุ้มค่า:
Infrastructure:
□ NTP/Chrony sync ครบทุกโหนด (offset < 200ms)
□ Kernel parameters ตั้งค่าแล้ว (file descriptors, vm.max_map_count)
□ Transparent HugePages ปิดแล้ว
□ Disk Benchmark ผ่าน (IOPS > 2000 สำหรับ Production)
□ Network Bandwidth ทดสอบระหว่าง Nodes (> 1 Gbps)
□ Firewall Rules ตั้งค่าครบ
Security:
□ TLS Certificates สร้างครบทุกโหนด (CA, Node, Client)
□ Certificate SAN ครอบคลุมทุก Hostname/IP
□ Docker Secrets สร้างและ Mount ถูกต้อง
□ Certificate Expiry Monitoring ตั้งค่าแล้ว
□ Database Users แยกตาม Role (Least Privilege)
□ Admin UI ไม่ Expose สู่ Internet โดยตรง
High Availability:
□ 5 Nodes (ขั้นต่ำ) พร้อม Locality Labels
□ Nodes กระจายข้าม Failure Domains
□ Load Balancer ตั้งค่าพร้อม Health Check
□ Connection Pooling (PgBouncer) ตั้งค่าแล้ว
□ Fault Tolerance ทดสอบแล้ว (Kill 2 Nodes พร้อมกัน)
Backup & Recovery:
□ Backup Strategy กำหนดไว้ (Full + Incremental)
□ Backup ทดสอบ Restore แล้ว
□ Backup ไปยัง External Storage (S3/MinIO)
□ RTO และ RPO กำหนดและทดสอบแล้ว
Monitoring:
□ Prometheus scraping ทุก Node
□ Grafana Dashboard ตั้งค่าแล้ว
□ Alert Rules สำหรับ Critical Metrics
□ Log Aggregation (Loki/ELK) ตั้งค่าแล้ว
□ Runbook สำหรับแต่ละ Alert เขียนแล้ว
Query Performance:
□ ทุก Table มี Primary Key ที่เหมาะสม (หลีกเลี่ยง Sequential Integer)
□ Index ครอบคลุม Common Query Patterns
□ ใช้ EXPLAIN ANALYZE ตรวจสอบ Query Plan
□ ไม่มี Full Table Scan ใน Critical Path
Hardware:
□ ใช้ NVMe SSD (ไม่ใช่ SATA SSD หรือ HDD)
□ RAM เพียงพอสำหรับ Cache + SQL Memory
□ Network Latency ระหว่าง Nodes < 5ms
Memory:
□ --cache = 25% RAM
□ --max-sql-memory = 25% RAM
□ Container Memory Limit = RAM Total × 0.85
Application:
□ ใช้ Connection Pooling
□ หลีกเลี่ยง Long-running Transactions
□ ใช้ Prepared Statements
□ Retry Logic สำหรับ Serialization Errors
| Paper | เนื้อหา | ลิงก์ |
|---|---|---|
| Spanner (2012) | Google's Globally Distributed Database | OSDI 2012 |
| Raft (2014) | In Search of an Understandable Consensus Algorithm | USENIX ATC 2014 |
| Percolator (2010) | Large-Scale Incremental Processing (MVCC basis) | OSDI 2010 |
| CockroachDB (2020) | CockroachDB: The Resilient Geo-Distributed SQL Database | SIGMOD 2020 |
| Calvin (2012) | Fast Distributed Transactions for Partitioned DB | SIGMOD 2012 |
# cockroach demo — ทดลองใช้ Local Cluster โดยไม่ต้องติดตั้ง
docker run -it --rm cockroachdb/cockroach:v24.1.0 demo
# workload simulator — ทดสอบ Performance
docker exec -it $(docker ps -q -f name=crdb_cockroach1) \
cockroach workload init tpcc \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257
docker exec -it $(docker ps -q -f name=crdb_cockroach1) \
cockroach workload run tpcc \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257 \
--duration=5m
# คำสั่ง Debug ที่มีประโยชน์
cockroach debug zip /tmp/cockroach-debug.zip \
--certs-dir=/cockroach/certs \
--host=cockroach1:26257
# ส่งไฟล์นี้ให้ Support หรือ Forum เมื่อต้องการความช่วยเหลือ
สรุป: CockroachDB บน Docker Swarm เป็นทางเลือกที่ยอดเยี่ยมสำหรับ Application ที่ต้องการ Distributed SQL พร้อม High Availability โดยไม่ต้องพึ่งพา Cloud Provider เฉพาะราย กุญแจสำคัญคือการเตรียม Infrastructure ให้ถูกต้อง (โดยเฉพาะ NTP และ Security) และการ Monitor อย่างสม่ำเสมอ เพื่อให้ระบบทำงานได้อย่างมั่นคงในระยะยาว