#การใช้งาน GitLab CI/CD

GitLab CI/CD คือระบบ Continuous Integration / Continuous Delivery (Deployment) ที่รวมอยู่ใน GitLab โดยตรง ช่วยให้เราสามารถกำหนดกระบวนการตรวจสอบโค้ด ทดสอบ สร้างแพ็กเกจ สร้าง Docker Image และ Deploy ระบบแบบอัตโนมัติทุกครั้งที่เกิดเหตุการณ์ เช่น Push, Merge Request, Tag, Schedule หรือสั่งรัน Pipeline ด้วยตนเอง

หัวใจสำคัญของ GitLab CI/CD คือไฟล์:

.gitlab-ci.yml

ไฟล์นี้มักวางไว้ที่ root directory ของ repository และใช้ YAML สำหรับกำหนด Pipeline, Stage, Job, Script, Rule, Artifact, Cache, Variable และ Environment


#1. ภาพรวมการทำงานของ GitLab CI/CD

โครงสร้างโดยทั่วไปสามารถมองได้ดังนี้

Developer
   |
   | git push / Merge Request
   v
GitLab Repository
   |
   v
GitLab Pipeline
   |
   +--> Stage: lint
   |
   +--> Stage: test
   |
   +--> Stage: build
   |
   +--> Stage: deploy
            |
            v
       Server / Cloud

องค์ประกอบหลักมีดังนี้

องค์ประกอบ หน้าที่
Pipeline กระบวนการ CI/CD ทั้งหมดของการรันหนึ่งครั้ง
Stage กลุ่มลำดับการทำงาน เช่น test, build, deploy
Job งานย่อยที่ Runner ต้องรัน
Runner Agent ที่รับ Job ไปประมวลผล
Script คำสั่งที่ Job ต้องรัน
Artifact ไฟล์ผลลัพธ์จาก Job ที่ต้องเก็บหรือส่งต่อ
Cache ไฟล์ที่เก็บไว้เพื่อลดเวลาการติดตั้ง dependency
Rule เงื่อนไขว่าจะให้ Job ทำงานเมื่อใด
Variable ค่าคอนฟิกหรือ Secret ที่ใช้ใน Pipeline
Environment สภาพแวดล้อม เช่น staging หรือ production

โดยปกติ Stages ทำงานตามลำดับ แต่ Jobs ที่อยู่ใน Stage เดียวกันสามารถทำงานพร้อมกันได้ หากมี Runner รองรับเพียงพอ


#2. Pipeline แรกด้วย .gitlab-ci.yml

สร้างไฟล์:

.gitlab-ci.yml

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

stages:
  - build
  - test
  - deploy

build-app:
  stage: build
  script:
    - echo "Building application..."

test-app:
  stage: test
  script:
    - echo "Running tests..."

deploy-app:
  stage: deploy
  script:
    - echo "Deploying application..."

เมื่อ Commit และ Push ไฟล์นี้ขึ้น GitLab ระบบจะสร้าง Pipeline ตามเงื่อนไขที่กำหนด

git add .gitlab-ci.yml
git commit -m "Add GitLab CI pipeline"
git push

จากนั้นเปิด GitLab แล้วไปที่:

Build > Pipelines

ชื่อเมนูอาจแตกต่างเล็กน้อยตาม GitLab edition/version แต่สามารถค้นหา Pipeline ของโปรเจกต์ได้จากส่วน CI/CD/Build ของโปรเจกต์


#3. Stage และ Job

ตัวอย่าง:

stages:
  - quality
  - test

lint:
  stage: quality
  script:
    - npm run lint

unit-test:
  stage: test
  script:
    - npm test

Pipeline จะทำงานตามลำดับ:

quality
   |
   v
test

แต่ถ้ามีหลาย Job ใน Stage เดียวกัน:

stages:
  - test

unit-test:
  stage: test
  script:
    - npm test

type-check:
  stage: test
  script:
    - npm run typecheck

unit-test และ type-check สามารถรันพร้อมกันได้


#4. เลือก Docker Image สำหรับ Job

หาก Runner ใช้ Docker executor สามารถกำหนด image ได้ เช่น:

image: node:22-alpine

test:
  script:
    - node --version
    - npm ci
    - npm test

หรือกำหนด image แยกแต่ละ Job:

node-test:
  image: node:22-alpine
  script:
    - npm ci
    - npm test

python-test:
  image: python:3.13-slim
  script:
    - pip install -r requirements.txt
    - pytest

ข้อดีคือแต่ละ Job มี environment ที่ค่อนข้างคงที่และ reproducible มากขึ้น


#5. before_script และ after_script

กรณีที่ต้องติดตั้ง dependency ก่อนรันทุก Job สามารถใช้ default ได้

default:
  image: node:22-alpine
  before_script:
    - npm ci

test:
  script:
    - npm test

build:
  script:
    - npm run build

ส่วน after_script เหมาะกับงาน cleanup หรือ diagnostic เช่น:

test:
  script:
    - npm test
  after_script:
    - echo "Test job finished"

#6. ใช้ Cache ลดเวลาติดตั้ง Dependency

การรัน npm ci, pip install, Maven หรือ Gradle ซ้ำทุก Pipeline อาจใช้เวลามาก จึงสามารถใช้ cache

ตัวอย่าง npm:

default:
  image: node:22-alpine
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - .npm/
  before_script:
    - npm ci --cache .npm --prefer-offline

test:
  script:
    - npm test

แนวคิดสำคัญ:

cache = dependency ที่ใช้ซ้ำเพื่อให้ Job เร็วขึ้น
artifact = ผลลัพธ์ของ Job ที่ต้องเก็บหรือส่งต่อ

ไม่ควรใช้ Cache แทน Artifact โดยไม่จำเป็น


#7. Artifact

สมมติ frontend สร้างไฟล์ใน dist/

build:
  image: node:22-alpine
  stage: build
  script:
    - npm ci
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

GitLab จะเก็บ dist/ เป็น Artifact

สามารถนำไปใช้ใน Job ถัดไป เช่น deploy ได้


#8. ใช้ rules ควบคุมว่า Job จะทำงานเมื่อใด

ตัวอย่างให้ test ทำงานเมื่อ Push branch หรือ Merge Request:

test:
  script:
    - npm test
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    - if: '$CI_COMMIT_BRANCH'

ให้ Deploy เฉพาะ default branch:

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'

ให้ Deploy แบบ manual:

deploy-production:
  stage: deploy
  script:
    - ./deploy.sh
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual

วิธีนี้เหมาะกับ Production เพราะ Pipeline ผ่าน Test และ Build ได้ แต่ยังต้องกดอนุมัติ Job deploy เอง


#9. CI/CD Variables และ Secret

ไม่ควรเขียน Password, API Key, SSH Key หรือ Token ลงใน .gitlab-ci.yml

ตัวอย่างที่ ไม่ควรทำ:

variables:
  DB_PASSWORD: "my-super-secret-password"

ค่าที่ไม่เป็นความลับสามารถเขียนใน YAML ได้:

variables:
  NODE_ENV: "test"

ส่วน Secret ควรเพิ่มผ่าน:

Project
  > Settings
  > CI/CD
  > Variables

ตัวอย่างชื่อ Variable:

DEPLOY_HOST
DEPLOY_USER
SSH_PRIVATE_KEY
SSH_KNOWN_HOSTS
DATABASE_URL
API_TOKEN

GitLab รองรับการตั้งค่า Variable แบบ Masked, Hidden และ Protected ตามความเหมาะสม

ใน Job สามารถใช้งานได้เหมือน environment variable:

deploy:
  script:
    - echo "Deploying to $DEPLOY_HOST"

หลีกเลี่ยงการ echo Secret ออกสู่ Job log


#10. ตัวอย่าง Pipeline สำหรับ Node.js / React

ตัวอย่างนี้ครอบคลุม lint, test, build และ deploy แบบ manual

stages:
  - quality
  - test
  - build
  - deploy

default:
  image: node:22-alpine
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - .npm/
  before_script:
    - npm ci --cache .npm --prefer-offline

lint:
  stage: quality
  script:
    - npm run lint

test:
  stage: test
  script:
    - npm test

build:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

deploy-production:
  image: alpine:3.22
  stage: deploy
  before_script:
    - apk add --no-cache openssh-client rsync
    - mkdir -p ~/.ssh
    - cp "$SSH_PRIVATE_KEY" ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519
    - printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  script:
    - rsync -az --delete dist/ "$DEPLOY_USER@$DEPLOY_HOST:/var/www/myapp/"
  environment:
    name: production
    url: https://example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual

ตัวอย่างข้างต้นสมมติว่า SSH_PRIVATE_KEY ถูกสร้างเป็น CI/CD Variable ชนิด File และมีการเตรียมสิทธิ์บน Server ปลายทางเรียบร้อยแล้ว


#11. Pipeline สำหรับ Merge Request

CI ที่ดีควรตรวจโค้ดก่อน Merge เช่น:

test-merge-request:
  image: node:22-alpine
  script:
    - npm ci
    - npm test
  rules:
    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'

Workflow จะเป็น:

Create branch
   |
   v
Commit / Push
   |
   v
Create Merge Request
   |
   v
GitLab CI
   |
   +--> Lint
   +--> Test
   +--> Security Check
   |
   v
Code Review
   |
   v
Merge

#12. ใช้ needs เพื่อให้ Pipeline เร็วขึ้น

Pipeline แบบ Stage ปกติต้องรอทุก Job ใน Stage ก่อนหน้าเสร็จก่อน

บางระบบสามารถใช้ needs เพื่อกำหนด dependency ระหว่าง Jobs โดยตรง

stages:
  - build
  - test
  - deploy

build-frontend:
  stage: build
  script:
    - echo "Build frontend"

build-backend:
  stage: build
  script:
    - echo "Build backend"

test-frontend:
  stage: test
  needs:
    - build-frontend
  script:
    - echo "Test frontend"

test-backend:
  stage: test
  needs:
    - build-backend
  script:
    - echo "Test backend"

โครงสร้างจะใกล้เคียง DAG:

build-frontend ---> test-frontend
build-backend  ---> test-backend

จึงไม่จำเป็นต้องรอ Job ที่ไม่เกี่ยวข้องทุกตัว


#13. สร้าง Docker Image ด้วย GitLab CI

GitLab สามารถเชื่อมกับ Container Registry ได้โดยใช้ predefined variables เช่น:

CI_REGISTRY
CI_REGISTRY_IMAGE
CI_REGISTRY_USER
CI_REGISTRY_PASSWORD
CI_COMMIT_SHA

ตัวอย่าง Docker-in-Docker:

docker-build:
  image: docker:27.5.1-cli
  services:
    - docker:27.5.1-dind
  stage: build
  variables:
    DOCKER_TLS_CERTDIR: "/certs"
  before_script:
    - echo "$CI_REGISTRY_PASSWORD" |
      docker login -u "$CI_REGISTRY_USER" --password-stdin "$CI_REGISTRY"
  script:
    - docker build -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA" .
    - docker push "$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA"

เพื่อความเสถียร ควร pin version ของ Docker image แทนการใช้ docker:latest

Docker-in-Docker อาจต้องการการตั้งค่า Runner เพิ่มเติม โดยเฉพาะ privileged mode ควรพิจารณาขอบเขตความปลอดภัยก่อนใช้งานในระบบจริง


#14. Environment: Staging และ Production

GitLab สามารถผูก Job กับ Environment ได้

deploy-staging:
  stage: deploy
  script:
    - ./deploy-staging.sh
  environment:
    name: staging
    url: https://staging.example.com

deploy-production:
  stage: deploy
  script:
    - ./deploy-production.sh
  environment:
    name: production
    url: https://example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual

แนวทางที่นิยม:

feature branch
     |
     v
Merge Request
     |
     v
lint + test
     |
     v
main/default branch
     |
     v
staging
     |
     v
manual approval
     |
     v
production

#15. GitLab Runner คืออะไร

Runner คือ Agent ที่รับ Job จาก GitLab มารัน

แนวคิด:

GitLab
  |
  | Job
  v
GitLab Runner
  |
  +--> Shell Executor
  +--> Docker Executor
  +--> Kubernetes Executor

สำหรับ GitLab.com มี Instance Runner ให้ใช้งานในหลายกรณี ส่วน GitLab Self-Managed หรือระบบที่ต้องการทรัพยากรเฉพาะสามารถติดตั้ง Runner ของตนเองได้

Docker executor เหมาะกับงาน CI/CD จำนวนมาก เพราะช่วยแยก environment ของแต่ละ Job และกำหนด dependencies ผ่าน Docker image ได้ชัดเจน


#16. ตรวจสอบ Syntax ก่อน Commit

GitLab มี CI Lint สำหรับตรวจ .gitlab-ci.yml

สามารถเปิดจากส่วน CI/CD Editor หรือ Pipeline Editor ของโปรเจกต์เพื่อ Validate configuration ก่อนรันจริง

ข้อผิดพลาดที่พบบ่อย เช่น:

- YAML indentation ผิด
- stage ที่ Job อ้างถึงไม่มีใน stages
- rules เขียนเงื่อนไขผิด
- Runner ไม่มี tag ที่ Job ต้องการ
- Variable ไม่ถูกกำหนด
- Artifact path ไม่ตรงกับไฟล์ที่ Build

#17. ใช้ Runner Tag

กรณีมี Runner หลายประเภท:

runner-linux
runner-docker
runner-production
runner-gpu

สามารถกำหนด tag ใน Job:

deploy-production:
  stage: deploy
  tags:
    - production
  script:
    - ./deploy.sh

Job จะถูกเลือกไปยัง Runner ที่ตรงกับเงื่อนไข tag


#18. Retry และ Allow Failure

บาง Job อาจต้อง retry เมื่อเกิดปัญหาชั่วคราว:

integration-test:
  stage: test
  script:
    - npm run test:integration
  retry: 2

Job ที่ไม่ต้องการให้ Pipeline ล้มทั้งหมด:

code-quality-experimental:
  stage: test
  script:
    - ./experimental-check.sh
  allow_failure: true

ควรใช้ allow_failure อย่างระมัดระวัง โดยเฉพาะ Security Test และ Quality Gate ที่มีผลต่อความถูกต้องของ Production


#19. ตัวอย่างโครงสร้าง Pipeline ที่เหมาะกับ Web Application

                 Git Push / MR
                       |
                       v
                +-------------+
                |    Lint     |
                +-------------+
                       |
                       v
          +--------------------------+
          | Unit Test | Type Check   |
          +--------------------------+
                       |
                       v
                +-------------+
                |    Build    |
                +-------------+
                       |
                       v
                +-------------+
                |   Staging   |
                +-------------+
                       |
                 Manual Approval
                       |
                       v
                +-------------+
                | Production  |
                +-------------+

ตัวอย่าง Stage:

stages:
  - quality
  - test
  - build
  - staging
  - production

#20. แนวทางใช้งาน GitLab CI/CD ที่แนะนำ

#20.1 อย่าเก็บ Secret ใน Git Repository

ใช้:

Settings > CI/CD > Variables

และพิจารณา Masked, Hidden, Protected หรือ External Secrets

#20.2 Pin Version ของ Image

ควรใช้:

image: node:22-alpine

แทน:

image: node:latest

เพื่อให้ Pipeline reproducible มากขึ้น

#20.3 แยก Test ออกจาก Deploy

ไม่ควร deploy หาก tests สำคัญยังไม่ผ่าน

#20.4 ใช้ Artifact เมื่อ Job ถัดไปต้องใช้ไฟล์ Build

เช่น:

build -> dist/ -> deploy

#20.5 ใช้ Cache สำหรับ Dependency

เช่น:

npm cache
pip cache
Maven repository
Gradle cache

#20.6 ใช้ rules แทนการรันทุก Job ทุก Branch

เช่น:

Merge Request -> Test
main -> Build
tag -> Release
production -> Manual

#20.7 ทำ Pipeline ให้ Feedback เร็ว

วางงานที่เร็วและสำคัญไว้ก่อน เช่น:

lint -> unit test -> integration test -> build -> deploy

และใช้ needs เมื่อมี dependency graph ที่สามารถรันขนานได้


#21. ตัวอย่าง .gitlab-ci.yml แบบใช้งานจริง

stages:
  - quality
  - test
  - build
  - deploy

default:
  image: node:22-alpine
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - .npm/
  before_script:
    - npm ci --cache .npm --prefer-offline

lint:
  stage: quality
  script:
    - npm run lint

unit-test:
  stage: test
  script:
    - npm test

build:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

deploy-production:
  image: alpine:3.22
  stage: deploy
  before_script:
    - apk add --no-cache openssh-client rsync
    - mkdir -p ~/.ssh
    - cp "$SSH_PRIVATE_KEY" ~/.ssh/id_ed25519
    - chmod 600 ~/.ssh/id_ed25519
    - printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
  script:
    - rsync -az --delete dist/ "$DEPLOY_USER@$DEPLOY_HOST:/var/www/myapp/"
  environment:
    name: production
    url: https://example.com
  rules:
    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
      when: manual

Pipeline:

Commit
  |
  v
Lint
  |
  v
Unit Test
  |
  v
Build
  |
  v
Manual Deploy
  |
  v
Production

#22. GitLab CI/CD เทียบกับ GitHub Actions และ Jenkins

หัวข้อ GitLab CI/CD GitHub Actions Jenkins
Config .gitlab-ci.yml .github/workflows/*.yml Jenkinsfile
Platform Integration แนบกับ GitLab โดยตรง แนบกับ GitHub โดยตรง แยกเป็น CI Server
Runner/Agent GitLab Runner GitHub Runner Jenkins Agent
Container Support ดีมาก ดีมาก ดี แต่ต้องดูแลเพิ่ม
Self-hosted รองรับ รองรับ จุดเด่นหลัก
Merge Request Integration แน่น ใช้ Pull Request ต้องเชื่อม integration
Maintenance ต่ำ-กลาง ต่ำ กลาง-สูง

ถ้า Source Code อยู่บน GitLab อยู่แล้ว การใช้ GitLab CI/CD จะลดจำนวนเครื่องมือที่ต้องเชื่อมต่อและทำให้ Repository, Merge Request, Pipeline, Registry และ Deployment อยู่ใน workflow เดียวกันได้ง่าย


#23. สรุป

GitLab CI/CD สามารถเริ่มต้นได้จากแนวคิดเพียงไม่กี่ส่วน:

.gitlab-ci.yml
      |
      v
Pipeline
      |
      v
Stages
      |
      v
Jobs
      |
      v
Runner
      |
      v
Build / Test / Deploy

ขั้นตอนเริ่มต้นที่แนะนำคือ:

  1. สร้าง .gitlab-ci.yml
  2. เริ่มจาก lint และ test
  3. เพิ่ม build
  4. ใช้ artifacts เก็บผลลัพธ์
  5. ใช้ cache ลดเวลา dependency installation
  6. เก็บ Secret ใน CI/CD Variables
  7. เพิ่ม staging
  8. ทำ production deployment แบบ manual ก่อน
  9. ใช้ rules จำกัดเงื่อนไขการรัน
  10. ปรับ Pipeline ด้วย needs เมื่อระบบใหญ่ขึ้น

เมื่อวาง Pipeline อย่างเหมาะสม GitLab CI/CD จะช่วยให้ทีมตรวจโค้ดเร็วขึ้น ลดงาน deploy แบบ manual และทำให้กระบวนการส่งมอบซอฟต์แวร์มีความสม่ำเสมอและตรวจสอบย้อนหลังได้ง่ายขึ้น


#แหล่งอ้างอิง