Skip to content

Latest commit

 

History

History
2247 lines (1789 loc) · 80.6 KB

File metadata and controls

2247 lines (1789 loc) · 80.6 KB

CI/CD Workshop

📑 สารบัญ

🔹 1. Introduction to CI/CD

🚀 CI/CD คืออะไร? ทำไมสำคัญ?

Continuous Integration (CI) และ Continuous Delivery/Deployment (CD) คือแนวทางในการพัฒนาซอฟต์แวร์ที่ช่วยให้ทีมสามารถส่งมอบการเปลี่ยนแปลงไปยังลูกค้าได้อย่างรวดเร็วและปลอดภัยยิ่งขึ้น

ทำไมสำคัญ?

  • ลดความเสี่ยงในการ deploy
  • ตรวจจับ bug ได้เร็วขึ้น
  • เพิ่มความเร็วในการพัฒนาและ delivery
  • ลดงานที่ซ้ำซ้อนและเพิ่ม automation
  • สร้างความมั่นใจในคุณภาพของโค้ด

⚙️ การทำงานของ Continuous Integration (CI)

CI เน้นที่การรวมโค้ดจากนักพัฒนาหลายคนเข้าด้วยกันอย่างสม่ำเสมอ:

  1. การ Commit โค้ดบ่อยๆ: นักพัฒนาควร push โค้ดไปยังระบบควบคุมเวอร์ชัน (เช่น Git) อย่างสม่ำเสมอ
  2. Automated Build: ระบบจะสร้าง build โปรเจคโดยอัตโนมัติเมื่อมีการ push โค้ด
  3. Automated Tests: ทดสอบโค้ดที่ถูก push เข้ามาโดยอัตโนมัติ
  4. 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]
Loading

🚢 การทำงานของ Continuous Deployment (CD)

CD เป็นกระบวนการที่ทำให้มั่นใจว่าซอฟต์แวร์พร้อมจะ deploy ได้ตลอดเวลา:

  1. Continuous Delivery: การเตรียมโค้ดให้พร้อมสำหรับการ deploy โดยอัตโนมัติ แต่ยังคงต้องอนุมัติการ deploy แบบ manual
  2. 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]
Loading

🤝 DevOps กับ CI/CD

DevOps เป็นวัฒนธรรมและแนวทางที่เน้นการทำงานร่วมกันระหว่างทีม Development และ Operations:

  • CI/CD เป็นเครื่องมือสำคัญในการทำ DevOps
  • ช่วยให้ทีมสามารถส่งมอบซอฟต์แวร์ได้อย่างรวดเร็ว มีประสิทธิภาพและคุณภาพ
  • สนับสนุนค่านิยมหลักของ DevOps: การทำงานร่วมกัน, automation, การวัดผล, และการแบ่งปัน

🔹 2. Overview of CI/CD Platforms

เปรียบเทียบ CI/CD Platforms ยอดนิยม

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

ทำไมต้องเลือก GitHub Actions?

  1. Native Integration: ทำงานร่วมกับ GitHub ได้อย่างลงตัวโดยไม่ต้องใช้บริการจากภายนอก
  2. YAML-based Configuration: ใช้ไฟล์ YAML ที่อ่านง่ายและเข้าใจได้ง่าย
  3. Community Actions: มี action มากมายที่พร้อมใช้งานจาก marketplace
  4. Free for Public Repositories: ฟรีสำหรับ public repositories และมี free minutes สำหรับ private repositories
  5. Matrix Builds: สร้าง matrix builds เพื่อทดสอบในหลาย environment ได้ง่าย
  6. Self-hosted Runners: สามารถใช้ runner บนเครื่องของตัวเองได้

การรัน GitHub Actions Runner บน On-Premises

การรัน runner บนเครื่องหรือเซิร์ฟเวอร์ของตนเอง (self-hosted runner) เป็นทางเลือกที่ดีเมื่อต้องการควบคุมสภาพแวดล้อมการทำงานหรือต้องการใช้ทรัพยากรที่มีอยู่

ประโยชน์ของ Self-hosted Runners

  1. รองรับฮาร์ดแวร์เฉพาะทาง - เช่น GPU หรือ CPU ที่มีประสิทธิภาพสูง
  2. เข้าถึงทรัพยากรภายใน - สามารถเข้าถึงระบบภายในที่ไม่เปิดให้ภายนอก
  3. ประหยัดเวลา - ไม่ต้องอัปโหลด/ดาวน์โหลดไฟล์ขนาดใหญ่ไปยัง GitHub
  4. การตั้งค่าเฉพาะ - กำหนดสภาพแวดล้อมให้ตรงกับ production ได้
  5. ไม่มีข้อจำกัดเวลา - ไม่มี timeout แบบที่ GitHub-hosted runners มี

การติดตั้งและตั้งค่าสภาพแวดล้อม

1. ติดตั้ง Docker (จำเป็นสำหรับการรัน workflow ที่ใช้ Docker)

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

2. การติดตั้งและลงทะเบียน GitHub Actions Runner

การติดตั้ง 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

3. การตั้งค่า Labels (ตัวเลือก)

การกำหนด 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]

4. ตั้งค่า sudo โดยไม่ต้องใส่รหัสผ่าน (สำหรับ workflows ที่ต้องใช้ sudo)

หากยังไม่มี package sudo ให้ติดตั้ง:

apt -y install sudo
sudo visudo

เพิ่มบรรทัดต่อไปนี้ที่ส่วนท้ายของไฟล์:

runner ALL=(ALL) NOPASSWD:ALL

โดยแทนที่ runner ด้วยชื่อผู้ใช้ของ runner

การดูแลรักษา Runner

  1. การอัปเดต Runner:

    cd actions-runner
    ./config.sh remove --token YOUR_REMOVE_TOKEN
    # หลังจากลบแล้ว ให้ติดตั้งเวอร์ชันใหม่
  2. การตรวจสอบการทำงาน:

    # ดูล็อกไฟล์
    tail -f ~/actions-runner/_diag/Runner_*.log
    
    # ดูสถานะ service (หากติดตั้งเป็น service)
    ./svc.sh status
  3. การหยุดและถอนการติดตั้ง Runner:

    # หยุดการทำงาน
    ./svc.sh stop
    
    # ถอนการติดตั้งจาก service
    ./svc.sh uninstall
    
    # ลบจาก GitHub
    ./config.sh remove --token YOUR_REMOVE_TOKEN

การใช้ Docker กับ Self-hosted Runner

หากต้องการใช้ 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

ความปลอดภัย

  1. แยก Runners ตามระดับความเสี่ยง:

    • ใช้ runner แยกสำหรับ public repositories และ private repositories
    • ไม่ควรใช้ runner เดียวกันสำหรับงานที่มีระดับความปลอดภัยต่างกัน
  2. จำกัดการเข้าถึง:

    • ตั้งค่า firewall อย่างเหมาะสม
    • ระวังการให้สิทธิ์บน runner machine

การรันด้วย user ปกติช่วยลดสิทธิ์ของ runner ทำให้ระบบปลอดภัยยิ่งขึ้น

🔹 3. Getting Started with GitHub Actions

GitHub Actions คืออะไร?

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

โครงสร้างของไฟล์ .yml

name: [ชื่อ Workflow]

on: [event ที่ทริกเกอร์ workflow]

jobs:
  job_id:
    runs-on: [runner]
    steps:
      - name: [ชื่อ step]
        uses: [action]  # หรือ
        run: [command]  # คำสั่ง shell
        env:  # ตัวแปรสภาพแวดล้อม (optional)
          KEY: value

ตัวอย่าง Workflow พื้นฐาน

ตัวอย่างพื้นฐานสำหรับ 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 }}

🔹 4. Core Concepts in GitHub Actions

Workflow Structure

GitHub Actions ทำงานตามลำดับดังนี้:

Event → Jobs → Steps → Actions

  1. Event: เหตุการณ์ที่ทริกเกอร์ให้ workflow เริ่มทำงาน
  2. Jobs: งานที่ต้องทำหลังจากเกิด event (รันแบบขนานโดยปกติ)
  3. Steps: ขั้นตอนย่อยในแต่ละ job (รันแบบลำดับ)
  4. 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]
Loading

Types of Events ที่ Trigger Workflow

  1. push: เมื่อมีการ push โค้ดเข้า repository

    on:
      push:
        branches: [ main, dev ]
        paths-ignore: [ 'docs/**', '**.md' ]
  2. pull_request: เมื่อมีการสร้างหรืออัปเดต Pull Request

    on:
      pull_request:
        types: [opened, synchronize, reopened]
        branches: [ main ]
  3. schedule: รันตามเวลาที่กำหนดด้วยรูปแบบ cron

    on:
      schedule:
        - cron: '0 0 * * *'  # รันทุกวันตอนเที่ยงคืน
  4. workflow_dispatch: รันด้วยตนเองผ่าน UI หรือ API

    on:
      workflow_dispatch:
        inputs:
          environment:
            description: 'Environment to deploy to'
            required: true
            default: 'staging'
  5. repository_dispatch: รันเมื่อ webhook ภายนอกเรียก API

    on:
      repository_dispatch:
        types: [deploy]

Runners และ Execution Environment

Runners คือเซิร์ฟเวอร์ที่รัน workflow ของคุณ มี 2 ประเภท:

GitHub-hosted Runners

  • 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

Self-hosted Runners

  • คุณติดตั้งและดูแลเอง
  • เหมาะสำหรับสภาพแวดล้อมพิเศษหรือต้องการ performance สูง
  • สามารถเป็นเครื่องจริง, VM หรือ container
jobs:
  build:
    runs-on: self-hosted
    # หรือระบุ labels เพิ่มเติม
    # runs-on: [self-hosted, linux, x64]

🔹 5. GitHub Actions Marketplace

ใช้ Actions สำเร็จรูปจาก Marketplace

GitHub Actions Marketplace คือแหล่งรวม actions สำเร็จรูปที่สามารถนำมาใช้ในโปรเจกต์ของคุณได้ทันที โดยส่วนใหญ่:

  • พัฒนาโดย GitHub, องค์กรชั้นนำ, หรือชุมชน
  • มีวิธีใช้งานและเอกสารประกอบชัดเจน
  • มีการอัปเดตสม่ำเสมอ

วิธีค้นหาและใช้งาน Actions

  1. การค้นหา Actions:

    • เข้าไปที่ GitHub Marketplace
    • ใช้คำค้นเพื่อหา action ที่ต้องการ
    • อ่านรีวิวและดูจำนวนดาวเพื่อประเมินความนิยม
  2. วิธีเพิ่ม Action ในไฟล์ workflow:

    steps:
      - name: ชื่อขั้นตอน
        uses: ชื่อ-action@version
        with:
          parameter-1: value-1
          parameter-2: value-2
  3. การระบุเวอร์ชัน:

    • @v1: ใช้เวอร์ชันหลักล่าสุด (เช่น v1.x.x)
    • @v1.2: ใช้เวอร์ชันรองล่าสุด (เช่น v1.2.x)
    • @v1.2.3: ใช้เวอร์ชันแน่นอน
    • @main: ใช้จากบรานช์หลัก (อาจไม่เสถียร)
    • @5a4ac9bb22: ใช้ commit hash แน่นอน (ปลอดภัยที่สุด)

ตัวอย่าง Actions ยอดนิยม

1. actions/checkout

ใช้สำหรับโคลน repository ไปยัง runner

- name: Checkout code
  uses: actions/checkout@v3
  with:
    # รองรับ 
    fetch-depth: 0  # โคลนประวัติทั้งหมด
    ref: feature-branch  # ระบุ branch หรือ tag

2. actions/setup-node

ตั้งค่าสภาพแวดล้อม 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

3. docker/build-push-action

สร้างและส่ง 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

4. actions/cache

แคชไฟล์เพื่อเร่งความเร็วในการรัน workflow

- name: Cache dependencies
  uses: actions/cache@v3
  with:
    path: ~/.npm
    key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
    restore-keys: |
      ${{ runner.os }}-npm-

5. JamesIves/github-pages-deploy-action

สำหรับ deploy เว็บไปยัง GitHub Pages

- name: Deploy to GitHub Pages
  uses: JamesIves/github-pages-deploy-action@v4
  with:
    branch: gh-pages
    folder: build
    clean: true

🔹 6. Secrets, Security & Permissions

การใช้ Secrets & Environment Variables

Secrets คือตัวแปรที่เข้ารหัสซึ่งใช้เก็บข้อมูลที่มีความอ่อนไหว โดยจะไม่แสดงในล็อก:

Environment Variables ทั่วไป

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_MODE

การเรียกใช้ Secrets

jobs:
  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

วิธีตั้งค่า Secrets

  1. ไปที่ repository ของคุณ > Settings > Secrets and variables > Actions
  2. คลิกปุ่ม New repository secret
  3. ใส่ชื่อ secret (เช่น API_TOKEN) และค่า
  4. คลิก Add secret

GitHub Secrets UI

ประเภทของ Secrets:

  • Repository secrets: ใช้ได้เฉพาะ repository หนึ่ง
  • Environment secrets: ใช้ได้เฉพาะ environment ที่กำหนด
  • Organization secrets: ใช้ได้ทุก repository ในองค์กร

การจำกัดสิทธิ์ (GITHUB_TOKEN, Fine-grained permissions)

GITHUB_TOKEN

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"}'

จำกัดสิทธิ์ของ GITHUB_TOKEN

permissions:
  # จำกัดสิทธิ์ทั้งหมด
  contents: read
  issues: write
  pull-requests: write
  # จะมีสิทธิ์เฉพาะ contents:read, issues:write, pull-requests:write เท่านั้น

หรือปิดการใช้งานทั้งหมด:

permissions: {}  # ไม่มีสิทธิ์ใดๆ

วิธีป้องกันข้อมูลรั่วไหล

  1. อย่าแสดง Secret โดยตรง:

    # BAD - secrets จะถูกแสดงในล็อก
    run: echo "My secret is ${{ secrets.MY_SECRET }}"
    
    # GOOD - ไม่แสดง secret ในล็อก
    run: |
      echo "Using secret for authentication..."
      # ใช้ secret โดยไม่แสดง
  2. จำกัดสิทธิ์:

    • ใช้หลักการ "least privilege" โดยให้สิทธิ์เท่าที่จำเป็น
    • กำหนด permissions อย่างชัดเจน
  3. ระวัง Workflow จาก Pull Request:

    # จำกัด secret เมื่อ workflow ถูกเรียกจาก pull request
    if: github.event_name != 'pull_request'
    env:
      TOKEN: ${{ secrets.TOKEN }}
  4. ใช้ Environment Protection Rules:

    jobs:
      deploy:
        runs-on: ubuntu-latest
        environment: production  # สภาพแวดล้อมที่มีการป้องกัน
  5. ตรวจสอบ Dependencies:

    • ใช้ actions/checkout@ ด้วยเวอร์ชันแน่นอน
    • ระวัง action ที่ไม่น่าเชื่อถือ
    • พิจารณาใช้ action hash แทนเวอร์ชัน

ตัวอย่างการตั้งค่า Environment Protection Rules:

  1. ไปที่ repository > Settings > Environments
  2. สร้าง environment ใหม่หรือคลิก environment ที่มีอยู่
  3. ตั้งค่า "Required reviewers" เพื่อจำเป็นต้องมีการอนุมัติ
  4. จำกัด 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 }}

🔹 7. Debugging & Monitoring GitHub Actions

การดีบักและติดตามการทำงานของ GitHub Actions เป็นทักษะสำคัญในการแก้ไขปัญหาและปรับปรุงประสิทธิภาพของ workflow

การดู Logs

GitHub มีระบบ logs ที่ช่วยให้เราตรวจสอบการทำงานของ workflow ได้อย่างละเอียด:

  1. การเข้าถึง Workflow Logs:

    • ไปที่ repository > Actions tab
    • เลือก workflow run ที่ต้องการตรวจสอบ
    • คลิกที่ job เพื่อดูรายละเอียดของแต่ละ step
  2. การอ่าน Logs:

    • แต่ละ step มีลูกศรสำหรับขยาย/ย่อ logs
    • สามารถค้นหาข้อความใน logs ได้
    • ดาวน์โหลด logs เป็นไฟล์ได้โดยใช้ปุ่มด้านบนขวา
  3. องค์ประกอบของ 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

การใช้ debug logs

เมื่อต้องการข้อมูลเพิ่มเติมเกี่ยวกับการทำงานของ workflow สามารถเปิดใช้ debug logs ได้:

  1. การเปิดใช้งาน Debug Logs:

    • สร้าง repository secret ชื่อ ACTIONS_RUNNER_DEBUG ค่าเป็น true
    • หรือสร้าง repository secret ชื่อ ACTIONS_STEP_DEBUG ค่าเป็น true
  2. การดู Debug Output ใน Actions Runner:

    steps:
      - name: Show debug info
        env:
          RUNNER_DEBUG: 1
        run: |
          echo "Debug information"
  3. 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 ที่ล้มเหลวหรือต้องการทดสอบซ้ำ:

  1. รีรันทั้ง Workflow:

    • ไปที่หน้า workflow run ที่ต้องการรีรัน
    • คลิกปุ่ม "Re-run all jobs" ที่มุมขวาบน
  2. รีรันเฉพาะ Jobs ที่ล้มเหลว:

    • ไปที่หน้า workflow run
    • คลิกปุ่ม "Re-run failed jobs" (จะปรากฏเมื่อมีงานที่ล้มเหลว)
  3. รีรันผ่าน 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

Debugging Techniques

เทคนิคขั้นสูงสำหรับการดีบัก workflow:

  1. ใช้ tmate สำหรับ Interactive Debugging:

    steps:
      - uses: actions/checkout@v3
      
      - name: Setup tmate session
        uses: mxschmitt/action-tmate@v3
        if: ${{ failure() }}  # รันเมื่อ step ก่อนหน้าล้มเหลว
  2. ตรวจสอบตัวแปรและสภาพแวดล้อม:

    - name: Debug environment
      run: |
        echo "GitHub workspace: $GITHUB_WORKSPACE"
        echo "GitHub event name: $GITHUB_EVENT_NAME"
        echo "GitHub ref: $GITHUB_REF"
        env
  3. แสดงข้อมูลเกี่ยวกับ 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
  4. สร้าง 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

🔹 8. Optimizing GitHub Actions Workflows

การปรับปรุงประสิทธิภาพของ workflow ช่วยประหยัดเวลาและทรัพยากร ทำให้กระบวนการ CI/CD เร็วขึ้นและมีประสิทธิภาพมากขึ้น

ลดระยะเวลา Execution

  1. ใช้ GitHub-hosted runner ที่เหมาะสม:

    • ubuntu-latest มีประสิทธิภาพสูงกว่า Windows และ macOS
    jobs:
      build:
        # เลือก OS ที่เร็วที่สุดสำหรับงาน
        runs-on: ubuntu-latest
  2. ลดขนาดของ Repository:

    - name: Checkout
      uses: actions/checkout@v3
      with:
        fetch-depth: 1  # ดึงเฉพาะ commit ล่าสุด
  3. ปรับแต่ง 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
  4. หลีกเลี่ยงการติดตั้งซอฟต์แวร์ที่ไม่จำเป็น:

    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '16'
        # ลดเวลาด้วยการติดตั้งเฉพาะที่จำเป็น
        check-latest: false

ใช้ Caching (actions/cache)

การใช้ cache ช่วยลดเวลาในการดาวน์โหลดและติดตั้งแพ็คเกจต่างๆ:

  1. 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-
  2. 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-
  3. 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-
  4. การใช้ cache ร่วมกับ setup- actions*:

    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '16'
        cache: 'npm'  # ทำ cache โดยอัตโนมัติ

Reusable Workflows

Reusable workflows ช่วยลดความซ้ำซ้อนและทำให้การจัดการ workflow ง่ายขึ้น:

  1. การสร้าง 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 }}
  2. การเรียกใช้ 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 }}
  3. ตัวอย่าง 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 & Matrix Jobs

การใช้ parallel jobs และ matrix strategy ช่วยให้สามารถรันงานหลายๆ งานพร้อมกัน:

  1. การใช้ 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
  2. การแบ่งงานทดสอบ:

    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 }}
  3. 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 }}
  4. การเพิ่มประสิทธิภาพด้วย 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 และช่วยให้ทีมพัฒนาได้รวดเร็วขึ้น

🔥 ตัวอย่างการใช้งาน GitHub Actions (Workshop + Use Cases)

🔹 9. Basic CI/CD Pipelines

✅ Example 1: รัน Unit Tests อัตโนมัติ

การรัน 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 สำหรับดูภายหลัง

✅ Example 2: Linting และ Code Quality Checks

การตรวจสอบคุณภาพโค้ดช่วยให้โค้ดสะอาด มีมาตรฐาน และลดปัญหาที่อาจเกิดขึ้น:

# .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

✅ Example 3: Auto Build & Test สำหรับ Frontend

สำหรับโปรเจค 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

🔹 10. Deployment Pipelines

✅ Example 4: Deploy React/Vue/Angular ไปที่ GitHub Pages

การ 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

✅ Example 5: Build & Push Docker Image ไปที่ Docker Hub

การสร้างและ 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

✅ Example 6: Deploy ไปที่ AWS (EC2, S3, Lambda)

การ 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

✅ Example 7: Deploy ไปที่ Firebase

การ 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

🔹 11. Advanced Use Cases

✅ Example 8: Multi-Environment Deployment (Dev, Staging, Prod)

การ 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 เสร็จสิ้น

✅ Example 9: Automating Database Migrations

การ 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 ล้มเหลว

✅ Example 10: Monitoring & Alerts (แจ้งเตือน Slack, Discord, Email)

การตั้งค่าการแจ้งเตือนผ่านช่องทางต่างๆ:

# .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

✅ Example 11: ใช้ GitHub Actions กับ Monorepo

การจัดการ 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:

🔹 12. ตัวอย่าง Workflow จากโปรเจค Workshop

ในส่วนนี้เราจะดูตัวอย่าง workflow ที่มีในโปรเจค workshop จริง ซึ่งแสดงการประยุกต์ใช้ GitHub Actions ในสถานการณ์จริง

ตัวอย่าง 1: ตัวอย่าง Workflow สำหรับ PHP Application

ตัวอย่าง 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:latest

workflow สำหรับ 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-main vs php-dev)
  • มีการระบุ network ชื่อ backend เพื่อให้ container สามารถติดต่อกับ containers อื่นๆ ในเครือข่ายเดียวกันได้
  • ใช้สัญลักษณ์ emoji ในชื่อ step เพื่อความสวยงามและง่ายต่อการอ่าน

ตัวอย่าง 2: ตัวอย่าง Workflow สำหรับ HTML Static Website

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

คุณสมบัติ 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 จาก Workshop

หากต้องการปรับปรุง workflows จากตัวอย่าง สามารถทำได้ดังนี้:

  1. ระบุ version ที่แน่นอนของ actions:

    - name: Checkout 🛎
      uses: actions/checkout@v3  # แทนที่จะใช้ @master
  2. เพิ่มการตรวจสอบ (testing) ก่อน deploy:

    - name: Run tests
      run: |
        cd www1
        # คำสั่งสำหรับทดสอบ เช่น
        # npm test หรือ php vendor/bin/phpunit
  3. เพิ่ม 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!"}'
  4. ใช้ 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)
  5. เพิ่ม 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

บทเรียนจากตัวอย่าง Workshop

  1. การประยุกต์ใช้ self-hosted runners

    • เหมาะสำหรับองค์กรที่ต้องการควบคุมสภาพแวดล้อมการ deploy
    • สามารถเข้าถึงทรัพยากรเครือข่ายภายใน
    • ไม่มีข้อจำกัดด้านเวลาในการรัน workflow
  2. การใช้ Docker ร่วมกับ CI/CD

    • ทำให้การ deploy มีความคงเส้นคงวา
    • รองรับการ roll back ได้ง่ายโดยการเปลี่ยนกลับไปใช้ image เวอร์ชันก่อนหน้า
    • ลดปัญหา "works on my machine"
  3. แนวทางการแบ่งแยก environments

    • การแยก workflow ตาม branch
    • การตั้งชื่อ resources (images, containers) ที่แตกต่างกันตาม environment

ตัวอย่าง workflows เหล่านี้แสดงให้เห็นถึงการใช้งาน GitHub Actions ในสถานการณ์จริง แม้จะเป็นตัวอย่างที่เรียบง่าย แต่ก็สามารถปรับปรุงและขยายเพิ่มเติมให้รองรับความต้องการที่ซับซ้อนมากขึ้นได้