การติดตั้ง CockroachDB บน Docker Swarm แบบ 5 Node ในโหมด Secure

Introduction: ทำความรู้จักกับ Distributed Database

Distributed Database คืออะไร?

Distributed Database เป็นระบบฐานข้อมูลที่กระจายข้อมูลไปยังหลายๆ เซิร์ฟเวอร์หรือ node ที่อยู่ในตำแหน่งทางกายภาพที่แตกต่างกัน แต่ทำงานร่วมกันเสมือนเป็นระบบเดียว โดยมีเป้าหมายหลักดังนี้:

CockroachDB: Distributed SQL Database

CockroachDB เป็น distributed SQL database ที่ออกแบบมาเพื่อให้มีความทนทาน (resilient) ปรับขนาดได้อย่างง่ายดาย (scalable) และมีความสอดคล้องของข้อมูลแบบ strong consistency โดยได้รับแรงบันดาลใจจาก Google Spanner

จุดเด่นของ CockroachDB:

  1. ACID Transactions: รองรับ ACID transactions แบบเต็มรูปแบบ
  2. SQL Compatible: ใช้ PostgreSQL wire protocol สามารถใช้ SQL syntax ที่คุ้นเคย
  3. Automatic Replication: กระจายและ replicate ข้อมูลอัตโนมัติ
  4. Auto-Sharding: แบ่งข้อมูลอัตโนมัติเพื่อการกระจายภาระ
  5. Self-Healing: ซ่อมแซมและ rebalance ข้อมูลอัตโนมัติเมื่อ node ล้มเหลว
  6. Cloud-Native: ออกแบบมาสำหรับ containerized environments

เปรียบเทียบ CockroachDB กับ Database อื่นๆ

คุณสมบัติ CockroachDB PostgreSQL MySQL MongoDB Cassandra
Architecture Distributed SQL Single-node/Replica Single-node/Replica Distributed Document Distributed Wide-Column
Consistency Model Strong Consistency Strong (single-node) Strong (single-node) Eventual/Tunable Eventual/Tunable
ACID Transactions ✅ Full ACID ✅ Full ACID ✅ Full ACID ⚠️ Limited ❌ No
SQL Support ✅ PostgreSQL compatible ✅ Full SQL ✅ Full SQL ❌ No (uses MQL) ⚠️ CQL (limited)
Horizontal Scaling ✅ Automatic ❌ Manual (via sharding) ❌ Manual (via sharding) ✅ Automatic ✅ Automatic
Auto-Sharding ✅ Yes ❌ No ❌ No ✅ Yes ✅ Yes
Multi-Region ✅ Built-in ⚠️ Via extensions ⚠️ Via replication ✅ Yes ✅ Yes
Automatic Failover ✅ Yes ⚠️ Requires setup ⚠️ Requires setup ✅ Yes ✅ Yes
Schema Management Fixed Schema (SQL) Fixed Schema (SQL) Fixed Schema (SQL) Flexible Schema Flexible Schema
Join Operations ✅ Full Support ✅ Full Support ✅ Full Support ⚠️ Limited ⚠️ Limited
Learning Curve Medium Low Low Medium Medium-High
Use Case Global apps, Strong consistency General purpose General purpose Document storage Time-series, IoT

สรุป:

สถาปัตยกรรมของ CockroachDB Cluster

graph TB
    subgraph "Docker Swarm Cluster"
        subgraph "Manager Node"
            SW[Swarm Manager]
        end
        
        subgraph "Worker Nodes"
            N1[Node 1
CockroachDB] N2[Node 2
CockroachDB] N3[Node 3
CockroachDB] N4[Node 4
CockroachDB] N5[Node 5
CockroachDB] end LB[Load Balancer
Port 26257] SW --> N1 SW --> N2 SW --> N3 SW --> N4 SW --> N5 LB --> N1 LB --> N2 LB --> N3 LB --> N4 LB --> N5 end CLIENT[Client Application] --> LB style N1 fill:#4CAF50 style N2 fill:#4CAF50 style N3 fill:#4CAF50 style N4 fill:#4CAF50 style N5 fill:#4CAF50 style LB fill:#2196F3 style SW fill:#FF9800

การทำงานของ Data Replication

graph LR
    subgraph "Range 1"
        R1A[Replica A
Node 1] R1B[Replica B
Node 2] R1C[Replica C
Node 3] end subgraph "Range 2" R2A[Replica A
Node 2] R2B[Replica B
Node 3] R2C[Replica C
Node 4] end subgraph "Range 3" R3A[Replica A
Node 3] R3B[Replica B
Node 4] R3C[Replica C
Node 5] end R1A -.sync.-> R1B R1B -.sync.-> R1C R1C -.sync.-> R1A R2A -.sync.-> R2B R2B -.sync.-> R2C R2C -.sync.-> R2A R3A -.sync.-> R3B R3B -.sync.-> R3C R3C -.sync.-> R3A style R1A fill:#4CAF50 style R1B fill:#4CAF50 style R1C fill:#4CAF50 style R2A fill:#2196F3 style R2B fill:#2196F3 style R2C fill:#2196F3 style R3A fill:#FF9800 style R3B fill:#FF9800 style R3C fill:#FF9800

ข้อกำหนดเบื้องต้น (Prerequisites)

ความต้องการของระบบ

สำหรับแต่ละ Node:

ซอฟต์แวร์ที่ต้องติดตั้ง

  1. Docker Engine (version 20.10+)
  2. Docker Swarm (initialized)
  3. อย่างน้อย 5 เครื่อง สำหรับ 5-node cluster

การเตรียม Docker Swarm

ก่อนเริ่มติดตั้ง CockroachDB เราต้องเตรียม Docker Swarm cluster ให้พร้อมก่อน

ขั้นตอนที่ 1: การตั้งค่า Docker Swarm

1.1 เตรียม Nodes

สมมติว่าเรามี 5 เครื่อง:

1.2 Initialize Swarm (บน Manager Node)

# บน manager1 (192.168.1.10)
docker swarm init --advertise-addr 192.168.1.10

# คำสั่งนี้จะแสดง token สำหรับ worker nodes
# ตัวอย่าง output:
# Swarm initialized: current node (xxxxx) is now a manager.
# 
# To add a worker to this swarm, run the following command:
#     docker swarm join --token SWMTKN-1-xxxxx 192.168.1.10:2377

1.3 เพิ่ม Worker Nodes

# บนแต่ละ worker node (worker1-4)
docker swarm join --token SWMTKN-1-xxxxx 192.168.1.10:2377

1.4 ตรวจสอบ Swarm Cluster

# บน manager node
docker node ls

# ตัวอย่าง output:
# ID                            HOSTNAME   STATUS    AVAILABILITY   MANAGER STATUS
# abc123 *                      manager1   Ready     Active         Leader
# def456                        worker1    Ready     Active         
# ghi789                        worker2    Ready     Active         
# jkl012                        worker3    Ready     Active         
# mno345                        worker4    Ready     Active         

ขั้นตอนที่ 2: การเตรียม Certificates สำหรับโหมด Secure

CockroachDB ในโหมด secure ต้องการ certificates สำหรับการเข้ารหัสการสื่อสารและการยืนยันตัวตน

2.1 สร้าง Directory สำหรับ Certificates

# บน manager node
mkdir -p /opt/cockroachdb/certs
mkdir -p /opt/cockroachdb/ca
cd /opt/cockroachdb

2.2 สร้าง CA Certificate

# ติดตั้ง cockroach binary สำหรับสร้าง certificates
# ดาวน์โหลด cockroach binary
wget -qO- https://binaries.cockroachdb.com/cockroach-v23.1.13.linux-amd64.tgz | tar xvz
sudo cp cockroach-v23.1.13.linux-amd64/cockroach /usr/local/bin/
sudo chmod +x /usr/local/bin/cockroach

# สร้าง CA certificate
cockroach cert create-ca \
  --certs-dir=/opt/cockroachdb/certs \
  --ca-key=/opt/cockroachdb/ca/ca.key \
  --overwrite

# สร้างไฟล์:
# - /opt/cockroachdb/certs/ca.crt (CA certificate)
# - /opt/cockroachdb/ca/ca.key (CA private key)

2.3 สร้าง Node Certificates

# สร้าง certificate สำหรับ nodes
# ใช้ wildcard หรือระบุทุก node
cockroach cert create-node \
  localhost \
  127.0.0.1 \
  cockroach-1 \
  cockroach-2 \
  cockroach-3 \
  cockroach-4 \
  cockroach-5 \
  *.cockroachdb \
  192.168.1.10 \
  192.168.1.11 \
  192.168.1.12 \
  192.168.1.13 \
  192.168.1.14 \
  --certs-dir=/opt/cockroachdb/certs \
  --ca-key=/opt/cockroachdb/ca/ca.key \
  --overwrite

# สร้างไฟล์:
# - /opt/cockroachdb/certs/node.crt
# - /opt/cockroachdb/certs/node.key

2.4 สร้าง Client Certificate สำหรับ root user

# สร้าง certificate สำหรับ root user
cockroach cert create-client \
  root \
  --certs-dir=/opt/cockroachdb/certs \
  --ca-key=/opt/cockroachdb/ca/ca.key \
  --overwrite

# สร้างไฟล์:
# - /opt/cockroachdb/certs/client.root.crt
# - /opt/cockroachdb/certs/client.root.key

2.5 ตั้งค่า Permissions

chmod 600 /opt/cockroachdb/certs/*.key
chmod 644 /opt/cockroachdb/certs/*.crt

2.6 แสดงรายการ Certificates

ls -la /opt/cockroachdb/certs/

# ควรมีไฟล์:
# ca.crt                 - CA certificate
# node.crt               - Node certificate
# node.key               - Node private key
# client.root.crt        - Root client certificate
# client.root.key        - Root client private key

ขั้นตอนที่ 3: การสร้าง Docker Network และ Volume

3.1 สร้าง Overlay Network

# สร้าง overlay network สำหรับ CockroachDB cluster
docker network create \
  --driver overlay \
  --attachable \
  cockroachdb-network

3.2 ตรวจสอบ Network

docker network ls | grep cockroachdb

# Output:
# xxxxxxxxxx   cockroachdb-network   overlay   swarm

3.3 Label Nodes สำหรับการกระจาย

# Label แต่ละ node เพื่อควบคุมการกระจาย containers
docker node update --label-add cockroachdb.node=1 manager1
docker node update --label-add cockroachdb.node=2 worker1
docker node update --label-add cockroachdb.node=3 worker2
docker node update --label-add cockroachdb.node=4 worker3
docker node update --label-add cockroachdb.node=5 worker4

# ตรวจสอบ labels
docker node inspect manager1 --format '{{ .Spec.Labels }}'

ขั้นตอนที่ 4: การสร้าง Docker Stack Configuration

4.1 สร้าง Docker Compose File

สร้างไฟล์ cockroachdb-stack.yml:

version: '3.8'

services:
  cockroach-1:
    image: cockroachdb/cockroach:v23.1.13
    hostname: cockroach-1
    networks:
      - cockroachdb-network
    volumes:
      - cockroach-1-data:/cockroach/cockroach-data
      - /opt/cockroachdb/certs:/cockroach/certs:ro
    command: start --certs-dir=/cockroach/certs --advertise-addr=cockroach-1 --join=cockroach-1,cockroach-2,cockroach-3,cockroach-4,cockroach-5 --cache=.25 --max-sql-memory=.25
    deploy:
      placement:
        constraints:
          - node.labels.cockroachdb.node == 1
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
    ports:
      - target: 26257
        published: 26257
        mode: host
      - target: 8080
        published: 8080
        mode: host

  cockroach-2:
    image: cockroachdb/cockroach:v23.1.13
    hostname: cockroach-2
    networks:
      - cockroachdb-network
    volumes:
      - cockroach-2-data:/cockroach/cockroach-data
      - /opt/cockroachdb/certs:/cockroach/certs:ro
    command: start --certs-dir=/cockroach/certs --advertise-addr=cockroach-2 --join=cockroach-1,cockroach-2,cockroach-3,cockroach-4,cockroach-5 --cache=.25 --max-sql-memory=.25
    deploy:
      placement:
        constraints:
          - node.labels.cockroachdb.node == 2
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
    ports:
      - target: 26257
        published: 26257
        mode: host
      - target: 8080
        published: 8080
        mode: host

  cockroach-3:
    image: cockroachdb/cockroach:v23.1.13
    hostname: cockroach-3
    networks:
      - cockroachdb-network
    volumes:
      - cockroach-3-data:/cockroach/cockroach-data
      - /opt/cockroachdb/certs:/cockroach/certs:ro
    command: start --certs-dir=/cockroach/certs --advertise-addr=cockroach-3 --join=cockroach-1,cockroach-2,cockroach-3,cockroach-4,cockroach-5 --cache=.25 --max-sql-memory=.25
    deploy:
      placement:
        constraints:
          - node.labels.cockroachdb.node == 3
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
    ports:
      - target: 26257
        published: 26257
        mode: host
      - target: 8080
        published: 8080
        mode: host

  cockroach-4:
    image: cockroachdb/cockroach:v23.1.13
    hostname: cockroach-4
    networks:
      - cockroachdb-network
    volumes:
      - cockroach-4-data:/cockroach/cockroach-data
      - /opt/cockroachdb/certs:/cockroach/certs:ro
    command: start --certs-dir=/cockroach/certs --advertise-addr=cockroach-4 --join=cockroach-1,cockroach-2,cockroach-3,cockroach-4,cockroach-5 --cache=.25 --max-sql-memory=.25
    deploy:
      placement:
        constraints:
          - node.labels.cockroachdb.node == 4
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
    ports:
      - target: 26257
        published: 26257
        mode: host
      - target: 8080
        published: 8080
        mode: host

  cockroach-5:
    image: cockroachdb/cockroach:v23.1.13
    hostname: cockroach-5
    networks:
      - cockroachdb-network
    volumes:
      - cockroach-5-data:/cockroach/cockroach-data
      - /opt/cockroachdb/certs:/cockroach/certs:ro
    command: start --certs-dir=/cockroach/certs --advertise-addr=cockroach-5 --join=cockroach-1,cockroach-2,cockroach-3,cockroach-4,cockroach-5 --cache=.25 --max-sql-memory=.25
    deploy:
      placement:
        constraints:
          - node.labels.cockroachdb.node == 5
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
    ports:
      - target: 26257
        published: 26257
        mode: host
      - target: 8080
        published: 8080
        mode: host

networks:
  cockroachdb-network:
    external: true

volumes:
  cockroach-1-data:
  cockroach-2-data:
  cockroach-3-data:
  cockroach-4-data:
  cockroach-5-data:

4.2 อธิบาย Configuration

Command Parameters:

Port Mappings:

Volumes:

ขั้นตอนที่ 5: Deploy CockroachDB Stack

5.1 Deploy Stack

# Deploy stack to Docker Swarm
docker stack deploy -c cockroachdb-stack.yml cockroachdb

# Output:
# Creating service cockroachdb_cockroach-1
# Creating service cockroachdb_cockroach-2
# Creating service cockroachdb_cockroach-3
# Creating service cockroachdb_cockroach-4
# Creating service cockroachdb_cockroach-5

5.2 ตรวจสอบ Services

# ดู services ที่สร้าง
docker stack services cockroachdb

# Output:
# ID             NAME                        MODE         REPLICAS   IMAGE
# xxx1           cockroachdb_cockroach-1     replicated   1/1        cockroachdb/cockroach:v23.1.13
# xxx2           cockroachdb_cockroach-2     replicated   1/1        cockroachdb/cockroach:v23.1.13
# xxx3           cockroachdb_cockroach-3     replicated   1/1        cockroachdb/cockroach:v23.1.13
# xxx4           cockroachdb_cockroach-4     replicated   1/1        cockroachdb/cockroach:v23.1.13
# xxx5           cockroachdb_cockroach-5     replicated   1/1        cockroachdb/cockroach:v23.1.13

5.3 ตรวจสอบ Containers

# ดู containers ที่ running
docker stack ps cockroachdb

# หรือดูแบบละเอียด
docker stack ps cockroachdb --no-trunc

5.4 ดู Logs

# ดู logs ของ service ใดๆ
docker service logs cockroachdb_cockroach-1 -f

# ดู logs ของทุก services
docker service logs cockroachdb_cockroach-1 cockroachdb_cockroach-2 cockroachdb_cockroach-3 -f

ขั้นตอนที่ 6: Initialize Cluster

6.1 Initialize Cluster

หลังจาก deploy แล้ว เราต้อง initialize cluster:

# Execute init command บน node แรก
docker exec -it $(docker ps -q -f name=cockroachdb_cockroach-1) \
  ./cockroach init \
  --certs-dir=/cockroach/certs \
  --host=cockroach-1

# Output:
# Cluster successfully initialized

6.2 ตรวจสอบ Cluster Status

# ตรวจสอบสถานะ cluster
docker exec -it $(docker ps -q -f name=cockroachdb_cockroach-1) \
  ./cockroach node status \
  --certs-dir=/cockroach/certs \
  --host=cockroach-1

# Output แสดง:
# id | address | sql_address | build | started_at | updated_at | locality | is_available | is_live

ขั้นตอนที่ 7: การเข้าถึง CockroachDB

7.1 เข้าถึงผ่าน SQL Shell

# เข้าสู่ SQL shell
docker exec -it $(docker ps -q -f name=cockroachdb_cockroach-1) \
  ./cockroach sql \
  --certs-dir=/cockroach/certs \
  --host=cockroach-1

# หรือจาก host machine (ถ้าติดตั้ง cockroach binary)
cockroach sql \
  --certs-dir=/opt/cockroachdb/certs \
  --host=192.168.1.10:26257

# Output:
# #
# # Welcome to the CockroachDB SQL shell.
# # All statements must be terminated by a semicolon.
# # To exit, type: \q.
# #

7.2 เข้าถึง Admin UI

เปิดเว็บบราวเซอร์และไปที่:

https://192.168.1.10:8080
หรือ
https://<any-node-ip>:8080

หมายเหตุ: เนื่องจากใช้ self-signed certificate จะมีคำเตือนจากบราวเซอร์ ให้ proceed ต่อได้

ขั้นตอนที่ 8: ตัวอย่างการใช้งาน

8.1 สร้าง Database และ Table

-- สร้าง database
CREATE DATABASE company;

-- ใช้ database
USE company;

-- สร้าง table
CREATE TABLE employees (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    first_name STRING NOT NULL,
    last_name STRING NOT NULL,
    email STRING UNIQUE NOT NULL,
    department STRING,
    salary DECIMAL(10,2),
    hire_date DATE DEFAULT CURRENT_DATE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    INDEX idx_department (department),
    INDEX idx_email (email)
);

-- สร้าง table สำหรับ departments
CREATE TABLE departments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name STRING NOT NULL,
    budget DECIMAL(12,2),
    manager_id UUID,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- แสดงโครงสร้างของ table
SHOW CREATE TABLE employees;

8.2 Insert ข้อมูล

-- Insert ข้อมูลพนักงาน
INSERT INTO employees (first_name, last_name, email, department, salary) VALUES
    ('สมชาย', 'ใจดี', 'somchai@company.com', 'Engineering', 55000.00),
    ('สมหญิง', 'รักงาน', 'somsing@company.com', 'Engineering', 52000.00),
    ('ประยุทธ', 'ทำงานหนัก', 'prayut@company.com', 'Sales', 48000.00),
    ('วิภา', 'ขยัน', 'wipa@company.com', 'Marketing', 50000.00),
    ('นพดล', 'สุขใจ', 'nopadon@company.com', 'HR', 45000.00);

-- Insert departments
INSERT INTO departments (name, budget) VALUES
    ('Engineering', 5000000.00),
    ('Sales', 3000000.00),
    ('Marketing', 2000000.00),
    ('HR', 1500000.00);

-- ตรวจสอบข้อมูล
SELECT * FROM employees;

8.3 Query ข้อมูล

-- Query พื้นฐาน
SELECT first_name, last_name, department, salary 
FROM employees 
ORDER BY salary DESC;

-- Query ด้วย JOIN
SELECT 
    e.first_name,
    e.last_name,
    e.department,
    d.budget as department_budget
FROM employees e
JOIN departments d ON e.department = d.name;

-- Aggregate functions
SELECT 
    department,
    COUNT(*) as employee_count,
    AVG(salary) as avg_salary,
    MAX(salary) as max_salary,
    MIN(salary) as min_salary
FROM employees
GROUP BY department
ORDER BY avg_salary DESC;

-- Window functions
SELECT 
    first_name,
    last_name,
    department,
    salary,
    AVG(salary) OVER (PARTITION BY department) as dept_avg_salary,
    RANK() OVER (PARTITION BY department ORDER BY salary DESC) as salary_rank
FROM employees;

8.4 Transactions

-- เริ่ม transaction
BEGIN;

-- Update salary
UPDATE employees 
SET salary = salary * 1.10 
WHERE department = 'Engineering';

-- ตรวจสอบผลลัพธ์
SELECT first_name, last_name, salary 
FROM employees 
WHERE department = 'Engineering';

-- Commit หรือ Rollback
COMMIT;
-- หรือ
-- ROLLBACK;

8.5 การทดสอบ High Availability

-- Query เพื่อดูการกระจายข้อมูล
SHOW RANGES FROM TABLE employees;

-- ดู replica placements
SELECT 
    start_key,
    end_key,
    replicas,
    lease_holder
FROM [SHOW RANGES FROM TABLE employees];

ขั้นตอนที่ 9: การเชื่อมต่อจาก Application

9.1 Connection String

# Connection string format
postgresql://root@<any-node-ip>:26257/defaultdb?sslmode=verify-full&sslrootcert=<path-to-ca.crt>&sslcert=<path-to-client.root.crt>&sslkey=<path-to-client.root.key>

9.2 Python Example

import psycopg2
from psycopg2 import sql

# Connection parameters
conn_params = {
    'host': '192.168.1.10',
    'port': 26257,
    'database': 'company',
    'user': 'root',
    'sslmode': 'verify-full',
    'sslrootcert': '/path/to/ca.crt',
    'sslcert': '/path/to/client.root.crt',
    'sslkey': '/path/to/client.root.key'
}

# สร้าง connection
try:
    conn = psycopg2.connect(**conn_params)
    conn.set_session(autocommit=True)
    
    # สร้าง cursor
    cur = conn.cursor()
    
    # Execute query
    cur.execute("SELECT first_name, last_name, department FROM employees")
    
    # Fetch results
    rows = cur.fetchall()
    for row in rows:
        print(f"Name: {row[0]} {row[1]}, Department: {row[2]}")
    
    # Close cursor และ connection
    cur.close()
    conn.close()
    
except Exception as e:
    print(f"Error: {e}")

9.3 Node.js Example

const { Pool } = require('pg');
const fs = require('fs');

// สร้าง connection pool
const pool = new Pool({
    host: '192.168.1.10',
    port: 26257,
    database: 'company',
    user: 'root',
    ssl: {
        rejectUnauthorized: true,
        ca: fs.readFileSync('/path/to/ca.crt').toString(),
        cert: fs.readFileSync('/path/to/client.root.crt').toString(),
        key: fs.readFileSync('/path/to/client.root.key').toString()
    }
});

// Query function
async function queryEmployees() {
    const client = await pool.connect();
    try {
        const result = await client.query(
            'SELECT first_name, last_name, department FROM employees'
        );
        console.log('Employees:', result.rows);
    } finally {
        client.release();
    }
}

// Execute
queryEmployees().catch(console.error);

9.4 Go Example

package main

import (
    "database/sql"
    "fmt"
    "log"
    
    _ "github.com/lib/pq"
)

func main() {
    // Connection string
    connStr := "postgresql://root@192.168.1.10:26257/company?" +
        "sslmode=verify-full&" +
        "sslrootcert=/path/to/ca.crt&" +
        "sslcert=/path/to/client.root.crt&" +
        "sslkey=/path/to/client.root.key"
    
    // เปิด connection
    db, err := sql.Open("postgres", connStr)
    if err != nil {
        log.Fatal(err)
    }
    defer db.Close()
    
    // Test connection
    err = db.Ping()
    if err != nil {
        log.Fatal(err)
    }
    
    // Query
    rows, err := db.Query("SELECT first_name, last_name, department FROM employees")
    if err != nil {
        log.Fatal(err)
    }
    defer rows.Close()
    
    // Iterate results
    for rows.Next() {
        var firstName, lastName, department string
        err := rows.Scan(&firstName, &lastName, &department)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Printf("Name: %s %s, Department: %s\n", firstName, lastName, department)
    }
}

ขั้นตอนที่ 10: Monitoring และ Maintenance

10.1 ตรวจสอบ Cluster Health

-- ตรวจสอบ cluster metrics
SELECT * FROM crdb_internal.node_metrics;

-- ตรวจสอบ replication status
SHOW CLUSTER SETTING cluster.organization;
SHOW ALL CLUSTER SETTINGS;

-- ดู active queries
SELECT query_id, node_id, user_name, start, query 
FROM crdb_internal.cluster_queries 
ORDER BY start DESC 
LIMIT 10;

-- ดู slow queries
SELECT * FROM crdb_internal.cluster_queries 
WHERE now() - start > INTERVAL '5 seconds';

10.2 Backup และ Restore

-- Backup แบบ full
BACKUP DATABASE company 
TO 'nodelocal://1/backups/company-2024-01-01' 
WITH revision_history;

-- Backup แบบ incremental
BACKUP DATABASE company 
TO 'nodelocal://1/backups/company-2024-01-01' 
AS OF SYSTEM TIME '-10s';

-- Restore database
RESTORE DATABASE company 
FROM 'nodelocal://1/backups/company-2024-01-01';

-- Restore table
RESTORE TABLE employees 
FROM 'nodelocal://1/backups/company-2024-01-01';

10.3 Performance Tuning

-- สร้าง index เพิ่มเติม
CREATE INDEX idx_hire_date ON employees(hire_date);

-- Analyze table statistics
ANALYZE employees;

-- ดู execution plan
EXPLAIN SELECT * FROM employees WHERE department = 'Engineering';
EXPLAIN ANALYZE SELECT * FROM employees WHERE department = 'Engineering';

-- ปรับแต่ง cluster settings
SET CLUSTER SETTING sql.stats.automatic_collection.enabled = true;
SET CLUSTER SETTING kv.range_split.by_load_enabled = true;

10.4 Monitoring Scripts

สร้าง monitoring script monitor-cluster.sh:

#!/bin/bash

# สี
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo "=== CockroachDB Cluster Health Check ==="
echo ""

# ตรวจสอบ services
echo "Checking services..."
docker stack services cockroachdb

echo ""
echo "=== Node Status ==="
docker exec $(docker ps -q -f name=cockroachdb_cockroach-1) \
    ./cockroach node status \
    --certs-dir=/cockroach/certs \
    --host=cockroach-1

echo ""
echo "=== Container Health ==="
for i in {1..5}; do
    container=$(docker ps -q -f name=cockroachdb_cockroach-$i)
    if [ -n "$container" ]; then
        status=$(docker inspect --format='{{.State.Health.Status}}' $container 2>/dev/null)
        if [ "$status" == "healthy" ] || [ -z "$status" ]; then
            echo -e "${GREEN}${NC} cockroach-$i: Running"
        else
            echo -e "${RED}${NC} cockroach-$i: $status"
        fi
    else
        echo -e "${RED}${NC} cockroach-$i: Not found"
    fi
done

echo ""
echo "=== Disk Usage ==="
docker exec $(docker ps -q -f name=cockroachdb_cockroach-1) \
    df -h /cockroach/cockroach-data

ขั้นตอนที่ 11: Load Balancer Configuration

11.1 สร้าง HAProxy Configuration

สร้างไฟล์ haproxy.cfg:

global
    maxconn 4096
    log stdout format raw local0

defaults
    log     global
    mode    tcp
    option  tcplog
    option  dontlognull
    retries 3
    timeout connect 5000ms
    timeout client  50000ms
    timeout server  50000ms

# Admin UI
listen admin
    bind *:8080
    mode http
    stats enable
    stats uri /
    stats refresh 10s

# CockroachDB SQL
listen cockroachdb-sql
    bind *:26257
    mode tcp
    balance roundrobin
    option tcp-check
    server cockroach-1 192.168.1.10:26257 check
    server cockroach-2 192.168.1.11:26257 check
    server cockroach-3 192.168.1.12:26257 check
    server cockroach-4 192.168.1.13:26257 check
    server cockroach-5 192.168.1.14:26257 check

# CockroachDB HTTP/UI
listen cockroachdb-http
    bind *:8081
    mode tcp
    balance roundrobin
    option tcp-check
    server cockroach-1 192.168.1.10:8080 check
    server cockroach-2 192.168.1.11:8080 check
    server cockroach-3 192.168.1.12:8080 check
    server cockroach-4 192.168.1.13:8080 check
    server cockroach-5 192.168.1.14:8080 check

11.2 Deploy HAProxy

# สร้าง directory
mkdir -p /opt/haproxy

# คัดลอก config
cp haproxy.cfg /opt/haproxy/

# Deploy HAProxy
docker service create \
    --name haproxy \
    --publish 26257:26257 \
    --publish 8081:8081 \
    --publish 8080:8080 \
    --mount type=bind,source=/opt/haproxy/haproxy.cfg,target=/usr/local/etc/haproxy/haproxy.cfg \
    --network cockroachdb-network \
    haproxy:latest

การทดสอบ High Availability

Test 1: Node Failure Simulation

# หยุด node 1
docker service scale cockroachdb_cockroach-1=0

# รอสักครู่และตรวจสอบ
docker exec $(docker ps -q -f name=cockroachdb_cockroach-2) \
    ./cockroach node status \
    --certs-dir=/cockroach/certs \
    --host=cockroach-2

# ลอง query ข้อมูล (ควรยังทำงานได้ปกติ)
cockroach sql \
    --certs-dir=/opt/cockroachdb/certs \
    --host=192.168.1.11:26257 \
    --execute="SELECT COUNT(*) FROM company.employees;"

# Start node 1 กลับมา
docker service scale cockroachdb_cockroach-1=1

Test 2: Network Partition Test

# จำลอง network partition (บน worker1)
# Block traffic จาก worker1 ไปยัง nodes อื่น
sudo iptables -A OUTPUT -d 192.168.1.10 -j DROP
sudo iptables -A OUTPUT -d 192.168.1.12 -j DROP

# รอดู automatic rebalancing
# ตรวจสอบ cluster status
docker exec $(docker ps -q -f name=cockroachdb_cockroach-3) \
    ./cockroach node status \
    --certs-dir=/cockroach/certs \
    --host=cockroach-3

# ลบ rules เพื่อ restore network
sudo iptables -D OUTPUT -d 192.168.1.10 -j DROP
sudo iptables -D OUTPUT -d 192.168.1.12 -j DROP

Best Practices

1. Security Best Practices

2. Performance Best Practices

3. High Availability Best Practices

4. Operational Best Practices

Troubleshooting

ปัญหา 1: Node ไม่สามารถ Join Cluster

อาการ:

failed to connect to node

วิธีแก้:

  1. ตรวจสอบ network connectivity:
docker exec $(docker ps -q -f name=cockroachdb_cockroach-1) ping cockroach-2
  1. ตรวจสอบ certificates:
docker exec $(docker ps -q -f name=cockroachdb_cockroach-1) \
    ls -la /cockroach/certs/
  1. ตรวจสอบ logs:
docker service logs cockroachdb_cockroach-1 | tail -50

ปัญหา 2: Certificate Errors

อาการ:

x509: certificate signed by unknown authority

วิธีแก้:

  1. ตรวจสอบว่า ca.crt ถูก mount ถูกต้อง
  2. Regenerate certificates:
cd /opt/cockroachdb
cockroach cert create-node ... --overwrite

ปัญหา 3: Performance Issues

อาการ: Queries ช้า, high latency

วิธีแก้:

  1. ตรวจสอบ slow queries:
SELECT * FROM crdb_internal.cluster_queries 
WHERE now() - start > INTERVAL '5 seconds';
  1. ดู execution plans:
EXPLAIN ANALYZE <your-slow-query>;
  1. สร้าง indexes ที่เหมาะสม:
CREATE INDEX idx_name ON table_name(column_name);

ปัญหา 4: Disk Space Full

อาการ:

insufficient disk space

วิธีแก้:

  1. ตรวจสอบ disk usage:
docker exec $(docker ps -q -f name=cockroachdb_cockroach-1) \
    df -h /cockroach/cockroach-data
  1. Clean up old data หรือ expand disk
  2. ปรับแต่ง GC settings:
SET CLUSTER SETTING kv.range_merge.queue_enabled = true;

การ Upgrade CockroachDB

Step-by-Step Upgrade Process

# 1. Backup ก่อน upgrade
cockroach sql --certs-dir=/opt/cockroachdb/certs \
    --host=192.168.1.10:26257 \
    --execute="BACKUP DATABASE company TO 'nodelocal://1/backups/pre-upgrade';"

# 2. Update image version ใน docker-compose file
# แก้ image: cockroachdb/cockroach:v23.1.13
# เป็น image: cockroachdb/cockroach:v23.2.0

# 3. Update stack (rolling update)
docker stack deploy -c cockroachdb-stack.yml cockroachdb

# 4. Monitor upgrade process
watch docker stack ps cockroachdb

# 5. Verify cluster health
docker exec $(docker ps -q -f name=cockroachdb_cockroach-1) \
    ./cockroach node status \
    --certs-dir=/cockroach/certs \
    --host=cockroach-1

# 6. Finalize upgrade (if needed)
cockroach sql --certs-dir=/opt/cockroachdb/certs \
    --host=192.168.1.10:26257 \
    --execute="SET CLUSTER SETTING version = '23.2';"

สรุป

การติดตั้ง CockroachDB บน Docker Swarm ในโหมด secure ช่วยให้เราได้ระบบฐานข้อมูลที่:

  1. มีความทนทานสูง - ทนต่อการล้มเหลวของ nodes
  2. ปรับขนาดได้ง่าย - เพิ่ม nodes ได้ตามต้องการ
  3. มีความปลอดภัย - ใช้ TLS encryption ในการสื่อสาร
  4. รองรับ SQL - ใช้ SQL syntax ที่คุ้นเคย
  5. Distributed - กระจายข้อมูลอัตโนมัติ

Key Takeaways

แหล่งข้อมูลเพิ่มเติม