ในบทความ Part 1 เราได้เรียนรู้พื้นฐานของ GitLab CI/CD ตั้งแต่การสร้าง Pipeline, Stage, Job และการใช้ Docker Images มาถึง Part 2 นี้ เราจะเจาะลึกเทคนิคระดับกลางที่จะทำให้ Pipeline ของคุณทำงานได้ฉลาดขึ้น เร็วขึ้น และปลอดภัยขึ้น
- การใช้ Variables อย่างมีประสิทธิภาพ
- Artifacts และ Cache ต่างกันอย่างไร
- Rules และ Conditions สำหรับควบคุมการทำงาน
- DAG Pipelines ด้วย
needs - Matrix Jobs สำหรับทดสอบหลาย Version
Variables (ตัวแปร)
Variables เป็นหัวใจสำคัญของ CI/CD Pipeline ที่ยืดหยุ่น ช่วยให้เราสามารถเก็บข้อมูลที่เปลี่ยนแปลงได้ เช่น API Keys, Configuration Settings หรือค่าต่างๆ ที่ไม่อยากเขียนลงในโค้ดโดยตรง
🔤 Predefined Variables
GitLab มีตัวแปรที่กำหนดไว้ล่วงหน้ามากมายที่พร้อมใช้งาน:
ตัวอย่างการใช้ Predefined Variables YAMLjob_show_vars: stage: test script: # ข้อมูล Commit - echo "Commit SHA = $CI_COMMIT_SHA" - echo "Short SHA = $CI_COMMIT_SHORT_SHA" - echo "Commit Message = $CI_COMMIT_MESSAGE" - echo "Commit Author = $CI_COMMIT_AUTHOR" # ข้อมูล Branch/Tag - echo "Branch = $CI_COMMIT_BRANCH" - echo "Tag = $CI_COMMIT_TAG" - echo "Ref Name = $CI_COMMIT_REF_NAME" # ข้อมูล Pipeline - echo "Pipeline ID = $CI_PIPELINE_ID" - echo "Pipeline URL = $CI_PIPELINE_URL" - echo "Pipeline Source = $CI_PIPELINE_SOURCE" # ข้อมูล Project - echo "Project ID = $CI_PROJECT_ID" - echo "Project Name = $CI_PROJECT_NAME" - echo "Project URL = $CI_PROJECT_URL" # ข้อมูล Runner - echo "Runner ID = $CI_RUNNER_ID" - echo "Runner Tags = $CI_RUNNER_TAGS" # ข้อมูล Environment - echo "Environment = $CI_ENVIRONMENT_NAME" - echo "Environment URL = $CI_ENVIRONMENT_URL"
📊 ตาราง Predefined Variables ที่ใช้บ่อย
| Variable | คำอธิบาย | ตัวอย่างค่า |
|---|---|---|
CI_COMMIT_SHA |
Commit hash เต็ม | aa218c562a... |
CI_COMMIT_SHORT_SHA |
Commit hash แบบสั้น (8 ตัว) | aa218c56 |
CI_COMMIT_BRANCH |
ชื่อ Branch | main, develop |
CI_COMMIT_TAG |
ชื่อ Tag (ถ้ามี) | v1.0.0 |
CI_PIPELINE_ID |
ID ของ Pipeline | 123456 |
CI_PROJECT_NAME |
ชื่อ Project | my-awesome-app |
CI_REGISTRY |
URL ของ GitLab Registry | registry.gitlab.com |
⚙️ Custom Variables
เราสามารถสร้างตัวแปรเองได้ 3 ระดับ:
การกำหนด Custom Variables YAML# 1. Variables ในไฟล์ .gitlab-ci.yml variables: APP_NAME: "my-application" DOCKER_REGISTRY: "registry.example.com" DEPLOY_ENV: "staging" BUILD_OPTS: "--production --no-source-maps" # 2. Variables ระดับ Job build_production: variables: DEPLOY_ENV: "production" NODE_ENV: "production" script: - echo "Building for $DEPLOY_ENV" - npm run build $BUILD_OPTS # 3. Variables แบบ Global (แทนที่ทุกที่) variables: GIT_STRATEGY: "fetch" GIT_DEPTH: "10"
เมื่อมีการกำหนดตัวแปรชื่อซ้ำกัน GitLab จะใช้ลำดับความสำคัญนี้ (จากสูงไปต่ำ):
- Trigger variables (จาก API trigger)
- Manual pipeline variables
- Scheduled pipeline variables
- Variables ใน .gitlab-ci.yml (ระดับ Job)
- Protected variables
- Group variables
- Project variables
- Variables ใน .gitlab-ci.yml (ระดับ Global)
🔒 Protected Variables
Protected Variables จะถูกส่งไปยัง Pipeline เฉพาะเมื่อรันบน Protected Branch
(เช่น main, production) หรือ Protected Tag เท่านั้น
การตั้งค่า Protected Variables การตั้งค่า# ตั้งค่าที่ Settings > CI/CD > Variables Variable: PRODUCTION_API_KEY Value: sk_live_xxxxxxxxxxxx Type: Variable Protect variable: ✓ เปิดใช้งาน Mask variable: ✓ เปิดใช้งาน # ผลลัพธ์: # - รันบน branch 'main' → ได้รับ PRODUCTION_API_KEY ✓ # - รันบน branch 'feature/xxx' → ไม่ได้รับ PRODUCTION_API_KEY ✗
🎭 Masked Variables
Masked Variables จะถูกซ่อนใน Logs เพื่อความปลอดภัย:
ตัวอย่าง Masked Variable ใน Logs Log# ก่อน Mask $ echo $SECRET_API_KEY sk_live_abc123xyz789 # หลัง Mask $ echo $SECRET_API_KEY [MASKED] # ตัวอย่างการใช้งาน deploy_job: script: - echo "Deploying with API Key..." - curl -H "Authorization: Bearer $SECRET_API_KEY" https://api.example.com/deploy - echo "Deploy completed!" # Log Output: # $ echo "Deploying with API Key..." # Deploying with API Key... # $ curl -H "Authorization: Bearer $SECRET_API_KEY" https://api.example.com/deploy # [MASKED] ← Token ถูกซ่อน # {"status": "success"}
- ต้องมีความยาวอย่างน้อย 8 ตัวอักษร
- ใช้ได้เฉพาะตัวอักษรที่ Base64 รองรับ (a-z, A-Z, 0-9, +, /, =, @, :, ., -, _)
- จะไม่ถูก Mask ถ้าอยู่ใน Format อื่น เช่น JSON object
📁 File Variables
File Variables เก็บค่าเป็นไฟล์แทนที่จะเป็น String:
การใช้ File Variables YAML# ตั้งค่าที่ Settings > CI/CD > Variables Variable: KUBE_CONFIG Type: File Value: (เนื้อหาไฟล์ kubeconfig) # การใช้งานใน Pipeline deploy_kubernetes: image: bitnami/kubectl:latest script: # File variable จะถูกสร้างเป็นไฟล์ชั่วคราว - export KUBECONFIG=$KUBE_CONFIG - kubectl get pods - kubectl apply -f deployment.yaml # ตัวอย่าง: SSH Key Variable: SSH_PRIVATE_KEY Type: File deploy_ssh: script: - mkdir -p ~/.ssh - cp $SSH_PRIVATE_KEY ~/.ssh/id_rsa - chmod 600 ~/.ssh/id_rsa - ssh user@server 'deploy.sh'
Artifacts (ผลลัพธ์)
Artifacts เป็นไฟล์หรือโฟลเดอร์ที่ถูกสร้างโดย Job หนึ่ง และสามารถส่งต่อให้ Job อื่นใช้งานได้ เหมาะสำหรับเก็บ Build outputs, Test reports, หรือไฟล์ที่ต้องการ download
📦 การสร้าง Artifacts
ตัวอย่างการใช้ Artifacts YAMLstages: - build - test - deploy build_app: stage: build image: node:20 script: - npm ci - npm run build artifacts: paths: - dist/ # เก็บโฟลเดอร์ dist - build/ # เก็บโฟลเดอร์ build exclude: - dist/**/*.map # ยกเว้นไฟล์ .map expire_in: 1 week when: always name: "build-$CI_COMMIT_SHORT_SHA" test_app: stage: test image: node:20 script: - npm ci - npm test artifacts: paths: - coverage/ - junit.xml reports: junit: junit.xml coverage_report: coverage_format: jacoco path: coverage/jacoco.xml expire_in: 30 days
📊 Artifacts Options
| Option | คำอธิบาย | ตัวอย่าง |
|---|---|---|
paths |
ไฟล์/โฟลเดอร์ที่จะเก็บ | dist/, build/ |
exclude |
ไฟล์ที่จะยกเว้น | *.map, *.log |
expire_in |
ระยะเวลาเก็บ (ค่า default: 30 days) | 1 week, 1 hour, never |
when |
เก็บเมื่อไร | on_success, on_failure, always |
name |
ชื่อไฟล์ archive | build-$CI_COMMIT_SHA |
🔄 การใช้ Artifacts ข้าม Jobs
Build → Test → Deploy Pipeline YAMLbuild: stage: build script: - npm run build artifacts: paths: - dist/ expire_in: 1 week test: stage: test script: # dist/ จะถูก download มาอัตโนมัติ - ls -la dist/ - npm run test:e2e artifacts: paths: - test-results/ deploy: stage: deploy script: # dist/ จะยังคงอยู่ - rsync -avz dist/ server:/var/www/app/
Artifacts จะถูกส่งจาก Job หนึ่งไปยังทุก Jobs ใน Stages ถัดไปโดยอัตโนมัติ
แต่ถ้าต้องการควบคุมว่าจะรับ artifacts จาก job ไหน ให้ใช้ dependencies หรือ needs:artifacts
Cache (แคช)
Cache ใช้สำหรับเก็บ dependencies ที่ใช้บ่อย เพื่อให้ Pipeline รันเร็วขึ้น
เช่น node_modules, .m2/repository, pip cache
🔑 Cache Keys
Cache Key กำหนดว่าจะใช้ Cache ร่วมกันหรือแยกกัน:
Cache Key Strategies YAML# 1. Cache เดียวกันทุก Branches cache: key: global-cache paths: - node_modules/ # 2. Cache แยกตาม Branch cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ # 3. Cache ตามไฟล์ lock (แนะนำ!) cache: key: files: - package-lock.json - yarn.lock paths: - node_modules/ # 4. Cache แบบ prefix + lock file cache: key: prefix: ${CI_JOB_NAME} files: - composer.lock paths: - vendor/
📝 ตัวอย่าง: Cache node_modules และ pip packages
ตัวอย่างการใช้ Cache YAML# Node.js Project .node_template: image: node:20 cache: key: files: - package-lock.json paths: - node_modules/ - .npm/ install: extends: .node_template stage: prepare script: - npm ci --cache .npm --prefer-offline test: extends: .node_template stage: test script: # node_modules มีอยู่แล้วจาก cache - npm test # Python Project .python_template: image: python:3.11 cache: key: files: - requirements.txt paths: - .cache/pip/ - venv/ before_script: - pip install --cache-dir .cache/pip -r requirements.txt pytest: extends: .python_template script: - pytest tests/
Rules และ Conditions
Rules ช่วยให้เราควบคุมได้ว่า Job ไหนจะรัน หรือไม่รัน ภายใต้เงื่อนไขใด ถือเป็นฟีเจอร์ที่ทรงพลังที่สุดอย่างหนึ่งใน GitLab CI/CD
🎯 rules:if - เงื่อนไข
การใช้ rules:if YAML# รันเฉพาะ main branch deploy_production: stage: deploy rules: - if: $CI_COMMIT_BRANCH == "main" script: - ./deploy.sh production # รันเฉพาะ Tags release: stage: deploy rules: - if: $CI_COMMIT_TAG script: - ./release.sh # รันเฉพาะ Merge Requests code_review: stage: test rules: - if: $CI_PIPELINE_SOURCE == "merge_request_event" script: - npm run lint - npm run test:coverage # Multiple conditions deploy_staging: stage: deploy rules: - if: $CI_COMMIT_BRANCH == "develop" - if: $CI_COMMIT_BRANCH =~ /^release\// # release/* branches - if: $CI_COMMIT_BRANCH =~ /^feature\/.*/ && $RUN_STAGING == "true" script: - ./deploy.sh staging
📁 rules:changes - ตรวจจับไฟล์เปลี่ยน
การใช้ rules:changes YAML# รันเฉพาะเมื่อมีการเปลี่ยนแปลงในโฟลเดอร์ src/ test_frontend: stage: test rules: - changes: - src/**/* - package.json - package-lock.json script: - npm test # รันเฉพาะเมื่อไฟล์ Terraform เปลี่ยน terraform_plan: stage: test rules: - changes: - terraform/**/*.tf script: - terraform plan # รันเฉพาะเมื่อ Dockerfile เปลี่ยน build_docker: stage: build rules: - changes: - Dockerfile - docker-compose*.yml script: - docker build -t myapp .
📋 rules:exists - ตรวจสอบไฟล์มีอยู่
การใช้ rules:exists YAML# รันเฉพาะถ้ามี Dockerfile docker_build: rules: - exists: - Dockerfile script: - docker build -t $CI_REGISTRY_IMAGE . # รันเฉพาะถ้าเป็น Node.js project npm_test: rules: - exists: - package.json script: - npm test # รันเฉพาะถ้าเป็น Python project pytest: rules: - exists: - requirements.txt - setup.py script: - pytest
🔢 rules:variables - กำหนดตัวแปรตามเงื่อนไข
การใช้ rules:variables YAMLdeploy: stage: deploy rules: - if: $CI_COMMIT_BRANCH == "main" variables: DEPLOY_ENV: "production" APP_URL: "https://app.example.com" - if: $CI_COMMIT_BRANCH == "develop" variables: DEPLOY_ENV: "staging" APP_URL: "https://staging.example.com" - if: $CI_COMMIT_BRANCH =~ /^feature\// variables: DEPLOY_ENV: "review" APP_URL: "https://$CI_COMMIT_REF_SLUG.example.com" when: manual script: - echo "Deploying to $DEPLOY_ENV" - echo "App URL: $APP_URL" - ./deploy.sh $DEPLOY_ENV
only และ except เป็น syntax เก่าที่ GitLab ไม่แนะนำให้ใช้แล้ว
ให้เปลี่ยนมาใช้ rules แทน ซึ่งยืดหยุ่นและทรงพลังกว่า:
# ❌ Legacy (ไม่แนะนำ)
job:
only:
- main
- tags
# ✅ Modern (แนะนำ)
job:
rules:
- if: $CI_COMMIT_BRANCH == "main"
- if: $CI_COMMIT_TAG
⚙️ Workflow Rules
Workflow rules ควบคุม Pipeline ทั้งหมดว่าจะสร้างหรือไม่:
การใช้ Workflow Rules YAML# สร้าง Pipeline เฉพาะ main, develop และ MRs workflow: rules: - if: $CI_COMMIT_BRANCH == "main" - if: $CI_COMMIT_BRANCH == "develop" - if: $CI_MERGE_REQUEST_IID - if: $CI_COMMIT_TAG # ป้องกัน duplicate pipelines workflow: rules: - if: $CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_BRANCH - if: $CI_PIPELINE_SOURCE == "merge_request_event" - if: $CI_COMMIT_TAG
Needs (DAG Pipelines)
needs ช่วยให้เราสร้าง Directed Acyclic Graph (DAG) Pipelines ได้
ทำให้ Jobs รันพร้อมกันได้โดยไม่ต้องรอ Stage ก่อนหน้า เร่งความเร็ว Pipeline ได้มาก
⚡ การใช้ needs
ตัวอย่าง DAG Pipeline YAMLstages: - build - test - security - deploy build: stage: build script: - npm run build artifacts: paths: - dist/ # ทั้ง 4 jobs นี้จะรันพร้อมกันหลัง build เสร็จ # ไม่ต้องรอ Stage test ทั้งหมด test_unit: stage: test needs: [build] script: - npm run test:unit test_e2e: stage: test needs: [build] script: - npm run test:e2e security_scan: stage: security needs: [build] script: - npm audit - trivy fs . lint: stage: test needs: [build] script: - npm run lint # Deploy รอทุก jobs ที่ needs deploy: stage: deploy needs: - build - test_unit - test_e2e - security_scan - lint script: - ./deploy.sh
📦 needs:artifacts
ควบคุมว่าจะรับ artifacts จาก job ไหน:
การใช้ needs:artifacts YAMLbuild_linux: stage: build script: - make build-linux artifacts: paths: - bin/linux/ build_windows: stage: build script: - make build-windows artifacts: paths: - bin/windows/ build_macos: stage: build script: - make build-macos artifacts: paths: - bin/macos/ # Release รับ artifacts จากทุก build jobs release: stage: deploy needs: - job: build_linux artifacts: true - job: build_windows artifacts: true - job: build_macos artifacts: true script: - ls -la bin/ - ./create-release.sh # Test ไม่ต้องการ artifacts จาก build unit_test: stage: test needs: - job: build_linux artifacts: false # เร็วขึ้นเพราะไม่ต้อง download script: - npm test
Environment Variables
📄 การสร้าง .env files
การสร้าง .env จาก Variables YAMLcreate_env_file: stage: prepare script: # สร้าง .env file จาก CI/CD Variables - | cat > .env << EOF NODE_ENV=${NODE_ENV} API_URL=${API_URL} DATABASE_URL=${DATABASE_URL} SECRET_KEY=${SECRET_KEY} EOF artifacts: paths: - .env deploy: stage: deploy needs: [create_env_file] script: # .env จะถูก download มาอัตโนมัติ - docker run --env-file .env myapp:latest
📝 dotenv Artifact
GitLab สามารถโหลด environment variables จากไฟล์ dotenv:
การใช้ dotenv Artifacts YAMLgenerate_env: stage: build script: # สร้างไฟล์ dotenv - echo "BUILD_VERSION=$(date +%Y%m%d)-$CI_COMMIT_SHORT_SHA" >> build.env - echo "BUILD_DATE=$(date -I)" >> build.env - echo "DEPLOY_URL=https://$CI_COMMIT_REF_SLUG.example.com" >> build.env artifacts: reports: dotenv: build.env deploy: stage: deploy needs: [generate_env] script: # Variables จาก build.env พร้อมใช้งาน - echo "Deploying version $BUILD_VERSION" - echo "Deploy URL: $DEPLOY_URL" - kubectl set image deployment/app app=myapp:$BUILD_VERSION
🔄 envsubst - แทนที่ Variables ในไฟล์
การใช้ envsubst YAML# config.template.yaml apiVersion: v1 kind: ConfigMap metadata: name: app-config data: APP_NAME: "${APP_NAME}" ENVIRONMENT: "${DEPLOY_ENV}" VERSION: "${CI_COMMIT_SHORT_SHA}" # .gitlab-ci.yml deploy: variables: APP_NAME: "my-awesome-app" DEPLOY_ENV: "production" script: # แทนที่ $VAR ด้วยค่าจริง - envsubst < k8s/config.template.yaml > k8s/config.yaml - cat k8s/config.yaml # ดูผลลัพธ์ - kubectl apply -f k8s/config.yaml
Matrix Jobs
Matrix Jobs ช่วยให้เรารัน Job เดียวกันหลายครั้งด้วยตัวแปรต่างกัน เหมาะสำหรับ testing หลาย versions, browsers, หรือ platforms พร้อมกัน
🔢 parallel:matrix
Testing หลาย Node.js Versions YAMLtest: image: node:${NODE_VERSION} parallel: matrix: - NODE_VERSION: ["16", "18", "20", "21"] script: - node --version - npm ci - npm test # ผลลัพธ์: 4 jobs รันพร้อมกัน # - test: [NODE_VERSION: 16] # - test: [NODE_VERSION: 18] # - test: [NODE_VERSION: 20] # - test: [NODE_VERSION: 21]
🎯 Multi-dimensional Matrix
Matrix หลายมิติ YAMLtest_e2e: image: mcr.microsoft.com/playwright:${BROWSER}-${PW_VERSION} parallel: matrix: - BROWSER: ["chromium", "firefox", "webkit"] PW_VERSION: ["v1.40.0", "v1.41.0"] script: - npx playwright test --project=${BROWSER} # ผลลัพธ์: 3 x 2 = 6 jobs # [chromium, v1.40.0], [chromium, v1.41.0] # [firefox, v1.40.0], [firefox, v1.41.0] # [webkit, v1.40.0], [webkit, v1.41.0] # ตัวอย่าง: Build multi-architecture Docker images build_docker: image: docker:latest parallel: matrix: - ARCH: ["amd64", "arm64", "arm/v7"] script: - docker build --platform linux/${ARCH} -t myapp:${ARCH} . - docker push myapp:${ARCH}
📋 Matrix กับ Variables
การใช้ Matrix Variables YAMLdeploy: stage: deploy parallel: matrix: - ENVIRONMENT: [staging, production] environment: name: ${ENVIRONMENT} url: https://${ENVIRONMENT}.example.com rules: - if: $ENVIRONMENT == "production" && $CI_COMMIT_BRANCH != "main" when: never # ไม่ deploy production จาก branch อื่น - when: on_success script: - echo "Deploying to ${ENVIRONMENT}" - kubectl apply -f k8s/${ENVIRONMENT}/
Trigger (Multi-project Pipelines)
trigger ช่วยให้เราเรียก Pipeline ใน project อื่นได้
เหมาะสำหรับ Microservices architecture หรือการแยก concerns ระหว่าง projects
🔗 การใช้ trigger
Multi-project Pipeline YAML# ใน project หลัก (frontend) stages: - build - test - trigger_downstream build: stage: build script: - npm run build # Trigger pipeline ใน project อื่น trigger_backend: stage: trigger_downstream trigger: project: my-group/backend-api # project path branch: main strategy: depend # รอให้เสร็จก่อน # Trigger พร้อมส่ง variables trigger_deploy: stage: trigger_downstream trigger: project: my-group/deployment branch: main variables: FRONTEND_VERSION: ${CI_COMMIT_SHA} DEPLOY_ENV: "production"
🏗️ ตัวอย่าง: Microservices Pipeline
Orchestrator Pipeline สำหรับ Microservices YAML# ใน orchestrator project stages: - build - test - integration - deploy # Build ทุก services build_services: stage: build parallel: matrix: - SERVICE: [auth, user, order, payment, notification] trigger: project: my-group/${SERVICE}-service branch: ${CI_COMMIT_REF_NAME} strategy: depend # Integration tests integration_test: stage: integration needs: [build_services] trigger: project: my-group/integration-tests strategy: depend # Deploy ทุก services deploy_all: stage: deploy needs: [integration_test] trigger: project: my-group/deployment branch: main variables: DEPLOY_VERSION: ${CI_COMMIT_SHA} DEPLOY_ALL: "true"
📊 Trigger Strategies
| Strategy | คำอธิบาย | Use Case |
|---|---|---|
depend |
รอให้ downstream pipeline เสร็จ และสถานะตามไปด้วย | ต้องการให้ pipeline นี้ fail ถ้า downstream fail |
| ไม่ระบุ | Trigger แล้วไปเลย ไม่รอผล | Fire-and-forget downstream tasks |
สรุป
📦 สิ่งที่ควรใช้ Cache
- Dependencies (node_modules, vendor)
- Build caches (.cache, .npm)
- ไฟล์ที่ใช้ซ้ำบ่อย
- ไม่สำคัญถ้าหาย
🎨 สิ่งที่ควรใช้ Artifacts
- Build outputs (dist, build)
- Test reports, coverage
- ไฟล์ที่ต้อง download
- สำคัญต้องเก็บไว้
- Variables: ใช้ Protected + Masked สำหรับ secrets
- Artifacts: เก็บ outputs, กำหนด expire_in
- Cache: เก็บ dependencies, ใช้ key ตาม lock file
- Rules: ควบคุม job execution อย่างยืดหยุ่น
- Needs: สร้าง DAG pipeline เร่งความเร็ว
- Matrix: รันหลาย versions/platforms พร้อมกัน
- Trigger: เชื่อมโยง pipelines ระหว่าง projects