ติดตั้ง TICK Stack ใน IoT Workshop

ติดตั้ง TICK Stack ใน IoT Workshop

Showkhun · Workshop ·

Branch: workshop/dev-07-tick-setup Phase: Development (7/9) Repo: kangana1024/iot-workshop


ถ้าเรามองว่า sensor คือคนที่เก็บข้อมูล แล้วถามว่า — ข้อมูลพวกนั้นไปอยู่ที่ไหน? ใครดูแล? ใครเตือนเมื่อมีปัญหา?

คำตอบคือ TICK Stack ครับ มันคือทีมงานเบื้องหลังที่ทำให้ระบบ IoT เราฉลาดขึ้นทันที วันนี้เราจะติดตั้งมันครบ 4 ตัว พร้อม config ที่ใช้งานได้จริง มาลุยกันเลย! (ง •̀_•́)ง


น้องๆ จะได้อะไรจาก workshop นี้

  • เข้าใจว่า TICK Stack คืออะไร และ ทำไม ถึงต้องใช้กับ IoT
  • ติดตั้ง TICK Stack ครบด้วย Docker Compose
  • ตั้งค่า InfluxDB: database, retention policies, continuous queries
  • ตั้งค่า Telegraf agent เชื่อม InfluxDB
  • เปิด Chronograf dashboard และเชื่อมต่อ Kapacitor
  • รัน + ทดสอบ connectivity ทั้งระบบ

ก่อนอื่น — ทำไมต้อง TICK Stack?

ลองนึกภาพว่าอาคารมีเซ็นเซอร์วัดอุณหภูมิ 500 ตัว ส่งข้อมูลทุก 5 วินาที

ถ้าเราเก็บลง MySQL แบบปกติ… ตารางจะพองขึ้น หลายล้านแถวต่อวัน query ช้า index พัง ชีวิตพัง

Time-series database อย่าง InfluxDB ถูกออกแบบมาเพื่อเรื่องนี้โดยเฉพาะ — มันรู้ว่าข้อมูลมี “เวลา” เป็นแกนหลัก เก็บได้เร็ว query ได้เร็ว และลบข้อมูลเก่าอัตโนมัติได้

แล้ว T, C, K ที่เหลือล่ะ? ก็คือทีมที่ต้องทำงานร่วมกัน:

ตัวอักษรComponentเปรียบเหมือน
TelegrafAgent เก็บ metricsพนักงานไปรษณีย์ที่เก็บข้อมูลส่งให้ทุก 10 วินาที
InfluxDBTime-series databaseคลังเก็บของที่ฉลาดพอจะรู้ว่าของเก่าควรทิ้งเมื่อไหร่
ChronografWeb UI / Dashboardหน้าจอ monitor ที่ห้องควบคุม
KapacitorAlerting & Processingยามที่คอยเฝ้าและกดออดแจ้งเตือนเมื่อเจออะไรผิดปกติ

ภาพรวม Architecture

ก่อนลงมือ เราต้องรู้ว่าข้อมูลไหลยังไง:

Mermaid Diagram

ง่ายมาก — ข้อมูลไหลจากซ้ายไปขวา Telegraf คือตัวกลางที่รับจาก MQTT แล้วยัดเข้า InfluxDB ส่วน Chronograf และ Kapacitor นั่งรอดูอยู่ฝั่งขวา


Step 1: โครงสร้าง Directory

# ใน repository iot-workshop
mkdir -p deployments/{influxdb,telegraf,chronograf,kapacitor}
mkdir -p deployments/influxdb/{data,config}
mkdir -p deployments/telegraf/conf.d
mkdir -p deployments/kapacitor/{scripts,data}

โครงสร้างสุดท้ายจะหน้าตาแบบนี้:

deployments/
├── docker-compose.tick.yml
├── influxdb/
│   ├── data/               # persistent data volume
│   └── config/
│       └── influxdb.conf
├── telegraf/
│   ├── telegraf.conf
│   └── conf.d/
│       └── mqtt.conf
├── chronograf/
│   └── data/               # persistent data volume
└── kapacitor/
    ├── kapacitor.conf
    ├── data/               # persistent data volume
    └── scripts/            # TICKscript alerts

Step 2: Docker Compose สำหรับ TICK Stack

ทำไม ถึงใช้ Docker Compose? เพราะเราต้องการให้ทุก container อยู่ใน network เดียวกัน (iot-net) คุยกันได้ด้วย hostname โดยไม่ต้องใช้ IP — เหมือนคนในบ้านเดียวกันตะโกนหาได้เลยโดยไม่ต้องรู้หมายเลขห้อง

สร้างไฟล์ deployments/docker-compose.tick.yml:

version: '3.8'

networks:
  iot-net:
    external: true
    name: iot-workshop_iot-net

volumes:
  influxdb-data:
    driver: local
  influxdb-config:
    driver: local
  chronograf-data:
    driver: local
  kapacitor-data:
    driver: local

services:

  # ─────────────────────────────────────────
  # InfluxDB - Time Series Database
  # ─────────────────────────────────────────
  influxdb:
    image: influxdb:1.8.10
    container_name: iot-influxdb
    restart: unless-stopped
    ports:
      - "8086:8086"   # HTTP API
      - "8088:8088"   # RPC for backup/restore
    environment:
      INFLUXDB_DB: iot_data
      INFLUXDB_ADMIN_USER: admin
      INFLUXDB_ADMIN_PASSWORD: ${INFLUXDB_ADMIN_PASSWORD:-admin123}
      INFLUXDB_USER: telegraf
      INFLUXDB_USER_PASSWORD: ${INFLUXDB_USER_PASSWORD:-telegraf123}
      INFLUXDB_READ_USER: grafana
      INFLUXDB_READ_USER_PASSWORD: ${INFLUXDB_READ_USER_PASSWORD:-grafana123}
      INFLUXDB_HTTP_AUTH_ENABLED: "true"
      INFLUXDB_DATA_ENGINE: tsm1
      INFLUXDB_REPORTING_DISABLED: "true"
      INFLUXDB_HTTP_FLUX_ENABLED: "false"
    volumes:
      - influxdb-data:/var/lib/influxdb
      - ./influxdb/config/influxdb.conf:/etc/influxdb/influxdb.conf:ro
    networks:
      - iot-net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8086/ping"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 30s

  # ─────────────────────────────────────────
  # Telegraf - Metrics Collection Agent
  # ─────────────────────────────────────────
  telegraf:
    image: telegraf:1.29
    container_name: iot-telegraf
    restart: unless-stopped
    depends_on:
      influxdb:
        condition: service_healthy
    environment:
      INFLUXDB_URL: http://influxdb:8086
      INFLUXDB_USER: telegraf
      INFLUXDB_PASSWORD: ${INFLUXDB_USER_PASSWORD:-telegraf123}
      MQTT_BROKER: tcp://mosquitto:1883
      MQTT_USERNAME: ${MQTT_USERNAME:-iot_client}
      MQTT_PASSWORD: ${MQTT_PASSWORD:-mqtt123}
    volumes:
      - ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro
      - ./telegraf/conf.d:/etc/telegraf/telegraf.d:ro
    networks:
      - iot-net

  # ─────────────────────────────────────────
  # Chronograf - Web UI & Dashboard
  # ─────────────────────────────────────────
  chronograf:
    image: chronograf:1.10
    container_name: iot-chronograf
    restart: unless-stopped
    ports:
      - "8888:8888"
    depends_on:
      influxdb:
        condition: service_healthy
    environment:
      INFLUXDB_URL: http://influxdb:8086
      INFLUXDB_USERNAME: admin
      INFLUXDB_PASSWORD: ${INFLUXDB_ADMIN_PASSWORD:-admin123}
      KAPACITOR_URL: http://kapacitor:9092
      TOKEN_SECRET: ${CHRONOGRAF_TOKEN_SECRET:-supersecrettoken}
      GENERIC_NAME: "IoT Workshop"
    volumes:
      - chronograf-data:/var/lib/chronograf
    networks:
      - iot-net

  # ─────────────────────────────────────────
  # Kapacitor - Alerting & Processing
  # ─────────────────────────────────────────
  kapacitor:
    image: kapacitor:1.7
    container_name: iot-kapacitor
    restart: unless-stopped
    ports:
      - "9092:9092"
    depends_on:
      influxdb:
        condition: service_healthy
    environment:
      KAPACITOR_INFLUXDB_0_URLS_0: http://influxdb:8086
      KAPACITOR_INFLUXDB_0_USERNAME: admin
      KAPACITOR_INFLUXDB_0_PASSWORD: ${INFLUXDB_ADMIN_PASSWORD:-admin123}
      KAPACITOR_HOSTNAME: kapacitor
    volumes:
      - ./kapacitor/kapacitor.conf:/etc/kapacitor/kapacitor.conf:ro
      - kapacitor-data:/var/lib/kapacitor
    networks:
      - iot-net

สังเกตว่า Telegraf และ Chronograf มี depends_on: influxdb: condition: service_healthy — นั่นคือมันจะรอจนกว่า InfluxDB healthcheck ผ่านก่อนค่อยเริ่ม ไม่งั้น connect ไม่ติดตั้งแต่แรก (ˊ•͈ ꇴ •͈ˋ)


Step 3: InfluxDB Configuration

ทำไม ต้องมี config file แยก? เพราะ environment variable ตั้งได้แค่ basic settings ส่วน performance tuning (cache size, compaction, timeout) ต้องใช้ config file ถึงจะครบ

สร้างไฟล์ deployments/influxdb/config/influxdb.conf:

[meta]
  dir = "/var/lib/influxdb/meta"
  retention-autocreate = true
  logging-enabled = true

[data]
  dir = "/var/lib/influxdb/data"
  engine = "tsm1"
  wal-dir = "/var/lib/influxdb/wal"

  # ปรับ cache size ตาม RAM ที่มี
  cache-max-memory-size = "1g"
  cache-snapshot-memory-size = "25m"
  cache-snapshot-write-cold-duration = "10m"

  # Compaction settings
  compact-full-write-cold-duration = "4h"
  max-concurrent-compactions = 0     # 0 = auto detect CPUs

  # TSM settings
  max-series-per-database = 1000000
  max-values-per-tag = 100000

[http]
  enabled = true
  bind-address = ":8086"
  auth-enabled = true
  log-enabled = true
  write-tracing = false
  pprof-enabled = false
  https-enabled = false

  # Increase timeouts for heavy queries
  read-timeout = "10s"
  max-row-limit = 10000
  max-connection-limit = 0

[logging]
  format = "logfmt"
  level = "info"
  suppress-logo = false

[subscriber]
  enabled = true
  http-timeout = "30s"
  insecure-skip-verify = false
  ca-certs = ""
  write-concurrency = 40
  write-buffer-size = 1000

[continuous_queries]
  enabled = true
  log-enabled = true
  query-stats-enabled = false
  run-interval = "1s"

Step 4: ตั้งค่า Database และ Retention Policies

นี่คือส่วนที่สำคัญมากครับ เราจะคุยเรื่อง ทำไม ก่อนเลย

ทำไมต้องมี Retention Policy?

ลองนึกภาพ hard disk เป็นตู้เก็บเอกสาร ถ้าเก็บทุกอย่างไว้ตลอดกาล ตู้เต็มแน่นอน

Retention Policy คือ “นโยบายทำลายเอกสาร” — raw data อยู่แค่ 30 วัน (เอาไว้ดู detail) แต่ข้อมูลสรุปรายชั่วโมงอยู่ได้ 1 ปี และสรุปรายวันอยู่ได้ถึง 5 ปี

Raw (30 วัน)  →  Hourly aggregate (1 ปี)  →  Daily aggregate (5 ปี)
[ละเอียดมาก]      [พอประมาณ]                  [ภาพรวม]

ทำไมต้องมี Continuous Query?

เพราะเราไม่อยากนั่งสรุปข้อมูลเองทุกชั่วโมง Continuous Query คือ scheduled task ที่ InfluxDB รันให้อัตโนมัติ — มันจะรวบข้อมูล raw ทุกชั่วโมงแล้วเขียนสรุปไว้ใน retention policy ที่นานกว่า

เชื่อมต่อและตั้งค่า:

# เชื่อมต่อ InfluxDB CLI
docker exec -it iot-influxdb influx -username admin -password admin123

# หรือใช้ curl
curl -X POST http://localhost:8086/query \
  -u admin:admin123 \
  --data-urlencode "q=SHOW DATABASES"

สร้าง Database และ Retention Policies

-- สร้าง database สำหรับข้อมูล IoT
CREATE DATABASE iot_data;

-- สร้าง database สำหรับ telemetry ระยะสั้น (raw data)
CREATE DATABASE iot_raw WITH DURATION 7d REPLICATION 1 NAME "raw_7d";

-- เปลี่ยนไปใช้ iot_data database
USE iot_data;

-- ─────────────────────────────────────
-- Retention Policies
-- ─────────────────────────────────────

-- 1. Raw data: เก็บ 30 วัน (ค่า default)
CREATE RETENTION POLICY "raw_30d"
  ON "iot_data"
  DURATION 30d
  REPLICATION 1
  DEFAULT;

-- 2. Hourly aggregates: เก็บ 1 ปี
CREATE RETENTION POLICY "hourly_1y"
  ON "iot_data"
  DURATION 365d
  REPLICATION 1;

-- 3. Daily aggregates: เก็บ 5 ปี
CREATE RETENTION POLICY "daily_5y"
  ON "iot_data"
  DURATION 1825d
  REPLICATION 1;

-- ตรวจสอบ retention policies
SHOW RETENTION POLICIES ON iot_data;

Continuous Queries สำหรับ Data Downsampling

-- ─────────────────────────────────────
-- Continuous Query: Hourly Aggregation
-- ─────────────────────────────────────

-- อุณหภูมิ: สรุปรายชั่วโมง
CREATE CONTINUOUS QUERY "cq_temperature_hourly"
ON "iot_data"
BEGIN
  SELECT
    MEAN(value)  AS mean_value,
    MAX(value)   AS max_value,
    MIN(value)   AS min_value,
    STDDEV(value) AS stddev_value,
    COUNT(value) AS sample_count
  INTO "iot_data"."hourly_1y"."sensor_data"
  FROM "iot_data"."raw_30d"."sensor_data"
  WHERE "measurement_type" = 'temperature'
  GROUP BY time(1h), "device_id", "location", "building"
END;

-- Humidity: สรุปรายชั่วโมง
CREATE CONTINUOUS QUERY "cq_humidity_hourly"
ON "iot_data"
BEGIN
  SELECT
    MEAN(value)  AS mean_value,
    MAX(value)   AS max_value,
    MIN(value)   AS min_value,
    COUNT(value) AS sample_count
  INTO "iot_data"."hourly_1y"."sensor_data"
  FROM "iot_data"."raw_30d"."sensor_data"
  WHERE "measurement_type" = 'humidity'
  GROUP BY time(1h), "device_id", "location", "building"
END;

-- ─────────────────────────────────────
-- Continuous Query: Daily Aggregation
-- ─────────────────────────────────────

CREATE CONTINUOUS QUERY "cq_sensor_daily"
ON "iot_data"
RESAMPLE EVERY 1h FOR 2h
BEGIN
  SELECT
    MEAN(mean_value)  AS mean_value,
    MAX(max_value)    AS max_value,
    MIN(min_value)    AS min_value,
    SUM(sample_count) AS sample_count
  INTO "iot_data"."daily_5y"."sensor_data"
  FROM "iot_data"."hourly_1y"."sensor_data"
  GROUP BY time(1d), "device_id", "location", "building", "measurement_type"
END;

-- ─────────────────────────────────────
-- Device Status Aggregation
-- ─────────────────────────────────────

CREATE CONTINUOUS QUERY "cq_device_uptime_hourly"
ON "iot_data"
BEGIN
  SELECT
    COUNT(status) AS total_reports,
    SUM(IF(status = 1, 1, 0)) AS online_count
  INTO "iot_data"."hourly_1y"."device_status_summary"
  FROM "iot_data"."raw_30d"."device_status"
  GROUP BY time(1h), "device_id", "location"
END;

-- ตรวจสอบ continuous queries
SHOW CONTINUOUS QUERIES;

ทดสอบ Write และ Query ข้อมูล

# Write ข้อมูล test
curl -X POST "http://localhost:8086/write?db=iot_data&precision=s" \
  -u telegraf:telegraf123 \
  --data-binary 'sensor_data,device_id=device_001,location=floor_1,building=A,measurement_type=temperature value=25.3 1711411200'

# Query ข้อมูล
curl -G "http://localhost:8086/query" \
  -u admin:admin123 \
  --data-urlencode "db=iot_data" \
  --data-urlencode "q=SELECT * FROM sensor_data WHERE time > now() - 1h LIMIT 10"

Step 5: Telegraf Basic Configuration

ทำไม Telegraf ถึงต้องมี config เยอะขนาดนี้? เพราะมันเป็นตัว collect แบบ pluggable — เราบอกมันว่า “รับข้อมูลจากไหน (inputs) แล้วส่งไปไหน (outputs)” ในไฟล์เดียว เปลี่ยน pipeline แค่แก้ config ไม่ต้องแก้ code

สร้างไฟล์ deployments/telegraf/telegraf.conf:

# ─────────────────────────────────────────────────────
# Telegraf Configuration - IoT Workshop
# ─────────────────────────────────────────────────────

[global_tags]
  environment = "production"
  project = "iot-workshop"

[agent]
  interval = "10s"
  round_interval = true
  metric_batch_size = 1000
  metric_buffer_limit = 10000
  collection_jitter = "0s"
  flush_interval = "10s"
  flush_jitter = "0s"
  precision = "0s"
  hostname = "telegraf-agent"
  omit_hostname = false
  debug = false
  quiet = false
  logfile = ""

# ─────────────────────────────────────────────────────
# OUTPUT: InfluxDB
# ─────────────────────────────────────────────────────
[[outputs.influxdb]]
  urls = ["$INFLUXDB_URL"]
  database = "iot_data"
  retention_policy = "raw_30d"
  username = "$INFLUXDB_USER"
  password = "$INFLUXDB_PASSWORD"
  timeout = "10s"
  write_consistency = "any"

  # Connection pool
  content_encoding = "gzip"

# ─────────────────────────────────────────────────────
# INPUT: Internal Telegraf Metrics (self-monitoring)
# ─────────────────────────────────────────────────────
[[inputs.internal]]
  collect_memstats = true

# ─────────────────────────────────────────────────────
# INPUT: CPU & Memory (agent host)
# ─────────────────────────────────────────────────────
[[inputs.cpu]]
  percpu = false
  totalcpu = true
  collect_cpu_time = false
  report_active = false

[[inputs.mem]]
  # no config needed

Step 6: Chronograf Dashboard Setup

Chronograf คือ “ห้องควบคุม” ของเรา เปิดขึ้นมาแล้วเห็นกราฟสวยงาม แต่ก่อนจะสวย ต้องตั้งค่าก่อน

เปิด Chronograf UI

  1. เปิด browser ไปที่ http://localhost:8888
  2. สร้าง account ครั้งแรก (กำหนด username/password)
  3. เชื่อมต่อ InfluxDB

เชื่อมต่อ InfluxDB ผ่าน Chronograf

ที่หน้า Configuration > Add Data Source:

Connection URL:      http://influxdb:8086
Connection Name:     IoT Workshop InfluxDB
Username:            admin
Password:            admin123
Default Retention Policy: raw_30d

สร้าง Dashboard สำหรับ IoT

ที่หน้า Dashboards > Create Dashboard:

Dashboard: IoT Sensor Overview

Cell 1 - Temperature (Line Chart):

Query:
SELECT MEAN("value") AS "temperature"
FROM "iot_data"."raw_30d"."sensor_data"
WHERE "measurement_type" = 'temperature'
  AND time > now() - 1h
GROUP BY time(1m), "device_id"

Visualization: Line Chart
Y-axis: Temperature (°C)

Cell 2 - Humidity (Gauge):

SELECT LAST("value") AS "humidity"
FROM "iot_data"."raw_30d"."sensor_data"
WHERE "measurement_type" = 'humidity'
  AND time > now() - 5m
GROUP BY "device_id"

Cell 3 - Device Status (Table):

SELECT LAST("status") AS "status",
       LAST("battery_level") AS "battery"
FROM "iot_data"."raw_30d"."device_status"
WHERE time > now() - 5m
GROUP BY "device_id", "location"

Step 7: Kapacitor Connection และ Configuration

ทำไม ต้องมี Kapacitor แยก? ก็เพราะ Chronograf ทำได้แค่ “ดู” แต่ Kapacitor ทำได้ “ตัดสินใจ” — มันนั่งอ่าน stream ข้อมูลตลอดเวลา พอเจออุณหภูมิเกิน 35°C ก็ยิง Slack alert ออกมาเลย โดยไม่ต้องรอให้คนมาดูหน้าจอ

เหมือนเปรียบ Chronograf = แพทย์ที่ดูผล Kapacitor = เครื่อง monitor ที่ส่งสัญญาณเตือนเองเมื่อ vital sign ผิดปกติ

Kapacitor Configuration

สร้างไฟล์ deployments/kapacitor/kapacitor.conf:

# ─────────────────────────────────────────────────────
# Kapacitor Configuration - IoT Workshop
# ─────────────────────────────────────────────────────

hostname = "kapacitor"
data_dir = "/var/lib/kapacitor"
skip-config-overrides = false
default-retention-policy = ""

[http]
  bind-address = ":9092"
  auth-enabled = false
  log-enabled = true
  write-tracing = false
  pprof-enabled = false
  https-enabled = false

[logging]
  file = "STDOUT"
  level = "INFO"

# ─────────────────────────────────────────────────────
# InfluxDB Connection
# ─────────────────────────────────────────────────────
[[influxdb]]
  enabled = true
  name = "default"
  default = true
  urls = ["http://influxdb:8086"]
  username = "admin"
  password = "${INFLUXDB_ADMIN_PASSWORD}"
  timeout = 0
  insecure-skip-verify = false
  startup-timeout = "5m"
  disable-subscriptions = false
  subscription-protocol = "http"
  subscription-mode = "cluster"
  kapacitor-hostname = ""
  http-port = 0
  udp-bind = ""
  udp-buffer = 1000
  udp-read-buffer = 0

  [influxdb.subscriptions]
    # Subscribe to all measurements in iot_data
    iot_data = ["raw_30d"]

  [influxdb.excluded-subscriptions]
    _internal = ["monitor"]

# ─────────────────────────────────────────────────────
# Alert Handlers
# ─────────────────────────────────────────────────────

# Slack Notifications
[slack]
  enabled = true
  default = true
  workspace = "iot-workshop"
  url = "${SLACK_WEBHOOK_URL}"
  channel = "#iot-alerts"
  username = "Kapacitor"
  icon-emoji = ":bell:"
  global = false
  state-changes-only = false

# SMTP Email
[smtp]
  enabled = true
  host = "${SMTP_HOST}"
  port = 587
  username = "${SMTP_USER}"
  password = "${SMTP_PASSWORD}"
  from = "[email protected]"
  to = ["[email protected]"]
  no-verify = false
  global = false
  state-changes-only = false

# HTTP (Webhook to Go API)
[httppost]
  [[httppost.endpoint]]
    name = "go-api"
    url = "http://backend:8080/api/v1/alerts/webhook"
    headers = {Content-Type = "application/json", X-Alert-Source = "kapacitor"}
    history-size = 100

เชื่อมต่อ Kapacitor กับ Chronograf

ที่หน้า Chronograf > Configuration > Add Kapacitor Connection:

Kapacitor URL:  http://kapacitor:9092
Name:           IoT Workshop Kapacitor
Username:       (leave empty)
Password:       (leave empty)

Step 8: Network และ การรันระบบ

ตรวจสอบว่า IoT Network มีอยู่แล้ว

# ตรวจสอบ network
docker network ls | grep iot

# ถ้ายังไม่มี สร้างใหม่
docker network create \
  --driver bridge \
  --subnet 172.20.0.0/16 \
  --ip-range 172.20.240.0/20 \
  iot-workshop_iot-net

รัน TICK Stack

cd deployments

# สร้าง .env file
cat > .env << 'EOF'
INFLUXDB_ADMIN_PASSWORD=admin123_secure
INFLUXDB_USER_PASSWORD=telegraf123_secure
INFLUXDB_READ_USER_PASSWORD=grafana123_secure
MQTT_USERNAME=iot_client
MQTT_PASSWORD=mqtt_secure_pass
CHRONOGRAF_TOKEN_SECRET=your_random_secret_here
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
SMTP_HOST=smtp.example.com
[email protected]
SMTP_PASSWORD=smtp_password
EOF

# รัน TICK stack
docker compose -f docker-compose.tick.yml up -d

# ตรวจสอบสถานะ
docker compose -f docker-compose.tick.yml ps

# ดู logs
docker compose -f docker-compose.tick.yml logs -f influxdb
docker compose -f docker-compose.tick.yml logs -f telegraf

ทดสอบ Connectivity

ถ้า TICK Stack ขึ้นมาครบแล้ว ลองยิง ping ดูทีละตัว:

[InfluxDB :8086] --> ping? --> HTTP 204  ✓
[Kapacitor :9092] --> ping? --> {"version":"..."} ✓
[Chronograf :8888] --> ping? --> HTML page ✓
# ทดสอบ InfluxDB
curl -u admin:admin123 http://localhost:8086/ping
# ควรได้ HTTP 204

# ทดสอบ Kapacitor
curl http://localhost:9092/kapacitor/v1/ping
# ควรได้ {"version":"..."}

# ทดสอบ Chronograf
curl http://localhost:8888
# ควรได้ HTML page

# ทดสอบ Write ข้อมูลไปยัง InfluxDB
curl -X POST "http://localhost:8086/write?db=iot_data" \
  -u telegraf:telegraf123 \
  --data-binary 'sensor_data,device_id=test001,measurement_type=temperature value=26.5'

echo "Write successful"

# ทดสอบ Query
curl -G "http://localhost:8086/query" \
  -u admin:admin123 \
  --data-urlencode "db=iot_data" \
  --data-urlencode "q=SELECT * FROM sensor_data WHERE time > now() - 1m"

Step 9: Makefile Commands

เพิ่ม targets ใน Makefile หลัก เพื่อให้ทีมใช้งานง่ายขึ้น ไม่ต้องจำ docker compose command ยาวๆ:

# TICK Stack commands
tick-up:
	@echo "Starting TICK Stack..."
	cd deployments && docker compose -f docker-compose.tick.yml up -d
	@echo "✓ TICK Stack started"

tick-down:
	@echo "Stopping TICK Stack..."
	cd deployments && docker compose -f docker-compose.tick.yml down
	@echo "✓ TICK Stack stopped"

tick-logs:
	cd deployments && docker compose -f docker-compose.tick.yml logs -f

tick-status:
	cd deployments && docker compose -f docker-compose.tick.yml ps

influx-cli:
	docker exec -it iot-influxdb influx -username admin -password $(INFLUXDB_ADMIN_PASSWORD)

tick-reset:
	@echo "WARNING: This will delete all InfluxDB data!"
	@read -p "Continue? [y/N] " confirm && [ "$$confirm" = "y" ]
	cd deployments && docker compose -f docker-compose.tick.yml down -v
	@echo "✓ TICK Stack data cleared"

สรุปสิ่งที่ทำใน Workshop นี้

 ___________________________
|  TICK Stack is ready!     |
|   T - Telegraf      [OK]  |
|   I - InfluxDB      [OK]  |
|   C - Chronograf    [OK]  |
|   K - Kapacitor     [OK]  |
|___________________________|
         \(^o^)/

น้องๆ ทำอะไรไปบ้างในตอนนี้:

สิ่งที่ทำรายละเอียด
Docker Composedeploy ครบ TICK stack บน iot-net
InfluxDBdatabase, retention policies (raw/hourly/daily), continuous queries
Telegrafagent config พร้อม InfluxDB output
Chronografเชื่อมต่อ InfluxDB + Kapacitor, สร้าง dashboard template
Kapacitorconfig พร้อม webhook/Slack/email handlers
Networkทุก container อยู่ใน iot-workshop_iot-net เดียวกัน

สิ่งที่เราสร้างวันนี้คือ “กระดูกสันหลัง” ของระบบ IoT ทั้งหมด ข้อมูลจะไหลจาก sensor ผ่าน MQTT → Telegraf → InfluxDB แล้ว Chronograf กับ Kapacitor ก็นั่งรอดูอยู่


ขั้นตอนต่อไป

ตอนหน้าเราจะไปตั้งค่า Telegraf Pipeline ให้ดึงข้อมูลจาก MQTT แล้วประมวลผลก่อนยัดเข้า InfluxDB — นั่นคือส่วนที่ทำให้ระบบ “ฉลาด” จริงๆ อย่าพลาดนะครับ! (ง •̀_•́)ง