GitLab 함수(Function) 만들기
GitLab 함수(Function) 만들기
GitLab 함수(Function)는 함수의 인터페이스와 구현을 정의하는 func.yml 파일이 들어 있는 디렉터리예요. 함수는 로컬에서 실행하거나 OCI 레지스트리에 게시해서 여러 job과 프로젝트에서 재사용할 수 있습니다.
출처: 문서
본문
- 티어(Tier): Free, Premium, Ultimate
- 제공 방식(Offering): GitLab.com, GitLab Self-Managed, GitLab Dedicated
- 상태(Status): Experiment
CI/CD job에서 함수를 사용하는 방법은 GitLab Functions 문서를, 예시 함수는 GitLab Functions 예시 문서를 참고하세요.
함수 구조
함수는 최소한 func.yml 파일과, 구현에 필요한 지원 파일들로 이루어진 디렉터리예요:
my-function/
├── func.yml
└── my-script.sh
func.yml 파일은 ---로 구분된 두 개의 YAML 문서를 담고 있어요. 하나는 함수의 입력·출력을 정의하는 spec이고, 다른 하나는 함수가 무엇을 하는지 설명하는 정의(definition)입니다.
# Document 1: spec
spec:
inputs:
message:
type: string
outputs:
result:
type: string
---
# Document 2: definition
exec:
command: ["${{ func_dir }}/my-script.sh", "${{ inputs.message }}"]
Spec: 입출력 선언하기
spec은 함수의 인터페이스를 설명해요.
입력(Inputs)
각 입력에는 type이 필요해요. default 값이 있는 입력은 선택 사항이고, 기본값이 없는 입력은 호출자가 반드시 제공해야 합니다.
입력 이름은 영숫자와 밑줄만 사용할 수 있고, 숫자로 시작할 수 없어요.
입력은 다음 유형 중 하나여야 해요:
| 유형 | 예시 | 설명 |
|---|---|---|
array |
["a","b"] |
타입이 없는 항목의 목록 |
boolean |
true |
참 또는 거짓 |
number |
56.77 |
64비트 부동소수점 |
string |
"brown cow" |
텍스트 |
struct |
{"k1":"v1","k2":"v2"} |
구조화된 내용 |
예를 들어:
spec:
inputs:
# Required string input
message:
type: string
# Optional input with a default
count:
type: number
default: 1
# Struct input for passing structured data
config:
type: struct
default: {}
출력(Outputs)
출력은 함수가 이후 단계에 반환하는 값을 정의해요. 각 출력에는 type이 필요합니다. default 값이 있는 출력은 선택 사항이고, 함수가 출력 값을 쓰지 않을 때 기본값이 사용됩니다.
출력은 입력과 같은 유형 및 명명 규칙을 사용해요.
예를 들어:
spec:
outputs:
# Required string output
artifact_path:
type: string
# Optional output with a default
compressed:
type: boolean
default: false
런타임에서 함수는 ${{ output_file }}이 가리키는 경로에 출력 값을 써요. 각 줄은 name과 value 필드를 가진 JSON 객체여야 합니다:
echo '{"name":"artifact_path","value":"/dist/app.tar.gz"}' >> "${{ output_file }}"
echo '{"name":"compressed","value":true}' >> "${{ output_file }}"
출력 위임(Delegate outputs)
함수에 여러 단계가 있고 함수의 출력이 특정 단계에서 나오게 하려면, spec에서 outputs: delegate를 사용하고 정의에서 delegate: <step_name>을 사용해요:
spec:
outputs: delegate
---
run:
- name: build
func: ./build
- name: package
func: ./package
delegate: package # use the package step outputs as this function outputs
정의: 함수 구현하기
func.yml의 두 번째 문서는 구현을 설명해요. 함수는 두 가지 방식으로 구현할 수 있습니다.
exec
exec는 단일 명령이나 스크립트를 실행해요. 명령은 셸 없이 OS에 직접 전달되므로 문자열 배열이어야 합니다.
spec:
inputs:
message:
type: string
---
exec:
command: ["./greet", "${{ inputs.message }}"]
작업 디렉터리는 기본적으로 CI_PROJECT_DIR이에요. 바꾸려면 work_dir을 사용하세요. work_dir 키워드는 run: 정의가 아닌 exec 정의에서만 유효합니다.
명령이 func.yml과 같은 디렉터리의 파일을 참조해야 한다면 work_dir을 ${{ func_dir }}로 설정하세요:
exec:
command: ["./build.sh"]
work_dir: "${{ func_dir }}"
명령이 0이 아닌 종료 코드로 끝나면 함수는 실패해요.
run
run은 다른 함수를 순서대로 호출하는 함수에 사용해요.
시퀀스에서 어떤 단계든 실패하면 함수는 실패해요. 실패 이후의 후속 단계는 실행되지 않습니다.
spec:
inputs:
environment:
type: string
outputs:
url:
type: string
---
run:
- name: build
func: ./build
- name: push
func: registry.example.com/my-org/push:1.0.0
inputs:
artifact: ${{ steps.build.outputs.artifact_path }}
- name: deploy
func: ./deploy
inputs:
env: ${{ inputs.environment }}
image: ${{ steps.push.outputs.image_ref }}
outputs:
url: ${{ steps.deploy.outputs.url }}
환경 변수 설정하기
정의에서 env를 사용해 exec 명령이나 run: 시퀀스의 모든 단계에 대한 환경 변수를 설정할 수 있어요. 값에는 표현식을 사용할 수 있습니다:
spec:
---
run:
- name: test
func: ./run-tests
env:
GOFLAGS: "-race"
TARGET_ENV: "${{ inputs.environment }}"
환경 변수 내보내기
환경 변수를 함수 이후에 실행되는 모든 단계에 job이 끝날 때까지 사용할 수 있게 하려면 ${{ export_file }}에 쓰세요. 각 줄은 name과 value 필드를 가진 JSON 객체여야 합니다:
echo '{"name":"INSTALL_PATH","value":"/opt/myapp"}' >> "${{ export_file }}"
환경 변수로 내보낼 수 있는 값은 string, number, boolean뿐이에요.
내보낸 변수가 env: 및 더 넓은 환경과 어떻게 상호작용하는지에 대한 자세한 내용은 환경 변수 문서를 참고하세요.
표현식(Expressions)
표현식은 ${{ }} 문법을 사용하며 함수가 실행되기 직전에 평가돼요. inputs 값, env 값, exec 명령 인자, work_dir에 나타날 수 있습니다.
표현식 문서에 설명된 변수 외에, 함수 정의 안에서 다음 컨텍스트 변수를 사용할 수 있어요:
| 변수 | 설명 |
|---|---|
inputs.<name> |
이 함수에 전달된 이름 있는 입력의 값. |
func_dir |
이 func.yml이 있는 디렉터리의 절대 경로. 번들 파일 참조에 사용. |
output_file |
출력을 쓰기 위한 파일 경로. |
export_file |
환경 변수를 내보내기 위한 파일 경로. |
steps.<step_name>.outputs.<output_name> |
이름 있는 단계의 출력(run: 정의에서만 사용 가능). |
완전한 예시
다음 함수는 파일 경로를 받아 gzip으로 압축하고 압축된 파일 경로를 반환해요.
함수 만들기
디렉터리 구조:
compress/
├── func.yml
└── compress.sh
func.yml:
spec:
inputs:
input_path:
type: string
outputs:
output_path:
type: string
---
exec:
command: ["${{ func_dir }}/compress.sh", "${{ inputs.input_path }}", "${{ output_file }}"]
compress.sh (실행 권한이 있어야 해요):
#!/usr/bin/env sh
set -e
INPUT_PATH="$1"
OUTPUT_FILE="$2"
gzip --keep "$INPUT_PATH"
echo "{\"name\":\"output_path\",\"value\":\"${INPUT_PATH}.gz\"}" >> "$OUTPUT_FILE"
job에서 함수 사용하기
이 함수는 job 환경에 gzip이 있어야 해요. 이 예시는 gzip이 job이 실행되는 인스턴스에 이미 설치되어 있다고 가정합니다. 없다면 script: 단계로 먼저 설치하거나, compress를 호출하기 전에 설치를 처리하는 함수를 호출하면 돼요.
my-job:
run:
- name: compress_artifact
func: ./compress
inputs:
input_path: "dist/app.tar"
- name: list_compressed
script: ls -lh ${{ steps.compress_artifact.outputs.output_path }}
더 많은 함수 예시는 GitLab Functions 예시를 참고하세요.
함수 빌드 및 릴리스
함수는 OCI 이미지로 배포돼요. 단계 러너(step runner)는 함수 이미지를 빌드하고 게시하는 두 가지 내장 함수를 제공합니다.
빌드
builtin://function/oci/build 함수는 프로젝트 디렉터리의 파일들로 멀티 아키텍처 함수 OCI 이미지를 빌드하고 CI_PROJECT_DIR에 function-image.tar로 아카이브해요.
common.files는 모든 플랫폼에서 공유되는 파일을 복사하고, platforms.<os/arch>.files는 그 플랫폼 전용 파일을 복사해요. 두 경우 모두 맵 키는 이미지의 목적지 경로, 값은 CI_PROJECT_DIR 기준 소스 경로입니다.
다음 예시에서 function-image.tar는 linux/amd64와 linux/arm64 두 플랫폼을 지원하는 함수 OCI 이미지예요. 각 플랫폼 이미지는 func.yml, my-script.sh, bin/my-binary 세 파일을 갖고 있어요. 플랫폼 바이너리에 같은 파일명을 사용하면 func.yml을 플랫폼 독립적으로 유지할 수 있습니다.
build_function:
artifacts:
paths:
- function-image.tar
run:
- name: build
func: builtin://function/oci/build
inputs:
version: "1.2.3"
common:
files:
func.yml: func.yml
my-script.sh: my-script.sh
platforms:
linux/amd64:
files:
bin/my-binary: bin/linux-amd64/my-binary
linux/arm64:
files:
bin/my-binary: bin/linux-arm64/my-binary
릴리스
builtin://function/oci/publish 함수는 function/oci/build의 아카이브를 OCI 레지스트리에 게시해요.
publish 함수는 함수 이미지 태그에 시맨틱 버저닝을 사용해요: 1.0.0, 1.1.0, 2.0.0. 함수는 function-image.tar 파일에서 버전을 추출합니다. 필요에 따라 major, major.minor, major.minor.patch 및 latest 태그를 업데이트해요.
릴리스 후보(RC)는 1.2.0-rc1 같은 사전 릴리스 접미사를 사용해요. 릴리스 후보를 게시하면 정확한 major.minor.patch-prerelease 태그만 만들고, major, major.minor, latest 태그는 업데이트하지 않습니다.
publish_function:
needs: [build_function]
run:
- name: publish
func: builtin://function/oci/publish
inputs:
archive: function-image.tar # version is baked into the tar file
to_repository: registry.example.com/my-org/my-function
레지스트리에 인증하기
프라이빗 레지스트리에 게시하려면 function/oci/publish를 실행하기 전에 인증하세요. Docker Auth 함수로 DOCKER_AUTH_CONFIG를 생성·내보내는 단계를 게시 전에 넣어요:
publish_function:
needs: [build_function]
run:
- name: auth
func: registry.gitlab.com/gitlab-org/ci-cd/runner-tools/gitlab-functions-examples/docker-auth:1
inputs:
registry: ${{ vars.CI_REGISTRY }}
username: ${{ vars.CI_REGISTRY_USER }}
password: ${{ vars.CI_REGISTRY_PASSWORD }}
- name: publish
func: builtin://function/oci/publish
inputs:
archive: function-image.tar
to_repository: ${{ vars.CI_REGISTRY_IMAGE }}
docker-auth는 DOCKER_AUTH_CONFIG를 이후 모든 단계로 내보내므로 function/oci/publish가 자동으로 가져다 씁니다.
게시된 후에는 호출자가 레지스트리 URL과 태그로 함수를 참조해요:
run:
- name: run_my_function
func: registry.example.com/my-org/my-function:1.2.3