· 32분 읽기
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
변수 우선순위
동일한 이름의 변수가 여러 위치에 정의되면 아래 우선순위(높은 순)로 값이 결정됩니다.
- Trigger variables (트리거 시 전달)
- Manual pipeline variables (수동 실행 시 입력)
- Scheduled pipeline variables (스케줄 설정)
- Job-level variables (
.gitlab-ci.ymlJob 내) - Global variables (
.gitlab-ci.yml전역) - Project variables (UI 설정)
- Group variables (상위 그룹 포함)
- Instance variables (Admin 설정)
- 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
변수 보안에서 흔한 실수
- Protected 미사용: 프로덕션 배포 토큰은 Protected로 설정하고, 배포 Job도
rules로 main 브랜치에 한정합니다. - Masked 누락: 민감한 모든 변수에 Masked 옵션을 활성화합니다.
- 전역 스코프 남용: 프로덕션 시크릿을 전역
variables에 두면 모든 Job이 접근할 수 있습니다. 필요한 Job에서만 사용하도록 좁힙니다.
# 잘못된 예: 모든 Job에서 프로덕션 시크릿 접근
variables:
PROD_DB_PASSWORD: $PROD_DB_PASSWORD
# 올바른 예: 필요한 Job에서만 사용
deploy-prod:
variables:
DB_PASSWORD: $PROD_DB_PASSWORD
script:
- deploy.sh
- 시크릿 방치: 정기적으로 로테이션합니다. 변수 업데이트는 기존 파이프라인에 영향 없이 새 값이 적용됩니다.
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 제어, 외부 통합은 하편에서 다룹니다.