Webhooks ใน GitHub/GitLab คืออะไร? ตั้งค่า Auto Deploy

Webhooks คืออะไร?

Webhook เป็นวิธีการสื่อสารจากระบบหนึ่ง (GitHub/GitLab) ไปยังแอปพลิเคชันของคุณ เมื่อมีเหตุการณ์เกิดขึ้น เช่น Push Code หรือ Merge Request ในแง่ของการพัฒนาบน ผู้ให้บริการโฮสติ้ง Cloud VPS หรือ Cloud Hosting ของเรา Webhooks เป็นเครื่องมือที่สำคัญในการสร้าง CI/CD Pipeline อัตโนมัติ ทำให้การ Deploy เป็นไปอย่างรวดเร็วและมีประสิทธิภาพ

วิธีการทำงานของ Webhooks

  • ผู้ใช้ Push Code ไปยัง GitHub/GitLab
  • GitHub/GitLab ส่ง HTTP Request ไปยัง URL ที่คุณลงทะเบียน
  • Webhook Receiver ของคุณรับข้อมูลและตรวจสอบ Signature
  • ดำเนินการ Deploy หรือ Action อื่นๆ โดยอัตโนมัติ
  • ส่งคำตอบกลับไปยัง GitHub/GitLab

ตั้งค่า Webhook ใน GitHub

ขั้นตอน:

  1. ไปที่ Settings > Webhooks
  2. คลิก Add webhook
  3. ใส่ Payload URL (เช่น https://example.com/webhook/github)
  4. เลือก Content type เป็น application/json
  5. เลือก Events (Push, Pull Request, Release เป็นต้น)
  6. เปิด Active
  7. บันทึก

ตั้งค่า Webhook ใน GitLab

  • ไปที่ Settings > Integrations > Webhooks
  • ใส่ URL ของ Webhook Receiver
  • เลือก Trigger Events (Push events, Merge requests, Tag push events)
  • เพิ่ม Secret Token เพื่อความปลอดภัย
  • คลิก Add webhook

สร้าง Webhook Receiver (Node.js)

const express = require('express');
const { execSync } = require('child_process');
const crypto = require('crypto');
const app = express();

app.use(express.json());
app.use(express.raw({ type: 'application/json' }));

function verifySignature(req, secret) {
  const signature = req.headers['x-hub-signature-256'];
  if (!signature) return false;
  const body = req.rawBody || JSON.stringify(req.body);
  const hash = crypto.createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  return `sha256=${hash}` === signature;
}

app.post('/webhook/github', (req, res) => {
  if (!verifySignature(req, process.env.WEBHOOK_SECRET)) {
    return res.status(401).send('Unauthorized');
  }

  const event = req.headers['x-github-event'];
  const payload = req.body;

  if (event === 'push') {
    console.log('Received push event from:', payload.repository.name);
    try {
      const deployDir = process.env.DEPLOY_DIR || '/var/www/app';
      execSync(`cd ${deployDir} && git fetch origin && git checkout ${payload.ref} && npm install && npm run build`, {
        stdio: 'inherit'
      });
      console.log('Deploy successful for:', payload.ref);
      res.status(200).send('Deploy successful');
    } catch (error) {
      console.error('Deploy failed:', error.message);
      res.status(500).send('Deploy failed: ' + error.message);
    }
  } else if (event === 'pull_request') {
    console.log('Received PR event:', payload.action);
    res.status(200).send('PR event processed');
  } else {
    res.status(200).send('Event received but no action taken');
  }
});

app.listen(3000, () => {
  console.log('Webhook receiver running on port 3000');
  console.log('Listening for GitHub webhooks...');
});

Webhook Receiver ด้วย PHP

<?php
// webhook-handler.php
$secret = $_ENV['WEBHOOK_SECRET'] ?? '';
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_HUB_SIGNATURE_256'] ?? '';

// ตรวจสอบ Signature
if ($secret) {
    $expected = 'sha256=' . hash_hmac('sha256', $payload, $secret);
    if (!hash_equals($expected, $signature)) {
        http_response_code(401);
        die('Unauthorized');
    }
}

$event = $_SERVER['HTTP_X_GITHUB_EVENT'] ?? '';
$data = json_decode($payload, true);

if ($event === 'push') {
    error_log("Received push event to: " . $data['ref']);
    $deploy_dir = $_ENV['DEPLOY_DIR'] ?? '/var/www/app';
    $output = shell_exec("cd {$deploy_dir} && git fetch origin && git checkout {$data['ref']} && npm install && npm run build 2>&1");
    error_log($output);
    echo "Deploy successful";
} else {
    echo "Event: $event processed";
}
?>

ความปลอดภัย Webhooks

  • ตั้งค่า Secret Token และตรวจสอบ Signature ทุกครั้ง
  • ใช้ HTTPS เสมอ เพราะข้อมูล Webhook ระบาย payload ของ code
  • จำกัด IP addresses หากเป็นไปได้ (GitHub IP ranges)
  • ตรวจสอบประเภท Event ที่ได้รับเพื่อหลีกเลี่ยงการดำเนินการที่ไม่จำเป็น
  • จำกัดสิทธิ์ของ Deploy User ให้มีเฉพาะสิ่งที่จำเป็น
  • บันทึก Log ของ Webhook Events ทั้งหมด
  • ตั้งเวลา Timeout สำหรับการดำเนินการ Deploy

ตัวอย่างการตรวจสอบ Signature (GitHub)

const crypto = require('crypto');

function verifyGitHubSignature(req, secret) {
  const signature = req.headers['x-hub-signature-256'];
  const body = req.rawBody; // เก็บ raw body ก่อนแปลง JSON
  
  if (!signature) {
    console.warn('No signature found in request headers');
    return false;
  }
  
  const hash = crypto.createHmac('sha256', secret)
    .update(body)
    .digest('hex');
  
  const expectedSignature = `sha256=${hash}`;
  const isValid = crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
  
  return isValid;
}

app.post('/webhook/github', (req, res) => {
  if (!verifyGitHubSignature(req, process.env.WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Unauthorized' });
  }
  
  // Deploy logic here
  res.json({ success: true, message: 'Webhook verified' });
});

บูรณาการกับ Auto Deploy

การใช้ Webhooks ร่วมกับ Auto Deploy บน VPS ทำให้คุณสามารถสร้าง CI/CD Pipeline ที่สมบูรณ์ นอกจากนี้คุณสามารถศึกษาเพิ่มเติมเกี่ยวกับ GitHub Actions สำหรับ CI/CD และ การตั้งค่า Git Auto Deploy ด้วย SSH เพื่อสร้างระบบ Deploy ที่เข้มแข็งยิ่งขึ้น

Webhook ใน GitLab CI/CD

GitLab มี GitLab CI/CD Pipeline ที่รองรับ Webhooks อย่างเต็มที่ ซึ่งสามารถใช้งานได้บน Cloud VPS ของ ผู้ให้บริการโฮสติ้ง โดยไม่ต้องการ Configuration เพิ่มเติมมากมาย

การตรวจสอบและ Debugging

  • ใน GitHub ให้ไปที่ Settings > Webhooks แล้วดู Recent Deliveries
  • ตรวจสอบ Status Code และ Response Body
  • ดู Request/Response Headers
  • เปิด Debug Mode ในแอปพลิเคชันของคุณ

สรุป

Webhooks เป็นวิธีที่มีประสิทธิภาพและความปลอดภัยสูงในการสร้าง Auto Deploy Pipeline ที่ตอบสนองต่อการเปลี่ยนแปลงของ Code ในเวลาจริง ด้วยการตั้งค่า Signature Verification ที่ถูกต้อง คุณสามารถรับประกันความปลอดภัยของระบบ Deploy ของคุณได้