GitLab Functions
GitLab Functions
파이프라인이 커질수록 script 블록은 유지하기 어려워져요. 잡마다 로직이 중복되고, 스크립트를 외부 소스에서 가져오게 되며, 사소한 변경에도 여러 곳을 고쳐야 하죠. GitLab Functions는 이런 문제를 해결하기 위해 나온 실험적 기능으로, CI/CD 잡의 script를 대체하는 재사용 가능한 잡 로직 단위예요.
출처: 문서
본문
GitLab Functions는 GitLab CI/CD 잡의 script를 대체하는 재사용 가능한 CI/CD 잡 로직 단위를 제공합니다.
GitLab Functions는 활발히 개발 중인 실험적 기능이며 breaking change의 대상이 될 수 있습니다. 자세한 내용은 changelog를 참고하세요.
왜 functions인가
파이프라인이 커지면 script 블록은 유지하기 어려워집니다. 로직이 잡들 사이에서 중복되고, 스크립트를 런타임에 외부 소스에서 가져오며, 사소한 변경에도 많은 곳을 업데이트해야 해요. GitLab Functions는 이런 문제를 해결하도록 설계되었습니다.
functions의 장점은 다음과 같습니다.
- Functions는 자체 포함되어 있고 버전 관리됩니다. function은 로직, 지원 스크립트나 바이너리, 그리고 입력과 출력을 설명하는 스펙을 패키징한 OCI 이미지예요. 단계(step)가 실행될 때 GitLab이 function을 자동으로 가져옵니다. 잡 시작 시 스크립트를 가져오거나 외부 의존성을 수동으로 관리할 필요가 없어요. 특정 버전 태그의 function을 참조하면 매번 정확히 그 버전을 얻습니다.
- Functions는 잡과 프로젝트 전체에서 재사용 가능합니다. OCI 레지스트리에 function을 게시하면 어떤 잡이든 단일
func참조로 사용할 수 있고, 각 저장소에 스크립트 파일을 복사하고 관리할 필요가 없어요. - Functions는 데이터 흐름을 명시적으로 만듭니다.
script블록에서는 셸 변수를 통해 명령 사이에 값이 전달되는데, 이를 어떤 순서로든 설정·덮어쓰기·읽기할 수 있어요.run목록에서는 각 단계가 자신의 입력과 출력을 선언하고, 단계는 이미 실행된 단계의 출력에만 접근할 수 있습니다. - Functions는 독립적으로 테스트 가능합니다. function이 자신의 입력과 출력을 정의하므로, 전체 파이프라인을 실행하지 않고도 격리해서 실행하고 테스트할 수 있어요.
- Function 실행은 플랫폼 전반에서 안정적입니다. 전용 에이전트가 빌드 호스트에서 function 실행을 관리하며, 와이어로 전송된 스크립트를 해석하지 않아요. 덕분에 function은 적절한 프로세스 제어, 크로스-플랫폼 일관성, 재개 가능한 잡의 기반을 얻습니다. 이런 기능은 셸 스크립트만으로는 달성하기 어렵거나 불가능합니다.
기존 셸 스크립트를 재사용하려면, run 목록에서 script 단계로 직접 실행하면서 점진적으로 마이그레이션할 수 있어요. 한 번에 모두 변환하지 않고도 functions를 사용할 수 있습니다.
Functions 이해하기
전통적인 CI/CD 잡에서 script 키워드는 셸 명령 목록을 담고 있습니다. 잡이 모든 단계를 소유하고 로직이 YAML에 직접 살아 있어서, 결과를 얻는 정확한 방법을 서술합니다. 파이프라인이 커지면 이 접근 방식은 재사용하고, 테스트하고, 프로젝트 간에 공유하기 어려워져요.
GitLab Functions에서는 run 키워드로 단계 목록을 선언합니다. 각 단계는 구현을 담은 function을 참조하고, 잡은 '어떻게'가 아니라 '무엇이' 일어나야 하는지를 서술합니다. 로직은 YAML이 아니라 functions에 존재해요.
다음은 JavaScript 프로젝트의 전통적인 .gitlab-ci.yml 예시입니다.
build_and_release:
script:
- npm run lint
- npm test
- npm run bundle
- BUNDLE_PATH=$(find dist -name '*.js' | head -1)
- npm run minify -- --input $BUNDLE_PATH
- npm run deploy -- --artifact $MINIFIED_PATH --env production
같은 파이프라인을 GitLab Functions로 작성한 것입니다.
build_and_release:
run:
- name: validate
func: registry.gitlab.com/js/validate:1.0.0
- name: release
func: registry.gitlab.com/js/release:1.0.0
inputs:
environment: production
각 잡은 단계를 통해 무엇이 일어나야 하는지 선언합니다. 구현은 functions 자체에 들어 있어요.
GitLab Functions 용어집
이 용어집은 GitLab Functions와 관련된 용어에 대한 정의를 제공합니다.
CI/CD Steps에서 이름 변경
GitLab Functions는 이전에 CI/CD Steps라고 불렸습니다. 기능과 문법이 이름 변경되었습니다.
| 이전 | 새 이름 |
|---|---|
| CI/CD Steps | GitLab Functions |
step: (deprecated) |
func: |
step.yml (deprecated) |
func.yml |
${{ step_dir }} (deprecated) |
${{ func_dir }} |
${{ job.<variable_name> }} (deprecated) |
${{ vars.<variable_name> }} |
Components와 functions
Components와 functions는 파이프라인의 서로 다른 수준에서 동작하며 서로 다른 문제를 해결합니다.
CI/CD Components는 파이프라인 수준에서 재사용 가능합니다. GitLab은 어떤 잡이 실행되기 전에 component를 포함하고, 파이프라인에 잡·스테이지·구성을 기여합니다. Components는 파이프라인에 어떤 잡이 존재하는지 서술해요.
GitLab Functions는 잡 수준에서 재사용됩니다. 잡 안에서 실행되며 script를 대체합니다.
Components와 functions는 서로 다른 수준에서 동작하며 서로를 잘 보완합니다. component가 잡을 정의하고 내부적으로 function을 사용해 구현할 수 있어요. component를 include하면 내부가 어떻게 동작하는지 알 필요 없이 완전히 구성된 잡을 얻게 됩니다. component 작성자로서 당신은 잡이 하는 일의 복잡함을 다루기 위해 functions를 사용해요.
표현식 문법
Components와 functions는 평가되는 시점이 다르기 때문에 서로 다른 표현식 문법을 사용합니다.
$[[ ]]표현식은 파이프라인 생성 중, 어떤 잡이 실행되기 전에 평가됩니다. CI/CD inputs과 component inputs에 이 문법을 사용하세요.${{ }}표현식은 잡 실행 중, 각 단계가 실행되기 직전에 평가됩니다. function inputs, 환경 변수, 런타임 상태에 의존하는 값에 이 문법을 사용하세요.
두 문법 모두 CI/CD Component YAML 구성 파일에 나타날 수 있습니다.
spec:
inputs:
go_version:
default: "1.22"
---
my-format-job:
run:
- name: install_go
func: ./languages/go/install
inputs:
version: $[[ inputs.go_version ]] # resolved at pipeline creation
- name: format
func: ./languages/go/go-fmt
inputs:
go_binary: ${{ steps.install_go.outputs.go_binary }} # resolved during job execution
Function 실행 모델
Functions는 입력을 받고, 출력을 반환하고, 환경 변수를 내보낼 수 있는 자체 포함 패키지입니다. Functions는 인스턴스가 호스트 머신이든 컨테이너든 CI 잡의 환경에서 실행됩니다. file system, OCI 레지스트리, Git 저장소에 function을 로컬로 호스팅할 수 있습니다.
run 목록의 각 단계는 순서대로 실행됩니다. 단계들은 공유 셸 상태가 아니라 입력, 출력, 내보낸 환경 변수를 통해 서로 통신합니다.
한 단계의 출력은 ${{ steps.<step-name>.outputs.<output-name> }} 표현식을 통해 이후 단계에서 사용할 수 있습니다. 단계가 내보낸 환경 변수는 이후 모든 단계에서 사용할 수 있어요. 출력과 환경 변수 모두 단계가 완료된 후에만 사용할 수 있습니다.
러너가 run 목록이 있는 잡을 집으면, 단계 러너(step runner)를 호출해 실행을 관리합니다. 목록의 각 단계에 대해 단계 러너는:
- function 참조를 해석하고 file system, OCI 저장소, 또는 Git 저장소에서 function 패키지를 가져옵니다.
- 단계의 입력과 환경 변수에 있는 표현식을 평가합니다.
- function을 실행하고 해석된 입력과 환경을 전달합니다.
- function이 출력 파일에 쓴 출력을 읽어 이후 단계에서 사용 가능하게 합니다.
- function이 내보낸 환경 변수를 읽어 전역 환경에 추가합니다.
- 다음 단계로 이동하거나, 단계가 실패하면 중지합니다.
Function 요구 사항
Functions를 사용하려면 사용하는 러너 실행기에 단계 러너(step runner)를 설치해야 할 수 있습니다. 자세한 내용은 단계 러너를 수동으로 설치를 참고하세요.
Functions 사용하기
run 키워드로 GitLab CI/CD 잡이 functions를 사용하도록 구성합니다. functions를 실행할 때는 잡에서 before_script, after_script, script를 사용할 수 없어요.
단계로 function 실행하기
run 키워드는 실행할 단계 목록을 받습니다. 단계는 목록에 정의된 순서대로 한 번에 하나씩 실행됩니다. 각 단계는 name과 func 또는 script를 가지며, 선택적으로 inputs와 env를 가져요.
name은 영숫자 문자와 밑줄로만 구성되어야 하며 숫자로 시작할 수 없습니다.
Function 호출하기
단계는 func 키워드로 function 참조를 제공해 function을 호출할 수 있어요. inputs 키워드로 function에 입력을 전달하고, env 키워드로 환경 값을 덮어씁니다. func 값과 inputs, env의 키·값에 표현식을 사용하세요.
Functions는 호출된 function이 작업 디렉터리를 덮어쓰지 않는 한 CI_PROJECT_DIR 디렉터리에서 실행됩니다.
예를 들어 아래 echo function을 실행하면 잡 로그에 Hi Sally! 메시지가 출력됩니다.
my-job:
variables:
FRIEND: "Sally"
run:
- name: say_hi
func: registry.gitlab.com/gitlab-org/ci-cd/runner-tools/gitlab-functions-examples/echo:1
inputs:
message: "Hi ${{ vars.FRIEND }}!"
스크립트 실행하기
단계는 script 키워드로 스크립트를 호출할 수 있어요. env로 스크립트에 전달된 환경 변수는 셸에 설정됩니다. 스크립트 단계는 bash 셸을 사용하며, bash가 없으면 sh로 대체됩니다. script 값과 env의 키·값에 표현식을 사용할 수 있습니다. 스크립트 단계는 CI_PROJECT_DIR 디렉터리에서 실행됩니다.
functions 옆에 커스텀한 것이 필요할 때 스크립트 단계를 사용하세요. 내부적으로 functions는 스크립트를 function 호출로 변환하고 입력으로 전달합니다.
예를 들어 다음 스크립트 단계는 잡 로그에 Hi Sally! 메시지를 출력합니다.
my-job:
variables:
FRIEND: "Sally"
run:
- name: say_hi
script: echo 'Hi ${{ vars.FRIEND }}!'
Function 참조
Functions는 file system 또는 OCI 저장소에서 로드됩니다. Git 저장소에서 로드하는 것은 지원되지만 deprecated입니다.
OCI 저장소에서 로드
- GitLab Runner 18.9에서 도입되었습니다.
OCI 저장소에서 function을 로드하려면 레지스트리, 저장소, 버전(태그)을 제공하세요. 이 방법이 함수를 배포하고 소비하는 권장 방식입니다.
Function OCI 이미지는 여러 플랫폼을 지원합니다. 단계 러너는 실행 중인 플랫폼과 일치하는 이미지를 다운로드합니다. 일치하는 것이 없으면 단계가 실패합니다.
# prints 'Hi from GitLab Functions'
my-job:
run:
- name: echo
func: registry.gitlab.com/gitlab-org/ci-cd/runner-tools/gitlab-functions-examples/echo:1
inputs:
message: "Hi from GitLab Functions"
function이 루트에 없으면 이미지의 하위 디렉터리와 파일명을 지정할 수도 있어요.
# prints 'snoitcnuF baLtiG morf iH'
my-job:
run:
- name: echo
func: registry.gitlab.com/gitlab-org/ci-cd/runner-tools/gitlab-functions-examples/echo:1 reverse/func.yml
inputs:
message: "Hi from GitLab Functions"
프라이빗 OCI 저장소에 인증하려면 Docker 구성 파일 형식의 값으로 DOCKER_AUTH_CONFIG 환경 변수를 설정하세요. function으로서의 인증 작업 예시는 Docker Auth function을 참고하세요.
file system에서 로드
상대 경로로 file system에서 function을 로드하려면 function 참조를 .로 시작하세요. 경로는 호출하는 function의 디렉터리를 기준으로 합니다. 잡에서 직접 function을 호출하면 경로는 CI_PROJECT_DIR을 기준으로 해요.
/로 function 참조를 시작하면 절대 경로로 file system에서 function을 로드합니다.
경로는 단계가 실행될 때 function 디렉터리가 됩니다. 이 디렉터리에 function 정의 YAML이 존재해야 합니다. 비표준이라면 function 정의 YAML 파일명을 선택적으로 제공할 수 있어요. 운영체제와 관계없이 경로 구분자는 슬래시 /를 사용해야 합니다.
예를 들어:
- 상대 디렉터리에서 로드:
- name: my_step
func: ./path/to/my-function
- 절대 디렉터리에서 로드:
- name: my_step
func: /opt/gitlab-functions/my-function
- 커스텀 function 정의 파일로 로드:
- name: my_step
func: ./funcs/release/dry-run.yml
Git 저장소에서 로드 (deprecated)
GitLab은 향후 릴리스에서 Git 저장소에서 function을 로드하는 지원을 제거할 계획입니다. 대신 OCI 저장소에서 function을 로드하세요.
Git 저장소에서 function을 로드하려면 저장소의 URL과 리비전(커밋, 브랜치, 또는 태그)을 제공하세요. 저장소에 인증하려면 URL에 사용자 이름과 비밀번호를 추가합니다.
func에서 Git function 참조를 텍스트로 제공할 때는 함수가 steps 하위 디렉터리에 존재해야 합니다. 긴 형식의 Git function 참조인 git을 사용할 때는 함수가 dir 디렉터리에 존재해야 합니다.
Git 저장소에는 컴파일된 코드가 아니라 소스가 들어 있습니다. 가능하면 OCI 저장소에서 function을 로드하세요.
예를 들어:
- 태그로 function 지정:
- name: my_step
func: gitlab.com/funcs/[email protected]
- 브랜치로 function 지정:
- name: my_step
func: gitlab.com/funcs/my-git-repo@main
- 디렉터리, 파일명, Git 커밋으로 function 지정:
- name: my_step
func: gitlab.com/funcs/my-git-repo/-/reverse/my-func.yml@3c63f399ace12061db4b8b9a29f522f41a3d7f25
- 가져올 때 Git에 인증:
- name: my_step
func: gitlab-ci-token:${{ vars.CI_JOB_TOKEN }}@gitlab.com/funcs/[email protected]
steps 폴더 밖의 디렉터리나 파일을 지정하려면 확장된 func 문법을 사용하세요.
my-job:
run:
- name: my_step
func:
git:
url: gitlab.com/funcs/my-git-repo
rev: main
dir: my-functions/sub-directory # optional, defaults to the repository root
file: my-func.yml # optional, defaults to `func.yml`
표현식
잡이 실행될 때까지 알 수 없는 값(이전 단계의 출력, 잡 변수, 또는 계산된 값)이 필요할 때 표현식을 사용하세요.
표현식은 ${{ }} 문법을 사용하며 각 function이 실행되기 전에 평가됩니다. 연산자, 데이터 구조, 내장 함수를 포함한 전체 표현식 언어 참조는 Moa 표현식 언어를 참고하세요.
표현식은 다음에서 사용할 수 있습니다.
- 입력 값 (
inputs) - 환경 변수 값 (
env) - function 참조 (
func) - 스크립트 내용 (
script)
사용 가능한 컨텍스트
GitLab Functions를 사용할 때 다음 컨텍스트 변수를 사용하세요. 전체 컨텍스트 참조는 Moa 표현식 언어를 참고하세요.
| 변수 | 유형 | 설명 |
|---|---|---|
env.<name> |
String | function이 실행될 때의 환경. OS, 러너, 그리고 이전에 실행된 단계가 내보낸 환경 변수를 포함합니다. env는 CI/CD 잡 변수를 포함하지 않습니다. |
vars.<name> |
String | 러너에서 전달된 CI/CD 잡 변수. env와 달리 이 변수는 단계 내보내기의 영향을 받지 않습니다. |
inputs.<name> |
Any | 현재 function에 전달된 입력 값. |
steps.<step_name>.outputs.<output_name> |
Any | 현재 run 목록에서 이전에 완료된 단계의 출력 값. |
func_dir |
String | function 정의 파일이 있는 디렉터리의 경로. function과 함께 번들된 파일을 참조하는 데 사용합니다. |
work_dir |
String | 현재 실행의 작업 디렉터리 경로. |
예시
- 이전 단계의 출력 참조:
my-job:
run:
- name: generate_rand
func: registry.gitlab.com/gitlab-org/ci-cd/runner-tools/gitlab-functions-examples/random:1
- name: echo
func: registry.gitlab.com/gitlab-org/ci-cd/runner-tools/gitlab-functions-examples/echo:1
inputs:
message: "The random value is: ${{ steps.generate_rand.outputs.random_value }}"
- 기본값으로 폴백하는 잡 변수 사용:
run:
- name: deploy
func: ./deploy
inputs:
environment: ${{ vars.CI_COMMIT_REF_NAME == "main" && "production" || "staging" }}
환경 변수
환경 변수는 두 가지 방식으로 단계 사이를 이동합니다. env로 설정하거나 function을 통해 내보냅니다. 이 둘은 범위가 다르기 때문에 차이를 이해하는 게 중요합니다.
CI/CD 잡 변수는 환경 변수로는 사용할 수 없습니다. 잡 변수에는 ${{ vars.<name> }}로 접근하세요.
단계에 환경 변수 설정하기
단계에 env 키워드를 사용해 그 단계와 내부적으로 호출하는 function들에 대한 환경 변수를 설정할 수 있습니다. env로 설정된 변수는 환경에 이미 있는 모든 변수에 더해 그 단계에서 사용할 수 있어요. 변수가 이미 존재하면 env로 설정된 값이 우선합니다. 이렇게 설정된 변수는 같은 run 목록의 이후 단계에서는 사용할 수 없습니다.
run:
- name: build
func: ./build
env:
BUILD_TARGET: release # available to build and its child steps only
- name: test
func: ./test # BUILD_TARGET is not available here
env의 키와 값에 표현식을 사용하세요.
내보낸 환경 변수
function이 ${{ export_file }}에 쓰면, 쓴 변수들이 run 목록의 이후 모든 단계로 내보내집니다. Functions는 이 방법으로 나중 단계와 상태를 공유합니다.
내보낸 변수는 표현식에서 env를 통해 사용할 수 있습니다.
run:
- name: setup
func: ./setup # exports INSTALL_PATH during execution
- name: build
func: ./build
inputs:
path: ${{ env.INSTALL_PATH }} # available because setup exported it
우선순위
같은 변수가 여러 곳에 설정되어 있을 때 다음 순서가 적용됩니다. 높은 것에서 낮은 것 순입니다.
- function 정의(
func.yml)의env run목록의 단계에 설정된env- 이전에 실행된 단계가 내보낸 값
- 러너가 설정한 값
- OS 프로세스 환경이 설정한 값
나만의 function 만들기
function을 만들려면 GitLab Function 만들기를 참고하세요.
예시 function은 GitLab Functions 예시를 참고하세요.
문제 해결
HTTPS URL에서 functions 가져오기
tls: failed to verify certificate: x509: certificate signed by unknown authority 같은 오류 메시지는 운영체제가 function을 호스팅하는 서버를 인식하거나 신뢰하지 않는다는 뜻입니다.
흔한 원인은 신뢰할 수 있는 루트 인증서가 설치되지 않은 Docker 이미지입니다. 컨테이너에 인증서를 설치하거나 잡 image에 구워 넣어 이 문제를 해결하세요.
function을 가져오기 전에 script 단계로 의존성을 설치할 수 있습니다.
ubuntu_job:
image: ubuntu:24.04
run:
- name: install_certs
script: apt update && apt install --assume-yes --no-install-recommends ca-certificates
- name: echo_step
func: registry.gitlab.com/user/my_functions/hello_world:1.0.0
더 알아보기
GitLab Functions는 job 안에서 script를 대체하고, Components는 파이프라인 수준에서 재사용된다는 구분이 핵심이에요. 크게 만들 function은 OCI 레지스트리에 버전 태그로 게시하고, ${{ }} 표현식으로 단계 간 데이터 흐름을 명시적으로 이으면 됩니다. 다음으로는 function 생성 문서와 examples, 그리고 Moa 표현식 언어 문서를 함께 보는 걸 추천해요.