서비스

Tier: Free, Premium, Ultimate
Offering: GitLab.com, Self-managed, GitLab Dedicated

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

추가 이미지는 services 키워드를 사용하여 지정할 수 있습니다. 이 추가 이미지는 첫 번째 컨테이너와 연결된 다른 컨테이너를 생성하는 데 사용됩니다. 두 컨테이너는 서로에 접근할 수 있으며, 작업 수행 시 서로 통신할 수 있습니다.

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

기존 이미지를 사용하고 추가 컨테이너로 실행하는 것이, 예를 들어 매번 프로젝트가 빌드될 때마다 mysql을 설치하는 것보다 더 쉽고 빠릅니다.

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

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

서비스가 작업에 연결되는 방법

컨테이너 연결이 어떻게 작동하는지 더 잘 이해하려면 컨테이너 연결을 읽어보세요.

응용 프로그램에 mysql을 서비스로 추가하면, 이미지를 사용하여 작업 컨테이너에 연결된 컨테이너가 생성됩니다.

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

서비스의 헬스 체크가 작동하는 방법

서비스는 네트워크에 접근 가능한 추가 기능을 제공하기 위해 설계되었습니다. MySQL, Redis와 같은 데이터베이스일 수도 있고, docker:dind를 사용하여 Docker-in-Docker를 허용할 수도 있습니다. 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:7
    - node:latest
    - golang:1.10
  image: alpine:3.7
  script:
    - php -v
    - node -v
    - go version

스크립트에서 php, nodego를 사용해야 하는 경우, 다음 중 하나를 선택해야 합니다:

  • 모든 필요한 도구가 포함된 기존 Docker 이미지를 선택합니다.
  • 모든 필요한 도구가 포함된 자체 Docker 이미지를 생성하고, 이를 작업에서 사용합니다.

.gitlab-ci.yml 파일에 services 정의하기

작업마다 서로 다른 이미지와 서비스를 정의하는 것도 가능합니다:

default:
  before_script:
    - bundle install

test:2.6:
  image: ruby:2.6
  services:
    - postgres:11.7
  script:
    - bundle exec rake spec

test:2.7:
  image: ruby:2.7
  services:
    - postgres:12.2
  script:
    - bundle exec rake spec

또는 imageservices에 대한 확장 구성 옵션을 전달할 수 있습니다:

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

test:
  script:
    - bundle exec rake spec

서비스 접근하기

Wordpress 인스턴스가 필요하여 애플리케이션에 대한 API 통합을 테스트해야 한다고 가정해 보겠습니다. 이 경우 예를 들어, .gitlab-ci.yml 파일에서 tutum/wordpress 이미지를 사용할 수 있습니다:

services:
  - tutum/wordpress:latest

서비스 별칭을 지정하지 않는다면, 작업이 실행될 때 tutum/wordpress가 시작됩니다. 빌드 컨테이너에서 두 개의 호스트 이름 아래에서 액세스할 수 있습니다:

  • tutum-wordpress
  • tutum__wordpress

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

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

  • 콜론(:) 이후의 모든 내용이 제거됩니다.
  • 슬래시(/)는 이중 언더바(__)로 대체되며 기본 별칭이 생성됩니다.
  • 슬래시(/)는 단일 대시(-)로 대체되며 보조 별칭이 생성됩니다(이는 GitLab Runner v1.1.0 이상이 필요합니다).

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

서비스 연결하기

외부 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:14.3
      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 이미지서비스를 세밀하게 조정하기 위해 사용자 정의 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:11.7
      alias: db
      entrypoint: ["docker-entrypoint.sh"]
      command: ["postgres"]
  image:
    name: ruby:2.6
    entrypoint: ["/bin/bash"]
  before_script:
    - bundle install

test:
  script:
    - bundle exec rake spec

서비스에 대한 사용 가능한 설정

  • GitLab 및 GitLab Runner 9.4에서 도입되었습니다.
설정 필수 GitLab 버전 설명
name 다른 옵션과 함께 사용할 때 예 9.4 사용할 이미지의 전체 이름입니다. 전체 이미지 이름에 레지스트리 호스트 이름이 포함된 경우, alias 옵션을 사용하여 더 짧은 서비스 액세스 이름을 정의하십시오. 자세한 내용은 서비스 액세스를 참조하세요.
entrypoint 아니요 9.4 컨테이너의 진입점으로 실행할 명령이나 스크립트입니다. 컨테이너를 생성할 때 Docker --entrypoint 옵션으로 변환됩니다. 구문은 DockerfileENTRYPOINT 지시어와 유사하며, 각 쉘 토큰은 배열의 개별 문자열입니다.
command 아니요 9.4 컨테이너의 명령으로 사용해야 하는 명령이나 스크립트입니다. 이는 이미지 이름 뒤에 Docker에 전달되는 인수로 변환됩니다. 구문은 DockerfileCMD 지시어와 유사하며, 각 쉘 토큰은 배열의 개별 문자열입니다.
alias (1) 아니요 9.4 작업의 컨테이너에서 서비스에 액세스하기 위한 추가 별명입니다. 여러 별명은 공백이나 쉼표로 구분할 수 있습니다. 자세한 내용은 서비스 액세스를 참조하세요.
variables (2) 아니요 14.5 서비스에 독점적으로 전달되는 추가 환경 변수입니다. 구문은 Job Variables와 동일합니다. 서비스 변수는 자신을 참조할 수 없습니다.

(1) Kubernetes executor에 대한 별명 지원은 도입되었으며 GitLab Runner 12.8에서 사용 가능합니다. Kubernetes 버전 1.7 이상에서만 사용할 수 있습니다.

(2) Docker와 Kubernetes executor에 대한 서비스 변수 지원은 도입되었으며 GitLab Runner 14.8에서 사용 가능합니다.

동일한 이미지에서 여러 서비스 시작하기

  • GitLab 및 GitLab Runner 9.4에서 도입되었습니다. 확장된 구성 옵션에 대해 자세히 알아보세요.

새로운 확장 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 파일에서 설정한 별칭으로도 접근할 수 있습니다.

서비스의 명령 설정하기

  • GitLab 및 GitLab Runner 9.4에서 도입되었습니다. 확장된 구성 옵션에 대해 자세히 알아보세요.

super/sql:latest 이미지에 SQL 데이터베이스가 포함되어 있다고 가정해 보겠습니다.

이 이미지를 작업의 서비스로 사용하고자 하며, 이 이미지가 컨테이너 시작 시 데이터베이스 프로세스를 시작하지 않는다고 가정합니다. 사용자는 데이터베이스를 시작하기 위해 /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와 유사합니다.

docker runservices(Docker-in-Docker)를 나란히 사용하기

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

서비스 부팅이 비용이 많이 들거나 시간이 오래 걸릴 때, 이 기술을 사용하여 테스트된 서비스를 한 번만 부팅하면서도 다양한 클라이언트 환경에서 테스트를 실행할 수 있습니다.

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

이 솔루션이 작동하려면 다음을 수행해야 합니다:

도커 통합 작동 방식

아래는 작업 시간 동안 도커가 수행하는 단계에 대한 개요입니다.

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

서비스 컨테이너 로그 캡처

서비스 컨테이너에서 실행 중인 애플리케이션에서 생성된 로그는 이후 검토 및 디버깅을 위해 캡처할 수 있습니다.

서비스 컨테이너가 성공적으로 시작되었지만 예상대로 작동하지 않아 작업 실패로 이어질 때 서비스 컨테이너의 로그를 확인하고 싶을 수 있습니다.

로그는 컨테이너 내 서비스의 잘못된 구성 또는 누락된 구성을 나타낼 수 있습니다.

CI_DEBUG_SERVICES는 서비스 컨테이너의 로그를 적극적으로 디버깅할 때만 활성화해야 합니다.

서비스 컨테이너 로그를 캡처하면 저장 공간과 성능에 영향을 미치기 때문에 주의해야 합니다.

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

variables:
  CI_DEBUG_SERVICES: "true"

허용된 값은 다음과 같습니다:

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

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

활성화되면 모든 서비스 컨테이너의 로그가 캡처되어 다른 로그와 동시에 작업 추적 로그로 스트리밍됩니다.

각 컨테이너의 로그는 컨테이너의 별칭으로 접두사가 붙으며 다른 색상으로 표시됩니다.

참고: 로그를 캡처하려는 서비스 컨테이너의 로그 레벨을 조정하는 것이 좋습니다. 기본 로그 레벨이 작업 실패를 진단하는 데 충분한 세부 정보를 제공하지 않을 수 있습니다.

경고: CI_DEBUG_SERVICES를 활성화하면 마스킹된 변수가 노출될 수 있습니다. CI_DEBUG_SERVICES가 활성화되면 서비스 컨테이너 로그와 CI 작업의 로그가 작업 추적 로그로 동시에 스트리밍되므로 서비스 컨테이너 로그가 작업의 마스킹된 로그 _내부_에 삽입될 수 있습니다. 이는 변수 마스킹 메커니즘을 무력화하고 마스킹된 변수가 노출되는 결과를 초래할 수 있습니다.

CI/CD 변수 마스크하기를 참조하십시오.

로컬에서 작업 디버그하기

다음 명령은 루트 권한 없이 실행됩니다. 사용자의 계정으로 도커를 실행할 수 있어야 합니다.

먼저 build_script라는 파일을 생성하는 것부터 시작합니다:

cat <<EOF > 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_script는 표준 입력으로 bash 인터프리터에 파이핑되어 build 컨테이너 내에서 build_script를 실행합니다.

테스트가 끝나고 더 이상 컨테이너가 필요하지 않으면 다음 명령으로 제거할 수 있습니다:

docker rm -f -v build service-redis

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

서비스 컨테이너를 사용할 때의 보안

Docker 특권 모드는 서비스에 적용됩니다. 이것은 서비스 이미지 컨테이너가 호스트 시스템에 접근할 수 있음을 의미합니다. 신뢰할 수 있는 출처의 컨테이너 이미지만 사용해야 합니다.

공유 /builds 디렉토리

빌드 디렉토리는 /builds 아래에 볼륨으로 마운트되며 작업과 서비스 간에 공유됩니다. 작업은 서비스가 실행된 후 /builds/$CI_PROJECT_PATH에 프로젝트를 체크아웃합니다. 따라서 서비스가 프로젝트의 파일을 필요로 하거나 예를 들어 아티팩트로 제공할 파일을 그 디렉토리에 넣고 싶을 경우, 해당 디렉토리가 존재하고 $CI_COMMIT_SHA가 체크아웃될 때까지 기다려야 할 수 있습니다. 작업이 체크아웃 프로세스를 끝내기 전에 yapılan 변경 사항은 체크아웃 프로세스에 의해 제거됩니다.