sayu.day

GitLab CI/CD 완벽 가이드 (상): 파이프라인 구조, 변수, Runner

GitLab CI/CD는 GitLab에 내장된 지속적 통합(CI)과 지속적 배포(CD) 도구입니다. 코드가 저장소에 푸시될 때마다 빌드, 테스트, 배포를 자동으로 실행합니다. 이 글은 GitLab CI/CD 가이드 상편으로, .gitlab-ci.yml의 구조와 Pipeline 실행 흐름, 변수와 시크릿 관리 전략, 그리고 Job을 실제로 실행하는 Runner와 Executor(특히 Docker-in-Docker)까지 순서대로 정리합니다.

전체 그림: 핵심 구성 요소

flowchart LR
    subgraph Developer
        Code[코드 작성]
    end

    subgraph GitLab
        Push[git push]
        CI[CI/CD Pipeline]
        Runner[GitLab Runner]
    end

    subgraph Artifacts
        Build[빌드 결과물]
        Report[테스트 리포트]
    end

    Code --> Push --> CI
    CI --> Runner
    Runner --> Build
    Runner --> Report
구성 요소 역할
.gitlab-ci.yml 파이프라인 정의 파일 (프로젝트 루트)
Pipeline Jobs의 집합, 코드 변경 시 트리거
Stage Jobs를 그룹화하는 단계
Job 실제 작업을 수행하는 단위
Runner Jobs를 실행하는 에이전트

.gitlab-ci.yml 기본 구조

프로젝트 루트에 .gitlab-ci.yml 파일을 생성하면 GitLab이 자동으로 인식합니다. 최소 예제는 다음과 같습니다.

# 가장 간단한 .gitlab-ci.yml
build-job:
  script:
    - echo "Hello, GitLab CI!"

script는 필수 키워드이며, 이 한 줄만으로도 파이프라인이 생성됩니다. 실무에서 쓰는 기본 골격은 다음과 같습니다.

# 1. 전역 기본값 설정
default:
  image: node:20-alpine
  before_script:
    - npm ci

# 2. Stages 정의 (실행 순서)
stages:
  - build
  - test
  - deploy

# 3. Jobs 정의
build-job:
  stage: build
  script:
    - npm run build
  artifacts:
    paths:
      - dist/

test-job:
  stage: test
  script:
    - npm run test

deploy-job:
  stage: deploy
  script:
    - echo "Deploying to production..."
  when: manual  # 수동 승인 필요

Stages: 실행 순서 정의

Stages는 Jobs를 그룹화하고 실행 순서를 정의합니다.

stages:
  - build      # 1단계: 모든 build 스테이지 Jobs 병렬 실행
  - test       # 2단계: build 완료 후 test Jobs 병렬 실행
  - deploy     # 3단계: test 완료 후 deploy Jobs 실행
flowchart LR
    subgraph Build [Stage: build]
        B1[build-frontend]
        B2[build-backend]
    end

    subgraph Test [Stage: test]
        T1[unit-test]
        T2[integration-test]
        T3[lint]
    end

    subgraph Deploy [Stage: deploy]
        D1[deploy-staging]
        D2[deploy-prod]
    end

    B1 --> T1 & T2 & T3
    B2 --> T1 & T2 & T3
    T1 & T2 & T3 --> D1
    T1 & T2 & T3 --> D2

중요

같은 Stage 내의 Jobs는 병렬로 실행됩니다. 다음 Stage는 이전 Stage의 모든 Jobs가 성공해야 시작됩니다.

stages를 명시하지 않으면 기본값으로 .pre, build, test, deploy, .post가 적용됩니다. .pre는 항상 첫 번째, .post는 항상 마지막에 실행됩니다.

Jobs: 실제 작업 단위

Job은 파이프라인의 기본 실행 단위이며, 각 Job은 독립적인 환경에서 실행됩니다.

job-name:                    # Job 이름 (자유롭게 지정)
  stage: test                # 소속 Stage
  image: python:3.12         # 실행 환경 (Docker 이미지)
  script:                    # 실행할 명령어 (필수)
    - pip install -r requirements.txt
    - pytest
  tags:                      # Runner 선택 태그
    - docker

주의할 점 두 가지가 있습니다.

  • image, services, stages, include, variables, default 등은 예약어라서 Job 이름으로 사용할 수 없습니다.
  • 점(.)으로 시작하는 Job은 실행되지 않는 템플릿이며, extends로 상속해 중복 설정을 줄입니다.
.test-template:      # 실행되지 않음, 템플릿
  stage: test
  before_script:
    - setup-test-env.sh

unit-test:
  extends: .test-template  # 템플릿 상속
  script:
    - pytest unit/

integration-test:
  extends: .test-template
  script:
    - pytest integration/

Script: 명령어 실행 단계

job:
  before_script:     # script 이전에 실행
    - echo "Setting up..."
    - apt-get update

  script:            # 메인 명령어 (필수)
    - echo "Running main task..."
    - npm run build

  after_script:      # script 이후에 항상 실행 (실패해도)
    - echo "Cleaning up..."
    - rm -rf temp/

after_script는 Job의 성공/실패와 관계없이 항상 실행됩니다. 리소스 정리에 유용합니다.

여러 줄 스크립트는 세 가지 방식으로 작성합니다.

job:
  script:
    # 방법 1: 배열로 나열
    - echo "First command"
    - echo "Second command"

    # 방법 2: 리터럴 블록
    - |
      echo "Multi-line"
      echo "commands"
      if [ "$DEBUG" = "true" ]; then
        echo "Debug mode"
      fi

    # 방법 3: 폴딩 블록 (한 줄로 연결)
    - >
      curl -X POST
      -H "Content-Type: application/json"
      -d '{"key": "value"}'
      https://api.example.com

Pipeline 트리거

파이프라인은 다양한 이벤트로 생성되며, 어떤 이벤트였는지는 $CI_PIPELINE_SOURCE 변수로 구분합니다.

트리거
push 브랜치에 커밋 푸시
merge_request_event MR 생성/업데이트
schedule 스케줄 파이프라인 (cron)
web GitLab UI에서 수동 실행
api API 호출
trigger 다른 파이프라인에서 트리거
pipeline 멀티 프로젝트 파이프라인
parent_pipeline Parent 파이프라인에서

rules와 조합하면 트리거별 조건 분기가 가능합니다.

build:
  stage: build
  script:
    - npm run build
  rules:
    - if: $CI_PIPELINE_SOURCE == "push"
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

deploy:
  stage: deploy
  script:
    - deploy.sh
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual  # main 브랜치는 수동 배포

실전 예제 1: Node.js 파이프라인

여기까지의 개념을 모두 적용한 Node.js 프로젝트용 파이프라인입니다.

default:
  image: node:20-alpine

stages:
  - install
  - build
  - test
  - deploy

# 캐시 설정 (의존성 재사용)
.node-cache:
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
    policy: pull

install-deps:
  stage: install
  extends: .node-cache
  cache:
    policy: pull-push  # 캐시 저장
  script:
    - npm ci
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour

build-app:
  stage: build
  extends: .node-cache
  script:
    - npm run build
  artifacts:
    paths:
      - dist/
    expire_in: 1 week

lint:
  stage: test
  extends: .node-cache
  script:
    - npm run lint
  allow_failure: true  # 실패해도 파이프라인 계속

unit-test:
  stage: test
  extends: .node-cache
  script:
    - npm run test:unit -- --coverage
  coverage: '/All files\s+\|\s+(\d+\.?\d*)\s*\|/'
  artifacts:
    reports:
      junit: junit.xml
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml

e2e-test:
  stage: test
  image: mcr.microsoft.com/playwright:v1.40.0
  extends: .node-cache
  script:
    - npm run test:e2e
  artifacts:
    when: on_failure
    paths:
      - test-results/

deploy-staging:
  stage: deploy
  script:
    - echo "Deploying to staging..."
  environment:
    name: staging
    url: https://staging.example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "develop"

deploy-production:
  stage: deploy
  script:
    - echo "Deploying to production..."
  environment:
    name: production
    url: https://example.com
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      when: manual

파이프라인 디버깅

.gitlab-ci.yml 문법 검증은 GitLab UI의 CI/CD > Pipelines > CI lint 또는 API로 할 수 있습니다.

curl --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
  --data "content=$(cat .gitlab-ci.yml)" \
  "https://gitlab.com/api/v4/ci/lint"

상세 로그가 필요하면 디버그 옵션을 켭니다.

variables:
  CI_DEBUG_TRACE: "true"  # 상세 로그 출력

job:
  script:
    - set -x  # 명령어 출력
    - echo "Debug info"

주의

CI_DEBUG_TRACE는 시크릿을 포함한 모든 변수를 로그에 출력합니다. 프로덕션에서 사용 시 주의하세요.

변수 유형과 Predefined Variables

변수는 파이프라인 동작 제어, 환경별 설정 관리, 시크릿 전달의 핵심 메커니즘입니다.

유형 정의 위치 예시
Predefined GitLab 자동 제공 $CI_COMMIT_SHA, $CI_JOB_ID
File-defined .gitlab-ci.yml variables: 블록
Project Settings > CI/CD API 키, 배포 토큰
Group 그룹 Settings 공통 시크릿
Instance Admin > CI/CD 전역 설정

GitLab은 200개 이상의 사전 정의 변수를 제공합니다. 자주 쓰는 것들입니다.

job:
  script:
    # Pipeline 정보
    - echo "Pipeline ID: $CI_PIPELINE_ID"
    - echo "Pipeline Source: $CI_PIPELINE_SOURCE"

    # Commit 정보
    - echo "Branch: $CI_COMMIT_BRANCH"
    - echo "Tag: $CI_COMMIT_TAG"
    - echo "SHA: $CI_COMMIT_SHA"
    - echo "Short SHA: $CI_COMMIT_SHORT_SHA"

    # Merge Request
    - echo "MR IID: $CI_MERGE_REQUEST_IID"
    - echo "Target Branch: $CI_MERGE_REQUEST_TARGET_BRANCH_NAME"

    # Project / Job
    - echo "Project Path: $CI_PROJECT_PATH"
    - echo "Job Name: $CI_JOB_NAME"
    - echo "Job Stage: $CI_JOB_STAGE"

File-defined 변수와 변수 확장

.gitlab-ci.yml에서는 전역과 Job 레벨로 변수를 정의할 수 있습니다.

variables:
  GLOBAL_VAR: "global"

job1:
  variables:
    JOB_VAR: "job1-specific"
  script:
    - echo "$GLOBAL_VAR"  # global
    - echo "$JOB_VAR"     # job1-specific

job2:
  script:
    - echo "$GLOBAL_VAR"  # global
    - echo "$JOB_VAR"     # (비어있음)

변수 안에서 다른 변수를 참조할 수 있고, 필요하면 확장을 비활성화합니다.

variables:
  PROJECT: "myapp"
  VERSION: "1.0.0"
  IMAGE_TAG: "$PROJECT:$VERSION"  # myapp:1.0.0

  # 확장 비활성화 (GitLab 15.6+)
  PASSWORD:
    value: "pa$$word"
    expand: false

변수 우선순위

동일한 이름의 변수가 여러 위치에 정의되면 아래 우선순위(높은 순)로 값이 결정됩니다.

  1. Trigger variables (트리거 시 전달)
  2. Manual pipeline variables (수동 실행 시 입력)
  3. Scheduled pipeline variables (스케줄 설정)
  4. Job-level variables (.gitlab-ci.yml Job 내)
  5. Global variables (.gitlab-ci.yml 전역)
  6. Project variables (UI 설정)
  7. Group variables (상위 그룹 포함)
  8. Instance variables (Admin 설정)
  9. Predefined variables (GitLab 제공)
variables:
  MY_VAR: "from-gitlab-ci"  # 5번 우선순위

job:
  variables:
    MY_VAR: "from-job"      # 4번 우선순위 (이 값 사용)
  script:
    - echo "$MY_VAR"        # from-job

Protected와 Masked 변수

민감한 정보는 .gitlab-ci.yml에 직접 작성하지 않고 GitLab UI(Settings > CI/CD > Variables)에서 설정합니다.

옵션 설명
Protected Protected 브랜치/태그에서만 사용 가능
Masked Job 로그에서 [MASKED]로 표시
Expanded 다른 변수 참조 허용 (기본 true)

중요

Protected 변수는 Protected 브랜치/태그에서 실행되는 파이프라인에서만 사용할 수 있습니다. feature/* 브랜치에서 프로덕션 시크릿에 접근하는 것을 막아 줍니다.

Masked 변수는 로그에 노출되지 않는 대신 조건이 있습니다. 최소 8자, Base64 유효 문자만(A-Za-z0-9+/=@:.~), 여러 줄 값은 사용할 수 없습니다.

job:
  script:
    - echo "Token: $SECRET_TOKEN"
    # 로그 출력: Token: [MASKED]

Job 간 동적 변수 전달: dotenv

빌드 버전처럼 파이프라인 실행 중 계산된 값은 dotenv artifacts로 다음 Job에 전달합니다.

generate-version:
  stage: build
  script:
    - VERSION=$(date +%Y%m%d%H%M%S)
    - echo "VERSION=$VERSION" >> build.env
    - echo "COMMIT_HASH=$CI_COMMIT_SHORT_SHA" >> build.env
  artifacts:
    reports:
      dotenv: build.env

use-version:
  stage: deploy
  needs:
    - job: generate-version
      artifacts: true
  script:
    - echo "Deploying version: $VERSION"
    - echo "Commit: $COMMIT_HASH"

큰 데이터나 인증서(kubeconfig, GCP 서비스 계정 JSON, SSH 프라이빗 키, TLS 인증서)는 File 타입 변수로 저장합니다. 이때 변수 값은 파일 경로가 됩니다.

job:
  script:
    - kubectl --kubeconfig=$KUBE_CONFIG get pods

외부 시크릿 저장소 연동

HashiCorp Vault

GitLab Premium 이상에서는 Vault 네이티브 통합을 지원합니다.

job:
  id_tokens:
    VAULT_ID_TOKEN:
      aud: https://vault.example.com
  secrets:
    DATABASE_PASSWORD:
      vault: production/db/password@secrets
      file: false
  script:
    - echo "Password: $DATABASE_PASSWORD"

무료 플랜에서는 JWT 인증으로 직접 연동할 수 있습니다.

variables:
  VAULT_ADDR: "https://vault.example.com"

get-secrets:
  image: hashicorp/vault:latest
  script:
    - export VAULT_TOKEN=$(vault write -field=token auth/jwt/login role=myapp jwt=$CI_JOB_JWT)
    - vault kv get -field=password secret/myapp/db > db_password.txt
  artifacts:
    paths:
      - db_password.txt

AWS Secrets Manager

variables:
  AWS_DEFAULT_REGION: ap-northeast-2

get-aws-secret:
  image: amazon/aws-cli:latest
  script:
    - SECRET=$(aws secretsmanager get-secret-value --secret-id prod/db/password --query SecretString --output text)
    - echo "SECRET=$SECRET" >> aws.env
  artifacts:
    reports:
      dotenv: aws.env

변수 보안에서 흔한 실수

  1. Protected 미사용: 프로덕션 배포 토큰은 Protected로 설정하고, 배포 Job도 rules로 main 브랜치에 한정합니다.
  2. Masked 누락: 민감한 모든 변수에 Masked 옵션을 활성화합니다.
  3. 전역 스코프 남용: 프로덕션 시크릿을 전역 variables에 두면 모든 Job이 접근할 수 있습니다. 필요한 Job에서만 사용하도록 좁힙니다.
# 잘못된 예: 모든 Job에서 프로덕션 시크릿 접근
variables:
  PROD_DB_PASSWORD: $PROD_DB_PASSWORD

# 올바른 예: 필요한 Job에서만 사용
deploy-prod:
  variables:
    DB_PASSWORD: $PROD_DB_PASSWORD
  script:
    - deploy.sh
  1. 시크릿 방치: 정기적으로 로테이션합니다. 변수 업데이트는 기존 파이프라인에 영향 없이 새 값이 적용됩니다.

GitLab Runner와 Executor

GitLab Runner는 CI/CD Jobs를 실제로 실행하는 에이전트입니다. GitLab 서버와 분리되어 동작하며, 다양한 환경에서 실행할 수 있습니다.

유형 범위 설정 위치
Shared 인스턴스 전체 Admin Area
Group 특정 그룹과 하위 프로젝트 Group Settings
Project 특정 프로젝트만 Project Settings
# Runner 설치 (Linux)
curl -L "https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.deb.sh" | sudo bash
sudo apt-get install gitlab-runner

# Runner 등록
sudo gitlab-runner register \
  --url "https://gitlab.com/" \
  --registration-token "PROJECT_REGISTRATION_TOKEN" \
  --description "My Docker Runner" \
  --executor "docker" \
  --docker-image "alpine:latest"

Executor는 Jobs가 어떤 환경에서 실행되는지 결정합니다.

Executor 격리 수준 용도
Shell 없음 간단한 스크립트, 호스트 직접 접근
Docker 컨테이너 일반적인 CI/CD
Docker Machine VM + 컨테이너 Auto-scaling
Kubernetes Pod 클라우드 네이티브
VirtualBox VM 완전 격리 필요 시

주의

Shell Executor는 격리가 없어 보안에 취약합니다. 신뢰할 수 있는 코드만 실행하세요.

가장 일반적인 것은 Docker Executor로, 각 Job이 독립된 컨테이너에서 실행됩니다.

# /etc/gitlab-runner/config.toml
[[runners]]
  name = "docker-runner"
  executor = "docker"
  [runners.docker]
    image = "alpine:latest"
    privileged = false
    volumes = ["/cache"]
    shm_size = 0

Docker-in-Docker(DinD)

CI 파이프라인에서 Docker 이미지를 빌드해야 할 때 DinD를 사용합니다. 일반 Docker Executor의 Job 컨테이너 안에는 Docker 데몬이 없어 docker 명령을 사용할 수 없기 때문입니다.

default:
  image: docker:24.0.5
  services:
    - name: docker:24.0.5-dind
      alias: docker

variables:
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"
  DOCKER_TLS_VERIFY: 1
  DOCKER_CERT_PATH: "$DOCKER_TLS_CERTDIR/client"

build:
  stage: build
  script:
    - docker info
    - docker build -t myapp:$CI_COMMIT_SHA .
    - docker push myapp:$CI_COMMIT_SHA
flowchart TB
    subgraph Env [Runner 실행 환경]
        subgraph JobContainer [Job Container]
            CLI[docker CLI]
        end

        subgraph DinD [DinD Service Container]
            Daemon[Docker Daemon]
            Images[(Images)]
        end

        CLI -->|TCP :2376| Daemon
        Daemon --> Images
    end

TLS는 기본 활성화(포트 2376)가 권장이며, 테스트 용도로만 비활성화(포트 2375)합니다.

# TLS 비활성화 (테스트용)
variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_TLS_CERTDIR: ""

경고

TLS 비활성화는 암호화되지 않은 통신을 사용합니다. 프로덕션에서는 사용하지 마세요.

DinD를 쓰려면 Runner에 privileged = true 설정이 필수입니다.

[[runners]]
  name = "docker-runner"
  executor = "docker"
  [runners.docker]
    tls_verify = false
    image = "docker:24.0.5"
    privileged = true  # DinD 필수
    disable_entrypoint_overwrite = false
    oom_kill_disable = false
    disable_cache = false
    volumes = ["/certs/client", "/cache"]
    shm_size = 0

대안: Docker Socket Binding

DinD 대신 호스트의 Docker 소켓을 마운트하는 방식도 있습니다.

[[runners]]
  [runners.docker]
    volumes = ["/var/run/docker.sock:/var/run/docker.sock"]
특성 DinD Socket Binding
격리 완전 격리 호스트와 공유
보안 안전 호스트 접근 가능
성능 약간 느림 빠름
캐시 Job마다 초기화 호스트 캐시 공유
빌드 레이어 매번 다운로드 캐시 활용

중요

Socket Binding은 호스트 Docker에 직접 접근하므로 신뢰할 수 있는 코드만 실행해야 합니다. 보안 관점에서는 DinD가 더 안전합니다.

Kubernetes Executor

Kubernetes 클러스터에서 Jobs를 Pod로 실행합니다.

[[runners]]
  name = "k8s-runner"
  executor = "kubernetes"
  [runners.kubernetes]
    namespace = "gitlab-runner"
    image = "alpine:latest"
    privileged = false

    cpu_request = "100m"
    cpu_limit = "1"
    memory_request = "128Mi"
    memory_limit = "1Gi"

    service_cpu_request = "100m"
    service_memory_request = "128Mi"

    poll_interval = 5
    poll_timeout = 3600
sequenceDiagram
    participant GitLab as GitLab Server
    participant Runner as GitLab Runner
    participant K8s as Kubernetes API
    participant Pod as Job Pod

    GitLab->>Runner: Job 할당
    Runner->>K8s: Pod 생성 요청
    K8s->>Pod: Pod 스케줄링
    Pod->>Pod: Job 실행
    Pod->>Runner: 결과 전송
    Runner->>GitLab: Job 완료 보고
    Runner->>K8s: Pod 삭제

Kubernetes에서 DinD를 쓰려면 privileged 설정과 DinD 서비스 컨테이너를 함께 구성합니다.

[[runners]]
  [runners.kubernetes]
    privileged = true

    [[runners.kubernetes.services]]
      name = "docker:24.0.5-dind"
      alias = "docker"
      command = ["--storage-driver=overlay2"]

Runner 태그와 성능 최적화

Runner에 태그를 지정해 특정 Jobs만 실행하도록 제한합니다. GPU 빌드나 아키텍처별 빌드처럼 실행 환경이 다른 경우에 필수입니다.

[[runners]]
  name = "gpu-runner"
  tags = ["gpu", "cuda", "ml"]
  run_untagged = true  # 태그 없는 Job도 실행
train-model:
  tags:
    - gpu
    - ml
  script:
    - python train.py

동시 실행 수와 캐시도 함께 조정합니다.

concurrent = 10  # 전체 Runner가 동시 실행할 수 있는 최대 Job 수

[[runners]]
  limit = 5  # 이 Runner의 최대 동시 Job 수

  [runners.cache]
    Type = "s3"
    Shared = true
    [runners.cache.s3]
      ServerAddress = "s3.amazonaws.com"
      BucketName = "gitlab-runner-cache"
      BucketLocation = "ap-northeast-2"

의존성 캐시는 lock 파일 기준 키로 관리하면 재사용률이 높습니다.

default:
  cache:
    key:
      files:
        - package-lock.json
    paths:
      - node_modules/
    policy: pull-push

build:
  cache:
    policy: pull  # 읽기만

실전 예제 2: 멀티 아키텍처 Docker 빌드

DinD와 Runner 태그를 조합한 멀티 아키텍처 이미지 빌드 예제입니다.

stages:
  - build
  - manifest

variables:
  DOCKER_HOST: tcp://docker:2376
  DOCKER_TLS_CERTDIR: "/certs"

.docker-build:
  image: docker:24.0.5
  services:
    - docker:24.0.5-dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

build-amd64:
  extends: .docker-build
  tags:
    - amd64
  script:
    - docker build --platform linux/amd64 -t $CI_REGISTRY_IMAGE:amd64 .
    - docker push $CI_REGISTRY_IMAGE:amd64

build-arm64:
  extends: .docker-build
  tags:
    - arm64
  script:
    - docker build --platform linux/arm64 -t $CI_REGISTRY_IMAGE:arm64 .
    - docker push $CI_REGISTRY_IMAGE:arm64

create-manifest:
  extends: .docker-build
  stage: manifest
  script:
    - docker manifest create $CI_REGISTRY_IMAGE:latest
        $CI_REGISTRY_IMAGE:amd64
        $CI_REGISTRY_IMAGE:arm64
    - docker manifest push $CI_REGISTRY_IMAGE:latest

정리

개념 설명
.gitlab-ci.yml 프로젝트 루트에 위치한 파이프라인 정의 파일
Stage / Job 실행 순서 그룹 / 독립 환경에서 실행되는 작업 단위
변수 우선순위 Trigger > Manual > Scheduled > Job > Global > Project > Group > Instance
Protected / Masked Protected 브랜치 한정 접근 / 로그 마스킹
dotenv Job 간 동적 변수 전달
Runner / Executor Job 실행 에이전트 / 실행 환경(Shell, Docker, K8s)
DinD 컨테이너 안에서 Docker 빌드, TLS와 privileged 필요
Socket Binding 호스트 Docker 공유, 빠르지만 보안 주의

Parent-Child 파이프라인과 Multi-Project 파이프라인, rules/needs 기반 고급 Job 제어, 외부 통합은 하편에서 다룹니다.

참고 자료