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 }}이 가리키는 경로에 출력 값을 써요. 각 줄은 namevalue 필드를 가진 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 }}에 쓰세요. 각 줄은 namevalue 필드를 가진 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_DIRfunction-image.tar로 아카이브해요.

common.files는 모든 플랫폼에서 공유되는 파일을 복사하고, platforms.<os/arch>.files는 그 플랫폼 전용 파일을 복사해요. 두 경우 모두 맵 키는 이미지의 목적지 경로, 값은 CI_PROJECT_DIR 기준 소스 경로입니다.

다음 예시에서 function-image.tarlinux/amd64linux/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.patchlatest 태그를 업데이트해요.

릴리스 후보(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-authDOCKER_AUTH_CONFIG를 이후 모든 단계로 내보내므로 function/oci/publish가 자동으로 가져다 씁니다.

게시된 후에는 호출자가 레지스트리 URL과 태그로 함수를 참조해요:

run:
  - name: run_my_function
    func: registry.example.com/my-org/my-function:1.2.3

더 알아보기 (Learn more)