ติดตั้ง Apache Kafka บน VPS ระบบ Event Streaming

ติดตั้ง Apache Kafka บน VPS ระบบ Event Streaming

มิถุนายน 2026 · อ่าน 9 นาที

Apache Kafka คือ distributed message broker สำหรับ event streaming — ออกแบบให้ส่ง message ได้นับล้านต่อวินาที ด้วยประสิทธิภาพสูง ทำให้เหมาะสำหรับระบบ real-time data pipeline ต่างจาก RabbitMQ ที่เหมาะสำหรับการส่งข้อความแบบเป้าหมายชัดเจน Kafka เหมาะสำหรับการเก็บเหตุการณ์ (event log) ที่ต้องการให้ consumer หลายตัวอ่านได้พร้อมกัน บทความนี้สอนตั้ง Kafka cluster ด้วย Docker Compose พร้อม Kafka UI dashboard, producer/consumer example, และการจัดการ topic

สถาปัตยกรรม Apache Kafka

ติดตั้ง Kafka ด้วย Docker Compose

mkdir -p /opt/kafka && cd /opt/kafka

# สร้าง docker-compose.yml
cat > docker-compose.yml << 'EOF'
version: "3.8"

services:
  kafka:
    image: confluentinc/cp-kafka:7.5.0
    restart: unless-stopped
    container_name: kafka-broker
    ports:
      - "127.0.0.1:9092:9092"
      - "127.0.0.1:29092:29092"
    environment:
      KAFKA_NODE_ID: 1
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: CONTROLLER:PLAINTEXT,PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:29092,PLAINTEXT_HOST://localhost:9092
      KAFKA_PROCESS_ROLES: broker,controller
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
      KAFKA_CONTROLLER_QUORUM_VOTERS: 1@kafka:29093
      KAFKA_LISTENERS: PLAINTEXT://kafka:29092,CONTROLLER://kafka:29093,PLAINTEXT_HOST://0.0.0.0:9092
      KAFKA_INTER_BROKER_LISTENER_NAME: PLAINTEXT
      KAFKA_CONTROLLER_LISTENER_NAMES: CONTROLLER
      KAFKA_LOG_DIRS: /var/lib/kafka/data
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: "true"
      CLUSTER_ID: MkwNYEubTXePEKdc6tH9kQ

  kafka-ui:
    image: provectuslabs/kafka-ui:latest
    restart: unless-stopped
    container_name: kafka-ui
    ports:
      - "127.0.0.1:8080:8080"
    environment:
      KAFKA_CLUSTERS_0_NAME: kafka-cluster
      KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:29092

volumes:
  kafka_data:
EOF

docker-compose up -d

ตั้ง Nginx Reverse Proxy สำหรับ Kafka UI

nano /etc/nginx/sites-available/kafka-ui

server {
    listen 80;
    server_name kafka.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name kafka.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/kafka.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/kafka.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
    }
}

ln -s /etc/nginx/sites-available/kafka-ui /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx
certbot --nginx -d kafka.yourdomain.com

สร้าง Topic และ Producer/Consumer

# ตรวจสอบว่า Kafka ready
docker exec kafka-broker kafka-broker-api-versions.sh --bootstrap-server localhost:9092

# สร้าง topic "orders" ด้วย 3 partition
docker exec kafka-broker kafka-topics.sh --bootstrap-server localhost:9092 \
  --create --topic orders \
  --partitions 3 \
  --replication-factor 1

# ดู topic ที่สร้าง
docker exec kafka-broker kafka-topics.sh --bootstrap-server localhost:9092 --list

# ดูรายละเอียด topic
docker exec kafka-broker kafka-topics.sh --bootstrap-server localhost:9092 \
  --describe --topic orders

Producer — ส่ง Message ไปยัง Topic

# เปิด console producer สำหรับ topic "orders"
docker exec -it kafka-broker kafka-console-producer.sh \
  --bootstrap-server localhost:9092 \
  --topic orders

# พิมพ์ message แล้วกด Enter (ทำให้ producer ส่งไปยัง topic)
# ตัวอย่าง message:
{"order_id": 1001, "customer": "Alice", "amount": 500}
{"order_id": 1002, "customer": "Bob", "amount": 1200}
{"order_id": 1003, "customer": "Charlie", "amount": 750}

Consumer — อ่าน Message จาก Topic

# เปิด console consumer สำหรับ topic "orders" (อ่าน message ทั้งหมดตั้งแต่เริ่มต้น)
docker exec -it kafka-broker kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic orders \
  --from-beginning \
  --group my-consumer-group

# ถ้าต้องการอ่าน message เฉพาะอันที่ส่งใหม่ (ตั้งแต่ตอนนี้เป็นต้นไป)
docker exec -it kafka-broker kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic orders \
  --group my-consumer-group

# ดู consumer group ทั้งหมด
docker exec kafka-broker kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --list

# ดู offset ของ consumer group
docker exec kafka-broker kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --describe \
  --group my-consumer-group

ใช้ Kafka Producer/Consumer ใน Application (Python)

# ติดตั้ง library
pip install kafka-python

# Producer (send_order.py)
from kafka import KafkaProducer
import json

producer = KafkaProducer(
    bootstrap_servers=['localhost:9092'],
    value_serializer=lambda v: json.dumps(v).encode('utf-8')
)

# ส่ง message 10 ครั้ง
for i in range(10):
    message = {
        'order_id': 1000 + i,
        'customer': f'Customer{i}',
        'amount': 100 * (i + 1)
    }
    producer.send('orders', value=message)
    print(f"Sent: {message}")

producer.close()

# Consumer (consume_order.py)
from kafka import KafkaConsumer
import json

consumer = KafkaConsumer(
    'orders',
    bootstrap_servers=['localhost:9092'],
    group_id='python-consumer',
    auto_offset_reset='earliest',
    value_deserializer=lambda m: json.loads(m.decode('utf-8'))
)

for message in consumer:
    print(f"Received: {message.value}")
    print(f"Partition: {message.partition}, Offset: {message.offset}")

ติดตั้ง Kafka Connect สำหรับ Data Pipeline

# เพิ่มใน docker-compose.yml (เหนือ volumes:)
  kafka-connect:
    image: confluentinc/cp-kafka-connect:7.5.0
    restart: unless-stopped
    container_name: kafka-connect
    ports:
      - "127.0.0.1:8083:8083"
    environment:
      CONNECT_BOOTSTRAP_SERVERS: kafka:29092
      CONNECT_GROUP_ID: connect-cluster
      CONNECT_CONFIG_STORAGE_TOPIC: _connect-configs
      CONNECT_OFFSET_STORAGE_TOPIC: _connect-offsets
      CONNECT_STATUS_STORAGE_TOPIC: _connect-status
      CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1
      CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1
      CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1
      CONNECT_REST_ADVERTISED_HOST_NAME: kafka-connect
      CONNECT_REST_PORT: 8083
    depends_on:
      - kafka

# Restart docker-compose
docker-compose down && docker-compose up -d

# ตรวจสอบว่า Kafka Connect พร้อม
curl -s http://localhost:8083 | python3 -m json.tool

# ดู plugins ที่ติดตั้งแล้ว
curl -s http://localhost:8083/connector-plugins | python3 -m json.tool
Tip: สำหรับ production ให้ตั้ง replication factor = 3 ขึ้นไป และมี broker อย่างน้อย 3 ตัว ทำให้ถ้า broker 1 ตัวล้ม cluster ยังทำงานได้ต่อ นอกจากนี้ ให้ตั้ง retention.ms เพื่อลบ message เก่าโดยอัตโนมัติ ประหยัด disk space

ตรวจสอบว่า Kafka ทำงานถูกต้อง

# ตรวจ container ทั้งหมด
docker-compose ps

# ตรวจสอบ Kafka broker status ด้วย API
curl -s http://localhost:8080/api/clusters/kafka-cluster | python3 -m json.tool

# ดู topics ทั้งหมด
docker exec kafka-broker kafka-topics.sh --bootstrap-server localhost:9092 --list

# ดูขนาด log ของ topic
docker exec kafka-broker kafka-log-dirs.sh --bootstrap-server localhost:9092 --describe --topic-list orders

# ตรวจ consumer group offset
docker exec kafka-broker kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --describe --group my-consumer-group

# ดู metrics ของ broker (JMX)
curl -s http://localhost:9999 2>/dev/null || echo "JMX metrics ไม่เปิด"
Tip: ใน Kafka UI (https://kafka.yourdomain.com) สามารถดู topics, consumer groups, messages, brokers status ได้ทั้งหมด สะดวกกว่าใช้ CLI — สำหรับเรียนรู้และ debug ใช้ Kafka UI ก่อน เมื่อเข้าใจแล้วค่อยใช้ CLI

แก้ปัญหาที่พบบ่อย

Error: "Connection refused" เมื่อ producer/consumer เชื่อมต่อ

Broker ยังไม่พร้อม หรือ advertised listener ไม่ถูกต้อง — ตรวจสอบว่า KAFKA_ADVERTISED_LISTENERS ชี้ที่ host/port ที่ client สามารถเชื่อมต่อได้

# ตรวจ logs
docker-compose logs kafka

# ถ้า broker ยังไม่เริ่ม รอสัก 10-15 วินาที แล้ว restart
docker-compose restart kafka

# ตรวจ port 9092 ว่าเปิดอยู่
netstat -tlnp | grep 9092 || ss -tlnp | grep 9092

Error: "Topic already exists" เมื่อสร้าง topic ที่มีชื่อเดียวกัน

Topic ได้ถูกสร้างไปแล้ว — ถ้าต้องการสร้างใหม่ให้ลบเก่าออกก่อน

# ลบ topic
docker exec kafka-broker kafka-topics.sh --bootstrap-server localhost:9092 \
  --delete --topic orders

# รอสัก 2-3 วินาที แล้วสร้างใหม่
docker exec kafka-broker kafka-topics.sh --bootstrap-server localhost:9092 \
  --create --topic orders --partitions 3 --replication-factor 1

Consumer ไม่อ่าน message แม้ producer ส่งไปแล้ว

Consumer group มี offset ชี้อยู่ไกล — ถ้ากำหนด --from-beginning ต้องตรวจสอบ auto.offset.reset เป็น earliest

# ดู offset ปัจจุบัน
docker exec kafka-broker kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --describe \
  --group my-consumer-group

# รีเซ็ต offset ไปยัง earliest
docker exec kafka-broker kafka-consumer-groups.sh \
  --bootstrap-server localhost:9092 \
  --reset-offsets \
  --to-earliest \
  --execute \
  --group my-consumer-group \
  --topic orders

# ลอง consumer อีกครั้ง
docker exec -it kafka-broker kafka-console-consumer.sh \
  --bootstrap-server localhost:9092 \
  --topic orders \
  --group my-consumer-group

Kafka UI ไม่แสดง messages

ปกติเกิดจากการเข้าสู่ระบบ UI ช้า หรือ bootstrap connection หลุด — ลอง refresh หน้า

# ตรวจ Kafka UI logs
docker-compose logs kafka-ui

# ตรวจ connection จาก UI ไปยัง broker
docker exec kafka-ui curl -s http://kafka:29092 -I

# ถ้าไม่ได้ restart UI
docker-compose restart kafka-ui

Disk space เต็มเพราะ Kafka log ใหญ่เกินไป

Topic มี retention policy ที่ไม่จำกัด — ตั้ง retention.ms เพื่อลบ message เก่า

# ดู segment ของ topic
ls -lh /var/lib/docker/volumes/kafka_data/_data/

# ตั้ง retention ให้ลบ message เก่ากว่า 24 ชั่วโมง
docker exec kafka-broker kafka-configs.sh --bootstrap-server localhost:9092 \
  --entity-type topics \
  --entity-name orders \
  --alter \
  --add-config retention.ms=86400000

# ลบ segments เก่า
docker exec kafka-broker kafka-log-cleaner.sh --bootstrap-server localhost:9092

คำถามที่พบบ่อย

Kafka กับ RabbitMQ ต่างกันอย่างไร?

Kafka ออกแบบมาสำหรับ event streaming ที่ต้องการให้หลาย consumer อ่านข้อมูลเดียวกันได้ (fan-out) และเก็บ message เป็นเวลานาน RabbitMQ เหมาะสำหรับ message queue ที่ต้องส่งไปยัง consumer เพียงตัวเดียว (point-to-point) และ message ลบออกเมื่อ consumer อ่านแล้ว Kafka ใช้เยอะขึ้นสำหรับระบบ modern data pipeline ที่ต้องการ data replay + multiple subscribers

Kafka partition ต้องตั้งกี่ตัว?

ขึ้นกับจำนวน consumer และปริมาณข้อมูล — หลักการคือ จำนวน partition ควรเท่ากับจำนวน consumer สูงสุด เพราะแต่ละ consumer ใน group จะอ่าน partition เพียงตัวเดียว ถ้า partition มีมากกว่า consumer จะมี partition ที่ไม่มี consumer อ่าน ถ้า consumer มากกว่า partition จะมี consumer ที่ว่างงาน เริ่มต้นตั้ง 3 partition ก่อน แล้วปรับตามการใช้งานจริง

เก็บ Kafka message ได้นานแค่ไหน?

ขึ้นกับ retention policy และ disk space — ตั้ง retention.ms เป็นเวลาที่ต้องการ (เช่น 7 วัน = 604800000 milliseconds) หรือ retention.bytes เป็นขนาดสูงสุด (เช่น 10 GB) Kafka จะลบ segment เก่าโดยอัตโนมัติ ส่วน message ใหม่ที่ยังไม่ลบ consumer ใหม่สามารถอ่านได้ (ต่างจาก RabbitMQ ที่ลบเมื่อ consumer อ่านแล้ว)