Cloudflare Workers: พลังแห่ง Serverless

Cloudflare Workers

Cloudflare Workers — Photo by Carlos Muza on Unsplash

Cloudflare Workers: พลังแห่ง Serverless ที่ขอบของเครือข่าย

ปฏิวัติการพัฒนาเว็บด้วย JavaScript ที่ทำงานใกล้ผู้ใช้มากที่สุด


1. ทำความเข้าใจ Cloudflare Workers

Workers คืออะไร?

  • นิยาม: Cloudflare Workers คือแพลตฟอร์ม serverless ที่ให้คุณรันโค้ด JavaScript/WebAssembly ที่ edge ของเครือข่าย Cloudflare
  • ทำงานที่ไหน: ทำงานบนเซิร์ฟเวอร์กว่า 275 เมืองทั่วโลก ใกล้กับผู้ใช้มากที่สุด
  • ความแตกต่าง: ไม่เหมือนกับ cloud functions ทั่วไปที่ทำงานในศูนย์ข้อมูลเพียงไม่กี่แห่ง
  • ประสิทธิภาพ: เริ่มทำงานในเวลาน้อยกว่า 1 มิลลิวินาที (cold start เร็วมาก)

ทำไมต้องใช้ Workers?

  • ความเร็ว: ลด latency เพราะโค้ดทำงานใกล้ผู้ใช้
  • ความยืดหยุ่น: ปรับแต่งการทำงานของเว็บไซต์ได้โดยไม่ต้องแก้ไขแอปพลิเคชันหลัก
  • ประหยัด: ฟรีสำหรับ 100,000 requests ต่อวัน
  • ง่ายต่อการ scale: รองรับการขยายตัวโดยอัตโนมัติ ไม่ต้องจัดการเซิร์ฟเวอร์

2. เปรียบเทียบ Workers กับ Serverless Platforms อื่นๆ

Workers vs AWS Lambda

คุณสมบัติ Cloudflare Workers AWS Lambda
Cold Start < 1 ms 100-400 ms
ภาษาที่รองรับ JavaScript, WebAssembly หลากหลายภาษา
การกระจายตัว 275+ เมืองทั่วโลก 20+ regions
Free Tier 100,000 requests/วัน 1,000,000 requests/เดือน
ราคาเพิ่มเติม $0.50/ล้าน requests $0.20/ล้าน requests + ค่า compute time

Workers vs Google Cloud Functions

คุณสมบัติ Cloudflare Workers Google Cloud Functions
Cold Start < 1 ms 100-500 ms
ภาษาที่รองรับ JavaScript, WebAssembly Node.js, Python, Go, Java, .NET
การกระจายตัว 275+ เมืองทั่วโลก 20+ regions
Free Tier 100,000 requests/วัน 2,000,000 requests/เดือน
ข้อจำกัด 10ms CPU time/request (ฟรี) 400,000 GB-seconds/เดือน

3. การเริ่มต้นใช้งาน Workers

ขั้นตอนการติดตั้ง

  1. ติดตั้ง Wrangler CLI

    npm install -g @cloudflare/wrangler
    
  2. ล็อกอินเข้าสู่ Cloudflare

    wrangler login
    
  3. สร้างโปรเจกต์ใหม่

    wrangler init my-worker
    cd my-worker
    
  4. แก้ไขไฟล์ src/index.js

    export default {
      async fetch(request, env, ctx) {
        return new Response("Hello World!");
      }
    };
    
  5. ทดสอบโค้ดในเครื่อง

    wrangler dev
    
  6. เผยแพร่ Worker

    wrangler publish
    

โครงสร้างโปรเจกต์พื้นฐาน

my-worker/
├── src/
│   └── index.js      # โค้ดหลักของ Worker
├── wrangler.toml     # ไฟล์การตั้งค่า
└── package.json      # dependencies

การตั้งค่าใน wrangler.toml

name = "my-worker"
main = "src/index.js"
compatibility_date = "2023-05-18"

[triggers]
routes = [
  { pattern = "example.com/api/*", zone_name = "example.com" }
]

4. การเขียน Worker แบบพื้นฐาน

รูปแบบการเขียน Worker

export default {
  // ฟังก์ชัน fetch จะทำงานเมื่อมี HTTP request เข้ามา
  async fetch(request, env, ctx) {
    // request: Request Object ของ fetch API
    // env: environment variables
    // ctx: context object สำหรับใช้งานฟีเจอร์เพิ่มเติม

    // สร้าง Response กลับไป
    return new Response("Hello World!", {
      headers: { "Content-Type": "text/plain" }
    });
  }
};

การจัดการกับ Request

export default {
  async fetch(request, env, ctx) {
    // ดึงข้อมูลจาก URL
    const url = new URL(request.url);
    const path = url.pathname;

    // ดึง HTTP method
    const method = request.method;

    // ดึง headers
    const userAgent = request.headers.get("User-Agent");

    // ดึง query parameters
    const name = url.searchParams.get("name") || "Guest";

    // สร้าง response ตามเงื่อนไข
    if (path === "/api/hello") {
      return new Response(`Hello, ${name}!`, {
        headers: { "Content-Type": "text/plain" }
      });
    }

    // ถ้าไม่ตรงกับเงื่อนไขใดๆ
    return new Response("Not Found", { status: 404 });
  }
};

การจัดการกับ JSON

export default {
  async fetch(request, env, ctx) {
    // ตรวจสอบว่าเป็น POST request หรือไม่
    if (request.method === "POST") {
      // แปลง request body เป็น JSON
      const data = await request.json();

      // ประมวลผลข้อมูล
      const result = {
        message: `Hello, ${data.name}!`,
        timestamp: new Date().toISOString()
      };

      // ส่ง JSON response กลับไป
      return new Response(JSON.stringify(result), {
        headers: {
          "Content-Type": "application/json"
        }
      });
    }

    return new Response("Method not allowed", { status: 405 });
  }
};

5. ตัวอย่างการใช้งานจริง

1. API Proxy และ Transformation

export default {
  async fetch(request, env, ctx) {
    // สร้าง URL ของ API ที่ต้องการเรียก
    const apiUrl = "https://jsonplaceholder.typicode.com/posts";

    // เรียก API ภายนอก
    const response = await fetch(apiUrl);
    const posts = await response.json();

    // แปลงข้อมูลก่อนส่งกลับ
    const transformedPosts = posts.slice(0, 5).map(post => ({
      title: post.title.toUpperCase(),
      snippet: post.body.slice(0, 50) + "..."
    }));

    // ส่งข้อมูลที่แปลงแล้วกลับไป
    return new Response(JSON.stringify(transformedPosts), {
      headers: {
        "Content-Type": "application/json",
        "Cache-Control": "s-maxage=60"
      }
    });
  }
};

2. A/B Testing

export default {
  async fetch(request, env, ctx) {
    // สร้าง URL object
    const url = new URL(request.url);

    // สุ่มเลือกเวอร์ชัน A หรือ B
    const version = Math.random() < 0.5 ? "A" : "B";

    // สร้าง response ตามเวอร์ชัน
    let response;
    if (version === "A") {
      response = new Response(`
        <html>
          <body style="background-color: #f0f0f0;">
            <h1>Welcome to Version A</h1>
            <p>This is the control version of our page.</p>
          </body>
        </html>
      `, {
        headers: { "Content-Type": "text/html" }
      });
    } else {
      response = new Response(`
        <html>
          <body style="background-color: #e0f7fa;">
            <h1>Welcome to Version B</h1>
            <p>This is the experimental version with a new design.</p>
          </body>
        </html>
      `, {
        headers: { "Content-Type": "text/html" }
      });
    }

    // เพิ่ม header เพื่อติดตามว่าผู้ใช้เห็นเวอร์ชันไหน
    response.headers.set("X-Version", version);

    return response;
  }
};

3. Image Optimization

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const imageUrl = url.searchParams.get("url");
    const width = url.searchParams.get("width") || "800";
    const format = url.searchParams.get("format") || "webp";

    if (!imageUrl) {
      return new Response("Missing 'url' parameter", { status: 400 });
    }

    // สร้าง URL สำหรับ Cloudflare Image Resizing
    const resizeUrl = new URL(imageUrl);
    resizeUrl.searchParams.set("width", width);
    resizeUrl.searchParams.set("format", format);

    // เรียกใช้ Cloudflare Image Resizing API
    const imageResponse = await fetch(resizeUrl.toString(), {
      cf: {
        image: {
          width: parseInt(width),
          format: format
        }
      }
    });

    // ส่งรูปภาพที่ปรับขนาดแล้วกลับไป
    return new Response(imageResponse.body, {
      headers: {
        "Content-Type": `image/${format}`,
        "Cache-Control": "public, max-age=31536000"
      }
    });
  }
};

6. การใช้งาน Workers KV

Workers KV คืออะไร?

  • นิยาม: Key-Value storage ที่ทำงานร่วมกับ Workers
  • ความเร็ว: อ่านข้อมูลได้เร็วมาก (เฉลี่ย < 10ms ทั่วโลก)
  • ขนาด: เก็บข้อมูลได้สูงสุด 25MB ต่อ key
  • Free Tier: 1GB storage, 100,000 read operations/วัน, 1,000 write operations/วัน

การตั้งค่า KV Namespace

  1. สร้าง KV Namespace

    wrangler kv:namespace create "MY_KV"
    
  2. เพิ่มใน wrangler.toml

    [[kv_namespaces]]
    binding = "MY_KV"
    id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    

การใช้งาน KV ใน Worker

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const path = url.pathname;

    // อ่านข้อมูลจาก KV
    if (path === "/api/get") {
      const key = url.searchParams.get("key");
      if (!key) {
        return new Response("Missing 'key' parameter", { status: 400 });
      }

      const value = await env.MY_KV.get(key);
      if (value === null) {
        return new Response("Key not found", { status: 404 });
      }

      return new Response(value);
    }

    // เขียนข้อมูลลง KV
    if (path === "/api/set" && request.method === "POST") {
      const { key, value } = await request.json();
      if (!key || !value) {
        return new Response("Missing 'key' or 'value'", { status: 400 });
      }

      await env.MY_KV.put(key, value);
      return new Response("Value stored successfully");
    }

    return new Response("Not Found", { status: 404 });
  }
};

ตัวอย่างการใช้ KV เป็น Cache

export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const apiPath = url.pathname.replace("/api/", "");

    // สร้าง cache key จาก URL
    const cacheKey = `data-${apiPath}`;

    // พยายามดึงข้อมูลจาก cache ก่อน
    const cachedData = await env.MY_KV.get(cacheKey, { type: "json" });
    if (cachedData) {
      return new Response(JSON.stringify(cachedData), {
        headers: {
          "Content-Type": "application/json",
          "X-Cache": "HIT"
        }
      });
    }

    // ถ้าไม่มีใน cache ให้เรียก API จริง
    const apiUrl = `https://api.example.com/${apiPath}`;
    const response = await fetch(apiUrl);
    const data = await response.json();

    // เก็บข้อมูลลง cache (หมดอายุใน 1 ชั่วโมง)
    await env.MY_KV.put(cacheKey, JSON.stringify(data), { expirationTtl: 3600 });

    // ส่งข้อมูลกลับไป
    return new Response(JSON.stringify(data), {
      headers: {
        "Content-Type": "application/json",
        "X-Cache": "MISS"
      }
    });
  }
};

7. การใช้งาน Durable Objects

Durable Objects คืออะไร?

  • นิยาม: Objects ที่เก็บสถานะและทำงานใน single-region
  • ความสามารถ: รองรับการทำงานที่ต้องการความสอดคล้องของข้อมูล (consistency)
  • Use Cases: เกม realtime, chat applications, counters, locks
  • ข้อจำกัด: ไม่มีใน Free Tier (ต้องใช้แพ็กเกจ Paid)

การตั้งค่า Durable Objects

  1. เพิ่มใน wrangler.toml

    [durable_objects]
    bindings = [
      { name = "COUNTER", class_name = "Counter" }
    ]
    
    [[migrations]]
    tag = "v1"
    new_classes = ["Counter"]
    
  2. สร้าง Migration

    wrangler publish --new-class Counter
    

ตัวอย่าง Counter ด้วย Durable Objects

// ประกาศ Durable Object class
export class Counter {
  constructor(state, env) {
    this.state = state;
    this.storage = state.storage;
  }

  // จัดการกับ HTTP requests
  async fetch(request) {
    const url = new URL(request.url);
    let value = await this.storage.get("counter") || 0;

    // เพิ่มค่า counter
    if (url.pathname === "/increment") {
      value++;
      await this.storage.put("counter", value);
    }

    // ลดค่า counter
    if (url.pathname === "/decrement") {
      value--;
      await this.storage.put("counter", value);
    }

    // ส่งค่าปัจจุบันกลับไป
    return new Response(value.toString());
  }
}

// Worker ที่เรียกใช้ Durable Object
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const counterId = env.COUNTER.idFromName("global_counter");
    const counterObj = env.COUNTER.get(counterId);

    // ส่งต่อ request ไปยัง Durable Object
    return counterObj.fetch(request);
  }
};

8. การ Debug และ Testing

การ Debug ใน Local Environment

  1. ใช้ wrangler dev

    wrangler dev
    
    • เปิด local server ที่ port 8787
    • Hot reloading เมื่อแก้ไขโค้ด
    • แสดง console.log() ใน terminal
  2. การใช้ console.log()

    export default {
      async fetch(request, env, ctx) {
        console.log("Request received:", request.url);
        console.log("Method:", request.method);
    
        // ทดลองดู headers
        const headers = {};
        for (const [key, value] of request.headers.entries()) {
          headers[key] = value;
          console.log(`Header: ${key} = ${value}`);
        }
    
        return new Response("Check your console for logs");
      }
    };
    

การทดสอบ Workers

  1. Unit Testing ด้วย Jest

    ติดตั้ง dependencies:

    npm install --save-dev jest @cloudflare/workers-types
    

    สร้างไฟล์ test:

    // worker.test.js
    import { unstable_dev } from 'wrangler';
    
    describe('Worker', () => {
      let worker;
    
      beforeAll(async () => {
        worker = await unstable_dev('src/index.js');
      });
    
      afterAll(async () => {
        await worker.stop();
      });
    
      it('should return Hello World', async () => {
        const resp = await worker.fetch();
        const text = await resp.text();
        expect(text).toContain('Hello World');
      });
    
      it('should return JSON for /api endpoint', async () => {
        const resp = await worker.fetch('/api');
        const data = await resp.json();
        expect(resp.headers.get('Content-Type')).toContain('application/json');
        expect(data).toHaveProperty('message');
      });
    });
    
  2. การทดสอบด้วย Postman หรือ cURL

    ทดสอบด้วย cURL:

    # GET request
    curl http://localhost:8787/api/hello?name=John
    
    # POST request
    curl -X POST http://localhost:8787/api/data \
      -H "Content-Type: application/json" \
      -d '{"name":"John","age":30}'
    

9. การ Deploy และ Monitoring

การ Deploy

  1. Deploy ด้วย Wrangler

    wrangler publish
    
  2. การตั้งค่า Custom Domain

    • ไปที่ Cloudflare Dashboard > Workers & Pages
    • เลือก Worker ที่ต้องการ
    • คลิก “Triggers” > “Add Custom Domain”
    • ใส่โดเมนที่ต้องการ (ต้องอยู่ใน Cloudflare แล้ว)
  3. การตั้งค่า Routes

    # ใน wrangler.toml
    [triggers]
    routes = [
      { pattern = "api.example.com/*", zone_name = "example.com" },
      { pattern = "example.com/api/*", zone_name = "example.com" }
    ]
    

การ Monitoring

  1. Cloudflare Analytics

    • ไปที่ Cloudflare Dashboard > Workers & Pages > Analytics
    • ดูข้อมูล:
      • จำนวน Requests
      • CPU Time
      • ข้อผิดพลาด
      • การกระจายตัวทางภูมิศาสตร์
  2. การใช้ Logs

    • เปิดใช้งาน Logs ใน Dashboard
    • ดู Logs แบบ Real-time ด้วย:
      wrangler tail
      
  3. การตั้งค่า Alerts

    • ตั้งค่าการแจ้งเตือนเมื่อ:
      • Error rate สูงเกินกำหนด
      • CPU time สูงเกินไป
      • จำนวน requests เกินโควต้า

10. ข้อจำกัดและการอัพเกรด

ข้อจำกัดของ Free Tier

  • Requests: 100,000 requests/วัน
  • CPU Time: 10ms ต่อ request
  • Memory: 128MB ต่อ request
  • Environment Variables: จำกัด 32 ตัว
  • Script Size: สูงสุด 1MB
  • KV Storage: 1GB
  • Durable Objects: ไม่มีในแพ็กเกจฟรี

เมื่อไรควรอัพเกรด

  1. ปริมาณการใช้งาน:

    • มี requests เกิน 100,000 ต่อวัน
    • ต้องการ CPU time มากกว่า 10ms ต่อ request
  2. ฟีเจอร์เพิ่มเติม:

    • ต้องการใช้ Durable Objects
    • ต้องการ KV storage มากกว่า 1GB
    • ต้องการ Cron Triggers (ทำงานตามเวลาที่กำหนด)
  3. การสนับสนุน:

    • ต้องการ Support แบบ Priority
    • ต้องการ SLA (Service Level Agreement)

แพ็กเกจที่แนะนำ

  • Workers Paid: $5/เดือน + $0.50/ล้าน requests

    • เหมาะสำหรับโปรเจกต์ที่มีทราฟฟิกปานกลาง
    • ได้ 10 ล้าน requests/เดือน
    • CPU time 50ms ต่อ request
  • Workers Unbound: จ่ายตามการใช้งานจริง

    • เหมาะสำหรับโปรเจกต์ขนาดใหญ่
    • ไม่มีข้อจำกัดด้าน requests
    • CPU time สูงสุด 30 วินาทีต่อ request

11. กรณีศึกษา: การใช้งานจริงของ Startup

กรณีศึกษา 1: E-commerce API Gateway

  • ความท้าทาย: Startup ด้าน e-commerce ต้องการลด latency ของ API และเพิ่มความปลอดภัย
  • การใช้ Workers:
    • สร้าง API Gateway ที่ทำหน้าที่ตรวจสอบ authentication
    • แคชข้อมูลสินค้าด้วย KV
    • ปรับแต่งข้อมูลก่อนส่งไปยังเบราว์เซอร์
  • ผลลัพธ์:
    • ลด latency ลง 60%
    • ลดภาระของ backend servers
    • เพิ่มความปลอดภัยด้วยการกรอง requests ที่ไม่ถูกต้อง

กรณีศึกษา 2: Content Personalization

  • ความท้าทาย: เว็บไซต์ข่าวต้องการแสดงเนื้อหาที่แตกต่างกันตามภูมิภาคของผู้ใช้
  • การใช้ Workers:
    • ตรวจสอบตำแหน่งของผู้ใช้จาก Cloudflare’s CF-IPCountry header
    • ปรับเปลี่ยนเนื้อหาตามภูมิภาค
    • แคชข้อมูลแยกตามภูมิภาคด้วย KV
  • ผลลัพธ์:
    • เพิ่ม engagement ของผู้ใช้ 25%
    • ลดเวลาในการโหลดเว็บไซต์
    • ไม่ต้องแก้ไขระบบหลัก

กรณีศึกษา 3: Authentication Middleware

  • ความท้าทาย: SaaS platform ต้องการระบบ authentication ที่เร็วและปลอดภัย
  • การใช้ Workers:
    • ตรวจสอบ JWT tokens ที่ edge
    • จัดการ rate limiting เพื่อป้องกัน brute force attacks
    • เก็บข้อมูล session ใน KV
  • ผลลัพธ์:
    • ลดภาระของ authentication servers
    • เพิ่มความปลอดภัยด้วยการตรวจสอบที่ edge
    • ลด latency ของการตรวจสอบ token

12. สรุป: ทำไม Cloudflare Workers ถึงเหมาะกับ Startup

  1. ประหยัดต้นทุน
    เริ่มต้นฟรี และขยายตามการเติบโตของธุรกิจ

  2. ประสิทธิภาพสูง
    ลด latency ด้วยการทำงานที่ edge ใกล้ผู้ใช้

  3. ลดความซับซ้อน
    ไม่ต้องจัดการเซิร์ฟเวอร์ ทำให้โฟกัสกับการพัฒนาธุรกิจ

  4. ความยืดหยุ่น
    ปรับแต่งการทำงานของเว็บไซต์ได้โดยไม่ต้องแก้ไขระบบหลัก

  5. ความปลอดภัย
    เพิ่มชั้นความปลอดภัยที่ edge ก่อนถึงระบบหลัก

“Cloudflare Workers เป็นเครื่องมือที่ทรงพลังสำหรับ Startup ที่ต้องการสร้างแอปพลิเคชันที่เร็ว ปลอดภัย และประหยัดต้นทุน โดยไม่ต้องกังวลเรื่องการจัดการ infrastructure”


13. แหล่งเรียนรู้เพิ่มเติม

เอกสารและบทความ:

คอมมูนิตี้:

คอร์สและวิดีโอ:

เคล็ดลับ: Cloudflare มีการอัพเดทฟีเจอร์ใหม่ๆ สำหรับ Workers อยู่เสมอ ติดตาม Cloudflare Blog และ Twitter เพื่อไม่พลาดฟีเจอร์ล่าสุดที่อาจช่วยให้แอปพลิเคชันของคุณดียิ่งขึ้น