1. บทนำ - CI/CD คืออะไร?
🔄 CI/CD คืออะไร?
CI/CD ย่อมาจาก Continuous Integration และ Continuous Deployment (หรือ Continuous Delivery) เป็นแนวปฏิบัติในการพัฒนาซอฟต์แวร์ที่ช่วยให้ทีมสามารถส่งมอบโค้ดได้อย่างรวดเร็ว แม่นยำ และมีคุณภาพ
📌 นิยามแบบเข้าใจง่าย
Continuous Integration (CI) = การรวมโค้ดจากนักพัฒนาหลายคนเข้าด้วยกันอัตโนมัติ พร้อมตรวจสอบความถูกต้องทุกครั้งที่มีการ push
Continuous Deployment (CD) = การปล่อยซอฟต์แวร์สู่ผู้ใช้งานอัตโนมัติ โดยไม่ต้องมีการแทรกแซงจากมนุษย์
❓ ทำไมต้องใช้ CI/CD?
ก่อนจะมี CI/CD การพัฒนาซอฟต์แวร์เผชิญปัญหาหลายอย่าง:
- 🔴 Integration Hell - รวมโค้ดทีละเยอะ เจอบั๊กเพียบ แก้ไม่จบ
- 🔴 Manual Testing - ทดสอบด้วยมือ ช้า ผิดพลาดง่าย
- 🔴 Deployment Errors - Deploy มือ ผิดพลาดบ่อย ย้อนกลับยาก
- 🔴 Slow Feedback - รู้บั๊กช้า แก้ไขช้า ส่งงานช้า
CI/CD ช่วยแก้ปัญหาเหล่านี้ได้อย่างไร?
⚡ รวดเร็ว
Automate ทุกขั้นตอน ลดเวลาจากวันเป็นชั่วโมง จากชั่วโมงเป็นนาที
🎯 แม่นยำ
ระบบทดสอบอัตโนมัติ ลด Human Error ตรวจจับบั๊กได้เร็ว
🔄 Consistent
สภาพแวดล้อมเหมือนกันทุกครั้ง ไม่มีเรื่อง "ในเครื่องฉันรันได้"
📊 มองเห็น
ติดตามสถานะได้ตลอด รู้ปัญหาทันที มี Log ครบถ้วน
⚔️ GitLab CI/CD vs Jenkins vs GitHub Actions
เมื่อพูดถึง CI/CD Tools มีตัวเลือกหลายตัว มาดูการเปรียบเทียบกัน:
| คุณสมบัติ | GitLab CI/CD | Jenkins | GitHub Actions |
|---|---|---|---|
| การตั้งค่า | ✓ ง่าย (YAML) | ✗ ซับซ้อน | ✓ ง่าย (YAML) |
| Built-in Container Registry | ✓ มี | ✗ ต้องติดตั้งเพิ่ม | ✗ ใช้ของ third-party |
| Free Tier | ✓ 400 นาที/เดือน | ✓ ฟรีทั้งหมด | ✓ 2,000 นาที/เดือน |
| Self-hosted | ✓ รองรับ | ✓ รองรับ | ✗ จำกัด |
| Kubernetes Integration | ✓ ดีเยี่ยม | ✓ ดี | ✓ ดี |
| Security Scanning | ✓ Built-in | ✗ ต้องติดตั้ง Plugin | ✓ มีบางส่วน |
| All-in-One Platform | ✓ ใช่ | ✗ เฉพาะ CI/CD | ✗ เฉพาะ CI/CD |
💡 ทำไมเราเลือก GitLab CI/CD?
GitLab เป็นแพลตฟอร์ม All-in-One ที่มีทุกอย่างในที่เดียว: Git Repository, Issue Tracking, CI/CD, Container Registry, Security Scanning และอีกมากมาย ทำให้ไม่ต้องจัดการหลาย Tools และการ Integration ซับซ้อน
2. Architecture Overview
🔄 CI/CD Pipeline Flow
มาดูภาพรวมของการทำงาน CI/CD Pipeline ตั้งแต่เขียนโค้ดจนถึงการ Deploy:
รูปที่ 1: ขั้นตอนการทำงานของ CI/CD Pipeline
🏗️ GitLab Runner Architecture
GitLab CI/CD ใช้สถาปัตยกรรมแบบ GitLab Server + GitLab Runner โดย Runner จะทำหน้าที่ execute jobs:
รูปที่ 2: สถาปัตยกรรม GitLab CI/CD และ GitLab Runner
🔔 จุดสำคัญที่ต้องจำ
- GitLab Server เก็บโค้ดและจัดการ Pipeline แต่ ไม่ได้รัน jobs เอง
- GitLab Runner เป็นตัวที่รัน jobs จริง สามารถอยู่คนละเครื่องกับ Server
- Runner รองรับหลาย Executor (Docker, Kubernetes, Shell, ฯลฯ)
3. พื้นฐาน .gitlab-ci.yml
ไฟล์ .gitlab-ci.yml เป็นหัวใจของ GitLab CI/CD ทุกอย่างเริ่มต้นที่ไฟล์นี้ โดยจะต้องวางไว้ที่ root ของ repository
📁 โครงสร้างไฟล์
โครงสร้างพื้นฐานของไฟล์ .gitlab-ci.yml ประกอบด้วย:
# 1. กำหนด Stages (ลำดับการทำงาน)
stages:
- build
- test
- deploy
# 2. กำหนด Variables ทั่วไป
variables:
APP_NAME: "my-app"
DOCKER_IMAGE: "node:18"
# 3. กำหนด Jobs (งานที่ต้องทำ)
build_job:
stage: build
image: $DOCKER_IMAGE
script:
- echo "Building $APP_NAME..."
- npm install
- npm run build
test_job:
stage: test
image: $DOCKER_IMAGE
script:
- npm test
deploy_job:
stage: deploy
script:
- echo "Deploying to production..."
only:
- main
🏷️ Keywords สำคัญที่ต้องรู้
stages
กำหนดลำดับการทำงานของ Pipeline (build → test → deploy)
stage
ระบุว่า Job นี้อยู่ใน Stage ไหน
script
คำสั่งที่จะรัน (จำเป็นต้องมีในทุก Job)
image
Docker Image ที่จะใช้รัน Job
variables
ตัวแปรที่ใช้ใน Pipeline
only / except
กำหนดเงื่อนไขการรัน (branch, tags, etc.)
before_script
คำสั่งที่รันก่อน script หลัก
after_script
คำสั่งที่รันหลัง script หลัก (แม้ job จะ fail)
📋 Keywords เพิ่มเติม
| Keyword | คำอธิบาย | ตัวอย่าง |
|---|---|---|
tags |
ระบุ Runner ที่จะรัน Job | tags: [docker, linux] |
allow_failure |
อนุญาตให้ Job fail ได้โดยไม่หยุด Pipeline | allow_failure: true |
when |
กำหนดเงื่อนไขการรัน | when: manual |
artifacts |
ไฟล์ที่เก็บไว้ให้ Job ถัดไปใช้ | artifacts: paths: [dist/] |
dependencies |
ระบุ Job ที่ต้องรอให้เสร็จก่อน | dependencies: [build] |
cache |
เก็บ cache ระหว่าง Pipeline | cache: paths: [node_modules/] |
extends |
สืบทอด configuration จาก Job อื่น | extends: .base_job |
rules |
กำหนดเงื่อนไขการรันแบบซับซ้อน (แทน only/except) | rules: - if: $CI_COMMIT_BRANCH == "main" |
⚠️ ข้อควรระวัง: only/except vs rules
only และ except เป็น syntax เก่าที่ GitLab แนะนำให้เปลี่ยนมาใช้ rules แทน เนื่องจากมีความยืดหยุ่นมากกว่าและจะได้รับการพัฒนาต่อไป
4. Jobs และ Stages
📦 Stage คืออะไร?
Stage เป็นการจัดกลุ่ม Jobs เข้าด้วยกันตามลำดับการทำงาน Jobs ใน Stage เดียวกันจะรัน พร้อมกัน (parallel) ส่วน Stage ถัดไปจะรันหลังจาก Stage ก่อนหน้าเสร็จสมบูรณ์
รูปที่ 3: การทำงานของ Jobs และ Stages - Jobs ใน Stage เดียวกันรันแบบ Parallel
🔨 การสร้าง Job
Job คือหน่วยย่อยที่สุดของงาน แต่ละ Job จะ:
- รันใน Runner คนละตัวกันได้
- มี environment แยกกัน
- มี log แยกกัน
- สามารถ fail หรือ pass ได้อิสระ
# ตัวอย่าง Job ง่ายๆ
hello_world:
stage: build
script:
- echo "Hello, GitLab CI/CD!"
- echo "This job runs in the build stage"
# Job ที่ซับซ้อนขึ้น
unit_tests:
stage: test
image: node:18-alpine
before_script:
- npm ci # ติดตั้ง dependencies ก่อน
script:
- npm run test:unit # รัน unit tests
- npm run test:coverage # สร้าง coverage report
after_script:
- echo "Test completed with status: $CI_JOB_STATUS"
artifacts:
reports:
junit: junit.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == "main"
🔗 Job Dependencies
บางครั้ง Job หนึ่งต้องรอผลจาก Job อื่น เราสามารถกำหนด dependencies ได้:
stages:
- build
- test
- deploy
# Job สร้าง artifact
build_app:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
# Job ทดสอบต้องใช้ artifact จาก build_app
test_app:
stage: test
dependencies:
- build_app # ดาวน์โหลด artifact จาก build_app
script:
- ls dist/ # จะเห็นไฟล์จาก build_app
- npm run test
# Deploy ต้องรอทั้ง build และ test
deploy_staging:
stage: deploy
dependencies:
- build_app
- test_app
script:
- rsync -avz dist/ user@staging:/var/www/
rules:
- if: $CI_COMMIT_BRANCH == "develop"
💡 Tips: needs vs dependencies
needs ให้ Job รันทันทีที่ Job ที่กำหนดเสร็จ โดยไม่ต้องรอ Stage อื่น ทำให้ Pipeline เร็วขึ้น:
deploy_preview:
stage: deploy
needs: [build_app] # ไม่ต้องรอ test stage
script:
- echo "Deploy preview environment"
5. GitLab Runner
🏃 Runner คืออะไร?
GitLab Runner เป็นแอปพลิเคชันที่ทำหน้าที่รัน jobs ที่ส่งมาจาก GitLab CI/CD โดย Runner สามารถติดตั้งบนเซิร์ฟเวอร์, VM, container, หรือแม้แต่เครื่องคอมพิวเตอร์ส่วนตัว
🏢 Shared Runner vs Specific Runner
| ประเภท | คำอธิบาย | ข้อดี | ข้อเสีย |
|---|---|---|---|
| Shared Runners | Runner ที่ GitLab จัดให้ (สำหรับ GitLab.com) หรือ Admin ตั้งค่าไว้ (Self-managed) | ✓ ไม่ต้องดูแล ✓ พร้อมใช้ทันที ✓ Auto-scaling |
✗ มีค่าใช้จ่าย (minutes) ✗ แบ่งกับโปรเจคอื่น ✗ จำกัดการปรับแต่ง |
| Specific Runners | Runner ที่ติดตั้งเองและผูกกับโปรเจคเฉพาะ | ✓ ควบคุมเต็มที่ ✓ ปรับแต่งได้ ✓ ไม่จำกัดนาที ✓ ความปลอดภัยสูง |
✗ ต้องดูแลเอง ✗ ต้องติดตั้งเอง ✗ ต้องจัดการทรัพยากร |
| Group Runners | Runner ที่ผูกกับ Group และใช้ได้กับทุกโปรเจคใน Group | ✓ แชร์ระหว่างโปรเจค ✓ จัดการจากที่เดียว |
✗ ทุกโปรเจคใช้ทรัพยากรร่วมกัน |
📥 การติดตั้ง GitLab Runner
ติดตั้งบน Linux (Ubuntu/Debian)
# เพิ่ม GitLab repository
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh | sudo bash
# ติดตั้ง GitLab Runner
sudo apt-get install gitlab-runner
# ตรวจสอบการติดตั้ง
sudo gitlab-runner --version
ติดตั้งบน macOS
# ใช้ Homebrew
brew install gitlab-runner
# ติดตั้งเป็น service
brew services start gitlab-runner
ติดตั้งด้วย Docker
# รัน GitLab Runner container
docker run -d --name gitlab-runner --restart always \
-v /srv/gitlab-runner/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
gitlab/gitlab-runner:latest
# ลงทะเบียน Runner
docker exec -it gitlab-runner gitlab-runner register
📝 การลงทะเบียน Runner
หลังติดตั้ง ต้องลงทะเบียน Runner กับ GitLab:
ดึง Registration Token
ไปที่ Settings → CI/CD → Runners ในโปรเจคของคุณ แล้ว copy token
รันคำสั่ง register
sudo gitlab-runner register
# จะมีคำถามดังนี้:
# Enter the GitLab instance URL:
https://gitlab.com
# Enter the registration token:
[paste your token here]
# Enter a description for the runner:
My Docker Runner
# Enter tags for the runner (comma-separated):
docker,linux,production
# Enter optional maintenance note:
Runner สำหรับ production deployment
# Enter an executor:
docker
# Enter the default Docker image:
node:18-alpine
ตรวจสอบการลงทะเบียน
# ดูรายการ Runners
sudo gitlab-runner list
# ตรวจสอบสถานะ
sudo gitlab-runner verify
# ดู config
cat /etc/gitlab-runner/config.toml
📌 Executors ที่ควรรู้จัก
- docker - รันใน Docker container (แนะนำ)
- kubernetes - รันใน Kubernetes pod
- shell - รัน command ตรงๆ บน host
- ssh - รันบนเครื่อง remote ผ่าน SSH
- virtualbox/parallels - รันใน VM
6. ตัวอย่าง Pipeline แรก
👋 Hello World Pipeline
มาเริ่มต้นด้วย Pipeline ง่ายที่สุด:
# .gitlab-ci.yml - Hello World
stages:
- hello
hello_job:
stage: hello
script:
- echo "🎉 Hello, GitLab CI/CD!"
- echo "📝 This is my first pipeline"
- echo "📅 Date: $(date)"
- echo "🌿 Branch: $CI_COMMIT_REF_NAME"
🔄 Build → Test → Deploy (Basic)
Pipeline ที่สมบูรณ์สำหรับ Node.js application:
# .gitlab-ci.yml - Basic Node.js Pipeline
# กำหนด stages
stages:
- build
- test
- deploy
# Global variables
variables:
NODE_VERSION: "18"
NPM_CONFIG_CACHE: ".npm"
# Cache node_modules ระหว่าง pipelines
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
- node_modules/
# ============================================
# BUILD STAGE
# ============================================
install_dependencies:
stage: build
image: node:${NODE_VERSION}-alpine
script:
- echo "📦 Installing dependencies..."
- npm ci --cache .npm
artifacts:
paths:
- node_modules/
expire_in: 1 hour
build_app:
stage: build
image: node:${NODE_VERSION}-alpine
needs: [install_dependencies]
script:
- echo "🔨 Building application..."
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
# ============================================
# TEST STAGE
# ============================================
lint:
stage: test
image: node:${NODE_VERSION}-alpine
needs: [install_dependencies]
script:
- echo "🔍 Running linter..."
- npm run lint
allow_failure: true
unit_test:
stage: test
image: node:${NODE_VERSION}-alpine
needs: [install_dependencies]
script:
- echo "🧪 Running unit tests..."
- npm run test:unit -- --coverage
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
integration_test:
stage: test
image: node:${NODE_VERSION}-alpine
needs: [install_dependencies, build_app]
script:
- echo "🔗 Running integration tests..."
- npm run test:integration
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_MERGE_REQUEST_IID
# ============================================
# DEPLOY STAGE
# ============================================
deploy_staging:
stage: deploy
image: alpine:latest
needs: [build_app, unit_test]
script:
- echo "🚀 Deploying to staging..."
- echo "Staging URL: https://staging.example.com"
environment:
name: staging
url: https://staging.example.com
rules:
- if: $CI_COMMIT_BRANCH == "develop"
deploy_production:
stage: deploy
image: alpine:latest
needs: [build_app, unit_test, integration_test]
script:
- echo "🌟 Deploying to production..."
- echo "Production URL: https://example.com"
environment:
name: production
url: https://example.com
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual # ต้องกดปุ่ม manual เพื่อ deploy
🖥️ การดูผลลัพธ์ใน GitLab UI
หลังจาก push โค้ด สามารถดู Pipeline ได้ที่:
เข้าหน้า CI/CD → Pipelines
ไปที่โปรเจค → CI/CD → Pipelines จะเห็นรายการ Pipeline ทั้งหมด
คลิกดู Pipeline Details
จะเห็นกราฟแสดง Stages และ Jobs พร้อมสถานะ (running, success, failed)
คลิก Job เพื่อดู Log
จะเห็น output แบบ real-time ขณะรัน และสามารถดู log ย้อนหลังได้
7. การ Debug Pipeline
📋 การดู Logs
Logs เป็นสิ่งสำคัญที่สุดในการ debug:
- Job Log - คลิกที่ Job → เห็น output ทุกบรรทัด
- Pipeline Log - เห็นภาพรวมของทั้ง Pipeline
- Download Artifacts - ดาวน์โหลดไฟล์ที่เก็บไว้
💡 Debug Tips
เพิ่ม CI_DEBUG_TRACE: "true" ใน variables เพื่อแสดง debug info ละเอียด:
variables:
CI_DEBUG_TRACE: "true"
❌ Common Errors
Error: Job failed (exit code: 1)
สาเหตุ: คำสั่งใน script return exit code ไม่ใช่ 0
แก้ไข: ตรวจสอบ log ว่าคำสั่งไหน fail และเพิ่ม error handling
script:
- npm run build || echo "Build failed but continuing..."
- npm run test 2>&1 | tee test-output.log
Error: No runner available
สาเหตุ: ไม่มี Runner ที่พร้อมรับงาน หรือ tags ไม่ตรง
แก้ไข: ตรวจสอบ Runner status และ tags
# ตรวจสอบว่า Job ต้องการ tags อะไร
job_name:
tags:
- docker
- linux
Error: Image not found
สาเหตุ: Docker image ไม่มีหรือไม่สามารถดาวน์โหลดได้
แก้ไข: ใช้ image ที่มีอยู่จริง หรือตรวจสอบ private registry credentials
job_name:
image: node:18-alpine # ใช้ image ที่มีอยู่ใน Docker Hub
# หรือ
image: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA # ใช้ image จาก GitLab Registry
Error: YAML syntax error
สาเหตุ: ไฟล์ .gitlab-ci.yml มี syntax ผิดพลาด
แก้ไข: ใช้ GitLab CI Lint tool หรือตรวจสอบ indentation
# ❌ ผิด: ใช้ tab แทน space
job:
script:
- echo "hello"
# ✅ ถูก: ใช้ space 2 ตัว
job:
script:
- echo "hello"
Error: Out of memory / Disk space
สาเหตุ: Runner มีทรัพยากรไม่เพียงพอ
แก้ไข: เพิ่มทรัพยากรหรือ optimize job
job_name:
tags:
- high-memory # ใช้ runner ที่มี memory มาก
variables:
NODE_OPTIONS: "--max-old-space-size=4096"
script:
- npm run build
after_script:
- rm -rf node_modules/ # cleanup หลังรัน
🔄 การ Retry Job
เมื่อ Job fail สามารถ retry ได้หลายวิธี:
- Manual Retry - คลิกปุ่ม Retry บน Job page
- Auto Retry - กำหนดให้ retry อัตโนมัติ:
job_name:
script:
- npm run flaky-test
retry:
max: 2 # retry สูงสุด 2 ครั้ง
when:
- script_failure # retry เฉพาะเมื่อ script fail
- runner_system_failure
⏹️ การ Cancel Pipeline
หากต้องการหยุด Pipeline ที่กำลังรัน:
- ไปที่ Pipeline page → คลิก Cancel
- หรือ cancel เฉพาะบาง Job → คลิก Job → คลิก Cancel
# กำหนดให้ cancel job ใหม่ถ้ามี job เดิมยังรันอยู่
job_name:
script:
- npm run deploy
interruptible: true # อนุญาตให้ interrupt
8. Tips for Beginners
💡 เคล็ดลับการเขียน .gitlab-ci.yml
🎯 ใช้ Lint
ตรวจสอบ syntax ด้วย GitLab CI Lint ก่อน push: CI/CD → Editor → "Validate"
📦 ใช้ Cache
Cache node_modules หรือ dependencies อื่นๆ เพื่อลดเวลา build
🎨 ใช้ extends
สร้าง hidden job (.template) และ extends ซ้ำเพื่อลดโค้ดซ้ำ
⚡ ใช้ needs
ใช้ needs เพื่อให้ job รันได้เร็วขึ้น ไม่ต้องรอ stage ก่อนหน้า
📝 Best Practices
# 1. ใช้ variables เก็บค่าที่ใช้ซ้ำ
variables:
DOCKER_IMAGE: "node:18-alpine"
BUILD_DIR: "dist"
# 2. สร้าง reusable templates
.node_template:
image: $DOCKER_IMAGE
before_script:
- npm ci --cache .npm
cache:
paths:
- .npm/
- node_modules/
# 3. extends จาก template
test_job:
extends: .node_template
stage: test
script:
- npm test
# 4. กำหนด rules ให้ชัดเจน
deploy_production:
stage: deploy
script:
- ./deploy.sh production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
- if: when: manual
# 5. ใช้ artifacts เก็บผลลัพธ์
build_job:
script:
- npm run build
artifacts:
paths:
- $BUILD_DIR/
expire_in: 1 week
when: always # เก็บแม้ job จะ fail
# 6. กำหนด failure handling
integration_test:
script:
- npm run test:integration
allow_failure: true # ไม่หยุด pipeline ถ้า fail
retry:
max: 2
⚠️ ข้อผิดพลาดที่พบบ่อย
| ปัญหา | สาเหตุ | วิธีแก้ |
|---|---|---|
| Pipeline รันทุก commit แม้ไม่จำเป็น | ไม่ได้กำหนด rules หรือ only/except | ใช้ rules เพื่อกำหนดเงื่อนไขการรัน |
| Build ช้ามาก | ติดตั้ง dependencies ใหม่ทุกครั้ง | ใช้ cache และ npm ci แทน npm install |
| Secrets รั่วไหล | Hardcode secrets ในไฟล์ | ใช้ CI/CD Variables แบบ Masked |
| Pipeline fail บน main แต่ผ่านบน branch | Environment ต่างกัน | ใช้ Docker image เดียวกัน และกำหนด env เหมือนกัน |
| Deploy fail แต่ไม่รู้จะ rollback ยังไง | ไม่มี rollback plan | ใช้ GitLab Environments และ Rollback button |
✅ Checklist ก่อน Push
- ✓ ตรวจสอบ syntax ด้วย CI Lint
- ✓ ทดสอบ script ในเครื่องก่อน
- ✓ ไม่ hardcode secrets
- ✓ มี proper error handling
- ✓ กำหนด artifacts สำหรับ debug
- ✓ ใช้ cache เพื่อ speed up
- ✓ เขียน comment อธิบายสิ่งที่ทำ
📚 Resources สำหรับเรียนรู้เพิ่มเติม
🎯 สรุป
ในบทความนี้เราได้เรียนรู้:
- ✅ CI/CD คืออะไร - แนวคิดและประโยชน์
- ✅ GitLab Architecture - GitLab Server + Runner
- ✅ .gitlab-ci.yml - โครงสร้างและ keywords สำคัญ
- ✅ Jobs และ Stages - การจัดกลุ่มและ dependencies
- ✅ GitLab Runner - การติดตั้งและลงทะเบียน
- ✅ Pipeline แรก - Build → Test → Deploy
- ✅ Debug Pipeline - การหาและแก้ไขปัญหา
- ✅ Best Practices - เคล็ดลับสำหรับผู้เริ่มต้น
📖 Next: Part 2 - Advanced GitLab CI/CD
ใน Part 2 เราจะเจาะลึกหัวข้อขั้นสูง: Multi-project Pipelines, Parent-Child Pipelines, Security Scanning, และ GitLab Pages