ในโพสต์ 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 ได้อย่างละเอียดและปลอดภัย ลองดูภาพรวมของกระบวนการทั้งหมด:
จากแผนภาพข้างต้น เราจะเห็นว่าเมื่อมีการ 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 workflow | push, pull_request, schedule |
| Job | ชุดของ steps ที่รันบน runner เดียว | build, test, deploy |
| Step | คำสั่งหรือ action ทีละตัว | run: npm test |
| Action | Reusable unit of code | actions/checkout@v4 |
| Runner | เครื่องที่รัน job | ubuntu-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
แผนภาพ 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 | ทำอะไร |
|---|---|---|
| Checkout | actions/checkout@v4 | ดึง code จาก repo |
| Node.js | actions/setup-node@v4 | ติดตั้ง Node.js |
| Python | actions/setup-python@v5 | ติดตั้ง Python |
| Docker | docker/build-push-action@v5 | Build + push image |
| Cache | actions/cache@v4 | Cache dependencies |
| Coverage | codecov/codecov-action@v4 | Upload test coverage |
| Deploy | aws-actions/configure-aws-credentials@v4 | AWS authentication |
| Release | softprops/action-gh-release@v2 | สร้าง GitHub Release |
| Slack | slackapi/slack-github-action@v1 | ส่ง notification |
| Security | github/codeql-action@v3 | Code 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
| Feature | GitHub-hosted | Self-hosted |
|---|---|---|
| Setup | ไม่ต้อง | ต้อง setup + maintain |
| Cost | Free tier (2,000 min/month) | ค่าเครื่องเอง |
| Speed | 2-core, 7GB RAM | ตามที่มี (ได้ 64-core!) |
| Customization | Limited | Full control |
| Network | Public internet | Private network access |
| GPU | ❌ | ✅ |
| เหมาะกับ | ทั่วไป, open source | Enterprise, 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 สูงและลดปัญหาในระยะยาว:
- Pin action versions — ใช้ SHA แทน tag (
@v4→@sha) - Use
npm ciไม่ใช่npm install— ใช้ lockfile, เร็วกว่า, deterministic - Cache dependencies — ลดเวลา install จาก 2 นาที → 10 วินาที
- Fail fast — lint ก่อน → unit test → integration → deploy
- Use environments — แยก secrets staging/production + require approvals
- Limit permissions —
permissions: contents: readไม่ให้ write ถ้าไม่จำเป็น - Concurrency control — ป้องกัน deploy ซ้อนกัน
- Reusable workflows — DRY! อย่า copy-paste workflow
- Timeout — ตั้ง
timeout-minutes: 15ป้องกัน stuck jobs - Status badges — ใส่ใน README ให้รู้สถานะ CI
# README.md — CI badge

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 ที่ตั้งไว้:
| Plan | Minutes/month | Storage |
|---|---|---|
| Free | 2,000 min | 500 MB |
| Team | 3,000 min | 2 GB |
| Enterprise | 50,000 min | 50 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 เมื่อความต้องการมากขึ้น:
- GitHub Actions = CI/CD built-in GitHub — เขียน YAML, รันอัตโนมัติ
- Workflow = Event → Jobs → Steps — ไฟล์อยู่ใน
.github/workflows/ - Triggers = push, PR, schedule, manual, release, workflow_call
- Secrets = เก็บ credentials ปลอดภัย + Environment secrets แยก staging/prod
- Matrix = ทดสอบหลาย OS × versions พร้อมกัน
- Caching = เร็วขึ้น 10x ด้วย dependency cache
- Marketplace = 20,000+ actions สำเร็จรูป อย่าเขียนเอง!
- Reusable Workflows = DRY! เรียก workflow ซ้ำได้
- Security = CodeQL, Trivy, dependency review, secret scanning
- Self-hosted = เครื่องตัวเอง สำหรับ GPU/heavy builds 🤖🐕