SiamCafe · Blog
Tailscale Mesh กับ GreenOps Sustainability —
บทความ

Tailscale Mesh กับ GreenOps Sustainability —

เผยแพร่ 28 พฤษภาคม 2569

Tailscale Mesh VPN

Tailscale เป็น Mesh VPN ที่สร้างบน WireGuard ให้อุปกรณ์เชื่อมต่อกันโดยตรง ไม่ต้องผ่าน Central Server ติดตั้งง่าย ไม่ต้อง Config Firewall รองรับทุก Platform

GreenOps นำ Sustainability เข้ามาใน IT Operations ลดพลังงานและ Carbon Emissions ใช้ Carbon-aware Computing Right-sizing Auto-scaling เลือก Green Regions

Tailscale Setup และ Configuration

# === Tailscale Installation และ Configuration ===

# 1. ติดตั้ง Tailscale
# Linux
curl -fsSL https://tailscale.com/install.sh | sh

# macOS
brew install tailscale

# Docker
docker run -d --name=tailscale \
  --hostname=my-server \
  -v /var/lib/tailscale:/var/lib/tailscale \
  -v /dev/net/tun:/dev/net/tun \
  --cap-add=NET_ADMIN \
  --cap-add=NET_RAW \
  tailscale/tailscale:latest

# 2. เชื่อมต่อ
sudo tailscale up

# เชื่อมต่อพร้อม Options
sudo tailscale up \
  --advertise-routes=10.0.0.0/24,192.168.1.0/24 \
  --accept-dns=true \
  --hostname=web-server-1

# 3. ตรวจสอบสถานะ
tailscale status
tailscale ip -4
tailscale netcheck
tailscale ping other-device

# 4. Exit Node (Route Internet Traffic)
# บน Exit Node Server
sudo tailscale up --advertise-exit-node

# บน Client
sudo tailscale up --exit-node=exit-node-hostname

# 5. Tailscale SSH
# เปิด Tailscale SSH
sudo tailscale up --ssh

# SSH โดยไม่ต้อง Config Keys
ssh user@device-name

# 6. ACL Policy (tailscale admin console)
# {
#   "acls": [
#     // Dev team สามารถเข้าถึง Dev servers
#     {
#       "action": "accept",
#       "src": ["group:dev"],
#       "dst": ["tag:dev-server:*"]
#     },
#     // Ops team เข้าถึงทุก Server
#     {
#       "action": "accept",
#       "src": ["group:ops"],
#       "dst": ["tag:server:*"]
#     },
#     // ทุกู้คืนเข้าถึง DNS
#     {
#       "action": "accept",
#       "src": ["*"],
#       "dst": ["*:53"]
#     }
#   ],
#   "groups": {
#     "group:dev": ["user1@example.com", "user2@example.com"],
#     "group:ops": ["admin@example.com"]
#   },
#   "tagOwners": {
#     "tag:dev-server": ["group:ops"],
#     "tag:server": ["group:ops"]
#   }
# }

echo "Tailscale configured"
echo "  Mesh VPN: WireGuard-based P2P"
echo "  Exit Node: Route internet traffic"
echo "  SSH: Passwordless via Tailscale"
echo "  ACL: Role-based access control"

GreenOps Automation

# greenops.py — GreenOps Automation สำหรับ Cloud
# pip install boto3 requests

from dataclasses import dataclass, field
from typing import List, Dict, Optional
from datetime import datetime, timedelta
import json

@dataclass
class CloudResource:
    id: str
    name: str
    type: str  # ec2, rds, eks, lambda
    region: str
    instance_type: str
    cpu_avg: float  # % average CPU utilization
    monthly_cost: float
    carbon_kg: float  # estimated monthly CO2 kg
    tags: Dict[str, str] = field(default_factory=dict)

@dataclass
class GreenRecommendation:
    resource_id: str
    action: str  # rightsize, shutdown, migrate, spot
    current_cost: float
    projected_cost: float
    carbon_saved_kg: float
    priority: str  # high, medium, low

class GreenOpsAnalyzer:
    """GreenOps Analyzer สำหรับ Cloud Resources"""

    # Carbon intensity per region (gCO2/kWh)
    CARBON_INTENSITY = {
        "us-east-1": 379,      # Virginia
        "us-west-2": 78,       # Oregon (hydro)
        "eu-west-1": 296,      # Ireland
        "eu-north-1": 8,       # Stockholm (hydro+wind)
        "ap-northeast-1": 462, # Tokyo
        "ap-southeast-1": 408, # Singapore
        "ca-central-1": 22,    # Canada (hydro)
    }

    GREEN_REGIONS = ["eu-north-1", "ca-central-1", "us-west-2"]

    def __init__(self):
        self.resources: List[CloudResource] = []
        self.recommendations: List[GreenRecommendation] = []

    def add_resource(self, resource: CloudResource):
        self.resources.append(resource)

    def analyze(self):
        """วิเคราะห์ Resources สำหรับ GreenOps"""
        self.recommendations = []

        for r in self.resources:
            # 1. Right-sizing: CPU < 10% = oversized
            if r.cpu_avg < 10 and r.type == "ec2":
                self.recommendations.append(GreenRecommendation(
                    resource_id=r.id,
                    action=f"Rightsize {r.instance_type} -> smaller",
                    current_cost=r.monthly_cost,
                    projected_cost=r.monthly_cost * 0.5,
                    carbon_saved_kg=r.carbon_kg * 0.5,
                    priority="high",
                ))

            # 2. Idle Resources: CPU < 2%
            if r.cpu_avg < 2:
                self.recommendations.append(GreenRecommendation(
                    resource_id=r.id,
                    action=f"Shutdown idle {r.name}",
                    current_cost=r.monthly_cost,
                    projected_cost=0,
                    carbon_saved_kg=r.carbon_kg,
                    priority="high",
                ))

            # 3. Region Migration
            if r.region not in self.GREEN_REGIONS:
                green_region = "eu-north-1"
                carbon_ratio = self.CARBON_INTENSITY.get(green_region, 100) / \
                              self.CARBON_INTENSITY.get(r.region, 400)
                self.recommendations.append(GreenRecommendation(
                    resource_id=r.id,
                    action=f"Migrate {r.region} -> {green_region}",
                    current_cost=r.monthly_cost,
                    projected_cost=r.monthly_cost * 1.05,
                    carbon_saved_kg=r.carbon_kg * (1 - carbon_ratio),
                    priority="medium",
                ))

    def dashboard(self):
        """แสดง GreenOps Dashboard"""
        self.analyze()

        total_cost = sum(r.monthly_cost for r in self.resources)
        total_carbon = sum(r.carbon_kg for r in self.resources)
        potential_savings = sum(r.current_cost - r.projected_cost for r in self.recommendations)
        potential_carbon = sum(r.carbon_saved_kg for r in self.recommendations)

        print(f"\n{'='*60}")
        print(f"GreenOps Dashboard — {datetime.now().strftime('%Y-%m-%d')}")
        print(f"{'='*60}")
        print(f"  Resources: {len(self.resources)}")
        print(f"  Monthly Cost: ")
        print(f"  Monthly Carbon: {total_carbon:,.0f} kg CO2")

        print(f"\n  Potential Savings:")
        print(f"    Cost: /month")
        print(f"    Carbon: {potential_carbon:,.0f} kg CO2/month")

        # By Region
        print(f"\n  Carbon by Region:")
        region_carbon = {}
        for r in self.resources:
            region_carbon[r.region] = region_carbon.get(r.region, 0) + r.carbon_kg
        for region, carbon in sorted(region_carbon.items(), key=lambda x: -x[1]):
            intensity = self.CARBON_INTENSITY.get(region, 0)
            green = " (GREEN)" if region in self.GREEN_REGIONS else ""
            print(f"    {region:<20} {carbon:>8,.0f} kg ({intensity} gCO2/kWh){green}")

        # Top Recommendations
        print(f"\n  Top Recommendations:")
        high = [r for r in self.recommendations if r.priority == "high"]
        for rec in high[:5]:
            print(f"    [{rec.priority:>6}] {rec.action}")
            print(f"      Save: /mo, "
                  f"{rec.carbon_saved_kg:,.0f} kg CO2")

# ตัวอย่าง
analyzer = GreenOpsAnalyzer()

resources = [
    CloudResource("i-001", "web-prod-1", "ec2", "us-east-1", "m5.xlarge", 45, 140, 35),
    CloudResource("i-002", "web-prod-2", "ec2", "us-east-1", "m5.xlarge", 42, 140, 35),
    CloudResource("i-003", "dev-server", "ec2", "us-east-1", "m5.2xlarge", 3, 280, 70),
    CloudResource("i-004", "staging", "ec2", "ap-northeast-1", "m5.large", 8, 90, 28),
    CloudResource("i-005", "ml-training", "ec2", "us-west-2", "p3.2xlarge", 65, 2200, 120),
]

for r in resources:
    analyzer.add_resource(r)

analyzer.dashboard()

Tailscale + GreenOps Monitoring

# === Monitoring Stack สำหรับ Tailscale + GreenOps ===
# docker-compose.yml

# version: '3.8'
# services:
#   prometheus:
#     image: prom/prometheus:latest
#     ports:
#       - "9090:9090"
#     volumes:
#       - ./prometheus.yml:/etc/prometheus/prometheus.yml
#
#   grafana:
#     image: grafana/grafana:latest
#     ports:
#       - "3000:3000"
#     environment:
#       GF_SECURITY_ADMIN_PASSWORD: admin
#
#   tailscale-exporter:
#     image: custom/tailscale-exporter:latest
#     environment:
#       TAILSCALE_API_KEY: 

# prometheus.yml
# scrape_configs:
#   - job_name: tailscale
#     static_configs:
#       - targets: ['tailscale-exporter:9100']
#   - job_name: node
#     static_configs:
#       - targets: ['node-exporter:9100']

# === Tailscale API Client ===
# tailscale_monitor.py

import json
from datetime import datetime

class TailscaleMonitor:
    """Monitor Tailscale Network"""

    def __init__(self):
        self.devices = []

    def add_device(self, name, ip, os, online, last_seen, rx_bytes, tx_bytes):
        self.devices.append({
            "name": name, "ip": ip, "os": os,
            "online": online, "last_seen": last_seen,
            "rx_bytes": rx_bytes, "tx_bytes": tx_bytes,
        })

    def network_status(self):
        print(f"\n{'='*55}")
        print(f"Tailscale Network Status")
        print(f"{'='*55}")

        online = sum(1 for d in self.devices if d["online"])
        total = len(self.devices)
        print(f"  Devices: {online}/{total} online")

        total_rx = sum(d["rx_bytes"] for d in self.devices)
        total_tx = sum(d["tx_bytes"] for d in self.devices)
        print(f"  Traffic: RX {total_rx/1e9:.1f} GB | TX {total_tx/1e9:.1f} GB")

        for d in self.devices:
            status = "ONLINE" if d["online"] else "offline"
            print(f"  [{status:>7}] {d['name']:<20} {d['ip']:<16} {d['os']}")

monitor = TailscaleMonitor()
monitor.add_device("web-prod-1", "100.64.0.1", "linux", True, "now", 5e9, 3e9)
monitor.add_device("web-prod-2", "100.64.0.2", "linux", True, "now", 4e9, 2e9)
monitor.add_device("dev-laptop", "100.64.0.3", "macos", True, "now", 2e9, 1e9)
monitor.add_device("ci-runner", "100.64.0.4", "linux", False, "2h ago", 1e9, 0.5e9)
monitor.network_status()

Best Practices

  • ACL First: ตั้ง ACL Policy ก่อนเพิ่ม Devices กำหนดสิทธิ์ตาม Role
  • MagicDNS: เปิด MagicDNS ใช้ Hostname แทน IP เช่น ssh web-prod-1
  • Exit Nodes: ใช้ Exit Nodes สำหรับ Route Traffic ผ่าน Region ที่ต้องการ
  • Green Regions: เลือก Cloud Region ที่ใช้พลังงานสะอาด เช่น eu-north-1, ca-central-1
  • Right-sizing: Review Instance Sizes ทุกเดือน ลดขนาดที่ CPU ต่ำกว่า 10%
  • Auto-shutdown: ตั้ง Schedule ปิด Dev/Staging นอกเวลาทำงาน

Tailscale คืออะไร

Mesh VPN บน WireGuard อุปกรณ์เชื่อมต่อ P2P ไม่ผ่าน Central Server ติดตั้งง่าย ไม่ต้อง Port Forwarding รองรับทุก Platform SSO Login ACL ควบคุมสิทธิ์