리소스 그룹

Tier: Free, Premium, Ultimate
Offering: GitLab.com, Self-managed, GitLab Dedicated

기본적으로, GitLab CI/CD의 파이프라인은 동시에 실행됩니다.
동시성은 병합 요청에서 피드백 루프를 개선하는 중요한 요소입니다.
그러나 특정 상황에서는 배포 작업의 동시성을 제한하여
작업을 하나씩 실행하고 싶을 수 있습니다.
리소스 그룹을 사용하여
안전하게 지속적 배포 워크플로우를 최적화할 수 있는
작업의 동시성을 전략적으로 제어하세요.

리소스 그룹 추가

다음과 같은 파이프라인 구성(.gitlab-ci.yml 파일)이 있다고 가정합니다:

build:
  stage: build
  script: echo "Your build script"

deploy:
  stage: deploy
  script: echo "Your deployment script"
  environment: production

브랜치에 새 커밋을 푸시할 때마다 builddeploy라는
두 개의 작업이 있는 새 파이프라인이 실행됩니다.
하지만 짧은 시간 간격에 여러 커밋을 푸시하면,
여러 파이프라인이 동시에 실행되기 시작합니다. 예를 들어:

  • 첫 번째 파이프라인은 build -> deploy 작업을 실행합니다.
  • 두 번째 파이프라인은 build -> deploy 작업을 실행합니다.

이 경우, 다양한 파이프라인에서의 deploy 작업은
production 환경에서 동시에 실행될 수 있습니다.
같은 인프라에 대한 여러 배포 스크립트를 실행하면
인스턴스에 손상을 주거나 혼란을 초래하여
최악의 경우 손상된 상태로 남길 수 있습니다.

deploy 작업이 한 번에 한 번만 실행되도록 하려면,
동시성에 민감한 작업에
resource_group 키워드를 지정할 수 있습니다:

deploy:
  ...
  resource_group: production

이 구성으로 배포의 안전성이 보장되며,
여전히 build 작업을 동시에 실행하여
파이프라인 효율성을 극대화할 수 있습니다.

전제 조건

제한 사항

리소스 그룹에는 하나의 리소스만 첨부할 수 있습니다.

프로세스 모드

배포 기본 설정에 따라 작업 동시성을 전략적으로 제어할
프로세스 모드를 선택할 수 있습니다.
다음 모드가 지원됩니다:

  • 정렬되지 않음: 이 기본 프로세스 모드는 실행 중인 작업의 동시성을 제한합니다.
    작업의 실행 순서를 신경 쓰지 않을 때 가장 쉽게 사용할 수 있는 옵션입니다.
    작업이 실행할 준비가 되면 언제든지 작업 처리를 시작합니다.

  • 가장 오래된 것 먼저: 이 프로세스 모드는 작업의 동시성을 제한합니다.
    리소스가 자유로울 때,
    created, scheduled, 또는 waiting_for_resource 상태의
    다가오는 작업 목록에서
    파이프라인 ID의 오름차순으로 정렬된 첫 번째 작업을 선택합니다.

    이 모드는 작업이 가장 오래된 파이프라인에서 실행되도록 보장할 때 효율적입니다.
    파이프라인 효율성 측면에서 unordered 모드에 비해 비효율적이지만,
    지속적인 배포에 대해 더 안전합니다.

  • 가장 새로운 것 먼저: 이 프로세스 모드는 작업의 동시성을 제한합니다.
    리소스가 자유로울 때,
    created, scheduled, 또는 waiting_for_resource 상태의
    다가오는 작업 목록에서
    파이프라인 ID의 내림차순으로 정렬된 첫 번째 작업을 선택합니다.

    이 모드는 작업이 가장 최신의 파이프라인에서 실행되도록 보장하고,
    오래된 배포 작업 방지 기능으로
    모든 오래된 배포 작업을 방지하고자 할 때 효율적입니다.
    이는 파이프라인 효율성 측면에서 가장 효율적인 옵션이지만,
    각 배포 작업이 아이도포턴트임을 보장해야 합니다.

프로세스 모드 변경

리소스 그룹의 프로세스 모드를 변경하려면 API를 사용하고
process_mode를 지정하여 기존 리소스 그룹 수정 요청을 보내야 합니다:

  • unordered
  • oldest_first
  • newest_first

프로세스 모드 간 차이에 대한 예

다음의 .gitlab-ci.yml 파일을 고려해보세요. 여기에는 두 개의 작업 builddeploy가 있으며, 각 작업은 자신의 단계에서 실행되고, deploy 작업은 production으로 설정된 리소스 그룹을 가지고 있습니다:

build:
  stage: build
  script: echo "Your build script"

deploy:
  stage: deploy
  script: echo "Your deployment script"
  environment: production
  resource_group: production

짧은 간격으로 프로젝트에 세 개의 커밋이 푸시되면, 세 개의 파이프라인이 거의 동시에 실행됩니다:

  • 첫 번째 파이프라인은 build -> deploy 작업을 실행합니다. 이 배포 작업을 deploy-1이라고 부릅시다.
  • 두 번째 파이프라인은 build -> deploy 작업을 실행합니다. 이 배포 작업을 deploy-2라고 부릅시다.
  • 세 번째 파이프라인은 build -> deploy 작업을 실행합니다. 이 배포 작업을 deploy-3이라고 부릅시다.

리소스 그룹의 프로세스 모드에 따라:

  • 프로세스 모드가 unordered로 설정된 경우:
    • deploy-1, deploy-2deploy-3는 동시에 실행되지 않습니다.
    • 작업 실행 순서에 대한 보장이 없으므로, 예를 들어 deploy-1deploy-3이 실행되기 전이나 후에 실행될 수 있습니다.
  • 프로세스 모드가 oldest_first인 경우:
    • deploy-1, deploy-2deploy-3는 동시에 실행되지 않습니다.
    • deploy-1이 먼저 실행되고, deploy-2가 두 번째로 실행되며, deploy-3이 마지막에 실행됩니다.
  • 프로세스 모드가 newest_first인 경우:
    • deploy-1, deploy-2deploy-3는 동시에 실행되지 않습니다.
    • deploy-3이 먼저 실행되고, deploy-2가 두 번째로 실행되며, deploy-1이 마지막에 실행됩니다.

교차 프로젝트/부모-자식 파이프라인을 통한 파이프라인 수준 동시성 제어

하위 파이프라인에 대해 resource_group을 정의할 수 있으며, 이는 동시 실행에 민감합니다.
trigger 키워드는 하위 파이프라인을 트리거 할 수 있으며,
resource_group 키워드는 함께 존재할 수 있습니다. resource_group
배포 파이프라인의 동시성을 제어하는 데 효율적이며, 다른 작업은 계속해서 동시 실행될 수 있습니다.

다음 예제에서는 프로젝트에 두 개의 파이프라인 구성이 있습니다. 파이프라인이 실행되기 시작하면,
민감하지 않은 작업이 먼저 실행되고 다른 파이프라인의 동시 실행에 영향을 받지 않습니다.
그러나 GitLab은 배포(자식) 파이프라인을 트리거하기 전에 다른 배포 파이프라인이 실행되고 있지 않도록 보장합니다.
다른 배포 파이프라인이 실행 중인 경우, GitLab은 해당 파이프라인이 완료될 때까지 기다립니다.

# .gitlab-ci.yml (부모 파이프라인)

build:
  stage: build
  script: echo "Building..."

test:
  stage: test
  script: echo "Testing..."

deploy:
  stage: deploy
  trigger:
    include: deploy.gitlab-ci.yml
    strategy: depend
  resource_group: AWS-production
# deploy.gitlab-ci.yml (자식 파이프라인)

stages:
  - provision
  - deploy

provision:
  stage: provision
  script: echo "Provisioning..."

deployment:
  stage: deploy
  script: echo "Deploying..."
  environment: production

trigger 키워드와 함께 strategy: depend를 정의해야 합니다.
이는 하위 파이프라인이 완료될 때까지 잠금이 해제되지 않도록 보장합니다.

관련 주제

문제 해결

파이프라인 구성에서 교착 상태 피하기

oldest_first 프로세스 모드는 작업이 파이프라인 순서로 실행되도록 강제하므로, 다른 CI 기능과 잘 작동하지 않는 경우가 있습니다.

예를 들어, 부모 파이프라인과 동일한 리소스 그룹을 요구하는 자식 파이프라인을 실행할 때, 교착 상태가 발생할 수 있습니다. 다음은 잘못된 설정의 예입니다:

# BAD
test:
  stage: test
  trigger:
    include: child-pipeline-requires-production-resource-group.yml
    strategy: depend

deploy:
  stage: deploy
  script: echo
  resource_group: production
  environment: production

부모 파이프라인에서는 test 작업을 실행하고, 이후 자식 파이프라인이 실행되며, strategy: depend 옵션으로 인해 test 작업은 자식 파이프라인이 완료될 때까지 기다립니다.

부모 파이프라인은 다음 단계에서 deploy 작업을 실행하며, 이 작업은 production 리소스 그룹의 리소스가 필요합니다. 프로세스 모드가 oldest_first인 경우, 가장 오래된 파이프라인부터 작업을 실행하므로, deploy 작업이 다음으로 실행됩니다.

그러나 자식 파이프라인도 production 리소스 그룹의 리소스를 요구합니다. 자식 파이프라인이 부모 파이프라인보다 최신이기 때문에, 자식 파이프라인은 deploy 작업이 완료될 때까지 기다립니다. 이는 절대 발생하지 않는 상황입니다.

이 경우, 부모 파이프라인 구성에 resource_group 키워드를 지정해야 합니다:

# GOOD
test:
  stage: test
  trigger:
    include: child-pipeline.yml
    strategy: depend
  resource_group: production # 부모 파이프라인에서 리소스 그룹을 지정합니다

deploy:
  stage: deploy
  script: echo
  resource_group: production
  environment: production

작업이 “리소스를 기다리는 중”으로 멈춤

때때로, 작업이 Waiting for resource: <resource_group> 메시지와 함께 멈출 수 있습니다. 이를 해결하기 위해 먼저 리소스 그룹이 올바르게 작동하는지 확인합니다:

  1. 작업 세부 정보 페이지로 이동합니다.
  2. 리소스가 작업에 할당된 경우, 리소스를 현재 사용 중인 작업 보기를 선택하고 작업 상태를 확인합니다.

    • 상태가 running 또는 pending이면 기능이 올바르게 작동하고 있는 것입니다. 작업이 완료되어 리소스를 해제할 때까지 기다립니다.
    • 상태가 created이고 프로세스 모드Oldest first 또는 Newest first인 경우 기능이 올바르게 작동하고 있습니다. 작업의 파이프라인 페이지를 방문하고 실행을 차단하는 상위 단계나 작업이 무엇인지 확인합니다.
    • 위 조건 중 어느 것도 충족되지 않으면 기능이 올바르게 작동하지 않을 수 있습니다. 문제를 GitLab에 보고하세요.
  3. 리소스를 현재 사용 중인 작업 보기 옵션이 사용 불가능하면, 해당 리소스는 작업에 할당되지 않은 것입니다. 대신, 리소스의 다가오는 작업을 확인하세요.

    1. REST API로 리소스의 다가오는 작업을 가져옵니다.
    2. 리소스 그룹의 프로세스 모드Oldest first인지 확인합니다.
    3. 다가오는 작업 목록에서 첫 번째 작업을 찾아 GraphQL을 통해 작업 세부 정보를 가져옵니다.
    4. 첫 번째 작업의 파이프라인이 오래된 파이프라인이면, 파이프라인이나 작업 자체를 취소해 보세요.
    5. 선택사항: 다음 다가오는 작업이 더 이상 실행되지 않아야 할 오래된 파이프라인에 있는 경우, 이 과정을 반복하세요.
    6. 문제가 지속되면, 문제를 GitLab에 보고하세요.

복잡하거나 바쁜 파이프라인의 경쟁 조건

위의 솔루션으로 문제를 해결할 수 없다면, 알려진 경쟁 조건 문제에 직면할 수 있습니다. 경쟁 조건은 복잡하거나 바쁜 파이프라인에서 발생합니다.

예를 들어, 다음과 같은 경우에 경쟁 조건을 겪을 수 있습니다:

  • 여러 자식 파이프라인을 가진 파이프라인.
  • 동시에 여러 파이프라인이 실행되고 있는 단일 프로젝트.

이 문제에 직면하고 있다고 생각하면, GitLab에 문제를 보고하세요하고 새로운 문제에 대한 링크와 함께 문제 436988에 댓글을 남기세요.

문제를 확인하기 위해 GitLab은 전체 파이프라인 구성과 같은 추가 세부사항을 요구할 수 있습니다.

임시 해결 방법으로는 다음을 수행할 수 있습니다:

  • 새로운 파이프라인을 시작합니다.
  • 멈춘 작업과 동일한 리소스 그룹을 가진 완료된 작업을 다시 실행합니다.

    예를 들어, setup_job과 동일한 리소스 그룹을 가진 deploy_job이 있을 경우, setup_job이 완료되면서 deploy_job이 “리소스를 기다리는 중” 상태에 멈출 수 있습니다. setup_job을 다시 실행하여 전체 프로세스를 재시작하고 deploy_job이 완료될 수 있도록 합니다.

GraphQL을 통한 작업 세부 정보 가져오기

GraphQL API를 통해 작업 정보를 가져올 수 있습니다. UI에서 트리거 작업에 접근할 수 없기 때문에 프로젝트 간/부모-자식 파이프라인이 있는 파이프라인 수준 동시성 제어를 사용하는 경우 GraphQL API를 사용해야 합니다.

GraphQL API에서 작업 정보를 얻으려면:

  1. 파이프라인 세부정보 페이지로 이동합니다.
  2. 작업 탭을 선택하고 멈춘 작업의 ID를 찾습니다.
  3. 인터랙티브 GraphQL 탐색기로 이동합니다.
  4. 다음 쿼리를 실행합니다:

    {
      project(fullPath: "<fullpath-to-your-project>") {
        name
        job(id: "gid://gitlab/Ci::Build/<job-id>") {
          name
          status
          detailedStatus {
            action {
              path
              buttonTitle
            }
          }
        }
      }
    }
    

    job.detailedStatus.action.path 필드는 리소스를 사용하는 작업 ID를 포함합니다.

  5. 다음 쿼리를 실행하고 위의 기준에 따라 job.status 필드를 확인합니다. pipeline.path 필드에서 파이프라인 페이지로 이동할 수도 있습니다.

    {
      project(fullPath: "<fullpath-to-your-project>") {
        name
        job(id: "gid://gitlab/Ci::Build/<job-id-currently-using-the-resource>") {
          name
          status
          pipeline {
            path
          }
        }
      }
    }
    

문제 보고

다음 정보를 포함하여 새 문제를 열어주세요:

  • 영향을 받은 작업의 ID.
  • 작업 상태.
  • 문제가 발생하는 빈도.
  • 문제를 재현하는 단계.

    추가 지원을 원하거나 개발 팀과 연락하기 위해서 지원팀에 연락할 수도 있습니다.