- 1. Introduction to CI/CD
- 2. Overview of CI/CD Platforms
- 3. Getting Started with GitHub Actions
- 4. Core Concepts in GitHub Actions
- 5. GitHub Actions Marketplace
- 6. Secrets, Security & Permissions
- 7. Debugging & Monitoring GitHub Actions
- 8. Optimizing GitHub Actions Workflows
- 9. Basic CI/CD Pipelines
- 10. Deployment Pipelines
- 11. Advanced Use Cases
- 12. ตัวอย่าง Workflow จากโปรเจค Workshop
Continuous Integration (CI) และ Continuous Delivery/Deployment (CD) คือแนวทางในการพัฒนาซอฟต์แวร์ที่ช่วยให้ทีมสามารถส่งมอบการเปลี่ยนแปลงไปยังลูกค้าได้อย่างรวดเร็วและปลอดภัยยิ่งขึ้น
ทำไมสำคัญ?
- ลดความเสี่ยงในการ deploy
- ตรวจจับ bug ได้เร็วขึ้น
- เพิ่มความเร็วในการพัฒนาและ delivery
- ลดงานที่ซ้ำซ้อนและเพิ่ม automation
- สร้างความมั่นใจในคุณภาพของโค้ด
CI เน้นที่การรวมโค้ดจากนักพัฒนาหลายคนเข้าด้วยกันอย่างสม่ำเสมอ:
- การ Commit โค้ดบ่อยๆ: นักพัฒนาควร push โค้ดไปยังระบบควบคุมเวอร์ชัน (เช่น Git) อย่างสม่ำเสมอ
- Automated Build: ระบบจะสร้าง build โปรเจคโดยอัตโนมัติเมื่อมีการ push โค้ด
- Automated Tests: ทดสอบโค้ดที่ถูก push เข้ามาโดยอัตโนมัติ
- Fail Fast: หากมีปัญหาเกิดขึ้น ระบบจะแจ้งเตือนทีมทันที
graph LR
A[Developer Commits] --> B[Automated Build]
B --> C[Automated Tests]
C --> D{Tests Pass?}
D -- Yes --> E[Ready for CD]
D -- No --> F[Notify Team]
CD เป็นกระบวนการที่ทำให้มั่นใจว่าซอฟต์แวร์พร้อมจะ deploy ได้ตลอดเวลา:
- Continuous Delivery: การเตรียมโค้ดให้พร้อมสำหรับการ deploy โดยอัตโนมัติ แต่ยังคงต้องอนุมัติการ deploy แบบ manual
- Continuous Deployment: ขั้นตอนถัดไปที่โค้ดจะถูก deploy ไปยัง production โดยอัตโนมัติหากผ่านทุกการทดสอบ
graph LR
A[CI Passed] --> B[Deploy to Test/Staging]
B --> C[Automated Integration Tests]
C --> D{Tests Pass?}
D -- Yes --> E[Deploy to Production]
D -- No --> F[Notify Team]
DevOps เป็นวัฒนธรรมและแนวทางที่เน้นการทำงานร่วมกันระหว่างทีม Development และ Operations:
- CI/CD เป็นเครื่องมือสำคัญในการทำ DevOps
- ช่วยให้ทีมสามารถส่งมอบซอฟต์แวร์ได้อย่างรวดเร็ว มีประสิทธิภาพและคุณภาพ
- สนับสนุนค่านิยมหลักของ DevOps: การทำงานร่วมกัน, automation, การวัดผล, และการแบ่งปัน
| Platform | รูปแบบ | ข้อดี | ข้อเสีย | Free Build Minutes | จุดเด่นด้าน CI/CD |
|---|---|---|---|---|---|
| GitHub Actions | Cloud/Self-hosted | - ใช้งานง่าย - รวมกับ GitHub - Marketplace ใหญ่ |
- เป็นแพลตฟอร์มที่ค่อนข้างใหม่กว่า | - ไม่จำกัดสำหรับ public repos - 2,000 นาที/เดือนสำหรับ private repos |
- Reusable workflows - Matrix builds - Environment protection rules - รวมกับ GitHub Packages |
| GitLab CI/CD | Cloud/Self-hosted | - All-in-one platform - Built-in container registry |
- UI ซับซ้อน - Self-hosted อาจยุ่งยาก |
- 400 นาที/เดือน | - Auto DevOps (สร้าง pipelines อัตโนมัติ) - Multi-project pipelines - SAST/DAST scanning ในตัว - Review apps |
| Jenkins | Self-hosted | - Customizable - Plugin ecosystem ใหญ่มาก |
- ต้องการ maintenance สูง - Setup ยาก |
- ไม่จำกัด (self-hosted) | - Pipeline-as-Code - Blue-Ocean UI - Shared libraries - Distributed builds |
| CircleCI | Cloud/Self-hosted | - ใช้งานง่าย - Orbs (reusable configurations) |
- ราคาอาจแพงสำหรับทีมขนาดใหญ่ | - 6,000 นาที/เดือน | - First-class Docker support - SSH debugging - Test splitting - Resource classes |
| Travis CI | Cloud | - Setup ง่าย - เหมาะกับ open source |
- จำกัดความสามารถในการ customize | - ไม่มีแผนฟรีสำหรับ private repos | - Auto-deployment - Build matrix - Cron jobs |
| Bitbucket Pipelines | Cloud | - รวมกับ Bitbucket - ใช้งานง่าย |
- ความยืดหยุ่นน้อย | - 50 นาที/เดือน | - รวมกับ Jira/Confluence - Deployments dashboard - Pipes ทดแทน actions - เชื่อมกับ Opsgenie |
GitHub Actions
- จุดเด่น: ผสานรวมกับ GitHub อย่างลงตัว, ใช้งานง่าย, Community actions จำนวนมาก
- ข้อเสีย: Debugging อาจทำได้ยาก, ตัวเลือกในการ caching ยังจำกัด
GitLab CI/CD
- จุดเด่น: ระบบครบวงจร, Built-in registry, ทำงานได้ดีกับ GitLab repositories
- ข้อเสีย: Learning curve สูง, Documentation อาจไม่ครบถ้วน
Jenkins
- จุดเด่น: ปรับแต่งได้สูงมาก, มี plugin มากกว่า 1,500 ตัว, Community ใหญ่
- ข้อเสีย: UI ล้าสมัย, ต้องการ maintenance มาก, ไม่เหมาะกับโปรเจคเล็ก
CircleCI
- จุดเด่น: Setup ง่าย, มีฟีเจอร์ dockerization ที่ดี
- ข้อเสีย: ราคาอาจสูงเมื่อมีทีมขนาดใหญ่, ปรับแต่งได้น้อยกว่า Jenkins
Travis CI
- จุดเด่น: ใช้งานง่าย, เหมาะสำหรับโปรเจค open source
- ข้อเสีย: ไม่ค่อยยืดหยุ่น, อาจมีข้อจำกัดใน free tier
- Native Integration: ทำงานร่วมกับ GitHub ได้อย่างลงตัวโดยไม่ต้องใช้บริการจากภายนอก
- YAML-based Configuration: ใช้ไฟล์ YAML ที่อ่านง่ายและเข้าใจได้ง่าย
- Community Actions: มี action มากมายที่พร้อมใช้งานจาก marketplace
- Free for Public Repositories: ฟรีสำหรับ public repositories และมี free minutes สำหรับ private repositories
- Matrix Builds: สร้าง matrix builds เพื่อทดสอบในหลาย environment ได้ง่าย
- Self-hosted Runners: สามารถใช้ runner บนเครื่องของตัวเองได้
การรัน runner บนเครื่องหรือเซิร์ฟเวอร์ของตนเอง (self-hosted runner) เป็นทางเลือกที่ดีเมื่อต้องการควบคุมสภาพแวดล้อมการทำงานหรือต้องการใช้ทรัพยากรที่มีอยู่
- รองรับฮาร์ดแวร์เฉพาะทาง - เช่น GPU หรือ CPU ที่มีประสิทธิภาพสูง
- เข้าถึงทรัพยากรภายใน - สามารถเข้าถึงระบบภายในที่ไม่เปิดให้ภายนอก
- ประหยัดเวลา - ไม่ต้องอัปโหลด/ดาวน์โหลดไฟล์ขนาดใหญ่ไปยัง GitHub
- การตั้งค่าเฉพาะ - กำหนดสภาพแวดล้อมให้ตรงกับ production ได้
- ไม่มีข้อจำกัดเวลา - ไม่มี timeout แบบที่ GitHub-hosted runners มี
apt-get update
apt-get install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update
apt-get install docker.io
systemctl start docker
systemctl enable dockerการติดตั้ง runner สามารถทำได้ทั้งโดยใช้ root หรือ user ปกติ แต่การใช้ user ปกติช่วยเพิ่มความปลอดภัยได้ดีกว่า:
วิธีที่ 1: สร้าง User ใหม่สำหรับรัน Runner (แนะนำ)
# สร้าง User ใหม่
useradd -m -s /bin/bash actions-runner
# เพิ่มผู้ใช้เข้ากลุ่ม docker
usermod -aG docker actions-runner
# เปลี่ยนไปใช้ User ใหม่
su - actions-runnerวิธีที่ 2: ใช้ User ปัจจุบัน
# เพิ่มผู้ใช้ปัจจุบันเข้ากลุ่ม docker (หากใช้ Docker)
sudo usermod -aG docker $USER
# ล็อกเอาท์และล็อกอินใหม่หรือรันคำสั่งต่อไปนี้เพื่อให้การเปลี่ยนแปลงมีผล
newgrp dockerดาวน์โหลดและติดตั้ง Runner:
# สร้างโฟลเดอร์และดาวน์โหลด runner
mkdir actions-runner && cd actions-runner
curl -o actions-runner-linux-x64-2.305.0.tar.gz -L https://github.com/actions/runner/releases/download/v2.305.0/actions-runner-linux-x64-2.305.0.tar.gz
# แตกไฟล์
tar xzf ./actions-runner-linux-x64-2.305.0.tar.gz
# กำหนดค่าและลงทะเบียน Runner
./config.sh --url https://github.com/YOUR-ORG/YOUR-REPO --token YOUR_TOKEN
# เริ่มการทำงานแบบทันที
./run.sh
# หรือติดตั้งเป็น service (แนะนำ)
./svc.sh install
./svc.sh startคุณสามารถดู token และคำสั่งติดตั้งที่เฉพาะเจาะจงได้ที่:
- สำหรับ repository:
Settings > Actions > Runners > New self-hosted runner - สำหรับองค์กร:
Organization Settings > Actions > Runners > New runner
การกำหนด labels ให้ runner ช่วยในการเลือกใช้ runner ที่เหมาะสมสำหรับแต่ละงาน:
./config.sh --url https://github.com/YOUR-ORG/YOUR-REPO --token YOUR_TOKEN --labels gpu,production,ubuntu-20.04การใช้ labels ใน workflow:
jobs:
build:
runs-on: [self-hosted, gpu, production]หากยังไม่มี package sudo ให้ติดตั้ง:
apt -y install sudo
sudo visudoเพิ่มบรรทัดต่อไปนี้ที่ส่วนท้ายของไฟล์:
runner ALL=(ALL) NOPASSWD:ALL
โดยแทนที่ runner ด้วยชื่อผู้ใช้ของ runner
-
การอัปเดต Runner:
cd actions-runner ./config.sh remove --token YOUR_REMOVE_TOKEN # หลังจากลบแล้ว ให้ติดตั้งเวอร์ชันใหม่
-
การตรวจสอบการทำงาน:
# ดูล็อกไฟล์ tail -f ~/actions-runner/_diag/Runner_*.log # ดูสถานะ service (หากติดตั้งเป็น service) ./svc.sh status
-
การหยุดและถอนการติดตั้ง Runner:
# หยุดการทำงาน ./svc.sh stop # ถอนการติดตั้งจาก service ./svc.sh uninstall # ลบจาก GitHub ./config.sh remove --token YOUR_REMOVE_TOKEN
หากต้องการใช้ Docker ในไฟล์ workflow:
jobs:
build:
runs-on: [self-hosted]
steps:
- name: Build Docker image
run: docker build -t myapp:latest .
- name: Run Docker container
run: docker run -d -p 8080:80 myapp:latestสำหรับการใช้งานร่วมกับ container:
jobs:
build:
runs-on: [self-hosted]
container:
image: node:14-
แยก Runners ตามระดับความเสี่ยง:
- ใช้ runner แยกสำหรับ public repositories และ private repositories
- ไม่ควรใช้ runner เดียวกันสำหรับงานที่มีระดับความปลอดภัยต่างกัน
-
จำกัดการเข้าถึง:
- ตั้งค่า firewall อย่างเหมาะสม
- ระวังการให้สิทธิ์บน runner machine
การรันด้วย user ปกติช่วยลดสิทธิ์ของ runner ทำให้ระบบปลอดภัยยิ่งขึ้น
GitHub Actions เป็นแพลตฟอร์ม CI/CD ที่ช่วยให้คุณสามารถสร้าง workflow automation ของคุณเองได้ภายใน repository ของ GitHub คุณสามารถ:
- ทดสอบและ build โค้ดโดยอัตโนมัติ
- Deploy applications
- Automate different parts ของ development workflow
1. Workflow
- ไฟล์ YAML ที่กำหนด automation process
- อยู่ในโฟลเดอร์
.github/workflows - ประกอบด้วย job หนึ่งหรือหลาย job
- ทริกเกอร์ด้วย event เช่น push, pull request
2. Jobs
- ชุดของ steps ที่รันบน runner เดียวกัน
- Jobs ต่างๆ จะรันแบบขนานโดยค่าเริ่มต้น
- สามารถกำหนดเงื่อนไขการรันได้ เช่น รันต่อเมื่อ job อื่นสำเร็จ
3. Steps
- งานย่อยของแต่ละ job
- สามารถรัน commands, scripts หรือ actions
- แต่ละ step รันแบบ sequential
- แต่ละ step แชร์ข้อมูลกันได้
4. Actions
- ชุดคำสั่งที่สามารถนำกลับมาใช้ซ้ำได้
- สามารถสร้างเองหรือใช้จาก GitHub Marketplace
- เช่น
actions/checkout@v2เพื่อ clone repository
5. Runners
- เครื่องที่รัน workflow
- มีทั้ง GitHub-hosted runners และ self-hosted runners
- มีหลาย OS ให้เลือก เช่น Ubuntu, Windows, macOS
name: [ชื่อ Workflow]
on: [event ที่ทริกเกอร์ workflow]
jobs:
job_id:
runs-on: [runner]
steps:
- name: [ชื่อ step]
uses: [action] # หรือ
run: [command] # คำสั่ง shell
env: # ตัวแปรสภาพแวดล้อม (optional)
KEY: valueตัวอย่างพื้นฐานสำหรับ Node.js project:
name: Node.js CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build --if-presentตัวอย่าง Workflow สำหรับ Docker:
name: Docker Build and Push
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: user/app:latestตัวอย่างสำหรับการ Deploy ไปยัง Firebase:
name: Firebase Deploy
on:
push:
branches: [ main ]
jobs:
build_and_deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Firebase
uses: w9jds/firebase-action@master
with:
args: deploy --only hosting
env:
FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}GitHub Actions ทำงานตามลำดับดังนี้:
Event → Jobs → Steps → Actions
- Event: เหตุการณ์ที่ทริกเกอร์ให้ workflow เริ่มทำงาน
- Jobs: งานที่ต้องทำหลังจากเกิด event (รันแบบขนานโดยปกติ)
- Steps: ขั้นตอนย่อยในแต่ละ job (รันแบบลำดับ)
- Actions: คำสั่งที่ใช้ในแต่ละ step
graph TD
A[Event] --> B[Job 1]
A --> C[Job 2]
A --> D[Job 3]
B --> B1[Step 1.1]
B --> B2[Step 1.2]
B1 --> B1a[Action]
B1 --> B1b[Command]
C --> C1[Step 2.1]
C --> C2[Step 2.2]
-
push: เมื่อมีการ push โค้ดเข้า repository
on: push: branches: [ main, dev ] paths-ignore: [ 'docs/**', '**.md' ]
-
pull_request: เมื่อมีการสร้างหรืออัปเดต Pull Request
on: pull_request: types: [opened, synchronize, reopened] branches: [ main ]
-
schedule: รันตามเวลาที่กำหนดด้วยรูปแบบ cron
on: schedule: - cron: '0 0 * * *' # รันทุกวันตอนเที่ยงคืน
-
workflow_dispatch: รันด้วยตนเองผ่าน UI หรือ API
on: workflow_dispatch: inputs: environment: description: 'Environment to deploy to' required: true default: 'staging'
-
repository_dispatch: รันเมื่อ webhook ภายนอกเรียก API
on: repository_dispatch: types: [deploy]
Runners คือเซิร์ฟเวอร์ที่รัน workflow ของคุณ มี 2 ประเภท:
- GitHub จัดเตรียมให้พร้อมใช้งาน
- มีหลาย OS ให้เลือก:
ubuntu-latest,windows-latest,macos-latest - มีซอฟต์แวร์ติดตั้งไว้แล้วจำนวนมาก
- ฟรีสำหรับ public repositories และมี quotas สำหรับ private repositories
jobs:
build:
runs-on: ubuntu-latest
# หรือ
# runs-on: windows-latest
# runs-on: macos-latest- คุณติดตั้งและดูแลเอง
- เหมาะสำหรับสภาพแวดล้อมพิเศษหรือต้องการ performance สูง
- สามารถเป็นเครื่องจริง, VM หรือ container
jobs:
build:
runs-on: self-hosted
# หรือระบุ labels เพิ่มเติม
# runs-on: [self-hosted, linux, x64]GitHub Actions Marketplace คือแหล่งรวม actions สำเร็จรูปที่สามารถนำมาใช้ในโปรเจกต์ของคุณได้ทันที โดยส่วนใหญ่:
- พัฒนาโดย GitHub, องค์กรชั้นนำ, หรือชุมชน
- มีวิธีใช้งานและเอกสารประกอบชัดเจน
- มีการอัปเดตสม่ำเสมอ
-
การค้นหา Actions:
- เข้าไปที่ GitHub Marketplace
- ใช้คำค้นเพื่อหา action ที่ต้องการ
- อ่านรีวิวและดูจำนวนดาวเพื่อประเมินความนิยม
-
วิธีเพิ่ม Action ในไฟล์ workflow:
steps: - name: ชื่อขั้นตอน uses: ชื่อ-action@version with: parameter-1: value-1 parameter-2: value-2
-
การระบุเวอร์ชัน:
@v1: ใช้เวอร์ชันหลักล่าสุด (เช่น v1.x.x)@v1.2: ใช้เวอร์ชันรองล่าสุด (เช่น v1.2.x)@v1.2.3: ใช้เวอร์ชันแน่นอน@main: ใช้จากบรานช์หลัก (อาจไม่เสถียร)@5a4ac9bb22: ใช้ commit hash แน่นอน (ปลอดภัยที่สุด)
ใช้สำหรับโคลน repository ไปยัง runner
- name: Checkout code
uses: actions/checkout@v3
with:
# รองรับ
fetch-depth: 0 # โคลนประวัติทั้งหมด
ref: feature-branch # ระบุ branch หรือ tagตั้งค่าสภาพแวดล้อม Node.js
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
# หรือใช้ version range
# node-version: '14.x'
cache: 'npm' # เปิดใช้การแคช npm dependencies
registry-url: 'https://registry.npmjs.org' # สำหรับ publish packagesสร้างและส่ง Docker image ไปยัง registry
- name: Build and push Docker image
uses: docker/build-push-action@v3
with:
context: .
push: true
tags: |
username/image:latest
username/image:v1
build-args: |
ARG1=value1
ARG2=value2
cache-from: type=registry,ref=username/image:latest
cache-to: type=inlineแคชไฟล์เพื่อเร่งความเร็วในการรัน workflow
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-สำหรับ deploy เว็บไปยัง GitHub Pages
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: gh-pages
folder: build
clean: trueSecrets คือตัวแปรที่เข้ารหัสซึ่งใช้เก็บข้อมูลที่มีความอ่อนไหว โดยจะไม่แสดงในล็อก:
jobs:
example-job:
runs-on: ubuntu-latest
env:
# ตัวแปรระดับ job
API_URL: https://api.example.com
steps:
- name: Step with env variable
env:
# ตัวแปรระดับ step
DEBUG_MODE: true
run: echo $API_URL $DEBUG_MODEjobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Use secret
env:
# เรียกใช้ secret จาก repository settings
API_TOKEN: ${{ secrets.API_TOKEN }}
run: |
echo "Using API token to authenticate"
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com- ไปที่ repository ของคุณ > Settings > Secrets and variables > Actions
- คลิกปุ่ม New repository secret
- ใส่ชื่อ secret (เช่น
API_TOKEN) และค่า - คลิก Add secret
ประเภทของ Secrets:
- Repository secrets: ใช้ได้เฉพาะ repository หนึ่ง
- Environment secrets: ใช้ได้เฉพาะ environment ที่กำหนด
- Organization secrets: ใช้ได้ทุก repository ในองค์กร
GitHub สร้าง token พิเศษสำหรับแต่ละ workflow run โดยอัตโนมัติ:
jobs:
job1:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Create issue using REST API
run: |
curl -X POST \
-H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
https://api.github.com/repos/${{ github.repository }}/issues \
-d '{"title":"Automated issue"}'permissions:
# จำกัดสิทธิ์ทั้งหมด
contents: read
issues: write
pull-requests: write
# จะมีสิทธิ์เฉพาะ contents:read, issues:write, pull-requests:write เท่านั้นหรือปิดการใช้งานทั้งหมด:
permissions: {} # ไม่มีสิทธิ์ใดๆ-
อย่าแสดง Secret โดยตรง:
# BAD - secrets จะถูกแสดงในล็อก run: echo "My secret is ${{ secrets.MY_SECRET }}" # GOOD - ไม่แสดง secret ในล็อก run: | echo "Using secret for authentication..." # ใช้ secret โดยไม่แสดง
-
จำกัดสิทธิ์:
- ใช้หลักการ "least privilege" โดยให้สิทธิ์เท่าที่จำเป็น
- กำหนด
permissionsอย่างชัดเจน
-
ระวัง Workflow จาก Pull Request:
# จำกัด secret เมื่อ workflow ถูกเรียกจาก pull request if: github.event_name != 'pull_request' env: TOKEN: ${{ secrets.TOKEN }}
-
ใช้ Environment Protection Rules:
jobs: deploy: runs-on: ubuntu-latest environment: production # สภาพแวดล้อมที่มีการป้องกัน
-
ตรวจสอบ Dependencies:
- ใช้
actions/checkout@ด้วยเวอร์ชันแน่นอน - ระวัง action ที่ไม่น่าเชื่อถือ
- พิจารณาใช้ action hash แทนเวอร์ชัน
- ใช้
ตัวอย่างการตั้งค่า Environment Protection Rules:
- ไปที่ repository > Settings > Environments
- สร้าง environment ใหม่หรือคลิก environment ที่มีอยู่
- ตั้งค่า "Required reviewers" เพื่อจำเป็นต้องมีการอนุมัติ
- จำกัด branches ที่สามารถ deploy ได้
name: Deploy to Production
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # ต้องมีการอนุมัติก่อนทำงาน
steps:
- uses: actions/checkout@v3
- name: Deploy
run: ./deploy.sh
env:
DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}การดีบักและติดตามการทำงานของ GitHub Actions เป็นทักษะสำคัญในการแก้ไขปัญหาและปรับปรุงประสิทธิภาพของ workflow
GitHub มีระบบ logs ที่ช่วยให้เราตรวจสอบการทำงานของ workflow ได้อย่างละเอียด:
-
การเข้าถึง Workflow Logs:
- ไปที่ repository > Actions tab
- เลือก workflow run ที่ต้องการตรวจสอบ
- คลิกที่ job เพื่อดูรายละเอียดของแต่ละ step
-
การอ่าน Logs:
- แต่ละ step มีลูกศรสำหรับขยาย/ย่อ logs
- สามารถค้นหาข้อความใน logs ได้
- ดาวน์โหลด logs เป็นไฟล์ได้โดยใช้ปุ่มด้านบนขวา
-
องค์ประกอบของ Logs:
- เวลาเริ่มต้นและสิ้นสุดของแต่ละ step
- คำสั่งที่รันและผลลัพธ์
- สถานะการทำงาน (success/failure)
- ข้อความ warning และ error
[2023-05-15 10:15:22] Starting: Setup Node.js
[2023-05-15 10:15:24] Node.js 16.x installed successfully
[2023-05-15 10:15:25] Completed: Setup Node.js
เมื่อต้องการข้อมูลเพิ่มเติมเกี่ยวกับการทำงานของ workflow สามารถเปิดใช้ debug logs ได้:
-
การเปิดใช้งาน Debug Logs:
- สร้าง repository secret ชื่อ
ACTIONS_RUNNER_DEBUGค่าเป็นtrue - หรือสร้าง repository secret ชื่อ
ACTIONS_STEP_DEBUGค่าเป็นtrue
- สร้าง repository secret ชื่อ
-
การดู Debug Output ใน Actions Runner:
steps: - name: Show debug info env: RUNNER_DEBUG: 1 run: | echo "Debug information"
-
Debug ด้วย
set-output:steps: - name: Set output id: debug-step run: echo "::set-output name=debug-data::This is debug data" - name: Use output run: echo "${{ steps.debug-step.outputs.debug-data }}"
มีหลายวิธีในการรีรัน workflow ที่ล้มเหลวหรือต้องการทดสอบซ้ำ:
-
รีรันทั้ง Workflow:
- ไปที่หน้า workflow run ที่ต้องการรีรัน
- คลิกปุ่ม "Re-run all jobs" ที่มุมขวาบน
-
รีรันเฉพาะ Jobs ที่ล้มเหลว:
- ไปที่หน้า workflow run
- คลิกปุ่ม "Re-run failed jobs" (จะปรากฏเมื่อมีงานที่ล้มเหลว)
-
รีรันผ่าน API:
curl -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/OWNER/REPO/actions/runs/RUN_ID/rerun
เทคนิคขั้นสูงสำหรับการดีบัก workflow:
-
ใช้
tmateสำหรับ Interactive Debugging:steps: - uses: actions/checkout@v3 - name: Setup tmate session uses: mxschmitt/action-tmate@v3 if: ${{ failure() }} # รันเมื่อ step ก่อนหน้าล้มเหลว
-
ตรวจสอบตัวแปรและสภาพแวดล้อม:
- name: Debug environment run: | echo "GitHub workspace: $GITHUB_WORKSPACE" echo "GitHub event name: $GITHUB_EVENT_NAME" echo "GitHub ref: $GITHUB_REF" env
-
แสดงข้อมูลเกี่ยวกับ Runner:
- name: Debug runner run: | echo "OS: $(uname -a)" echo "Disk space:" df -h echo "Installed packages:" if [ -x "$(command -v apt)" ]; then apt list --installed elif [ -x "$(command -v brew)" ]; then brew list elif [ -x "$(command -v yum)" ]; then yum list installed fi
-
สร้าง Artifacts สำหรับตรวจสอบ:
- name: Generate debug info run: | mkdir -p debug-info env > debug-info/environment.txt npm list > debug-info/npm-packages.txt - name: Upload debug info uses: actions/upload-artifact@v3 with: name: debug-artifacts path: debug-info
การปรับปรุงประสิทธิภาพของ workflow ช่วยประหยัดเวลาและทรัพยากร ทำให้กระบวนการ CI/CD เร็วขึ้นและมีประสิทธิภาพมากขึ้น
-
ใช้ GitHub-hosted runner ที่เหมาะสม:
ubuntu-latestมีประสิทธิภาพสูงกว่า Windows และ macOS
jobs: build: # เลือก OS ที่เร็วที่สุดสำหรับงาน runs-on: ubuntu-latest
-
ลดขนาดของ Repository:
- name: Checkout uses: actions/checkout@v3 with: fetch-depth: 1 # ดึงเฉพาะ commit ล่าสุด
-
ปรับแต่ง Timeout:
jobs: build: runs-on: ubuntu-latest timeout-minutes: 10 # กำหนด timeout สำหรับทั้ง job steps: - name: Long running process timeout-minutes: 5 # กำหนด timeout สำหรับเฉพาะ step run: ./long_process.sh
-
หลีกเลี่ยงการติดตั้งซอฟต์แวร์ที่ไม่จำเป็น:
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '16' # ลดเวลาด้วยการติดตั้งเฉพาะที่จำเป็น check-latest: false
การใช้ cache ช่วยลดเวลาในการดาวน์โหลดและติดตั้งแพ็คเกจต่างๆ:
-
Cache สำหรับ npm/yarn:
- name: Cache dependencies uses: actions/cache@v3 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node-
-
Cache สำหรับ pip (Python):
- name: Cache pip packages uses: actions/cache@v3 with: path: ~/.cache/pip key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} restore-keys: | ${{ runner.os }}-pip-
-
Cache สำหรับ Gradle:
- name: Cache Gradle packages uses: actions/cache@v3 with: path: | ~/.gradle/caches ~/.gradle/wrapper key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle-
-
การใช้ cache ร่วมกับ setup- actions*:
- name: Setup Node.js uses: actions/setup-node@v3 with: node-version: '16' cache: 'npm' # ทำ cache โดยอัตโนมัติ
Reusable workflows ช่วยลดความซ้ำซ้อนและทำให้การจัดการ workflow ง่ายขึ้น:
-
การสร้าง Reusable Workflow:
# .github/workflows/reusable.yml name: Reusable workflow on: workflow_call: inputs: node-version: required: true type: string secrets: token: required: true jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Node uses: actions/setup-node@v3 with: node-version: ${{ inputs.node-version }}
-
การเรียกใช้ Reusable Workflow:
# .github/workflows/caller.yml name: Caller workflow on: push: branches: [ main ] jobs: call-workflow: uses: ./.github/workflows/reusable.yml with: node-version: '16' secrets: token: ${{ secrets.GITHUB_TOKEN }}
-
ตัวอย่าง Reusable Workflow สำหรับ Deploy:
# .github/workflows/deploy-reusable.yml name: Reusable Deploy on: workflow_call: inputs: environment: required: true type: string jobs: deploy: runs-on: ubuntu-latest environment: ${{ inputs.environment }} steps: # ขั้นตอนการ deploy
การใช้ parallel jobs และ matrix strategy ช่วยให้สามารถรันงานหลายๆ งานพร้อมกัน:
-
การใช้ Matrix Strategy:
jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [14.x, 16.x, 18.x] os: [ubuntu-latest, windows-latest] # สามารถยกเว้นบาง combination ได้ exclude: - os: windows-latest node-version: 14.x steps: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} - run: npm test
-
การแบ่งงานทดสอบ:
jobs: test: runs-on: ubuntu-latest strategy: matrix: test-group: [unit, integration, e2e] steps: - uses: actions/checkout@v3 - name: Run ${{ matrix.test-group }} tests run: npm run test:${{ matrix.test-group }}
-
Job Dependencies:
jobs: setup: runs-on: ubuntu-latest # การสร้าง output สำหรับงานอื่น outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - id: set-matrix run: echo "::set-output name=matrix::{\"include\":[{\"project\":\"foo\",\"config\":\"Debug\"},{\"project\":\"bar\",\"config\":\"Release\"}]}" build: needs: setup runs-on: ubuntu-latest strategy: matrix: ${{ fromJson(needs.setup.outputs.matrix) }} steps: - run: build.sh ${{ matrix.project }} ${{ matrix.config }}
-
การเพิ่มประสิทธิภาพด้วย fail-fast:
jobs: test: runs-on: ubuntu-latest strategy: # หยุดทุก jobs เมื่อมี job หนึ่งล้มเหลว fail-fast: true # หรือยกเลิกการหยุดอัตโนมัติ # fail-fast: false matrix: node-version: [14.x, 16.x, 18.x] steps: - uses: actions/checkout@v3 - run: npm test
เทคนิคเหล่านี้ช่วยให้ workflow ของคุณทำงานเร็วขึ้น มีประสิทธิภาพมากขึ้น และง่ายต่อการบำรุงรักษา ลดเวลาในการรอ CI/CD และช่วยให้ทีมพัฒนาได้รวดเร็วขึ้น
การรัน unit tests อัตโนมัติเมื่อมีการ push โค้ดเป็นส่วนสำคัญของ CI เพื่อยืนยันว่าโค้ดใหม่ไม่สร้างปัญหา:
# .github/workflows/unit-tests.yml
name: Run Unit Tests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Generate test coverage report
run: npm run test:coverage
- name: Upload coverage report
uses: actions/upload-artifact@v3
with:
name: coverage-report
path: coverage/ข้อดี:
- รันเทสต์อัตโนมัติทุกครั้งที่มีการ push หรือ pull request
- สร้างรายงานความครอบคลุมของเทสต์ (test coverage)
- เก็บรายงานเป็น artifact สำหรับดูภายหลัง
การตรวจสอบคุณภาพโค้ดช่วยให้โค้ดสะอาด มีมาตรฐาน และลดปัญหาที่อาจเกิดขึ้น:
# .github/workflows/code-quality.yml
name: Code Quality
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run ESLint
run: npx eslint . --ext .js,.jsx,.ts,.tsx
- name: Run Prettier
run: npx prettier --check "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}"
sonarcloud:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}ข้อดี:
- ตรวจสอบและบังคับใช้รูปแบบโค้ด (code style)
- ตรวจหาปัญหาที่อาจเกิดขึ้น (potential bugs)
- วิเคราะห์คุณภาพโค้ดด้วย SonarCloud
สำหรับโปรเจค frontend เช่น React, Vue หรือ Angular สามารถสร้าง workflow เพื่อ build และทดสอบอัตโนมัติ:
# .github/workflows/frontend-ci.yml
name: Frontend CI
on:
push:
branches: [ main, develop ]
paths:
- 'frontend/**'
pull_request:
branches: [ main ]
paths:
- 'frontend/**'
jobs:
build_and_test:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run unit tests
run: npm run test:ci
- name: Build
run: npm run build
- name: Run E2E tests
run: npm run e2e:headless
- name: Archive build
uses: actions/upload-artifact@v3
with:
name: build
path: frontend/build/ข้อดี:
- จำกัดการทำงานเฉพาะเมื่อมีการเปลี่ยนแปลงโค้ดใน directory frontend
- รัน unit tests และ E2E tests
- จัดเก็บ build artifacts สำหรับการตรวจสอบหรือการ deploy
การ deploy Single Page Application (SPA) ไปยัง GitHub Pages อัตโนมัติ:
# .github/workflows/github-pages-deploy.yml
name: Deploy to GitHub Pages
on:
push:
branches: [ main ]
paths:
- 'src/**'
- 'public/**'
- 'package.json'
- '.github/workflows/github-pages-deploy.yml'
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
PUBLIC_URL: /${{ github.event.repository.name }}
- name: Deploy to GitHub Pages
uses: JamesIves/github-pages-deploy-action@v4
with:
folder: build
branch: gh-pages
clean: trueข้อดี:
- Deploy อัตโนมัติเมื่อมีการ push ไปยัง main branch
- จำกัดการทำงานเฉพาะเมื่อมีการเปลี่ยนแปลงไฟล์สำคัญ
- ตั้งค่า PUBLIC_URL สำหรับ subdirectory การ deploy
การสร้างและ push Docker image ไปยัง Docker Hub อัตโนมัติ:
# .github/workflows/docker-build-push.yml
name: Docker Build and Push
on:
push:
branches: [ main ]
tags:
- 'v*.*.*'
pull_request:
branches: [ main ]
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Docker metadata
id: meta
uses: docker/metadata-action@v4
with:
images: username/appname
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Login to Docker Hub
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v3
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=username/appname:buildcache
cache-to: type=registry,ref=username/appname:buildcache,mode=maxข้อดี:
- สร้าง tags อัตโนมัติจากชื่อ branch, PR หรือ version tags
- รองรับ multi-architecture builds ด้วย QEMU
- ใช้ cache เพื่อเร็วขึ้นในการ build
- แยก build-only สำหรับ PR และ build-and-push สำหรับ push หรือ tag
การ deploy ไปยัง AWS ด้วยการรวม AWS CLI และ Configure AWS credentials action:
# .github/workflows/aws-deploy.yml
name: Deploy to AWS
on:
push:
branches: [ main ]
workflow_dispatch:
inputs:
environment:
description: 'Environment to deploy to'
required: true
default: 'staging'
type: choice
options:
- staging
- production
jobs:
deploy-to-s3:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment || 'staging' }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-southeast-1
- name: Build frontend
run: |
npm ci
npm run build
- name: Deploy to S3
run: |
aws s3 sync ./build s3://${{ secrets.S3_BUCKET }} --delete
- name: Invalidate CloudFront cache
run: |
aws cloudfront create-invalidation --distribution-id ${{ secrets.CLOUDFRONT_DISTRIBUTION_ID }} --paths "/*"
deploy-lambda:
runs-on: ubuntu-latest
environment: ${{ github.event.inputs.environment || 'staging' }}
needs: deploy-to-s3
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-southeast-1
- name: Package Lambda function
run: |
cd lambda
npm ci
zip -r ../function.zip .
- name: Deploy Lambda function
run: |
aws lambda update-function-code \
--function-name ${{ secrets.LAMBDA_FUNCTION_NAME }} \
--zip-file fileb://function.zipข้อดี:
- รองรับการ deploy ทั้งแบบอัตโนมัติและ manual trigger
- ใช้ GitHub Environments สำหรับแยก secrets และ variables
- Deploy ไปยังหลาย services ของ AWS
- Invalidate CloudFront cache หลัง deploy
การ deploy โปรเจคไปยัง Firebase อัตโนมัติ:
# .github/workflows/firebase-deploy.yml
name: Deploy to Firebase
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build_and_preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Firebase Hosting preview channel
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
projectId: your-firebase-project-id
channelId: preview-${{ github.event.number || github.ref_name }}
deploy_production:
name: Deploy to Firebase Hosting
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to Firebase Hosting
uses: FirebaseExtended/action-hosting-deploy@v0
with:
repoToken: ${{ secrets.GITHUB_TOKEN }}
firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT }}
projectId: your-firebase-project-id
channelId: liveข้อดี:
- สร้าง preview channels สำหรับ pull requests
- Deploy ไปยัง production เฉพาะเมื่อ push ไปยัง main branch
- ใช้ Firebase service account สำหรับการ authentication
การ deploy แบบหลายสภาพแวดล้อมแตกต่างกันตาม branch หรือแบบ manual approval:
# .github/workflows/multi-environment-deploy.yml
name: Multi-Environment Deployment
on:
push:
branches: [develop, staging, main]
workflow_dispatch:
inputs:
environment:
description: 'Target environment'
required: true
type: choice
options:
- dev
- staging
- production
jobs:
determine_environment:
runs-on: ubuntu-latest
outputs:
environment: ${{ steps.set-env.outputs.environment }}
steps:
- id: set-env
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "environment=${{ github.event.inputs.environment }}" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
echo "environment=production" >> $GITHUB_OUTPUT
elif [[ "${{ github.ref }}" == "refs/heads/staging" ]]; then
echo "environment=staging" >> $GITHUB_OUTPUT
else
echo "environment=dev" >> $GITHUB_OUTPUT
fi
deploy:
needs: determine_environment
environment: ${{ needs.determine_environment.outputs.environment }}
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v1
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ap-southeast-1
- name: Install dependencies
run: npm ci
- name: Build with environment variables
run: |
echo "Building for ${{ needs.determine_environment.outputs.environment }} environment"
npm run build:${{ needs.determine_environment.outputs.environment }}
- name: Deploy to S3
run: |
aws s3 sync ./build s3://${{ secrets.S3_BUCKET_PREFIX }}-${{ needs.determine_environment.outputs.environment }} --delete
- name: Slack notification
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,message,commit,author,action,eventName,ref,workflow
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}ข้อดี:
- สามารถตรวจจับสภาพแวดล้อมจาก branch หรือ manual trigger
- ใช้ GitHub Environments สำหรับควบคุมการ approvals
- เรียกใช้สคริปต์ build ที่แตกต่างกันตามสภาพแวดล้อม
- แจ้งเตือนผ่าน Slack เมื่อ deploy เสร็จสิ้น
การ run database migrations อัตโนมัติเมื่อมีการ deploy:
# .github/workflows/db-migrations.yml
name: Database Migrations
on:
push:
branches: [ main ]
paths:
- 'migrations/**'
- 'schema/**'
jobs:
migrate_database:
runs-on: ubuntu-latest
environment: production
services:
# Test database for validation
postgres:
image: postgres:13
env:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: test_db
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
- name: Install dependencies
run: npm ci
- name: Validate migrations against test DB
run: |
npm run migrate:test
npm run migrate:verify
- name: Run migrations on production DB
env:
DB_HOST: ${{ secrets.DB_HOST }}
DB_USER: ${{ secrets.DB_USER }}
DB_PASS: ${{ secrets.DB_PASS }}
DB_NAME: ${{ secrets.DB_NAME }}
run: npm run migrate:production
- name: Create migration report
run: npm run migrate:report > migration_report.md
- name: Upload migration report
uses: actions/upload-artifact@v3
with:
name: migration-report
path: migration_report.md
- name: Notify on failure
if: failure()
uses: dawidd6/action-send-mail@v3
with:
server_address: ${{ secrets.SMTP_SERVER }}
server_port: 587
username: ${{ secrets.SMTP_USERNAME }}
password: ${{ secrets.SMTP_PASSWORD }}
subject: "[ALERT] Database migration failed!"
body: Migration to production DB failed. Check the logs.
to: dbadmin@example.com
from: CI System <ci@example.com>ข้อดี:
- รัน migrations เมื่อมีการเปลี่ยนแปลงไฟล์ที่เกี่ยวข้องกับ database
- ทดสอบ migrations บน test database ก่อน
- สร้างรายงานการ migrate สำหรับตรวจสอบ
- ส่งการแจ้งเตือนทาง email เมื่อ migrations ล้มเหลว
การตั้งค่าการแจ้งเตือนผ่านช่องทางต่างๆ:
# .github/workflows/monitoring-alerts.yml
name: Monitoring & Alerts
on:
workflow_run:
workflows: ["CI/CD Pipeline"]
types: [completed]
schedule:
- cron: '0 */3 * * *' # ทุก 3 ชั่วโมง
jobs:
health_check:
runs-on: ubuntu-latest
steps:
- name: Check API status
id: api_check
continue-on-error: true
run: |
response=$(curl -s -o /dev/null -w "%{http_code}" https://api.example.com/health)
echo "status=$response" >> $GITHUB_OUTPUT
test $response -eq 200
- name: Check website status
id: web_check
continue-on-error: true
run: |
response=$(curl -s -o /dev/null -w "%{http_code}" https://www.example.com)
echo "status=$response" >> $GITHUB_OUTPUT
test $response -eq 200
- name: Send Slack notification
if: steps.api_check.outcome == 'failure' || steps.web_check.outcome == 'failure'
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "🚨 System Alert!",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*System Health Check Failed!*"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "*API Status:* ${{ steps.api_check.outcome == 'success' && '✅ OK' || '❌ Failed' }} (${{ steps.api_check.outputs.status }})"
},
{
"type": "mrkdwn",
"text": "*Website Status:* ${{ steps.web_check.outcome == 'success' && '✅ OK' || '❌ Failed' }} (${{ steps.web_check.outputs.status }})"
}
]
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
- name: Send Discord notification
if: steps.api_check.outcome == 'failure' || steps.web_check.outcome == 'failure'
uses: sarisia/actions-status-discord@v1
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}
title: "System Health Check Failed!"
description: |
API Status: ${{ steps.api_check.outcome == 'success' && '✅ OK' || '❌ Failed' }} (${{ steps.api_check.outputs.status }})
Website Status: ${{ steps.web_check.outcome == 'success' && '✅ OK' || '❌ Failed' }} (${{ steps.web_check.outputs.status }})
color: 0xff0000
- name: Send Email Alert
if: steps.api_check.outcome == 'failure' || steps.web_check.outcome == 'failure'
uses: dawidd6/action-send-mail@v3
with:
server_address: ${{ secrets.SMTP_SERVER }}
server_port: 587
username: ${{ secrets.SMTP_USERNAME }}
password: ${{ secrets.SMTP_PASSWORD }}
subject: "🚨 System Health Alert"
body: |
System health check has detected issues:
API Status: ${{ steps.api_check.outcome == 'success' && 'OK' || 'Failed' }} (${{ steps.api_check.outputs.status }})
Website Status: ${{ steps.web_check.outcome == 'success' && 'OK' || 'Failed' }} (${{ steps.web_check.outputs.status }})
Please check systems immediately.
to: ops-team@example.com
from: Monitoring System <alerts@example.com>ข้อดี:
- ตรวจสุขภาพระบบตามระยะเวลาที่กำหนด
- ส่งการแจ้งเตือนผ่าน Slack, Discord และอีเมล
- รายงานสถานะที่เฉพาะเจาะจงของแต่ละส่วนของระบบ
- รันหลังจาก workflow CI/CD เสร็จสิ้นเพื่อตรวจสอบหลัง deploy
การจัดการ workflow สำหรับ monorepo ที่มีหลาย projects:
# .github/workflows/monorepo.yml
name: Monorepo CI/CD
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main, develop ]
jobs:
detect_changes:
runs-on: ubuntu-latest
outputs:
backend: ${{ steps.filter.outputs.backend }}
frontend: ${{ steps.filter.outputs.frontend }}
api: ${{ steps.filter.outputs.api }}
docs: ${{ steps.filter.outputs.docs }}
steps:
- uses: actions/checkout@v3
- name: Check for file changes
uses: dorny/paths-filter@v2
id: filter
with:
filters: |
backend:
- 'backend/**'
- 'shared/**'
frontend:
- 'frontend/**'
- 'shared/**'
api:
- 'api/**'
- 'shared/**'
docs:
- 'docs/**'
- '**/*.md'
backend:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.backend == 'true' }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./backend
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: backend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
frontend:
needs: detect_changes
if: ${{ needs.detect_changes.outputs.frontend == 'true' }}
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./frontend
steps:
- uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
cache-dependency-path: frontend/package-lock.json
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Build
run: npm run build
deploy:
needs: [detect_changes, backend, frontend]
# รันเมื่อมีการเปลี่ยนแปลงอย่างน้อย 1 โปรเจกต์และทุก jobs ที่เกี่ยวข้องสำเร็จ
if: |
always() &&
(needs.detect_changes.outputs.backend == 'true' ||
needs.detect_changes.outputs.frontend == 'true' ||
needs.detect_changes.outputs.api == 'true') &&
!contains(needs.*.result, 'failure') &&
github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Deploy backend
if: needs.detect_changes.outputs.backend == 'true'
run: echo "Deploying backend..."
- name: Deploy frontend
if: needs.detect_changes.outputs.frontend == 'true'
run: echo "Deploying frontend..."
- name: Deploy API
if: needs.detect_changes.outputs.api == 'true'
run:ในส่วนนี้เราจะดูตัวอย่าง workflow ที่มีในโปรเจค workshop จริง ซึ่งแสดงการประยุกต์ใช้ GitHub Actions ในสถานการณ์จริง
ตัวอย่าง workflow นี้ใช้สำหรับ build Docker image จาก PHP application และ deploy ไปยัง container บน environment ที่กำหนด
workflow สำหรับ main branch:
# .github/workflows/main.yml
name: deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Build Docker Image 🐳
run: docker build -f Dockerfile . -t php-main:latest
- name: Remove exist container 🏺
run: docker rm -f php-main
- name: Run Docker Container 🏺
run: docker run -p 80 --network backend --name php-main -d php-main:latestworkflow สำหรับ dev branch:
# .github/workflows/dev.yml
name: deploy
on:
push:
branches:
- dev
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Checkout 🛎
uses: actions/checkout@master
- name: Build Docker Image 🐳
run: docker build -f Dockerfile . -t php-dev:latest
- name: Remove exist container 🏺
run: docker rm -f php-dev:latest
- name: Run Docker Container 🏺
run: docker run -p 80 --network backend --name php-dev -d php-dev:latestคำอธิบาย:
- ใช้ self-hosted runner ในการรัน workflow ซึ่งสามารถเข้าถึงทรัพยากรเครือข่ายภายใน
- มีการตั้งชื่อ container และ image ที่แตกต่างกันตาม branch (
php-mainvsphp-dev) - มีการระบุ network ชื่อ
backendเพื่อให้ container สามารถติดต่อกับ containers อื่นๆ ในเครือข่ายเดียวกันได้ - ใช้สัญลักษณ์ emoji ในชื่อ step เพื่อความสวยงามและง่ายต่อการอ่าน
workflow นี้ใช้สำหรับ build และ deploy static HTML website ด้วย Docker:
# .github/workflows/racksync-ci.yml
name: racksync-ci
on:
push:
branches:
- main
jobs:
ci:
runs-on: self-hosted
steps:
- name: Checkout 🛎
uses: actions/checkout@master
with:
path: 'www1'
- name: Build Docker Image 👷🏻♂️
run: docker build www1/ -t www1
- name: Remove exist container 🚀
run: docker rm -f www1
- name: Run Docker Container
run: docker run -p 8088:80 --name www1 --restart always -d www1คำอธิบาย:
- ใช้
path: 'www1'ใน checkout action เพื่อกำหนดว่าต้องการ clone โค้ดไปยังโฟลเดอร์www1แทนที่จะเป็นโฟลเดอร์ปัจจุบัน - สร้าง Docker image จากโค้ดในโฟลเดอร์
www1/และตั้งชื่อ image ว่าwww1 - ทำการลบ container เดิมที่มีชื่อ
www1ก่อนที่จะสร้างใหม่ เพื่อหลีกเลี่ยงความขัดแย้งของชื่อ - รัน container พร้อมกำหนดการ mapping port จาก 8088 (host) ไปยัง 80 (container)
- ใช้ flag
--restart alwaysเพื่อให้ container รีสตาร์ทโดยอัตโนมัติเมื่อเกิดปัญหา หรือเมื่อ host มีการรีสตาร์ท
| คุณสมบัติ | Workflows จาก Workshop | Best Practices |
|---|---|---|
| การตั้งชื่อ steps | ✅ ใช้ emoji ทำให้อ่านง่าย | ควรใช้ชื่อที่มีความหมายชัดเจนและสามารถค้นหาได้ง่าย |
| การใช้ versioning | ❌ ใช้ @master สำหรับ actions |
ควรระบุเวอร์ชันที่แน่นอน เช่น @v3 |
| กลยุทธ์การ deploy | ใช้วิธีลบ container เก่าก่อนรันอันใหม่ | อาจพิจารณาใช้ blue-green deployment เพื่อลด downtime |
| การใช้ secrets | ไม่มีการใช้ secrets ในตัวอย่าง | ควรใช้ secrets สำหรับ credentials หรือค่า sensitive |
| การแบ่ง environments | มีการแยก workflow ตาม branch | ✅ เป็นวิธีที่ดีในการจัดการหลาย environment |
หากต้องการปรับปรุง workflows จากตัวอย่าง สามารถทำได้ดังนี้:
-
ระบุ version ที่แน่นอนของ actions:
- name: Checkout 🛎 uses: actions/checkout@v3 # แทนที่จะใช้ @master
-
เพิ่มการตรวจสอบ (testing) ก่อน deploy:
- name: Run tests run: | cd www1 # คำสั่งสำหรับทดสอบ เช่น # npm test หรือ php vendor/bin/phpunit
-
เพิ่ม notifications เมื่อ deploy สำเร็จหรือล้มเหลว:
- name: Notify on success if: success() run: curl -s -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" -d '{"text":"✅ Deployment successful!"}' - name: Notify on failure if: failure() run: curl -s -X POST "${{ secrets.SLACK_WEBHOOK_URL }}" -d '{"text":"❌ Deployment failed!"}'
-
ใช้ Docker tags ที่มีความหมายมากขึ้น:
- name: Build Docker Image 🐳 run: docker build -f Dockerfile . -t php-main:latest -t php-main:$(date +%Y%m%d%H%M%S)
-
เพิ่ม health check หลังจาก deploy:
- name: Health check run: | sleep 10 # รอให้ container เริ่มต้นอย่างสมบูรณ์ STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8088) if [ $STATUS -ne 200 ]; then echo "Health check failed with status $STATUS" exit 1 fi
-
การประยุกต์ใช้ self-hosted runners
- เหมาะสำหรับองค์กรที่ต้องการควบคุมสภาพแวดล้อมการ deploy
- สามารถเข้าถึงทรัพยากรเครือข่ายภายใน
- ไม่มีข้อจำกัดด้านเวลาในการรัน workflow
-
การใช้ Docker ร่วมกับ CI/CD
- ทำให้การ deploy มีความคงเส้นคงวา
- รองรับการ roll back ได้ง่ายโดยการเปลี่ยนกลับไปใช้ image เวอร์ชันก่อนหน้า
- ลดปัญหา "works on my machine"
-
แนวทางการแบ่งแยก environments
- การแยก workflow ตาม branch
- การตั้งชื่อ resources (images, containers) ที่แตกต่างกันตาม environment
ตัวอย่าง workflows เหล่านี้แสดงให้เห็นถึงการใช้งาน GitHub Actions ในสถานการณ์จริง แม้จะเป็นตัวอย่างที่เรียบง่าย แต่ก็สามารถปรับปรุงและขยายเพิ่มเติมให้รองรับความต้องการที่ซับซ้อนมากขึ้นได้
