InfoGrab Docs

서비스

요약

CI/CD를 구성할 때, 잡이 실행되는 컨테이너를 만드는 데 사용되는 이미지를 지정합니다. services 키워드를 사용하여 추가 이미지를 지정할 수 있습니다. 서비스 이미지는 어떤 애플리케이션이든 실행할 수 있지만, 가장 일반적인 사용 사례는 다음과 같이 데이터베이스 컨테이너를 실행하는 것입니다:

CI/CD를 구성할 때, 잡이 실행되는 컨테이너를 만드는 데 사용되는 이미지를 지정합니다. 이 이미지를 지정하려면 image 키워드를 사용합니다.

services 키워드를 사용하여 추가 이미지를 지정할 수 있습니다. 이 추가 이미지는 첫 번째 컨테이너에서 사용할 수 있는 또 다른 컨테이너를 만드는 데 사용됩니다. 두 컨테이너는 서로 접근할 수 있으며 잡 실행 중에 통신할 수 있습니다.

서비스 이미지는 어떤 애플리케이션이든 실행할 수 있지만, 가장 일반적인 사용 사례는 다음과 같이 데이터베이스 컨테이너를 실행하는 것입니다:

Note

서비스 간 네트워킹을 활성화하려면 FF_NETWORK_PER_BUILDtrue로 설정하세요. 이 플래그 없이는 서비스가 제대로 작동하지 않을 수 있습니다. 자세한 내용은 기능 플래그를 참조하세요.

저장소에 데이터베이스를 사용하는 콘텐츠 관리 시스템을 개발하고 있다고 생각해보세요. 애플리케이션의 모든 기능을 테스트하려면 데이터베이스가 필요합니다. 이런 시나리오에서 데이터베이스 컨테이너를 서비스 이미지로 실행하는 것이 좋은 사용 사례입니다.

프로젝트를 빌드할 때마다 mysql을 설치하는 대신 기존 이미지를 사용하여 추가 컨테이너로 실행하세요.

데이터베이스 서비스에만 제한되지 않습니다. .gitlab-ci.yml에 필요한 만큼 서비스를 추가하거나 config.toml을 수동으로 수정할 수 있습니다. Docker Hub 또는 개인 컨테이너 레지스트리에서 찾을 수 있는 모든 이미지를 서비스로 사용할 수 있습니다.

개인 이미지 사용에 대한 자세한 내용은 개인 컨테이너 레지스트리에서 이미지 접근을 참조하세요.

서비스는 CI 컨테이너와 동일한 DNS 서버, 검색 도메인 및 추가 호스트를 상속합니다.

서비스가 잡에 연결되는 방법#

컨테이너 연결 방법을 더 잘 이해하려면 컨테이너 함께 연결하기를 읽어보세요.

애플리케이션에 mysql을 서비스로 추가하면 해당 이미지가 잡 컨테이너에 연결된 컨테이너를 만드는 데 사용됩니다.

MySQL의 서비스 컨테이너는 호스트 이름 mysql로 접근할 수 있습니다. 데이터베이스 서비스에 접근하려면 소켓 또는 localhost 대신 mysql이라는 호스트에 연결하세요. 서비스 접근에서 더 읽어보세요.

서비스 헬스 체크 방법#

서비스는 네트워크 접근 가능한 추가 기능을 제공하도록 설계되었습니다. MySQL 또는 Redis 같은 데이터베이스일 수도 있고, Docker-in-Docker(DinD)를 사용할 수 있게 해주는 docker:dind일 수도 있습니다. CI/CD 잡 진행에 필요한 거의 모든 것이 될 수 있으며, 네트워크를 통해 접근합니다.

이 작업이 제대로 동작하도록 러너는:

  1. 기본적으로 컨테이너에서 노출되는 포트를 확인합니다.
  2. 이러한 포트에 접근할 수 있을 때까지 대기하는 특수 컨테이너를 시작합니다.

검사의 두 번째 단계가 실패하면 경고를 출력합니다: *** WARNING: Service XYZ probably didn't start properly. 이 문제는 다음과 같은 이유로 발생할 수 있습니다:

  • 서비스에 열린 포트가 없는 경우.
  • 서비스가 타임아웃 전에 올바르게 시작되지 않아 포트가 응답하지 않는 경우.

대부분의 경우 잡에 영향을 미치지만, 경고가 출력되어도 잡이 성공하는 경우도 있습니다. 예를 들어:

  • 경고가 발생한 직후에 서비스가 시작되었고 잡이 처음부터 연결된 서비스를 사용하지 않는 경우. 이 경우 잡이 서비스에 접근해야 할 때 이미 연결을 기다리고 있을 수 있습니다.
  • 서비스 컨테이너가 네트워크 서비스를 제공하지 않지만 잡 디렉토리(모든 서비스는 잡 디렉토리를 /builds 아래 볼륨으로 마운트합니다)로 무언가를 수행하는 경우. 이 경우 서비스는 자신의 역할을 수행하고, 잡이 연결을 시도하지 않기 때문에 실패하지 않습니다.

서비스가 성공적으로 시작되면 before_script가 실행되기 전에 시작됩니다. 따라서 서비스를 조회하는 before_script를 작성할 수 있습니다.

서비스는 잡이 실패하더라도 잡이 끝날 때 중지됩니다.

서비스 이미지에서 제공하는 소프트웨어 사용#

service를 지정하면 네트워크 접근 가능한 서비스가 제공됩니다. 데이터베이스가 이러한 서비스의 가장 간단한 예시입니다.

서비스 기능은 정의된 services 이미지의 소프트웨어를 잡 컨테이너에 추가하지 않습니다.

예를 들어, 잡에 다음과 같은 services가 정의되어 있으면, php, node 또는 go 명령어를 스크립트에서 사용할 수 없으며 잡이 실패합니다:

job:
  services:
    - php:8.4
    - node:latest
    - golang:1.25
  image: alpine:3.23
  script:
    - php -v
    - node -v
    - go version

스크립트에서 php, node, go를 사용할 수 있어야 한다면:

  • 필요한 모든 도구가 포함된 기존 Docker 이미지를 선택하거나,
  • 필요한 모든 도구가 포함된 자체 Docker 이미지를 만들어 잡 구성에서 사용하세요.

.gitlab-ci.yml 파일에서 services 정의#

잡별로 다른 이미지와 서비스를 정의할 수도 있습니다:

default:
  before_script:
    - bundle install

test:4.0:
  image: ruby:4.0
  services:
    - postgres:18
  script:
    - bundle exec rake spec

test:3.4:
  image: ruby:3.4
  services:
    - postgres:17
  script:
    - bundle exec rake spec

또는 imageservices에 대한 일부 확장 설정 옵션을 전달할 수 있습니다:

default:
  image:
    name: ruby:4.0
    entrypoint: ["/bin/bash"]
  services:
    - name: my-postgres:18
      alias: db,postgres,pg
      entrypoint: ["/usr/local/bin/db-postgres"]
      command: ["start"]
  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

서비스 접근 {#accessing-the-services}#

서비스 별칭을 지정하지 않으면, 빌드 컨테이너에서 두 개의 호스트 이름으로 접근할 수 있습니다:

  • namespace-projectname
  • namespace__projectname

밑줄이 있는 호스트 이름은 RFC에서 유효하지 않으며 서드파티 애플리케이션에서 문제를 일으킬 수 있습니다.

서비스 호스트 이름의 기본 별칭은 다음 규칙에 따라 이미지 이름에서 생성됩니다:

  • 콜론(:) 이후의 모든 것이 제거됩니다.
  • 슬래시(/)가 이중 밑줄(__)로 대체되어 기본 별칭이 생성됩니다.
  • 슬래시(/)가 단일 대시(-)로 대체되어 보조 별칭이 생성됩니다.

기본 동작을 재정의하려면 하나 이상의 서비스 별칭을 지정할 수 있습니다.

서비스 연결#

외부 API가 자체 데이터베이스와 통신해야 하는 엔드-투-엔드 테스트 같은 복잡한 잡에서 상호 의존적인 서비스를 사용할 수 있습니다.

예를 들어, API를 사용하는 프론트엔드 애플리케이션의 엔드-투-엔드 테스트에서, API가 데이터베이스를 필요로 하는 경우:

end-to-end-tests:
  image: node:latest
  services:
    - name: selenium/standalone-firefox:${FIREFOX_VERSION}
      alias: firefox
    - name: registry.gitlab.com/organization/private-api:latest
      alias: backend-api
    - name: postgres:18
      alias: db postgres db
  variables:
    FF_NETWORK_PER_BUILD: 1
    POSTGRES_PASSWORD: supersecretpassword
    BACKEND_POSTGRES_HOST: postgres
  script:
    - npm install
    - npm test

이 솔루션이 작동하려면 각 잡에 대해 새 네트워크를 만드는 네트워킹 모드를 사용해야 합니다.

CI/CD 변수를 서비스에 전달#

.gitlab-ci.yml 파일에서 Docker imagesservices를 직접 조정하기 위해 커스텀 CI/CD 변수를 전달할 수도 있습니다. 자세한 내용은 .gitlab-ci.yml 정의 변수를 참조하세요.

# 다음 변수들은 Postgres 컨테이너와 Ruby 컨테이너 모두에
# 자동으로 전달되며 각각에서 사용할 수 있습니다.
variables:
  HTTPS_PROXY: "https://10.1.1.1:8090"
  HTTP_PROXY: "https://10.1.1.1:8090"
  POSTGRES_DB: "my_custom_db"
  POSTGRES_USER: "postgres"
  POSTGRES_PASSWORD: "example"
  PGDATA: "/var/lib/postgresql/data"
  POSTGRES_INITDB_ARGS: "--encoding=UTF8 --data-checksums"

default:
  services:
    - name: postgres:18
      alias: db
      entrypoint: ["docker-entrypoint.sh"]
      command: ["postgres"]
  image:
    name: ruby:4.0
    entrypoint: ["/bin/bash"]
  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

services에 사용 가능한 설정 {#available-settings-for-services}#

services: 하위 키에 대한 자세한 내용은 CI/CD YAML 참조를 참조하세요.

같은 이미지에서 여러 서비스 시작#

새로운 확장 Docker 설정 옵션 이전에는 다음 설정이 제대로 작동하지 않았습니다:

services:
  - mysql:latest
  - mysql:latest

러너는 각각 mysql:latest 이미지를 사용하는 두 개의 컨테이너를 시작합니다. 그러나 둘 다 기본 호스트 이름 명명 규칙에 따라 mysql 별칭으로 잡 컨테이너에 추가됩니다. 이로 인해 서비스 중 하나에 접근할 수 없게 됩니다.

새로운 확장 Docker 설정 옵션 이후에는 앞의 예시가 다음과 같이 됩니다:

services:
  - name: mysql:latest
    alias: mysql-1
  - name: mysql:latest
    alias: mysql-2

러너는 여전히 mysql:latest 이미지를 사용하는 두 개의 컨테이너를 시작하지만, 이제 각각 .gitlab-ci.yml 파일에 구성된 별칭으로도 접근할 수 있습니다.

서비스에 대한 명령 설정#

SQL 데이터베이스가 있는 super/sql:latest 이미지가 있다고 가정해보겠습니다. 이를 잡의 서비스로 사용하려 합니다. 이 이미지가 컨테이너를 시작할 때 데이터베이스 프로세스를 시작하지 않는다고 가정해봅시다. 사용자는 수동으로 /usr/bin/super-sql run을 데이터베이스 시작 명령으로 사용해야 합니다.

새로운 확장 Docker 설정 옵션 이전에는 다음이 필요했습니다:

  • super/sql:latest 이미지를 기반으로 자체 이미지 생성.

  • 기본 명령 추가.

  • 잡 구성에서 이미지 사용.

    • my-super-sql:latest 이미지의 Dockerfile:

      FROM super/sql:latest
      CMD ["/usr/bin/super-sql", "run"]
      
    • .gitlab-ci.yml의 잡에서:

      services:
        - my-super-sql:latest
      

새로운 확장 Docker 설정 옵션 이후에는 .gitlab-ci.yml 파일에서 command를 설정할 수 있습니다:

services:
  - name: super/sql:latest
    command: ["/usr/bin/super-sql", "run"]

command 구문은 Dockerfile CMD와 유사합니다.

Kubernetes 실행기에서 서비스 컨테이너 이름으로 별칭 사용#

히스토리
  • GitLab 및 GitLab Runner 17.9에서 도입.

Kubernetes 실행기에서 서비스 컨테이너 이름으로 서비스 별칭을 사용할 수 있습니다. GitLab Runner는 다음 조건에 따라 컨테이너 이름을 지정합니다:

  • 서비스에 여러 별칭이 설정된 경우, 다음 조건을 충족하는 첫 번째 별칭으로 서비스 컨테이너가 명명됩니다:
  • 별칭을 서비스 컨테이너 이름으로 사용할 수 없는 경우, GitLab Runner는 svc-i 패턴으로 대체합니다.

다음 예시들은 Kubernetes 실행기에서 서비스 컨테이너 이름 지정에 별칭이 어떻게 사용되는지 보여줍니다.

서비스당 하나의 별칭#

다음 .gitlab-ci.yml 파일에서:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: alpine:latest
      alias: alpine
    - name: mysql:latest
      alias: mysql

시스템은 표준 buildhelper 컨테이너 외에 alpinemysql이라는 컨테이너가 있는 잡 Pod를 생성합니다. 다음 조건을 충족하기 때문에 이 별칭이 사용됩니다:

하지만 다음 .gitlab-ci.yml에서는:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: mysql:lts
      alias: mysql
    - name: mysql:latest
      alias: mysql

시스템은 buildhelper 컨테이너 외에 mysqlsvc-0이라는 두 개의 추가 컨테이너를 생성합니다. mysql 컨테이너는 mysql:lts 이미지에 해당하고, svc-0 컨테이너는 mysql:latest 이미지에 해당합니다.

서비스당 여러 별칭#

다음 .gitlab-ci.yml 파일에서:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: alpine:latest
      alias: alpine,alpine-latest
    - name: alpine:edge
      alias: alpine,alpine-edge,alpine-latest

시스템은 buildhelper 컨테이너 외에 네 개의 추가 컨테이너를 생성합니다:

  • alpinealpine:latest 이미지가 있는 컨테이너에 해당합니다.
  • alpine-edgealpine:edge 이미지가 있는 컨테이너에 해당합니다(alpine 별칭이 이전 컨테이너에서 이미 사용됨).

이 예시에서 alpine-latest 별칭은 사용되지 않습니다.

하지만 다음 .gitlab-ci.yml에서:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: alpine:latest
      alias: alpine,alpine-edge
    - name: alpine:edge
      alias: alpine,alpine-edge
    - name: alpine:3.21
      alias: alpine,alpine-edge

buildhelper 컨테이너 외에 여섯 개의 추가 컨테이너가 생성됩니다.

  • alpinealpine:latest 이미지가 있는 컨테이너를 참조해야 합니다.

  • alpine-edgealpine:edge 이미지가 있는 컨테이너를 참조해야 합니다(alpine 별칭이 이전 컨테이너에 이미 사용됨).

  • svc-0alpine:3.21 이미지가 있는 컨테이너를 참조해야 합니다(alpinealpine-edge 별칭이 이전 컨테이너들에 이미 사용됨).

    • svc-i 패턴의 i는 제공된 목록에서 서비스의 위치를 나타내지 않습니다. 대신 사용 가능한 별칭을 찾지 못했을 때 서비스의 위치를 나타냅니다.

    • 유효하지 않은 별칭이 제공되면(Kubernetes 제약을 충족하지 않으면), 잡은 다음 오류와 함께 실패합니다(별칭 alpine_edge의 예):

      ERROR: Job failed (system failure): prepare environment: setting up build pod: provided host alias
      alpine_edge for service alpine:edge is invalid DNS. a lowercase RFC 1123 subdomain must consist of lower
      case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g.
      'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*').
      Check https://docs.gitlab.com/runner/shells/index/#shell-profile-loading for more information.
      

docker run(Docker-in-Docker)과 함께 services 사용#

docker run으로 시작된 컨테이너도 GitLab에서 제공하는 서비스에 연결할 수 있습니다.

서비스 부팅이 비용이 많이 들거나 시간이 걸리는 경우, 테스트된 서비스를 한 번만 부팅하면서 다른 클라이언트 환경에서 테스트를 실행할 수 있습니다.

access-service:
  stage: build
  image: docker:20.10.16
  services:
    - docker:dind                    # docker run에 필요
    - traefik/whoami:latest
  variables:
    FF_NETWORK_PER_BUILD: "true"     # 컨테이너 간 네트워킹 활성화
  script: |
    docker run --rm --name curl \
      --volume  "$(pwd)":"$(pwd)"    \
      --workdir "$(pwd)"             \
      --network=host                 \
      curlimages/curl:latest curl "http://traefik-whoami"

이 솔루션이 작동하려면 다음이 필요합니다:

Docker 통합 방법#

다음은 잡 실행 중 Docker가 수행하는 단계의 상위 수준 개요입니다.

  1. 서비스 컨테이너 생성: mysql, postgresql, mongodb, redis.
  2. config.toml과 빌드 이미지의 Dockerfile에 정의된 모든 볼륨을 저장하기 위한 캐시 컨테이너 생성(ruby:4.0 등 앞의 예시에서처럼).
  3. 빌드 컨테이너 생성 및 서비스 컨테이너를 빌드 컨테이너에 연결.
  4. 빌드 컨테이너를 시작하고 컨테이너에 잡 스크립트를 전송.
  5. 잡 스크립트 실행.
  6. /builds/group-name/project-name/에 코드 체크아웃.
  7. .gitlab-ci.yml에 정의된 단계 실행.
  8. 빌드 스크립트의 종료 상태 확인.
  9. 빌드 컨테이너와 생성된 모든 서비스 컨테이너 제거.

서비스 컨테이너 로그 캡처#

서비스 컨테이너에서 실행 중인 애플리케이션이 생성하는 로그는 이후 검토 및 디버깅을 위해 캡처할 수 있습니다. 서비스 컨테이너가 성공적으로 시작되었지만 예기치 않은 동작으로 잡 실패를 유발할 때 서비스 컨테이너 로그를 확인하세요. 로그는 컨테이너의 서비스 누락 또는 잘못된 구성을 나타낼 수 있습니다.

CI_DEBUG_SERVICES는 서비스 컨테이너 로그 캡처에는 스토리지와 성능에 영향이 있으므로 서비스 컨테이너를 적극적으로 디버깅할 때만 활성화해야 합니다.

Warning

CI_DEBUG_SERVICES를 활성화하면 마스크된 변수가 노출될 수 있습니다. CI_DEBUG_SERVICES가 활성화되면 서비스 컨테이너 로그와 CI 잡의 로그가 잡의 추적 로그에 동시에 스트리밍됩니다. 따라서 서비스 컨테이너 로그가 잡의 마스크된 로그에 삽입될 수 있습니다. 이렇게 되면 변수 마스킹 메커니즘이 무력화되어 마스크된 변수가 노출될 수 있습니다.

서비스 로깅을 활성화하려면 프로젝트의 .gitlab-ci.yml 파일에 CI_DEBUG_SERVICES 변수를 추가하세요:

variables:
  CI_DEBUG_SERVICES: "true"

허용되는 값:

  • 활성화: TRUE, true, True
  • 비활성화: FALSE, false, False

그 외 다른 값은 오류 메시지를 발생시키고 기능을 효과적으로 비활성화합니다.

활성화되면 모든 서비스 컨테이너의 로그가 캡처되어 다른 로그와 함께 잡의 추적 로그에 동시에 스트리밍됩니다. 각 컨테이너의 로그는 컨테이너 별칭으로 접두사가 붙고 다른 색상으로 표시됩니다.

Note

잡 실패를 진단하려면 로그를 캡처할 서비스 컨테이너의 로깅 수준을 조정할 수 있습니다. 기본 로깅 수준은 충분한 문제 해결 정보를 제공하지 않을 수 있습니다.

CI/CD 변수 마스크 참조

로컬에서 잡 디버그#

다음 명령어는 루트 권한 없이 실행됩니다. 사용자 계정으로 Docker 명령어를 실행할 수 있는지 확인하세요.

먼저 build_script라는 파일을 만들어 시작하세요:

cat < build_script
git clone https://gitlab.com/gitlab-org/gitlab-runner.git /builds/gitlab-org/gitlab-runner
cd /builds/gitlab-org/gitlab-runner
make runner-bin-host
EOF

이 예시는 Makefile이 포함된 GitLab Runner 저장소를 사용하므로 make 실행 시 Makefile에 정의된 타겟이 실행됩니다. make runner-bin-host 대신 프로젝트에 맞는 명령어를 실행할 수 있습니다.

그런 다음 서비스 컨테이너를 만드세요:

docker run -d --name service-redis redis:latest

이전 명령어는 최신 Redis 이미지를 사용하여 service-redis라는 서비스 컨테이너를 만듭니다. 서비스 컨테이너는 백그라운드에서 실행됩니다(-d).

마지막으로 앞서 만든 build_script 파일을 실행하여 빌드 컨테이너를 만드세요:

docker run --name build -i --link=service-redis:redis golang:latest /bin/bash < build_script

이전 명령어는 golang:latest 이미지에서 생성되고 하나의 서비스가 연결된 build라는 컨테이너를 만듭니다. build_scriptstdin을 통해 bash 인터프리터에 파이프되어 build 컨테이너에서 build_script를 실행합니다.

테스트 완료 후 컨테이너를 제거하려면 다음 명령어를 사용하세요:

docker rm -f -v build service-redis

이 명령어는 build 컨테이너, 서비스 컨테이너, 컨테이너 생성 시 생성된 모든 볼륨(-v)을 강제로(-f) 제거합니다.

서비스 컨테이너 사용 시 보안#

Docker 권한 모드는 서비스에 적용됩니다. 즉, 서비스 이미지 컨테이너가 호스트 시스템에 접근할 수 있습니다. 신뢰할 수 있는 소스의 컨테이너 이미지만 사용해야 합니다.

공유 /builds 디렉토리#

빌드 디렉토리는 /builds 아래에 볼륨으로 마운트되며 잡과 서비스 간에 공유됩니다. 잡은 서비스가 실행된 후 /builds/$CI_PROJECT_PATH에 프로젝트를 체크아웃합니다. 서비스가 프로젝트 파일에 접근하거나 아티팩트를 저장해야 할 수 있습니다. 그렇다면 디렉토리가 존재하고 $CI_COMMIT_SHA가 체크아웃될 때까지 기다리세요. 잡이 체크아웃 프로세스를 완료하기 전에 변경된 내용은 체크아웃 프로세스에 의해 제거됩니다.

서비스는 잡 디렉토리가 채워지고 처리 준비가 되었을 때를 감지해야 합니다. 예를 들어 특정 파일이 사용 가능해질 때까지 기다리세요.

시작 즉시 작동하기 시작하는 서비스는 잡 데이터가 아직 사용 가능하지 않을 수 있으므로 실패할 가능성이 높습니다. 예를 들어, 컨테이너는 DinD 서비스에 대한 네트워크 연결을 위해 docker build 명령어를 사용합니다. 서비스는 API에 컨테이너 이미지 빌드를 시작하도록 지시합니다. Docker Engine은 Dockerfile에서 참조하는 파일에 접근할 수 있어야 합니다. 따라서 서비스에서 CI_PROJECT_DIR에 접근해야 합니다. 그러나 Docker Engine은 잡에서 docker build 명령어가 호출될 때까지 이에 접근하려고 시도하지 않습니다. 이 시점에서 /builds 디렉토리에는 이미 데이터가 채워져 있습니다. 시작 직후 CI_PROJECT_DIR에 쓰려고 하는 서비스는 No such file or directory 오류로 실패할 수 있습니다.

잡 데이터와 상호작용하는 서비스가 잡 자체에 의해 제어되지 않는 시나리오에서는 Docker 실행기 워크플로우를 고려해보세요.

서비스

Tier: Free, Premium, Ultimate
Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
원문 보기
요약

CI/CD를 구성할 때, 잡이 실행되는 컨테이너를 만드는 데 사용되는 이미지를 지정합니다. services 키워드를 사용하여 추가 이미지를 지정할 수 있습니다. 서비스 이미지는 어떤 애플리케이션이든 실행할 수 있지만, 가장 일반적인 사용 사례는 다음과 같이 데이터베이스 컨테이너를 실행하는 것입니다:

CI/CD를 구성할 때, 잡이 실행되는 컨테이너를 만드는 데 사용되는 이미지를 지정합니다. 이 이미지를 지정하려면 image 키워드를 사용합니다.

services 키워드를 사용하여 추가 이미지를 지정할 수 있습니다. 이 추가 이미지는 첫 번째 컨테이너에서 사용할 수 있는 또 다른 컨테이너를 만드는 데 사용됩니다. 두 컨테이너는 서로 접근할 수 있으며 잡 실행 중에 통신할 수 있습니다.

서비스 이미지는 어떤 애플리케이션이든 실행할 수 있지만, 가장 일반적인 사용 사례는 다음과 같이 데이터베이스 컨테이너를 실행하는 것입니다:

Note

서비스 간 네트워킹을 활성화하려면 FF_NETWORK_PER_BUILDtrue로 설정하세요. 이 플래그 없이는 서비스가 제대로 작동하지 않을 수 있습니다. 자세한 내용은 기능 플래그를 참조하세요.

저장소에 데이터베이스를 사용하는 콘텐츠 관리 시스템을 개발하고 있다고 생각해보세요. 애플리케이션의 모든 기능을 테스트하려면 데이터베이스가 필요합니다. 이런 시나리오에서 데이터베이스 컨테이너를 서비스 이미지로 실행하는 것이 좋은 사용 사례입니다.

프로젝트를 빌드할 때마다 mysql을 설치하는 대신 기존 이미지를 사용하여 추가 컨테이너로 실행하세요.

데이터베이스 서비스에만 제한되지 않습니다. .gitlab-ci.yml에 필요한 만큼 서비스를 추가하거나 config.toml을 수동으로 수정할 수 있습니다. Docker Hub 또는 개인 컨테이너 레지스트리에서 찾을 수 있는 모든 이미지를 서비스로 사용할 수 있습니다.

개인 이미지 사용에 대한 자세한 내용은 개인 컨테이너 레지스트리에서 이미지 접근을 참조하세요.

서비스는 CI 컨테이너와 동일한 DNS 서버, 검색 도메인 및 추가 호스트를 상속합니다.

서비스가 잡에 연결되는 방법#

컨테이너 연결 방법을 더 잘 이해하려면 컨테이너 함께 연결하기를 읽어보세요.

애플리케이션에 mysql을 서비스로 추가하면 해당 이미지가 잡 컨테이너에 연결된 컨테이너를 만드는 데 사용됩니다.

MySQL의 서비스 컨테이너는 호스트 이름 mysql로 접근할 수 있습니다. 데이터베이스 서비스에 접근하려면 소켓 또는 localhost 대신 mysql이라는 호스트에 연결하세요. 서비스 접근에서 더 읽어보세요.

서비스 헬스 체크 방법#

서비스는 네트워크 접근 가능한 추가 기능을 제공하도록 설계되었습니다. MySQL 또는 Redis 같은 데이터베이스일 수도 있고, Docker-in-Docker(DinD)를 사용할 수 있게 해주는 docker:dind일 수도 있습니다. CI/CD 잡 진행에 필요한 거의 모든 것이 될 수 있으며, 네트워크를 통해 접근합니다.

이 작업이 제대로 동작하도록 러너는:

  1. 기본적으로 컨테이너에서 노출되는 포트를 확인합니다.
  2. 이러한 포트에 접근할 수 있을 때까지 대기하는 특수 컨테이너를 시작합니다.

검사의 두 번째 단계가 실패하면 경고를 출력합니다: *** WARNING: Service XYZ probably didn't start properly. 이 문제는 다음과 같은 이유로 발생할 수 있습니다:

  • 서비스에 열린 포트가 없는 경우.
  • 서비스가 타임아웃 전에 올바르게 시작되지 않아 포트가 응답하지 않는 경우.

대부분의 경우 잡에 영향을 미치지만, 경고가 출력되어도 잡이 성공하는 경우도 있습니다. 예를 들어:

  • 경고가 발생한 직후에 서비스가 시작되었고 잡이 처음부터 연결된 서비스를 사용하지 않는 경우. 이 경우 잡이 서비스에 접근해야 할 때 이미 연결을 기다리고 있을 수 있습니다.
  • 서비스 컨테이너가 네트워크 서비스를 제공하지 않지만 잡 디렉토리(모든 서비스는 잡 디렉토리를 /builds 아래 볼륨으로 마운트합니다)로 무언가를 수행하는 경우. 이 경우 서비스는 자신의 역할을 수행하고, 잡이 연결을 시도하지 않기 때문에 실패하지 않습니다.

서비스가 성공적으로 시작되면 before_script가 실행되기 전에 시작됩니다. 따라서 서비스를 조회하는 before_script를 작성할 수 있습니다.

서비스는 잡이 실패하더라도 잡이 끝날 때 중지됩니다.

서비스 이미지에서 제공하는 소프트웨어 사용#

service를 지정하면 네트워크 접근 가능한 서비스가 제공됩니다. 데이터베이스가 이러한 서비스의 가장 간단한 예시입니다.

서비스 기능은 정의된 services 이미지의 소프트웨어를 잡 컨테이너에 추가하지 않습니다.

예를 들어, 잡에 다음과 같은 services가 정의되어 있으면, php, node 또는 go 명령어를 스크립트에서 사용할 수 없으며 잡이 실패합니다:

job:
  services:
    - php:8.4
    - node:latest
    - golang:1.25
  image: alpine:3.23
  script:
    - php -v
    - node -v
    - go version

스크립트에서 php, node, go를 사용할 수 있어야 한다면:

  • 필요한 모든 도구가 포함된 기존 Docker 이미지를 선택하거나,
  • 필요한 모든 도구가 포함된 자체 Docker 이미지를 만들어 잡 구성에서 사용하세요.

.gitlab-ci.yml 파일에서 services 정의#

잡별로 다른 이미지와 서비스를 정의할 수도 있습니다:

default:
  before_script:
    - bundle install

test:4.0:
  image: ruby:4.0
  services:
    - postgres:18
  script:
    - bundle exec rake spec

test:3.4:
  image: ruby:3.4
  services:
    - postgres:17
  script:
    - bundle exec rake spec

또는 imageservices에 대한 일부 확장 설정 옵션을 전달할 수 있습니다:

default:
  image:
    name: ruby:4.0
    entrypoint: ["/bin/bash"]
  services:
    - name: my-postgres:18
      alias: db,postgres,pg
      entrypoint: ["/usr/local/bin/db-postgres"]
      command: ["start"]
  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

서비스 접근 {#accessing-the-services}#

서비스 별칭을 지정하지 않으면, 빌드 컨테이너에서 두 개의 호스트 이름으로 접근할 수 있습니다:

  • namespace-projectname
  • namespace__projectname

밑줄이 있는 호스트 이름은 RFC에서 유효하지 않으며 서드파티 애플리케이션에서 문제를 일으킬 수 있습니다.

서비스 호스트 이름의 기본 별칭은 다음 규칙에 따라 이미지 이름에서 생성됩니다:

  • 콜론(:) 이후의 모든 것이 제거됩니다.
  • 슬래시(/)가 이중 밑줄(__)로 대체되어 기본 별칭이 생성됩니다.
  • 슬래시(/)가 단일 대시(-)로 대체되어 보조 별칭이 생성됩니다.

기본 동작을 재정의하려면 하나 이상의 서비스 별칭을 지정할 수 있습니다.

서비스 연결#

외부 API가 자체 데이터베이스와 통신해야 하는 엔드-투-엔드 테스트 같은 복잡한 잡에서 상호 의존적인 서비스를 사용할 수 있습니다.

예를 들어, API를 사용하는 프론트엔드 애플리케이션의 엔드-투-엔드 테스트에서, API가 데이터베이스를 필요로 하는 경우:

end-to-end-tests:
  image: node:latest
  services:
    - name: selenium/standalone-firefox:${FIREFOX_VERSION}
      alias: firefox
    - name: registry.gitlab.com/organization/private-api:latest
      alias: backend-api
    - name: postgres:18
      alias: db postgres db
  variables:
    FF_NETWORK_PER_BUILD: 1
    POSTGRES_PASSWORD: supersecretpassword
    BACKEND_POSTGRES_HOST: postgres
  script:
    - npm install
    - npm test

이 솔루션이 작동하려면 각 잡에 대해 새 네트워크를 만드는 네트워킹 모드를 사용해야 합니다.

CI/CD 변수를 서비스에 전달#

.gitlab-ci.yml 파일에서 Docker imagesservices를 직접 조정하기 위해 커스텀 CI/CD 변수를 전달할 수도 있습니다. 자세한 내용은 .gitlab-ci.yml 정의 변수를 참조하세요.

# 다음 변수들은 Postgres 컨테이너와 Ruby 컨테이너 모두에
# 자동으로 전달되며 각각에서 사용할 수 있습니다.
variables:
  HTTPS_PROXY: "https://10.1.1.1:8090"
  HTTP_PROXY: "https://10.1.1.1:8090"
  POSTGRES_DB: "my_custom_db"
  POSTGRES_USER: "postgres"
  POSTGRES_PASSWORD: "example"
  PGDATA: "/var/lib/postgresql/data"
  POSTGRES_INITDB_ARGS: "--encoding=UTF8 --data-checksums"

default:
  services:
    - name: postgres:18
      alias: db
      entrypoint: ["docker-entrypoint.sh"]
      command: ["postgres"]
  image:
    name: ruby:4.0
    entrypoint: ["/bin/bash"]
  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

services에 사용 가능한 설정 {#available-settings-for-services}#

services: 하위 키에 대한 자세한 내용은 CI/CD YAML 참조를 참조하세요.

같은 이미지에서 여러 서비스 시작#

새로운 확장 Docker 설정 옵션 이전에는 다음 설정이 제대로 작동하지 않았습니다:

services:
  - mysql:latest
  - mysql:latest

러너는 각각 mysql:latest 이미지를 사용하는 두 개의 컨테이너를 시작합니다. 그러나 둘 다 기본 호스트 이름 명명 규칙에 따라 mysql 별칭으로 잡 컨테이너에 추가됩니다. 이로 인해 서비스 중 하나에 접근할 수 없게 됩니다.

새로운 확장 Docker 설정 옵션 이후에는 앞의 예시가 다음과 같이 됩니다:

services:
  - name: mysql:latest
    alias: mysql-1
  - name: mysql:latest
    alias: mysql-2

러너는 여전히 mysql:latest 이미지를 사용하는 두 개의 컨테이너를 시작하지만, 이제 각각 .gitlab-ci.yml 파일에 구성된 별칭으로도 접근할 수 있습니다.

서비스에 대한 명령 설정#

SQL 데이터베이스가 있는 super/sql:latest 이미지가 있다고 가정해보겠습니다. 이를 잡의 서비스로 사용하려 합니다. 이 이미지가 컨테이너를 시작할 때 데이터베이스 프로세스를 시작하지 않는다고 가정해봅시다. 사용자는 수동으로 /usr/bin/super-sql run을 데이터베이스 시작 명령으로 사용해야 합니다.

새로운 확장 Docker 설정 옵션 이전에는 다음이 필요했습니다:

  • super/sql:latest 이미지를 기반으로 자체 이미지 생성.

  • 기본 명령 추가.

  • 잡 구성에서 이미지 사용.

    • my-super-sql:latest 이미지의 Dockerfile:

      FROM super/sql:latest
      CMD ["/usr/bin/super-sql", "run"]
      
    • .gitlab-ci.yml의 잡에서:

      services:
        - my-super-sql:latest
      

새로운 확장 Docker 설정 옵션 이후에는 .gitlab-ci.yml 파일에서 command를 설정할 수 있습니다:

services:
  - name: super/sql:latest
    command: ["/usr/bin/super-sql", "run"]

command 구문은 Dockerfile CMD와 유사합니다.

Kubernetes 실행기에서 서비스 컨테이너 이름으로 별칭 사용#

히스토리
  • GitLab 및 GitLab Runner 17.9에서 도입.

Kubernetes 실행기에서 서비스 컨테이너 이름으로 서비스 별칭을 사용할 수 있습니다. GitLab Runner는 다음 조건에 따라 컨테이너 이름을 지정합니다:

  • 서비스에 여러 별칭이 설정된 경우, 다음 조건을 충족하는 첫 번째 별칭으로 서비스 컨테이너가 명명됩니다:
  • 별칭을 서비스 컨테이너 이름으로 사용할 수 없는 경우, GitLab Runner는 svc-i 패턴으로 대체합니다.

다음 예시들은 Kubernetes 실행기에서 서비스 컨테이너 이름 지정에 별칭이 어떻게 사용되는지 보여줍니다.

서비스당 하나의 별칭#

다음 .gitlab-ci.yml 파일에서:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: alpine:latest
      alias: alpine
    - name: mysql:latest
      alias: mysql

시스템은 표준 buildhelper 컨테이너 외에 alpinemysql이라는 컨테이너가 있는 잡 Pod를 생성합니다. 다음 조건을 충족하기 때문에 이 별칭이 사용됩니다:

하지만 다음 .gitlab-ci.yml에서는:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: mysql:lts
      alias: mysql
    - name: mysql:latest
      alias: mysql

시스템은 buildhelper 컨테이너 외에 mysqlsvc-0이라는 두 개의 추가 컨테이너를 생성합니다. mysql 컨테이너는 mysql:lts 이미지에 해당하고, svc-0 컨테이너는 mysql:latest 이미지에 해당합니다.

서비스당 여러 별칭#

다음 .gitlab-ci.yml 파일에서:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: alpine:latest
      alias: alpine,alpine-latest
    - name: alpine:edge
      alias: alpine,alpine-edge,alpine-latest

시스템은 buildhelper 컨테이너 외에 네 개의 추가 컨테이너를 생성합니다:

  • alpinealpine:latest 이미지가 있는 컨테이너에 해당합니다.
  • alpine-edgealpine:edge 이미지가 있는 컨테이너에 해당합니다(alpine 별칭이 이전 컨테이너에서 이미 사용됨).

이 예시에서 alpine-latest 별칭은 사용되지 않습니다.

하지만 다음 .gitlab-ci.yml에서:

job:
  image: alpine:latest
  script:
    - sleep 10
  services:
    - name: alpine:latest
      alias: alpine,alpine-edge
    - name: alpine:edge
      alias: alpine,alpine-edge
    - name: alpine:3.21
      alias: alpine,alpine-edge

buildhelper 컨테이너 외에 여섯 개의 추가 컨테이너가 생성됩니다.

  • alpinealpine:latest 이미지가 있는 컨테이너를 참조해야 합니다.

  • alpine-edgealpine:edge 이미지가 있는 컨테이너를 참조해야 합니다(alpine 별칭이 이전 컨테이너에 이미 사용됨).

  • svc-0alpine:3.21 이미지가 있는 컨테이너를 참조해야 합니다(alpinealpine-edge 별칭이 이전 컨테이너들에 이미 사용됨).

    • svc-i 패턴의 i는 제공된 목록에서 서비스의 위치를 나타내지 않습니다. 대신 사용 가능한 별칭을 찾지 못했을 때 서비스의 위치를 나타냅니다.

    • 유효하지 않은 별칭이 제공되면(Kubernetes 제약을 충족하지 않으면), 잡은 다음 오류와 함께 실패합니다(별칭 alpine_edge의 예):

      ERROR: Job failed (system failure): prepare environment: setting up build pod: provided host alias
      alpine_edge for service alpine:edge is invalid DNS. a lowercase RFC 1123 subdomain must consist of lower
      case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g.
      'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*').
      Check https://docs.gitlab.com/runner/shells/index/#shell-profile-loading for more information.
      

docker run(Docker-in-Docker)과 함께 services 사용#

docker run으로 시작된 컨테이너도 GitLab에서 제공하는 서비스에 연결할 수 있습니다.

서비스 부팅이 비용이 많이 들거나 시간이 걸리는 경우, 테스트된 서비스를 한 번만 부팅하면서 다른 클라이언트 환경에서 테스트를 실행할 수 있습니다.

access-service:
  stage: build
  image: docker:20.10.16
  services:
    - docker:dind                    # docker run에 필요
    - traefik/whoami:latest
  variables:
    FF_NETWORK_PER_BUILD: "true"     # 컨테이너 간 네트워킹 활성화
  script: |
    docker run --rm --name curl \
      --volume  "$(pwd)":"$(pwd)"    \
      --workdir "$(pwd)"             \
      --network=host                 \
      curlimages/curl:latest curl "http://traefik-whoami"

이 솔루션이 작동하려면 다음이 필요합니다:

Docker 통합 방법#

다음은 잡 실행 중 Docker가 수행하는 단계의 상위 수준 개요입니다.

  1. 서비스 컨테이너 생성: mysql, postgresql, mongodb, redis.
  2. config.toml과 빌드 이미지의 Dockerfile에 정의된 모든 볼륨을 저장하기 위한 캐시 컨테이너 생성(ruby:4.0 등 앞의 예시에서처럼).
  3. 빌드 컨테이너 생성 및 서비스 컨테이너를 빌드 컨테이너에 연결.
  4. 빌드 컨테이너를 시작하고 컨테이너에 잡 스크립트를 전송.
  5. 잡 스크립트 실행.
  6. /builds/group-name/project-name/에 코드 체크아웃.
  7. .gitlab-ci.yml에 정의된 단계 실행.
  8. 빌드 스크립트의 종료 상태 확인.
  9. 빌드 컨테이너와 생성된 모든 서비스 컨테이너 제거.

서비스 컨테이너 로그 캡처#

서비스 컨테이너에서 실행 중인 애플리케이션이 생성하는 로그는 이후 검토 및 디버깅을 위해 캡처할 수 있습니다. 서비스 컨테이너가 성공적으로 시작되었지만 예기치 않은 동작으로 잡 실패를 유발할 때 서비스 컨테이너 로그를 확인하세요. 로그는 컨테이너의 서비스 누락 또는 잘못된 구성을 나타낼 수 있습니다.

CI_DEBUG_SERVICES는 서비스 컨테이너 로그 캡처에는 스토리지와 성능에 영향이 있으므로 서비스 컨테이너를 적극적으로 디버깅할 때만 활성화해야 합니다.

Warning

CI_DEBUG_SERVICES를 활성화하면 마스크된 변수가 노출될 수 있습니다. CI_DEBUG_SERVICES가 활성화되면 서비스 컨테이너 로그와 CI 잡의 로그가 잡의 추적 로그에 동시에 스트리밍됩니다. 따라서 서비스 컨테이너 로그가 잡의 마스크된 로그에 삽입될 수 있습니다. 이렇게 되면 변수 마스킹 메커니즘이 무력화되어 마스크된 변수가 노출될 수 있습니다.

서비스 로깅을 활성화하려면 프로젝트의 .gitlab-ci.yml 파일에 CI_DEBUG_SERVICES 변수를 추가하세요:

variables:
  CI_DEBUG_SERVICES: "true"

허용되는 값:

  • 활성화: TRUE, true, True
  • 비활성화: FALSE, false, False

그 외 다른 값은 오류 메시지를 발생시키고 기능을 효과적으로 비활성화합니다.

활성화되면 모든 서비스 컨테이너의 로그가 캡처되어 다른 로그와 함께 잡의 추적 로그에 동시에 스트리밍됩니다. 각 컨테이너의 로그는 컨테이너 별칭으로 접두사가 붙고 다른 색상으로 표시됩니다.

Note

잡 실패를 진단하려면 로그를 캡처할 서비스 컨테이너의 로깅 수준을 조정할 수 있습니다. 기본 로깅 수준은 충분한 문제 해결 정보를 제공하지 않을 수 있습니다.

CI/CD 변수 마스크 참조

로컬에서 잡 디버그#

다음 명령어는 루트 권한 없이 실행됩니다. 사용자 계정으로 Docker 명령어를 실행할 수 있는지 확인하세요.

먼저 build_script라는 파일을 만들어 시작하세요:

cat < build_script
git clone https://gitlab.com/gitlab-org/gitlab-runner.git /builds/gitlab-org/gitlab-runner
cd /builds/gitlab-org/gitlab-runner
make runner-bin-host
EOF

이 예시는 Makefile이 포함된 GitLab Runner 저장소를 사용하므로 make 실행 시 Makefile에 정의된 타겟이 실행됩니다. make runner-bin-host 대신 프로젝트에 맞는 명령어를 실행할 수 있습니다.

그런 다음 서비스 컨테이너를 만드세요:

docker run -d --name service-redis redis:latest

이전 명령어는 최신 Redis 이미지를 사용하여 service-redis라는 서비스 컨테이너를 만듭니다. 서비스 컨테이너는 백그라운드에서 실행됩니다(-d).

마지막으로 앞서 만든 build_script 파일을 실행하여 빌드 컨테이너를 만드세요:

docker run --name build -i --link=service-redis:redis golang:latest /bin/bash < build_script

이전 명령어는 golang:latest 이미지에서 생성되고 하나의 서비스가 연결된 build라는 컨테이너를 만듭니다. build_scriptstdin을 통해 bash 인터프리터에 파이프되어 build 컨테이너에서 build_script를 실행합니다.

테스트 완료 후 컨테이너를 제거하려면 다음 명령어를 사용하세요:

docker rm -f -v build service-redis

이 명령어는 build 컨테이너, 서비스 컨테이너, 컨테이너 생성 시 생성된 모든 볼륨(-v)을 강제로(-f) 제거합니다.

서비스 컨테이너 사용 시 보안#

Docker 권한 모드는 서비스에 적용됩니다. 즉, 서비스 이미지 컨테이너가 호스트 시스템에 접근할 수 있습니다. 신뢰할 수 있는 소스의 컨테이너 이미지만 사용해야 합니다.

공유 /builds 디렉토리#

빌드 디렉토리는 /builds 아래에 볼륨으로 마운트되며 잡과 서비스 간에 공유됩니다. 잡은 서비스가 실행된 후 /builds/$CI_PROJECT_PATH에 프로젝트를 체크아웃합니다. 서비스가 프로젝트 파일에 접근하거나 아티팩트를 저장해야 할 수 있습니다. 그렇다면 디렉토리가 존재하고 $CI_COMMIT_SHA가 체크아웃될 때까지 기다리세요. 잡이 체크아웃 프로세스를 완료하기 전에 변경된 내용은 체크아웃 프로세스에 의해 제거됩니다.

서비스는 잡 디렉토리가 채워지고 처리 준비가 되었을 때를 감지해야 합니다. 예를 들어 특정 파일이 사용 가능해질 때까지 기다리세요.

시작 즉시 작동하기 시작하는 서비스는 잡 데이터가 아직 사용 가능하지 않을 수 있으므로 실패할 가능성이 높습니다. 예를 들어, 컨테이너는 DinD 서비스에 대한 네트워크 연결을 위해 docker build 명령어를 사용합니다. 서비스는 API에 컨테이너 이미지 빌드를 시작하도록 지시합니다. Docker Engine은 Dockerfile에서 참조하는 파일에 접근할 수 있어야 합니다. 따라서 서비스에서 CI_PROJECT_DIR에 접근해야 합니다. 그러나 Docker Engine은 잡에서 docker build 명령어가 호출될 때까지 이에 접근하려고 시도하지 않습니다. 이 시점에서 /builds 디렉토리에는 이미 데이터가 채워져 있습니다. 시작 직후 CI_PROJECT_DIR에 쓰려고 하는 서비스는 No such file or directory 오류로 실패할 수 있습니다.

잡 데이터와 상호작용하는 서비스가 잡 자체에 의해 제어되지 않는 시나리오에서는 Docker 실행기 워크플로우를 고려해보세요.