`needs`로 잡을 더 일찍 시작하기
needs로 잡을 더 일찍 시작하기 (Make jobs start earlier with needs)
파이프라인에서 잡 의존성을 지정하려면 needs 키워드를 사용하세요. 잡은 파이프라인 스테이지가 끝날 때까지 기다리지 않고 의존성이 끝나는 즉시 시작돼요. 이렇게 하면 잡을 더 일찍 실행하고 불필요한 대기를 피할 수 있어요.
사용 사례:
- 모노레포(monorepo): 독립적인 서비스를 병렬 실행 경로로 빌드하고 테스트.
- 멀티 플랫폼 빌드: 모든 빌드가 끝날 때까지 기다리지 않고 서로 다른 플랫폼용으로 컴파일.
- 더 빠른 피드백: 테스트 결과와 오류를 더 일찍 받기.
needs: project와needs: pipeline키워드는 잡 의존성을 지정하는 데 사용되지 않아요. 다른 파이프라인에서 아티팩트를 가져오려면needs: project를 사용하세요. 상위 파이프라인의 파이프라인 상태를 미러링하려면needs: pipeline을 사용하세요.
출처: 문서
본문
needs 워크플로우
기본적으로 잡은 스테이지 단위로 실행돼요. 한 스테이지의 모든 잡이 성공적으로 끝나야 다음 스테이지의 잡이 시작될 수 있어요. 예를 들어 기본 build, test, deploy 스테이지가 있다면, test의 어떤 잡도 시작되기 전에 build의 모든 잡이 실행되고 끝나야 해요.
needs를 사용하면 잡이 의존하는 특정 잡을 나열해요. 그 잡은 이전 스테이지의 다른 잡이 아직 실행 중이어도 의존성이 끝나는 즉시 시작돼요. 이렇게 하면 일종의 방향성 비순환 그래프(DAG) 구조를 가진 파이프라인이 만들어져요.
같은 파이프라인에서 스테이지 잡과 needs 의존성이 있는 잡을 섞어 쓸 수 있어요.
추가로, needs: []를 사용하면 이전 잡이나 스테이지가 끝날 때까지 기다리지 않고 잡이 즉시 실행되도록 설정할 수 있어요. 소스 코드에서 바로 실행할 수 있고 빌드 결과에 의존하지 않는 lint 잡이나 스캐너를 즉시 실행하는 데 흔히 사용돼요.
needs와 스테이지 잡 비교
needs의 이점을 보여주기 위해 6개 잡이 있는 두 파이프라인을 비교해볼게요.
이 파이프라인은 6개 잡을 스테이지로 구성했어요. needs가 없으면 어떤 잡이 독립적이어도 한 스테이지의 모든 잡이 끝나야 다음 스테이지가 시작돼요:
graph TB
subgraph build["Build Stage"]
build_a["build_app_A"]
build_b["build_app_B"]
end
subgraph test["Test Stage"]
test_a["test_app_A"]
test_b["test_app_B"]
end
subgraph deploy["Deploy Stage"]
deploy_a["deploy_app_A"]
deploy_b["deploy_app_B"]
end
stages:
- build
- test
- deploy
build_app_A:
stage: build
script: echo "Building A..."
build_app_B:
stage: build
script: echo "Building B..."
test_app_A:
stage: test
script: echo "Testing A..."
test_app_B:
stage: test
script: echo "Testing B..."
deploy_app_A:
stage: deploy
script: echo "Deploying A..."
deploy_app_B:
stage: deploy
script: echo "Deploying B..."
이 예시에서 build 스테이지의 모든 잡이 완료될 때까지 어떤 test나 deploy 잡도 실행되지 않아요. B 잡이 실행되는 데 오래 걸린다면, A의 test·deploy 잡은 B 잡이 끝나기를 기다리며 지연될 수 있어요.
needs를 사용하면 두 개의 독립 실행 경로를 정의할 수 있어요. 각 잡은 실제로 필요한 잡에만 의존하며, 두 경로에서 병렬 실행이 가능해져요:
graph LR
subgraph build["Build Stage"]
build_a["build_app_A"]
build_b["build_app_B"]
end
subgraph test["Test Stage"]
test_a["test_app_A"]
test_b["test_app_B"]
end
subgraph deploy["Deploy Stage"]
deploy_a["deploy_app_A"]
deploy_b["deploy_app_B"]
end
build_a --> test_a
build_b --> test_b
test_a --> deploy_a
test_b --> deploy_b
stages:
- build
- test
- deploy
build_app_A:
stage: build
script: echo "Building A..."
build_app_B:
stage: build
script: echo "Building B..."
test_app_A:
stage: test
needs: ["build_app_A"]
script: echo "Testing A..."
test_app_B:
stage: test
needs: ["build_app_B"]
script: echo "Testing B..."
deploy_app_A:
stage: deploy
needs: ["test_app_A"]
script: echo "Deploying A..."
deploy_app_B:
stage: deploy
needs: ["test_app_B"]
script: echo "Deploying B..."
이 예시에서 test_app_A는 build_app_A가 성공적으로 끝나는 즉시 실행돼요. build_app_B가 아직 실행 중이어도 상관없어요. 마찬가지로 deploy_app_A는 build_app_B가 완료되기 전에 실행되고 배포될 수도 있어요.
잡 간 의존성 보기
파이프라인 그래프에서 잡 간 의존성을 볼 수 있어요.
이 보기를 활성화하려면 파이프라인 상세 페이지에서:
- Job dependencies를 선택하세요.
- 선택 사항. 어떤 잡이 연결되어 있는지 보여주는 선을 표시하려면 Show dependencies를 토글하세요.
needs 예시
needs로 잡 간 의존성을 만들고 잡이 시작을 기다리는 시간을 줄이세요. 패턴에는 fan-out, fan-in, 다이아몬드 의존성이 있어요.
Fan-out
fan-out 잡 의존성 그래프를 만들려면 여러 잡이 하나의 잡에 의존하도록 구성하세요.
예를 들면:
graph LR
subgraph build["Build Stage"]
build_job["build"]
end
subgraph test["Test Stage"]
test_unit["test_unit"]
test_integration["test_integration"]
test_performance["test_performance"]
end
build_job --> test_unit
build_job --> test_integration
build_job --> test_performance
stages:
- build
- test
build:
stage: build
script: echo "Building..."
test_unit:
stage: test
needs: ["build"]
script: echo "Unit tests..."
test_integration:
stage: test
needs: ["build"]
script: echo "Integration tests..."
test_performance:
stage: test
needs: ["build"]
script: echo "Performance tests..."
Fan-in
fan-in 의존성 그래프를 만들려면 하나의 잡이 여러 잡이 끝나기를 기다리도록 구성하세요.
예를 들면:
graph LR
subgraph build["Build Stage"]
build_frontend["build_frontend"]
build_backend["build_backend"]
end
subgraph test["Test Stage"]
test_frontend["test_frontend"]
test_backend["test_backend"]
end
subgraph deploy["Deploy Stage"]
deploy_job["deploy"]
end
build_frontend --> test_frontend
build_backend --> test_backend
test_frontend --> deploy_job
test_backend --> deploy_job
stages:
- build
- test
- deploy
build_frontend:
stage: build
script: echo "Building frontend..."
build_backend:
stage: build
script: echo "Building backend..."
test_frontend:
stage: test
needs: ["build_frontend"]
script: echo "Testing frontend..."
test_backend:
stage: test
needs: ["build_backend"]
script: echo "Testing backend..."
deploy:
stage: deploy
needs: ["test_frontend", "test_backend"]
script: echo "Deploying..."
다이아몬드 의존성
다이아몬드 의존성 그래프를 만들려면 fan-out과 fan-in을 결합하세요. 하나의 잡이 여러 잡으로 퍼지고(fan out), 그 잡들이 다시 하나의 잡으로 모여요(fan in). 예를 들면:
graph LR
subgraph build["Build Stage"]
build_job["build"]
end
subgraph test["Test Stage"]
test_unit["test_unit"]
test_integration["test_integration"]
test_performance["test_performance"]
end
subgraph deploy["Deploy Stage"]
deploy_job["deploy"]
end
build_job --> test_unit
build_job --> test_integration
build_job --> test_performance
test_unit --> deploy_job
test_integration --> deploy_job
test_performance --> deploy_job
stages:
- build
- test
- deploy
build:
stage: build
script: echo "Building..."
test_unit:
stage: test
needs: ["build"]
script: echo "Unit tests..."
test_integration:
stage: test
needs: ["build"]
script: echo "Integration tests..."
test_performance:
stage: test
needs: ["build"]
script: echo "Performance tests..."
deploy:
stage: deploy
needs: ["test_unit", "test_integration", "test_performance"]
script: echo "Deploying..."
즉시 시작
needs: []를 사용하면 파이프라인이 생성될 때 다른 잡이나 스테이지를 기다리지 않고 즉시 시작하도록 설정할 수 있어요. test처럼 더 나중 스테이지에 나타나야 하지만 즉시 실행할 수 있는 lint나 스캐닝 도구에 사용하세요.
예를 들면:
stages:
- build
- test
- deploy
build_app:
stage: build
script: echo "Building app..."
test_app:
stage: test
script: echo "Testing app..."
lint_yaml:
stage: test
needs: []
script: echo "Linting YAML..."
lint_code:
stage: test
needs: []
script: echo "Linting code..."
deploy_app:
stage: deploy
script: echo "Deploying app..."
이 예시에서 lint_yaml과 lint_code는 needs: []로 build_app이나 test 스테이지가 끝날 때까지 기다리지 않고 즉시 시작돼요. deploy_app은 needs를 사용하지 않으므로 시작 전에 앞선 스테이지의 모든 잡이 끝나기를 기다려요.
파이프라인 뷰는 잡을 스테이지별로 그룹화해서 보여줘요:
graph LR
subgraph build["Build Stage"]
build_app["build_app"]
end
subgraph test["Test Stage"]
test_app["test_app"]
lint_yaml["lint_yaml"]
lint_code["lint_code"]
end
subgraph deploy["Deploy Stage"]
deploy_app["deploy_app"]
end
build_app --> test_app
test_app --> deploy_app
잡은 가능한 한 일찍 실행되기 시작해요:
graph LR
start["Pipeline Start"]
start --> build_app["build_app"]
start --> lint_yaml["lint_yaml"]
start --> lint_code["lint_code"]
build_app --> test_app["test_app"]
test_app --> deploy_app["deploy_app"]
스테이지 없는 파이프라인 (Stageless pipelines)
stage와 stages 키워드를 생략하고 needs만 사용해 잡 순서를 정의할 수 있어요. stage 키워드가 없는 모든 잡은 기본 test 스테이지에서 실행돼요:
compile:
script: echo "Compiling..."
unit_tests:
needs: ["compile"]
script: echo "Running unit tests..."
integration_tests:
needs: ["compile"]
script: echo "Running integration tests..."
package:
needs: ["unit_tests", "integration_tests"]
script: echo "Packaging..."
이 파이프라인의 구조를 보려면 파이프라인 상세 페이지에서 Job dependencies를 선택하세요. 기본 뷰를 사용하면 모든 잡이 test 스테이지에 함께 그룹화돼요.
선택적 의존성 (Optional dependencies)
needs에서 optional: true를 사용하면 잡이 파이프라인에 존재하는 경우에만 의존하도록 할 수 있어요. needs를 rules와 결합할 때 실행될 수도 있고 아닐 수도 있는 잡을 처리하는 데 이 옵션을 사용하세요.
예를 들면:
stages:
- build
- test
- deploy
build:
stage: build
script: echo "Building..."
test:
stage: test
needs: ["build"]
script: echo "Testing..."
test_optional:
stage: test
rules:
- if: $RUN_OPTIONAL_TESTS == "true"
script: echo "Optional tests..."
deploy:
stage: deploy
needs:
- job: "test"
- job: "test_optional"
optional: true
script: echo "Deploying..."
이 예시에서:
deploy는 다음에 의존해요:test는 파이프라인에 항상 존재해요.test_optional은RUN_OPTIONAL_TESTS가true일 때만 파이프라인에 존재해요.RUN_OPTIONAL_TESTS가:false면test_optional은 파이프라인에 없고deploy는test가 끝난 후 실행돼요.true면test_optional이 파이프라인에 존재하고deploy는test와test_optional둘 다 끝나기를 기다려요.
optional: true가 없으면 deploy 잡이 test_optional을 기대하지만 파이프라인에 존재하지 않으므로 파이프라인 생성이 실패해요.
needs와 parallel:matrix 결합하기
needs 키워드는 parallel:matrix와 함께 사용해 병렬화된 잡을 가리키는 의존성을 정의할 수 있어요.
문제 해결
오류: 'job' does not exist in the pipeline
needs를 rules와 결합하면 파이프라인이 생성에 실패하고 이 오류가 표시될 수 있어요:
'unit_tests' job needs 'compile' job, but 'compile' does not exist in the pipeline.
This might be because of the only, except, or rules keywords. To need a job that
sometimes does not exist in the pipeline, use needs:optional.
이 오류는 어떤 잡의 needs가 파이프라인에 존재하지 않는 다른 잡을 가리킬 때 발생해요. 이 문제를 해결하려면 둘 중 하나를 해야 해요:
- 해당 잡이 파이프라인에 없을 때 무시되도록 잡 의존성에
optional: true를 추가하세요. - 필요한 잡이 필요할 때 항상 실행되도록 해당 잡의
rules구성을 업데이트하세요.
예를 들면:
#
# Method 1: Job with rules that may not exist
#
compile:
stage: build
rules:
- if: $COMPILE == "true"
script: echo "Compiling..."
unit_tests:
stage: test
needs:
- job: "compile" # If $COMPILE == "false", the `compile` job is not added
optional: true # to the pipeline and this needs is ignored.
script: echo "Running unit tests..."
#
# Method 2: Job with rules that always matches the dependent job
#
build:
stage: build
rules:
- if: $BUILD == "true"
script: echo "Building..."
test:
stage: test
rules: # Both jobs have identical `rules`, and will always exist
- if: $BUILD == "true" # in the pipeline together.
needs: ["build"]
script: echo "Testing..."
더 알아보기
다음으로는 needs 키워드와 needs: project 문서를 함께 보면, 다른 파이프라인의 아티팩트를 가져오거나 상위 파이프라인 상태를 미러링하는 고급 의존성 설정까지 익힐 수 있어요.
