Overview
Github Action의 Concurrency(동시성)에 대해서 알아본다.
GitHub Actions의 concurrency 기능은 동시에 실행되는 작업(job)이나 워크플로우 실행(run)을 제어하는 데 사용한다. 이를 통해 동시 실행을 제한하거나, 새로운 실행이 시작될 때 이전에 실행 중이던 작업이나 워크플로우를 자동으로 취소할 수 있다. concurrency는 특히 CI/CD 파이프라인에서 자원 사용을 최적화하고, 중복 빌드를 방지하는 데 유용하다.
2023.05.19 - [IaC/CI CD Tool] - 1. Github Action이란?
2023.05.22 - [IaC/CI CD Tool] - 2. Github Action (With Syntax)
2023.05.23 - [IaC/CI CD Tool] - 3. Github Action (With Automate Pull Request)
2024.03.12 - [IaC/CI CD Tool] - 4. Github Action (With Matrix Strategy)
mindmap
root((Using Concurrency))
defaultBehavior[Default Behavior: Enable Concurrency]
examples[Examples]
concurrencyGroups[Concurrency Groups]
cancelInProgress[Using Concurrency to Cancel Any In-Progress Job or Run]
fallbackValue[Using a Fallback Value]
cancelForCurrentWorkflow[Only Cancel In-Progress Jobs or Runs for the Current Workflow]
cancelForSpecificBranches[Only Cancel In-Progress Jobs on Specific Branches]
Using concurrency
Default behavior: Enable concurrency
GitHub Actions의 기본 동작은 여러 작업이나 워크플로우 실행을 동시에 허용하는 것이다. concurrency 키워드를 사용하면 이러한 동시 실행을 제한할 수 있다. concurrency는 워크플로우 파일 내에서 트리거 조건이 정의된 직후에 사용할 수 있으며, 특정 브랜치에 대한 전체 워크플로우 실행의 concurrency 을 제한하는 데 사용될 수 있다.
on:
push:
branches:
- main
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
GitHub Actions에서 concurrency 키워드가 어떻게 작동하는지, 특히 1분 간격으로 두 번 커밋한다고 가정해본다.
GitHub Actions에서 concurrency 기능을 사용하고 concurrency 그룹을 정의하면 GitHub는 해당 그룹 내에서 한 번에 하나의 작업 또는 워크플로만 실행될 수 있도록 보장한다. 동일한 그룹의 다른 실행이 이미 진행 중인 동안 새 실행이 트리거되는 경우 동작은 cancel-in-progress 옵션에 따라 달라진다.
- cancel-in-progress가 true로 설정된 경우 새 실행으로 인해 현재 실행 중인 작업이나 워크플로가 취소된 후 새 실행이 시작된다. 이렇게 하면 최신 커밋이 항상 처리되는 상태가 된다.
- cancel-in-progress가 지정되지 않거나 false로 설정된 경우 현재 실행이 완료될 때까지 새 실행이 대기열에 추가된다. 현재 실행이 완료된 후에만 대기 중인 실행이 시작한다.
1분 간격으로 두 번 커밋하는 시나리오를 고려하면 두 경우 모두 다음과 같다.
cancel-in-progress: true인 경우
- 첫 번째 커밋을 수행하여 워크플로 실행을 트리거한다.
- 1분 안에 두 번째 커밋을 수행한다. 그러면 다른 워크플로 실행이 트리거된다.
- `cancel-in-progress` 가 `true` 이므로 두 번째 커밋 실행은 첫 번째 실행을 취소하여(아직 진행 중인 경우) 최신 커밋에 대한 워크플로 실행만 실행되도록 한다.
cancel-in-progress가 설정되지 않았거나 false인 경우
- 첫 번째 커밋은 워크플로 실행을 트리거한다.
- 1분 후 두 번째 커밋이 또 다른 워크플로 실행을 트리거한다.
- 첫 번째 실행이 아직 진행 중이고(완료하는 데 1분 이상 걸린다고 가정) `cancel-in-progress` 가 활성화되지 않았기 때문에 두 번째 실행은 대기열에서 대기한다.
- 첫 번째 실행이 완료되면 두 번째 실행이 시작된다.
요약하자면 concurrency을 사용하면 다음과 같다.
- 워크플로 실행이 모두 실행되지 않는 상황은 발생하지 않는다. 적어도 하나(최신 항목 또는 순서대로)가 실행된다.
- `cancel-in-progress` 옵션은 최신 실행이 현재 실행 중인 선점인지, 동일한 concurrency 그룹에서 대기 중인 실행인지를 제어한다.
Example: Concurrency groups
이 구성은 같은 워크플로우와 같은 브랜치에 대한 실행이 동시에 일어나지 않도록 한다. 즉, 동일한 group 값을 가진 워크플로우 실행은 동시에 하나만 실행되며, 새로운 실행이 시작되면 이전 실행은 자동으로 취소된다.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
이 구성은 `group: staging_environment` 는 concurrency 그룹을 `staging_environment` 로 명시한다. 이는 해당 작업이 속한 모든 실행이 `staging_environment` 라는 같은 그룹 내에서 관리됨을 의미한다. 즉, 작업 수준내에서 concurrency 을 제어 한다.
jobs:
job-1:
runs-on: ubuntu-latest
concurrency:
group: staging_environment
cancel-in-progress: true
이 구성은 `group: ci-${{ github.ref }}` 는 각 실행의 concurrency 그룹을 동적으로 ci-에 브랜치 이름을 붙여 생성한다. 예를 들어, main 브랜치에 대한 실행은 `ci-refs/heads/main` 이라는 그룹에 속하게 된다. 즉, 워크플로우 전체에서 concurrency 을 제어한다.
on:
push:
branches:
- main
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
마지막으로 좀 더 복잡한 예시이다. 어떻게 동작할지는 위의 설명한 내용들을 참고해보면 유추할 수 있다.
on:
push:
branches:
- main
- feature/**
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Build step
run: echo "Building the project..."
test:
needs: build
runs-on: ubuntu-latest
steps:
- name: Test step
run: echo "Running tests..."
deploy:
if: github.ref == 'refs/heads/main'
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy step
run: echo "Deploying to production..."
Example: Using concurrency to cancel any in-progress job or run
이 설정은 특정 브랜치에 대한 실행이 시작될 때 해당 브랜치에서 진행 중인 이전 작업이나 실행을 자동으로 취소한다.
concurrency:
group: ${{ github.ref }}
cancel-in-progress: true
Example: Using a fallback value
group: 이 키워드는 concurrency 그룹의 이름을 정의한다. 여기서는 GitHub Actions의 컨텍스트 값인 `github.head_ref` 와 `github.run_id` 를 사용하여 그룹 이름을 동적으로 생성한다.
- `github.head_ref` 는 주로 풀 리퀘스트(PR) 이벤트에서 소스 브랜치의 이름을 나타내며, PR 이벤트에서만 정의된다. 예를 들어, `feature/branch-name` 과 같은 소스 브랜치 이름을 가질 수 있다.
- `github.run_id` 는 GitHub Actions 실행(run)의 고유 ID이다. 이 값은 모든 워크플로우 실행에서 고유하게 정의되며, 다른 이벤트 유형에서 `github.head_ref` 가 정의되지 않은 경우 대체값으로 사용된다.
concurrency:
group: ${{ github.head_ref || github.run_id }}
cancel-in-progress: true
이 설정의 핵심은 `github.head_ref || github.run_id` 표현식으로, 풀 리퀘스트 이벤트에서는 `github.head_ref` 를 사용하여 concurrency 그룹 이름을 생성하고, 풀 리퀘스트가 아닌 다른 이벤트에서는 `github.run_id` 를 대체값으로 사용한다.
- Pull request의 경우: `github.head_ref` 변수가 정의되어 있으며 풀 요청이 시작된 분기의 이름을 포함한다. 이 경우 `github.head_ref` 는 concurrency 그룹 이름을 구성하는 데 사용된다. 이는 동일한 소스 브랜치의 풀 요청 이벤트에 의해 트리거된 모든 워크플로 실행이 함께 그룹화되어 풀 요청의 소스 브랜치를 기반으로 concurrency 을 제어할 수 있음을 의미한다.
- 기타 이벤트의 경우: `github.head_ref` 가 정의되지 않은 컨텍스트(예: 푸시 이벤트, Workflow_dispatch 이벤트 등)에서는 `github.run_id` 에 대한 대체가 적용된다. `github.run_id` 는 각 워크플로 실행에 대한 고유 식별자이므로 이러한 다른 이벤트에 의해 트리거된 각 워크플로 실행을 자체 concurrency 그룹에 효과적으로 할당한다. 이렇게 하면 각 실행에 실행 ID를 기반으로 하는 고유한 concurrency 그룹 이름이 있으므로 이러한 실행이 다른 실행에 의해 취소되지 않는다.
Example: Only cancel in-progress jobs or runs for the current workflow
concurrency 설정에서 group 값을 동적으로 생성함으로써 특정 워크플로우의 실행만을 대상으로 동시 실행 제어를 할 수 있다. 여기서 사용된 `${{ github.workflow }}-${{ github.ref }}` 표현식은 두 가지 GitHub 컨텍스트 값을 활용한다.
- github.workflow: 현재 실행 중인 워크플로우의 이름이다. 이 값은 워크플로우 파일의 name 속성에 의해 정의된다.
- github.ref: 이벤트가 트리거된 Git 참조이다. 예를 들어, `refs/heads/main` 같은 브랜치 참조나 `refs/tags/v1.0.0` 같은 태그 참조가 될 수 있다.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
이 설정을 사용함으로써, 동일한 워크플로우 내에서 같은 Git 참조에 대해 발생한 여러 실행 중에 최신 실행만 유지하고 이전 실행들을 자동으로 취소할 수 있다. `cancel-in-progress: true` 는 새로운 실행이 시작될 때 동일 그룹 내의 다른 실행을 취소하도록 지정한다.
예를 들어, main 브랜치에 대한 두 번의 푸시 이벤트가 발생했다고 가정해 본다.
첫 번째 이벤트에 의해 시작된 워크플로우 실행이 아직 완료되지 않았을 때 두 번째 이벤트가 발생하면, concurrency 설정에 따라 첫 번째 실행은 취소되고 두 번째 실행만 완료된다.
이 과정은 해당 워크플로우에만 적용되므로, 같은 저장소 내의 다른 워크플로우는 이 설정의 영향을 받지 않는다.
Example: Only cancel in-progress jobs on specific branches
특정 브랜치에 대해서만 진행 중인 작업을 취소하는 방법을 설명한다. 여기서는 개발 브랜치에서는 진행 중인 작업을 취소하고 싶지만, 릴리즈 브랜치에서는 그렇게 하지 않기를 원할 때 사용한다.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ !contains(github.ref, 'release/') }}
- group: concurrency 그룹의 이름을 설정한다. 여기서는 github.workflow (현재 실행 중인 워크플로우의 이름)와 github.ref (이벤트를 트리거한 Git 참조, 예를 들어 브랜치 이름)를 결합하여 고유한 그룹 이름을 생성한다. 이로써 같은 워크플로우 내에서, 같은 브랜치에 대한 실행들이 같은 그룹에 속하게 된다.
- cancel-in-progress: 진행 중인 작업을 취소할지 여부를 결정한다. 여기서는 `${{ !contains(github.ref, 'release/') }}` 조건을 사용하여, `github.ref` 에 `release/` 가 포함되어 있지 않을 경우에만 `true` 가 되어 진행 중인 작업을 취소하도록 설정한다. 즉, `release/` 를 포함한 브랜치 이름 (예: release/1.2.3)으로 작업이 실행되고 있는 경우, 새로운 작업이 시작되어도 이전에 진행 중이던 작업은 취소되지 않는다.
아래는 개발(dev/) 브랜치에서는 더 빈번한 업데이트가 있을 수 있으므로, 최신 변경사항에 대해 빠르게 반응하기 위해 진행 중인 작업을 취소하고자 할 수 있다. 반면, 메인(main) 브랜치에 대해서는 안정성을 위해 현재 진행 중인 작업을 유지하고 싶을 수 있다.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ contains(github.ref, 'dev/') }}
- 이 설정은 `dev/` 로 시작하는 브랜치에 대해서만 진행 중인 작업을 취소한다. `github.ref` 에 `dev/` 가 포함되어 있을 때 `cancel-in-progress` 가 `true` 가 되어, 해당 조건을 만족하는 경우에만 이전 작업을 취소한다.
Reference
https://docs.github.com/en/enterprise-cloud@latest/actions/using-jobs/using-concurrency
'IaC > CI CD Tool' 카테고리의 다른 글
7. Github Action Build and Push(with GCP Artifact Registry) (0) | 2024.06.19 |
---|---|
ArgoCD SSO 구성 가이드(GCP Oauth) (0) | 2024.04.14 |
5. Github Action (With Using jobs in a workflow & Choosing the runner for a job) (0) | 2024.03.15 |
4. Github Action (With Matrix Strategy) (2) | 2024.03.12 |
Argo Workflow란? (2) | 2024.02.09 |