GitHub Actions CI/CD DevOps

GitHub Actions — Automate ทุกอย่างใน CI/CD Pipeline 🤖

By Anirach Mingkhwan DevOps & Vibe Coding 2026
GitHub Actions — hoodie dog at multi-monitor desk

ในโพสต์ CI/CD Pipeline เราเรียนแนวคิด CI/CD ทั่วไป แต่ถ้าถามว่า CI/CD tool ตัวไหนนิยมที่สุดตอนนี้? คำตอบคือ GitHub Actions — ฟรี, อยู่ใน GitHub เลย, ไม่ต้อง setup server แยก!

วันนี้มาเรียนรู้ GitHub Actions แบบละเอียด ตั้งแต่พื้นฐานไปจนถึง production-grade workflows 🤖🐕


🤖 GitHub Actions คืออะไร?

GitHub Actions คือ CI/CD platform ที่ built-in อยู่ใน GitHub — เขียน workflow เป็น YAML แล้ว GitHub รันให้อัตโนมัติ:

การทำงานของ GitHub Actions เป็นไปตามลำดับขั้นตอนที่ชัดเจน เริ่มตั้งแต่ developer push code ไปจนถึงการ deploy ขั้นสุดท้าย แต่ละขั้นตอนมีบทบาทและความสำคัญแตกต่างกัน ทำให้เราสามารถควบคุมกระบวนการ CI/CD ได้อย่างละเอียดและปลอดภัย ลองดูภาพรวมของกระบวนการทั้งหมด:

📝 git push
Trigger event
⚙️ Workflow
.github/workflows/
🏃 Jobs
Run on runners
✅ Steps
Actions + commands

จากแผนภาพข้างต้น เราจะเห็นว่าเมื่อมีการ push code แล้ว GitHub Actions จะเริ่มต้นกระบวนการโดยอัตโนมัติ โดยอ่านไฟล์ Workflow ที่เราเขียนไว้ จากนั้น Jobs ต่างๆ จะทำงานบน Runner (เครื่องเสมือน) และดำเนินการ Steps ต่างๆ ตามที่กำหนด การไหลของข้อมูลนี้ทำให้เรามั่นใจได้ว่าทุก commit จะผ่านการตรวจสอบและ deploy อย่างถูกต้อง

Key Concepts

ก่อนที่จะเริ่มเขียน Workflow แรก เราต้องเข้าใจแนวคิดหลักของ GitHub Actions เสียก่อน เพราะแต่ละ concept มีบทบาทและความสัมพันธ์กันอย่างชัดเจน การเข้าใจ concepts เหล่านี้จะช่วยให้เราออกแบบ CI/CD pipeline ได้อย่างมีประสิทธิภาพ และแก้ปัญหาได้เมื่อมีอะไรผิดพลาด:

Conceptคืออะไรตัวอย่าง
Workflowไฟล์ YAML ที่กำหนด automation.github/workflows/ci.yml
Eventสิ่งที่ trigger workflowpush, pull_request, schedule
Jobชุดของ steps ที่รันบน runner เดียวbuild, test, deploy
Stepคำสั่งหรือ action ทีละตัวrun: npm test
ActionReusable unit of codeactions/checkout@v4
Runnerเครื่องที่รัน jobubuntu-latest, self-hosted

ตารางข้างต้นแสดงให้เห็นว่า GitHub Actions มีโครงสร้างที่เป็นลำดับชั้น โดย Workflow เป็นระดับสูงสุดที่ประกอบด้วย Jobs หลายตัว แต่ละ Job ประกอบด้วย Steps หลายตัว และแต่ละ Step สามารถเป็น Action หรือ command ธรรมดาก็ได้ การเข้าใจโครงสร้างนี้จะช่วยให้เราเขียน Workflow ที่มีความซับซ้อนได้อย่างเป็นระบบ


📝 Workflow แรก — Hello GitHub Actions

เมื่อเข้าใจ concepts หลักแล้ว ตอนนี้มาลองเขียน Workflow แรกกัน เราจะเริ่มต้นด้วย Workflow ง่ายๆ ที่แสดงข้อมูลพื้นฐาน ซึ่งจะช่วยให้เราเข้าใจโครงสร้างของไฟล์ YAML และวิธีการ trigger แต่ละเหตุการณ์ การเขียน Workflow แรกนี้เป็นพื้นฐานสำคัญที่จะนำไปต่อยอดเป็น CI/CD pipeline ที่ซับซ้อนขึ้น:

# .github/workflows/hello.yml
name: Hello CI

# ─── Triggers ───
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# ─── Jobs ───
jobs:
  hello:
    runs-on: ubuntu-latest        # ใช้ Ubuntu runner

    steps:
      - name: Checkout code
        uses: actions/checkout@v4  # ดึง code จาก repo

      - name: Say hello
        run: echo "Hello from GitHub Actions! 🤖"

      - name: Show info
        run: |
          echo "Event: ${{ github.event_name }}"
          echo "Branch: ${{ github.ref_name }}"
          echo "Commit: ${{ github.sha }}"
          echo "Actor: ${{ github.actor }}"
💡 ไฟล์ต้องอยู่ใน .github/workflows/ เท่านั้น GitHub จะ detect อัตโนมัติ!

จาก Workflow ตัวอย่างข้างต้น เราจะเห็นองค์ประกอบสำคัญหลายอย่าง: การกำหนด triggers ผ่าน on, การระบุ runner ด้วย runs-on, การใช้ Actions จาก Marketplace เช่น actions/checkout, และการใช้ GitHub context variables อย่าง ${{ github.actor }} สิ่งเหล่านี้เป็นพื้นฐานที่เราจะใช้ใน Workflow ที่ซับซ้อนขึ้น แล้วก็สิ่งสำคัญคือการ organize โครงสร้างไฟล์ให้อยู่ใน folder ที่ถูกต้อง


🚀 Real-World CI/CD Pipeline

หลังจากลองเขียน Workflow พื้นฐานแล้ว ตอนนี้มาดู production-grade CI/CD pipeline ที่ใช้จริงในงาน pipeline นี้จะครอบคลุมตั้งแต่การทดสอบ, build Docker image, ไปจนถึงการ deploy แต่ละ job จะมี dependency กันและมีการ pass ข้อมูลระหว่างกัน การออกแบบ pipeline ดังนี้จึงต้องคำนึงถึงประสิทธิภาพ, ความปลอดภัย, และการจัดการ secrets อย่างถูกต้อง

Node.js — Test + Build + Deploy

# .github/workflows/ci-cd.yml
name: CI/CD Pipeline

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: 20
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  # ─── Job 1: Test ───
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'                    # cache node_modules

      - name: Install dependencies
        run: npm ci                        # clean install (ใช้ lockfile)

      - name: Lint
        run: npm run lint

      - name: Unit Tests
        run: npm test -- --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  # ─── Job 2: Build Docker Image ───
  build:
    needs: test                            # รอ test ผ่านก่อน
    runs-on: ubuntu-latest
    if: github.event_name == 'push'        # build เฉพาะ push (ไม่ build PR)

    permissions:
      contents: read
      packages: write

    outputs:
      image-tag: ${{ steps.meta.outputs.tags }}

    steps:
      - uses: actions/checkout@v4

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=
            type=ref,event=branch

      - name: Build & Push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          cache-from: type=gha             # GitHub Actions cache
          cache-to: type=gha,mode=max

  # ─── Job 3: Deploy ───
  deploy:
    needs: build
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'    # deploy เฉพาะ main
    environment: production                 # require approval

    steps:
      - uses: actions/checkout@v4

      - name: Deploy to production
        run: |
          echo "Deploying ${{ needs.build.outputs.image-tag }}"
          # kubectl set image deployment/app app=$IMAGE_TAG
          # หรือ update ArgoCD manifests

ตัวอย่าง Workflow ข้างต้นแสดงให้เห็นถึง production-grade CI/CD pipeline ที่มีความซับซ้อนและครอบคลุม โดยแบ่งออกเป็น 3 jobs หลัก: test, build, และ deploy แต่ละ job มี responsibility ที่ชัดเจน และใช้ needs เพื่อกำหนดลำดับการทำงาน การใช้ if conditions ช่วยให้เราควบคุมได้ว่า job ไหนควรรันเมื่อไร และการใช้ environment ช่วยให้เรามี approval gate สำหรับ production deployment

CI/CD Pipeline Flow
🧪 Test Job
Lint
Unit Tests
Coverage
🐳 Build Job
Docker Build
Push to GHCR
Cache layers
🚀 Deploy Job
Production env
Approval gate
Rollout

แผนภาพ architecture ข้างต้นแสดงให้เห็นการไหลของงานใน CI/CD pipeline อย่างชัดเจน เราจะเห็นว่า Test Job เป็น gatekeeper แรกที่ต้องผ่านก่อนถึงจะไปขั้นตอนถัดไป Build Job จะสร้าง Docker image และ push ไปยัง container registry พร้อมกับทำ caching เพื่อเพิ่มความเร็ว และ Deploy Job จะมี approval gate เพื่อความปลอดภัยใน production การออกแบบแบบนี้ช่วยให้เราจับ bugs ได้ตั้งแต่เนิ่นๆ และมั่นใจได้ว่าสิ่งที่ไป production มีคุณภาพ


🎯 Triggers — เมื่อไรจะรัน?

หนึ่งในสิ่งสำคัญที่สุดของ GitHub Actions คือการกำหนด triggers หรือเหตุการณ์ที่จะทำให้ Workflow ทำงาน การเลือก trigger ที่เหมาะสมจะส่งผลต่อประสิทธิภาพและต้นทุนของ CI/CD pipeline มาก เพราะ Workflow บางตัวอาจต้องการทำงานทุกครั้งที่มี code changes บางตัวอาจทำงานแค่ scheduled หรือเมื่อมี manual trigger เท่านั้น การเข้าใจ triggers แต่ละประเภทจะช่วยให้เราออกแบบ automation ที่เหมาะสมกับลักษณะงาน:

on:
  # Push to specific branches
  push:
    branches: [main, develop]
    paths:                               # รันเฉพาะเมื่อไฟล์เหล่านี้เปลี่ยน
      - 'src/**'
      - 'package.json'
    paths-ignore:
      - '**.md'                          # ไม่รันเมื่อแก้ docs

  # Pull Request
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Schedule (cron)
  schedule:
    - cron: '0 2 * * 1'                 # ทุกวันจันทร์ 02:00 UTC

  # Manual trigger (ปุ่มกด)
  workflow_dispatch:
    inputs:
      environment:
        description: 'Deploy environment'
        required: true
        default: 'staging'
        type: choice
        options:
          - staging
          - production

  # Tag/Release
  release:
    types: [published]

  # Triggered by another workflow
  workflow_call:                          # reusable workflow

ตัวอย่าง triggers ข้างต้นแสดงให้เห็นถึงความยืดหยุ่นของ GitHub Actions ในการตอบสนองต่อเหตุการณ์ต่างๆ การใช้ paths และ paths-ignore ช่วยให้เราไม่เสียเวลาไปรัน CI เมื่อแก้แค่ documentation การใช้ schedule เหมาะสำหรับ nightly builds หรือ security scans ส่วน workflow_dispatch ให้ flexibility ในการ trigger แบบ manual พร้อม parameters การเลือก triggers ที่เหมาะสมจะช่วยลดการใช้ minutes และทำให้ development workflow ราบรื่นขึ้น


🔐 Secrets & Variables

เมื่อเราต้องทำ deployment หรือเชื่อมต่อกับ external services เราจำเป็นต้องใช้ credentials และ sensitive data ต่างๆ GitHub Actions มี secrets management ที่ช่วยให้เราเก็บข้อมูลเหล่านี้อย่างปลอดภัย โดยไม่ต้องเขียน hardcode ไว้ใน source code การจัดการ secrets อย่างถูกต้องเป็นสิ่งสำคัญมากสำหรับความปลอดภัยของระบบ และยังช่วยให้เราแยก configuration ระหว่าง environment ต่างๆ ได้อย่างชัดเจน

# ตั้ง Secrets ใน GitHub:
# Settings → Secrets and variables → Actions → New repository secret

# ใช้ใน workflow:
jobs:
  deploy:
    steps:
      - name: Deploy
        env:
          API_KEY: ${{ secrets.API_KEY }}
          DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
        run: |
          echo "Deploying with secret credentials..."
          # $API_KEY และ $DB_PASSWORD ใช้ได้ใน step นี้
          # ⚠️ GitHub จะ mask secrets ใน logs อัตโนมัติ

      - name: Use GitHub Token
        # GITHUB_TOKEN สร้างให้อัตโนมัติ ไม่ต้องตั้งเอง!
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh pr list
          gh release create v1.0.0

จากตัวอย่างข้างต้น เราจะเห็นว่า GitHub สร้าง GITHUB_TOKEN ให้อัตโนมัติในทุก Workflow run ซึ่งช่วยให้เราเรียกใช้ GitHub API ได้โดยไม่ต้องสร้าง token เอง การ mask secrets ใน logs เป็นฟีเจอร์ที่สำคัญมาก เพราะป้องกันไม่ให้ sensitive data รั่วไหลแม้ว่าจะมีการ echo หรือ print ออกมาโดยไม่ตั้งใจ การใช้ secrets ในลักษณะนี้ทำให้เรามั่นใจได้ว่า credentials จะอยู่ในระดับการป้องกันที่เหมาะสม

Environment Secrets

นอกจาก repository-level secrets แล้ว GitHub ยังมี environment-specific secrets ที่ช่วยให้เราแยก configuration ระหว่าง staging และ production ได้ Environment secrets มีความสำคัญมากเพราะช่วยให้เราใช้ credentials ที่แตกต่างกันสำหรับแต่ละ environment และยังสามารถตั้ง protection rules เช่น required reviewers หรือ deployment windows ได้อีกด้วย:

jobs:
  deploy-staging:
    environment: staging               # ใช้ secrets ของ staging
    steps:
      - run: echo "DB=${{ secrets.DATABASE_URL }}"
        # → ได้ staging DB URL

  deploy-production:
    environment: production            # ใช้ secrets ของ production
    # + สามารถตั้ง required reviewers ได้!
    steps:
      - run: echo "DB=${{ secrets.DATABASE_URL }}"
        # → ได้ production DB URL (คนละค่ากับ staging)

Environment secrets เป็นเครื่องมือที่ทรงพลังมากสำหรับการแยก configuration ระหว่าง environments ต่างๆ ไม่เพียงแต่ช่วยในเรื่องความปลอดภัยเท่านั้น แต่ยังช่วยให้เรามีการควบคุมการ deploy ได้ดียิ่งขึ้น เช่น การตั้ง required reviewers สำหรับ production environment หรือการจำกัดเวลาที่สามารถ deploy ได้ ซึ่งเป็นสิ่งสำคัญสำหรับ enterprise environments ที่ต้องการ governance และ compliance


🔀 Matrix Strategy — ทดสอบหลาย versions

ในการพัฒนา software หนึ่งในความท้าทายที่พบบ่อยคือการรองรับ multiple platforms, languages versions, หรือ dependencies ต่างๆ การทดสอบแต่ละ combination แบบ manual จะใช้เวลานานมากและเสี่ยงต่อการพลาด GitHub Actions มี Matrix Strategy ที่ช่วยให้เราทดสอบหลาย combinations พร้อมกันอย่างอัตโนมัติ ซึ่งไม่เพียงแต่ประหยัดเวลาเท่านั้น แต่ยังช่วยให้เราจับ compatibility issues ได้เร็วขึ้น:

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: [18, 20, 22]
        # รัน 3 OS × 3 Node = 9 jobs พร้อมกัน!
      fail-fast: false                  # ไม่หยุดถ้า 1 job fail

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test

  # Matrix with include/exclude
  test-python:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python: ['3.10', '3.11', '3.12']
        django: ['4.2', '5.0']
        exclude:
          - python: '3.10'
            django: '5.0'              # Django 5 ไม่รองรับ Python 3.10
        include:
          - python: '3.12'
            django: '5.0'
            experimental: true         # เพิ่ม flag พิเศษ

Matrix Strategy เป็นหนึ่งในฟีเจอร์ที่ทรงพลังที่สุดของ GitHub Actions เพราะช่วยให้เราครอบคลุม test scenarios ได้อย่างครอบคลุม การใช้ fail-fast: false ทำให้เราเห็นผลลัพธ์ของทุก combinations แม้ว่าบางตัวจะ fail การใช้ exclude และ include ช่วยให้เรา fine-tune matrix ได้ตามความต้องการจริง ซึ่งสำคัญมากเพราะ combinations บางตัวอาจไม่มี practical value หรือไม่รองรับจริง ดังนั้นการออกแบบ matrix ที่ดีต้องสมดุลระหว่างความครอบคลุมกับประสิทธิภาพ


📦 Caching — เร็วขึ้น 10x!

หนึ่งในปัญหาที่พบบ่อยใน CI/CD คือเวลารอที่นานเกินไป โดยเฉพาะขั้นตอนการ download และ install dependencies ซึ่งบางครั้งใช้เวลานานกว่าการทำงานจริง GitHub Actions มี caching mechanism ที่ช่วยลดเวลาเหล่านี้ได้อย่างมาก โดยการเก็บ dependencies ที่ download แล้วไว้ใช้ซ้ำ การใช้ caching อย่างถูกต้องสามารถลดเวลา CI/CD ได้จาก minutes ลงเหลือ seconds ทำให้ development feedback loop เร็วขึ้นมาก:

# ❌ ช้า: install ทุกครั้ง (2-3 นาที)
- run: npm ci

# ✅ เร็ว: cache node_modules (10 วินาที)
- uses: actions/setup-node@v4
  with:
    node-version: 20
    cache: 'npm'                        # built-in cache!

# หรือ cache manual
- uses: actions/cache@v4
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

# Python
- uses: actions/setup-python@v5
  with:
    python-version: '3.12'
    cache: 'pip'

# Docker layer caching
- uses: docker/build-push-action@v5
  with:
    cache-from: type=gha
    cache-to: type=gha,mode=max

จากตัวอย่างข้างต้นจะเห็นว่า caching มีหลายรูปแบบ โดย setup actions ต่างๆ เช่น setup-node และ setup-python มี built-in caching ที่ใช้งานง่ายมาก แค่ระบุ cache: 'npm' หรือ cache: 'pip' ก็ได้ประโยชน์ทันที สำหรับ Docker caching การใช้ GitHub Actions cache (type=gha) จะช่วยให้ layers ที่ไม่เปลี่ยนแปลงไม่ต้อง build ใหม่ ซึ่งจะช่วยประหยัดเวลาและ bandwidth มากมาย การเข้าใจและใช้ caching ให้ถูกต้องเป็นหนึ่งใน quick wins ที่ทำให้ CI/CD pipeline เร็วขึ้นทันที


🧩 Marketplace Actions — อย่าเขียนเอง!

GitHub Marketplace มี actions สำเร็จรูปมากกว่า 20,000 ตัว — ใช้เลย ไม่ต้องเขียนเอง:

แทนที่จะเสียเวลาเขียน scripts หรือ commands ซ้ำๆ เราสามารถใช้ Actions ที่ community พัฒนาและทดสอบแล้ว ซึ่งไม่เพียงแต่ประหยัดเวลาเท่านั้น แต่ยังได้ความน่าเชื่อถือและ maintainability ที่ดีกว่า การเลือกใช้ Actions ที่มีคนใช้เยอะและมี active maintenance จะช่วยลดความเสี่ยงในระยะยาว นี่คือ Actions ยอดนิยมที่ควรรู้จัก:

หมวดActionทำอะไร
Checkoutactions/checkout@v4ดึง code จาก repo
Node.jsactions/setup-node@v4ติดตั้ง Node.js
Pythonactions/setup-python@v5ติดตั้ง Python
Dockerdocker/build-push-action@v5Build + push image
Cacheactions/cache@v4Cache dependencies
Coveragecodecov/codecov-action@v4Upload test coverage
Deployaws-actions/configure-aws-credentials@v4AWS authentication
Releasesoftprops/action-gh-release@v2สร้าง GitHub Release
Slackslackapi/slack-github-action@v1ส่ง notification
Securitygithub/codeql-action@v3Code scanning (SAST)
# Pin action versions ด้วย SHA (security best practice)
# ❌ ไม่ปลอดภัย
- uses: actions/checkout@v4

# ✅ ปลอดภัยกว่า (pin SHA)
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1

ตารางข้างต้นแสดง Actions ที่ใช้บ่อยที่สุด แต่ละตัวเฉพาะทางและมี maintainers ที่เชี่ยวชาญ เช่น actions/setup-node ทีม GitHub เองดูแล หรือ docker/build-push-action ทีม Docker ดูแล การใช้ Actions เหล่านี้ช่วยให้เราได้ best practices และ optimizations ที่ community สั่งสมมา แต่สิ่งสำคัญคือการ pin versions ด้วย SHA เพื่อป้องกัน supply chain attacks ที่อาจเกิดขึ้นหากมีคนแก้ไข Actions ตัวเดิม


♻️ Reusable Workflows — DRY!

เมื่อเรามี repositories หลายตัวหรือ workflows ที่มี patterns คล้ายๆ กัน การ copy-paste YAML จะกลายเป็นปัญหาในการ maintain Reusable Workflows เป็นแนวทางแก้ปัญหานี้โดยการสร้าง workflow template ที่สามารถเรียกใช้ซ้ำได้ พร้อม parameters และ secrets ที่ยืดหยุ่น การใช้ reusable workflows ช่วยให้เรามี central point สำหรับการ update และ improvement แทนที่จะต้องแก้ในทุก repository:

# .github/workflows/reusable-deploy.yml
# Workflow ที่ re-use ได้
name: Deploy

on:
  workflow_call:                         # เปิดให้เรียกจาก workflow อื่น
    inputs:
      environment:
        required: true
        type: string
      image-tag:
        required: true
        type: string
    secrets:
      DEPLOY_KEY:
        required: true

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: ${{ inputs.environment }}
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to ${{ inputs.environment }}
        env:
          DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
        run: |
          echo "Deploying ${{ inputs.image-tag }} to ${{ inputs.environment }}"

จาก reusable workflow ข้างต้น เราจะเห็นการใช้ workflow_call ที่ทำให้ workflow นี้สามารถถูกเรียกจาก workflow อื่นได้ การกำหนด inputs และ secrets ทำให้ workflow มี flexibility ในการรับ parameters ต่างๆ ขณะเดียวกันก็รักษา security ไว้ได้ การออกแบบ reusable workflow ที่ดีควรมี clear interface และ handle edge cases ต่างๆ เพื่อให้ใช้งานได้อย่างน่าเชื่อถือ

# .github/workflows/main.yml
# เรียกใช้ reusable workflow
name: Main Pipeline

on:
  push:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

  deploy-staging:
    needs: test
    uses: ./.github/workflows/reusable-deploy.yml    # เรียก reusable!
    with:
      environment: staging
      image-tag: ${{ github.sha }}
    secrets:
      DEPLOY_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}

  deploy-production:
    needs: deploy-staging
    uses: ./.github/workflows/reusable-deploy.yml    # เรียกซ้ำ!
    with:
      environment: production
      image-tag: ${{ github.sha }}
    secrets:
      DEPLOY_KEY: ${{ secrets.PROD_DEPLOY_KEY }}

ตัวอย่างการใช้ reusable workflow ข้างต้นแสดงให้เห็นถึงประโยชน์ที่ชัดเจน เราสามารถเรียกใช้ workflow เดียวกันสำหรับ staging และ production โดยเปลี่ยนแค่ parameters และ secrets การใช้ needs ช่วยให้เราควบคุมลำดับการ deploy ได้ และการแยก secrets ระหว่าง environments ช่วยให้เรามี appropriate access control reusable workflows นี้เป็น foundation ที่ดีสำหรับการสร้าง standardized deployment processes ทั่วทั้ง organization


🔒 Security Scanning

ในยุคที่ security threats เพิ่มมากขึ้นเรื่อยๆ การทำ security scanning ใน CI/CD pipeline กลายเป็นสิ่งจำเป็น GitHub Actions มี integrations กับ security tools หลากหลายที่ช่วยให้เราจับ vulnerabilities ได้ตั้งแต่ระยะ development ไม่ว่าจะเป็น static analysis, dependency scanning, container scanning, หรือ secret scanning การรัน security checks อย่างอัตโนมัติช่วยให้เรา shift left ในการจัดการความปลอดภัย และลดความเสี่ยงที่จะพบปัญหาใน production:

# .github/workflows/security.yml
name: Security

on:
  push:
    branches: [main]
  schedule:
    - cron: '0 6 * * 1'                 # scan ทุกวันจันทร์

jobs:
  # Code scanning (SAST)
  codeql:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
      - uses: actions/checkout@v4
      - uses: github/codeql-action/init@v3
        with:
          languages: javascript
      - uses: github/codeql-action/analyze@v3

  # Dependency scanning
  dependency-review:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v4
      - uses: actions/dependency-review-action@v4
        with:
          fail-on-severity: high

  # Container scanning
  trivy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: docker build -t myapp:test .
      - uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:test
          severity: CRITICAL,HIGH
          exit-code: 1                   # fail ถ้ามี critical/high

  # Secret scanning
  trufflehog:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0                 # full history
      - uses: trufflesecurity/trufflehog@main
        with:
          extra_args: --only-verified

จากตัวอย่าง security scanning workflow ข้างต้น เราจะเห็นการใช้ tools หลากหลาย: CodeQL สำหรับ static analysis, dependency review สำหรับตรวจสอบ dependencies ใหม่ใน PR, Trivy สำหรับ container scanning, และ TruffleHog สำหรับ secret scanning แต่ละ tool มี specialty และครอบคลุม attack vectors ที่แตกต่างกัน การรวม tools เหล่านี้เข้าด้วยกันจะให้ comprehensive security coverage และช่วยให้เรา catch issues ได้หลากหลายมากขึ้น


🏠 Self-hosted Runners

ถ้า GitHub-hosted runner ไม่พอ (เร็วไม่พอ, ต้องการ GPU, network access พิเศษ):

แม้ว่า GitHub-hosted runners จะสะดวกและใช้งานง่าย แต่บางครั้งเราอาจต้องการความยืดหยุ่นมากกว่านี้ เช่น การใช้ hardware พิเศษ, การเข้าถึง private networks, หรือการ customize environment แบบเจาะจง Self-hosted runners ให้ freedom ในการควบคุมทุกอย่าง แต่ก็มาพร้อมกับความรับผิดชอบในการ setup, maintain, และ secure infrastructure เอง การตัดสินใจเลือกระหว่าง GitHub-hosted และ self-hosted ต้องชั่งน้ำหนักระหว่าง convenience กับ control:

# ติดตั้ง self-hosted runner
# Settings → Actions → Runners → New self-hosted runner
# ทำตาม instructions: download, configure, run

# ใช้ใน workflow
jobs:
  build:
    runs-on: self-hosted                 # ใช้เครื่องตัวเอง!
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test

  # หรือ label specific
  gpu-test:
    runs-on: [self-hosted, gpu, linux]   # เครื่องที่มี GPU
    steps:
      - run: nvidia-smi                  # check GPU
      - run: python train.py

การ setup self-hosted runner ไม่ยากเมื่อทำตาม instructions ที่ GitHub ให้มา แต่สิ่งสำคัญคือการ label runners อย่างชัดเจนเพื่อให้ workflows สามารถเลือกเครื่องที่เหมาะสมได้ การมี self-hosted runners ช่วยให้เราทำงาน specialized tasks ได้ เช่น ML training ที่ต้องการ GPU, testing บน specific OS versions, หรือการเข้าถึง internal services ที่ไม่ได้ expose ไป public internet

GitHub-hosted vs Self-hosted

FeatureGitHub-hostedSelf-hosted
Setupไม่ต้องต้อง setup + maintain
CostFree tier (2,000 min/month)ค่าเครื่องเอง
Speed2-core, 7GB RAMตามที่มี (ได้ 64-core!)
CustomizationLimitedFull control
NetworkPublic internetPrivate network access
GPU
เหมาะกับทั่วไป, open sourceEnterprise, ML, heavy builds

ตารางเปรียบเทียบข้างต้นช่วยให้เราเห็นภาพชัดเจนของ trade-offs ระหว่างทั้งสองแนวทาง GitHub-hosted เหมาะสำหรับ standard workloads และ open source projects ที่ต้องการความสะดวกและไม่ต้องการ infrastructure overhead ส่วน self-hosted เหมาะสำหรับ enterprise environments ที่ต้องการ control, performance, หรือมี specific requirements การเลือกใช้แบบผสมผสานก็เป็นไปได้ เช่น ใช้ GitHub-hosted สำหรับ testing และ self-hosted สำหรับ deployment


💡 Advanced Patterns

หลังจากเข้าใจพื้นฐานของ GitHub Actions แล้ว มาดู patterns ขั้นสูงที่จะช่วยให้เรา handle complex scenarios ได้ดีขึ้น การใช้ conditional logic, artifact management, และ concurrency control จะช่วยให้ workflows มี robustness และ efficiency มากขึ้น patterns เหล่านี้มีประโยชน์มากสำหรับ production workflows ที่ต้องรองรับหลากหลายสถานการณ์และมี reliability requirements สูง:

Conditional Steps

steps:
  - name: Deploy to staging
    if: github.ref == 'refs/heads/develop'
    run: deploy-staging.sh

  - name: Deploy to production
    if: github.ref == 'refs/heads/main'
    run: deploy-production.sh

  - name: Comment on PR
    if: github.event_name == 'pull_request'
    uses: actions/github-script@v7
    with:
      script: |
        github.rest.issues.createComment({
          issue_number: context.issue.number,
          owner: context.repo.owner,
          repo: context.repo.repo,
          body: '✅ Tests passed! Ready to merge.'
        })

  - name: Notify on failure
    if: failure()                         # รันเมื่อ step ก่อนหน้า fail
    run: |
      curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
        -d '{"text":"❌ CI failed on ${{ github.ref }}"}'

Conditional steps เป็น pattern ที่ใช้บ่อยมากใน production workflows เพราะช่วยให้เราควบคุมการทำงานตาม context ต่างๆ ได้ การใช้ if กับ GitHub context variables ช่วยให้เราสร้าง smart workflows ที่ตอบสนองต่อสถานการณ์ต่างๆ อย่างเหมาะสม การใช้ failure() และ status functions อื่นๆ ช่วยให้เรา handle error cases ได้อย่างมีประสิทธิภาพ และการ integrate กับ external services เช่น Slack หรือ GitHub API ทำให้เรามี rich automation ที่ตอบสนองต่อเหตุการณ์ต่างๆ

Artifacts — ส่งไฟล์ระหว่าง Jobs

ในหลายกรณี เราต้องการส่งผลลัพธ์จาก job หนึ่งไปยังอีก job หนึ่ง เช่น build artifacts, test results, หรือ generated files GitHub Actions มี artifact system ที่ช่วยให้เราทำสิ่งนี้ได้อย่างปลอดภัยและมีประสิทธิภาพ การใช้ artifacts ไม่เพียงแต่ช่วยในการ pass data ระหว่าง jobs เท่านั้น แต่ยังเป็นวิธีที่ดีในการ store build outputs สำหรับการ debug หรือ manual verification:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm run build

      - name: Upload build
        uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: dist/
          retention-days: 7

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download build
        uses: actions/download-artifact@v4
        with:
          name: build-output
          path: dist/

      - name: Deploy
        run: aws s3 sync dist/ s3://my-bucket/

Artifacts เป็นเครื่องมือที่ทรงพลังสำหรับการ coordinate ระหว่าง jobs การใช้ upload-artifact และ download-artifact ช่วยให้เราแยก concerns ระหว่าง building และ deploying ได้อย่างชัดเจน การตั้ง retention-days ช่วยลดการใช้ storage และ cost ส่วนการ name artifacts อย่างมีความหมายจะช่วยให้ debug และ troubleshoot ได้ง่ายขึ้น artifacts ยังเป็นประโยชน์สำหรับการ store test results หรือ logs ที่ต้องการ review ภายหลัง

Concurrency — ป้องกัน deploy ซ้อน

เมื่อมีหลายคนทำงานใน team เดียวกัน หรือมีการ trigger workflows บ่อยๆ อาจเกิดปัญหาการ deploy ซ้อนกัน ซึ่งอาจทำให้เกิด conflicts หรือ inconsistent state Concurrency control ช่วยให้เราจัดการปัญหานี้โดยการจำกัดจำนวน workflows ที่สามารถทำงานพร้อมกันได้ และการ cancel workflows เก่าเมื่อมีตัวใหม่:

name: Deploy
concurrency:
  group: deploy-${{ github.ref }}        # 1 deploy per branch
  cancel-in-progress: true               # cancel ตัวเก่า ถ้ามีตัวใหม่

# ป้องกัน: push 3 ครั้งติดกัน → รันแค่ตัวล่าสุด

Concurrency control เป็น pattern ที่สำคัญมากสำหรับ production environments การใช้ group ช่วยให้เรา isolate concurrency ตาม context ต่างๆ เช่น branch หรือ environment การใช้ cancel-in-progress ช่วยประหยัด resources และลดเวลารอ แต่ต้องระวังในกรณีที่การ cancel อาจทำให้เกิดปัญหา เช่น partial deployments ดังนั้นการออกแบบ concurrency strategy ต้องคำนึงถึง nature ของงานและ consequences ที่อาจเกิดขึ้น


📋 Best Practices

การเขียน GitHub Actions ที่ดีไม่ใช่แค่ให้ทำงานได้เท่านั้น แต่ต้องคำนึงถึงความปลอดภัย, ประสิทธิภาพ, maintainability, และ cost optimization ด้วย best practices เหล่านี้เป็นผลสะสมจากประสบการณ์ของ community และ lessons learned จาก production environments การปฏิบัติตาม principles เหล่านี้จะช่วยให้ workflows ของเรามี reliability สูงและลดปัญหาในระยะยาว:

  1. Pin action versions — ใช้ SHA แทน tag (@v4@sha)
  2. Use npm ci ไม่ใช่ npm install — ใช้ lockfile, เร็วกว่า, deterministic
  3. Cache dependencies — ลดเวลา install จาก 2 นาที → 10 วินาที
  4. Fail fast — lint ก่อน → unit test → integration → deploy
  5. Use environments — แยก secrets staging/production + require approvals
  6. Limit permissionspermissions: contents: read ไม่ให้ write ถ้าไม่จำเป็น
  7. Concurrency control — ป้องกัน deploy ซ้อนกัน
  8. Reusable workflows — DRY! อย่า copy-paste workflow
  9. Timeout — ตั้ง timeout-minutes: 15 ป้องกัน stuck jobs
  10. Status badges — ใส่ใน README ให้รู้สถานะ CI
# README.md — CI badge
![CI](https://github.com/myorg/myapp/actions/workflows/ci.yml/badge.svg)

Best practices ข้างต้นไม่ใช่แค่ guidelines เท่านั้น แต่เป็นสิ่งที่จำเป็นสำหรับ production-grade workflows การ pin action versions ป้องกัน supply chain attacks การ cache dependencies ลด build time อย่างมาก การใช้ environments และ permissions อย่างถูกต้องช่วยในเรื่องความปลอดภัย และการมี status badges ช่วยให้ทีมเห็นสถานะ CI/CD ได้ทันที การปฏิบัติตาม best practices เหล่านี้จะทำให้เรามี confidence ในระบบ automation และลดเวลาที่เสียไปกับการแก้ปัญหา


💰 Pricing — ฟรีแค่ไหน?

การเข้าใจ pricing model ของ GitHub Actions เป็นสิ่งสำคัญสำหรับการวางแผนและการควบคุมต้นทุน โดยเฉพาะสำหรับ organizations ที่มี repositories เยอะ หรือ workflows ที่รันบ่อยๆ การรู้จัก limits และ optimization techniques จะช่วยให้เราใช้ GitHub Actions ได้อย่างมีประสิทธิภาพโดยไม่เกิน budget ที่ตั้งไว้:

PlanMinutes/monthStorage
Free2,000 min500 MB
Team3,000 min2 GB
Enterprise50,000 min50 GB
Public repos∞ (ไม่จำกัด!)
💡 Open source projects ได้ unlimited minutes ฟรี! — อีกเหตุผลที่ open source นิยม GitHub Actions

จากตารางราคาข้างต้น เราจะเห็นว่า GitHub Actions มี generous free tier ที่เหมาะสำหรับ small teams และ personal projects การที่ public repositories ได้ unlimited minutes ทำให้ open source community สามารถใช้ powerful CI/CD ได้โดยไม่ต้องกังวลเรื่องค่าใช้จ่าย สำหรับ enterprise ที่ต้องการ private repositories และ heavy usage ก็มี paid plans ที่ให้ resources มากขึ้น การวางแผนการใช้งานและ optimize workflows จะช่วยให้เราได้ประโยชน์สูงสุดจาก free tier


สรุป

GitHub Actions เป็นหนึ่งใน game-changing tools ที่ทำให้ CI/CD accessibility เพิ่มขึ้นมาก การที่ integrated กับ GitHub repository โดยตรงทำให้ setup และ maintenance ง่ายกว่า traditional CI/CD tools มาก ประกอบกับ rich ecosystem ของ marketplace actions ทำให้เราสามารถสร้าง sophisticated automation ได้โดยไม่ต้องเขียน code มากนัก สิ่งสำคัญคือการเริ่มต้นง่ายๆ แล้วค่อยๆ เพิ่ม complexity เมื่อความต้องการมากขึ้น:

บทความจากซีรีส์ DevOps & Vibe Coding 2026
← Previous
SRE — Site Reliability Engineering ที่ DevOps ต้องรู้
Next →
Code Quality & Review — เขียน Code ดีๆ ที่ทีมอยากอ่าน