서비스
GitLab v19.5Offering: GitLab.com, GitLab Self-Managed, GitLab Dedicated
요약
CI/CD를 구성할 때는 이미지를 지정하며, 이 이미지는 job이 실행되는 컨테이너를 생성하는 데 사용됩니다. services 키워드를 사용하면 추가 이미지를 지정할 수 있습니다. 서비스 이미지는 어떤 애플리케이션이든 실행할 수 있지만, 가장 일반적인 사용 사례는 다음과 같이 데이터베이스 컨테이너를 실행하는 것입니다.
CI/CD를 구성할 때는 이미지를 지정하며, 이 이미지는 job이 실행되는 컨테이너를 생성하는 데
사용됩니다. 이 이미지를 지정하려면 image 키워드를 사용합니다.
services 키워드를 사용하면 추가 이미지를 지정할 수 있습니다. 이 추가 이미지는 첫 번째
컨테이너에서 사용할 수 있는 또 다른 컨테이너를 생성하는 데 사용됩니다.
두 컨테이너는 서로 접근할 수 있으며 job을 실행하는 동안 통신할 수 있습니다.
서비스 이미지는 어떤 애플리케이션이든 실행할 수 있지만, 가장 일반적인 사용 사례는 다음과 같이 데이터베이스 컨테이너를 실행하는 것입니다.
- MySQL
- PostgreSQL
- Redis
- JSON API를 제공하는 마이크로서비스의 예시로 GitLab
서비스 간 네트워킹을 활성화하려면 FF_NETWORK_PER_BUILD를 true로 설정합니다.
이 플래그가 없으면 서비스가 제대로 작동하지 않을 수 있습니다. 자세한 내용은
feature flags를 참고합니다.
저장소로 데이터베이스를 사용하는 콘텐츠 관리 시스템을 개발한다고 가정해 보겠습니다. 애플리케이션의 모든 기능을 테스트하려면 데이터베이스가 필요합니다. 이런 상황에서는 데이터베이스 컨테이너를 서비스 이미지로 실행하는 것이 적합한 사용 사례입니다.
프로젝트를 빌드할 때마다 mysql을 설치하는 대신 기존 이미지를 사용해
추가 컨테이너로 실행합니다.
데이터베이스 서비스만 사용할 수 있는 것은 아닙니다. 필요한 만큼
.gitlab-ci.yml에 서비스를 추가하거나 config.toml을 직접 수정할 수 있습니다. Docker Hub나 프라이빗 컨테이너 레지스트리에서 찾은 모든 이미지를
서비스로 사용할 수 있습니다.
프라이빗 이미지 사용에 대한 자세한 내용은 프라이빗 컨테이너 레지스트리에서 이미지 접근을 참고합니다.
서비스는 CI 컨테이너 자체와 동일한 DNS 서버, 검색 도메인, 추가 호스트를 상속받습니다.
job에 서비스가 연결되는 방식#
컨테이너 링킹이 작동하는 방식을 더 잘 이해하려면 컨테이너 연결하기를 참고합니다.
애플리케이션에 mysql을 서비스로 추가하면 해당 이미지는
job 컨테이너에 연결된 컨테이너를 생성하는 데 사용됩니다.
MySQL의 서비스 컨테이너는 mysql 호스트 이름으로 접근할 수 있습니다.
데이터베이스 서비스에 접근하려면 소켓이나 localhost 대신 mysql이라는
호스트에 연결합니다. 자세한 내용은 서비스 접근하기를 참고합니다.
Docker 통합 워크플로#
다음은 job 실행 중 Docker가 수행하는 단계에 대한 개괄적인 설명입니다.
mysql,postgresql,mongodb,redis등 서비스 컨테이너를 생성합니다.config.toml과 빌드 이미지(앞의 예시에서는ruby:4.0)의Dockerfile에 정의된 모든 볼륨을 저장할 캐시 컨테이너를 생성합니다.- 빌드 컨테이너를 생성하고 모든 서비스 컨테이너를 빌드 컨테이너에 연결합니다.
- 빌드 컨테이너를 시작하고 job 스크립트를 컨테이너로 전송합니다.
- job 스크립트를 실행합니다.
/builds/group-name/project-name/에 코드를 체크아웃합니다..gitlab-ci.yml에 정의된 모든 단계를 실행합니다.- 빌드 스크립트의 종료 상태를 확인합니다.
- 빌드 컨테이너와 생성된 모든 서비스 컨테이너를 제거합니다.
서비스 상태 확인 워크플로#
서비스는 네트워크로 접근 가능한 추가 기능을 제공하도록 설계되어 있습니다.
MySQL이나 Redis 같은 데이터베이스일 수도 있고, Docker-in-Docker(DinD)를
사용할 수 있게 해주는 docker:dind일 수도 있습니다. CI/CD job을 진행하는 데
필요하면서 네트워크로 접근하는 것이라면 사실상 무엇이든 가능합니다.
이를 보장하기 위해 러너는 다음을 수행합니다.
- 컨테이너에서 기본적으로 노출되는 포트를 확인합니다.
- 이 포트에 접근할 수 있을 때까지 대기하는 특수 컨테이너를 시작합니다.
두 번째 확인 단계가 실패하면 *** WARNING: Service XYZ probably didn't start properly 경고가
출력됩니다. 이 문제는 다음과 같은 이유로 발생할 수 있습니다.
- 서비스에 열린 포트가 없습니다.
- 타임아웃 전에 서비스가 제대로 시작되지 않아 포트가 응답하지 않습니다.
대부분의 경우 이는 job에 영향을 미치지만, 해당 경고가 출력되어도 job이 성공하는 경우가 있을 수 있습니다. 예를 들면 다음과 같습니다.
- 경고가 발생한 직후 서비스가 시작되었고, job이 처음부터 연결된 서비스를 사용하지 않는 경우입니다. 이 경우 job이 서비스에 접근해야 할 시점에는 이미 서비스가 연결을 기다리고 있었을 수 있습니다.
- 서비스 컨테이너가 네트워킹 서비스를 전혀 제공하지 않지만 job의 디렉터리에
대해 무언가 작업을 수행하는 경우입니다(모든 서비스는 job 디렉터리를
/builds아래에 볼륨으로 마운트합니다). 이 경우 서비스는 자신의 역할을 수행하며, job이 해당 서비스에 연결을 시도하지 않으므로 실패하지 않습니다.
서비스가 성공적으로 시작되면
before_script가 실행되기 전에 시작됩니다. 즉, 서비스를
조회하는 before_script를 작성할 수 있습니다.
서비스는 job이 실패하더라도 job이 끝나면 중지됩니다.
서비스 이미지가 제공하는 소프트웨어 사용하기#
service를 지정하면 네트워크로 접근 가능한
서비스가 제공됩니다. 데이터베이스가 이런 서비스의 가장 단순한 예시입니다.
services 기능은 정의된
services 이미지의 소프트웨어를 job 컨테이너에 추가하지 않습니다.
예를 들어 job에 다음과 같이 services가 정의되어 있다면 스크립트에서 php,
node, go 명령을 사용할 수 없으며 job이 실패합니다.
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 이미지를 만들어 job에서 사용합니다.
.gitlab-ci.yml 파일에서 services 정의하기#
job마다 서로 다른 이미지와 서비스를 정의할 수도 있습니다.
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
또는 image와 services에 확장 구성 옵션을 전달할 수도 있습니다.
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
서비스 접근하기#
서비스 별칭을 지정하지 않으면 빌드 컨테이너에서 다음 두 가지 호스트 이름으로 서비스에 접근할 수 있습니다.
namespace-projectnamenamespace__projectname
밑줄이 포함된 호스트 이름은 RFC에 맞지 않으며 서드파티 애플리케이션에서 문제를 일으킬 수 있습니다.
서비스 호스트 이름의 기본 별칭은 다음 규칙에 따라 이미지 이름으로부터 생성됩니다.
- 콜론(
:) 뒤의 모든 문자는 제거됩니다. - 슬래시(
/)가 이중 밑줄(__)로 바뀌어 기본(primary) 별칭이 생성됩니다. - 슬래시(
/)가 단일 대시(-)로 바뀌어 보조(secondary) 별칭이 생성됩니다.
기본 동작을 재정의하려면 하나 이상의 서비스 별칭을 지정할 수 있습니다.
서비스 연결하기#
외부 API가 자체 데이터베이스와 통신해야 하는 엔드투엔드 테스트처럼 복잡한 job에서는 서로 의존하는 서비스를 사용할 수 있습니다.
예를 들어 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 # activate container-to-container networking
POSTGRES_PASSWORD: supersecretpassword
BACKEND_POSTGRES_HOST: postgres
script:
- npm install
- npm test
이 방법이 동작하려면 job마다 새 네트워크를 생성하는 네트워킹 모드를 사용해야 합니다.
서비스에 CI/CD 변수 전달하기#
.gitlab-ci.yml 파일에서 직접 사용자 지정 CI/CD 변수를
전달해 Docker images와 services를 세밀하게 조정할 수도 있습니다.
자세한 내용은 .gitlab-ci.yml에서 정의한 변수를 참고합니다.
# The following variables are automatically passed down to the Postgres container
# as well as the Ruby container and available within each.
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에 사용할 수 있는 설정#
services:의 하위 키에 대한 자세한 내용은 CI/CD YAML 레퍼런스를 참고합니다.
동일한 이미지에서 여러 서비스 시작하기#
새로운 확장 Docker 구성 옵션이 도입되기 전에는 다음 구성이 제대로 작동하지 않았습니다.
services:
- mysql:latest
- mysql:latest
러너는 각각 mysql:latest 이미지를 사용하는 두 개의 컨테이너를
시작합니다. 그러나 기본 호스트 이름 지정 방식에 따라 두 컨테이너
모두 mysql 별칭으로 job 컨테이너에 추가됩니다. 그 결과 서비스 중 하나는
접근할 수 없게 됩니다.
새로운 확장 Docker 구성 옵션을 사용하면 앞의 예시는 다음과 같이 바뀝니다.
services:
- name: mysql:latest
alias: mysql-1
- name: mysql:latest
alias: mysql-2
러너는 여전히 mysql:latest 이미지를 사용하는 두 개의 컨테이너를
시작하지만, 이제 각 컨테이너는 .gitlab-ci.yml 파일에 설정된 별칭으로도
접근할 수 있습니다.
서비스에 커맨드 설정하기#
어떤 SQL 데이터베이스가 들어 있는 super/sql:latest 이미지가 있고,
이를 job의 서비스로 사용하려 한다고 가정해 보겠습니다. 또한 이 이미지는
컨테이너를 시작할 때 데이터베이스 프로세스를 시작하지 않는다고 가정해
보겠습니다. 이 경우 사용자는 데이터베이스를 시작하기 위해
/usr/bin/super-sql run 명령을 수동으로 실행해야 합니다.
새로운 확장 Docker 구성 옵션이 도입되기 전에는 다음 작업이 필요했습니다.
-
super/sql:latest이미지를 기반으로 자체 이미지를 생성합니다. -
기본 커맨드를 추가합니다.
-
job 구성에서 해당 이미지를 사용합니다.
-
my-super-sql:latest이미지의 Dockerfile:FROM super/sql:latest CMD ["/usr/bin/super-sql", "run"] -
.gitlab-ci.yml의 job에서: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 실행자(executor)에서 별칭을 서비스 컨테이너 이름으로 사용하기#
히스토리
- GitLab 및 GitLab Runner 17.9에서 도입되었습니다.
Kubernetes 실행자에서는 서비스 별칭을 서비스 컨테이너 이름으로 사용할 수 있습니다. GitLab Runner는 다음 조건에 따라 컨테이너 이름을 지정합니다.
- 서비스에 여러 별칭이 설정된 경우, 서비스 컨테이너는 다음 조건을 만족하는
첫 번째 별칭의 이름을 따릅니다.
- 다른 서비스 컨테이너에서 아직 사용하지 않은 별칭입니다.
- Kubernetes의 레이블 이름 제약 조건을 따릅니다.
- 별칭을 서비스 컨테이너 이름으로 사용할 수 없는 경우, 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
시스템은 표준 build, helper 컨테이너 외에 alpine, mysql이라는
이름의 컨테이너가 포함된 job Pod를 생성합니다. 이 별칭이 사용된 이유는
다음과 같습니다.
- 다른 서비스 컨테이너에서 사용하지 않았습니다.
- Kubernetes의 레이블 이름 제약 조건을 따릅니다.
그러나 다음 .gitlab-ci.yml에서는:
job:
image: alpine:latest
script:
- sleep 10
services:
- name: mysql:lts
alias: mysql
- name: mysql:latest
alias: mysql
시스템은 build, helper 컨테이너 외에 mysql, svc-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
시스템은 build, helper 컨테이너 외에 네 개의 컨테이너를 추가로
생성합니다.
alpine:alpine:latest이미지를 사용하는 컨테이너에 해당합니다.alpine-edge:alpine: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
build, helper 컨테이너 외에 여섯 개의 컨테이너가 추가로
생성됩니다.
-
alpine:alpine:latest이미지를 사용하는 컨테이너를 가리킵니다. -
alpine-edge:alpine:edge이미지를 사용하는 컨테이너를 가리킵니다(alpine별칭은 이전 컨테이너에서 이미 사용했기 때문입니다). -
svc-0:alpine:3.21이미지를 사용하는 컨테이너를 가리킵니다(alpine과alpine-edge별칭은 이전 컨테이너들에서 이미 사용했기 때문입니다).-
svc-i패턴의i는 제공된 목록에서 서비스의 위치를 나타내지 않습니다. 대신 사용 가능한 별칭을 찾지 못했을 때의 서비스 위치를 나타냅니다. -
유효하지 않은 별칭이 제공되면(Kubernetes 제약 조건을 충족하지 못하는 경우) job은 다음과 같은 오류(별칭
alpine_edge를 사용한 예시)와 함께 실패합니다. 별칭은 job Pod에서 로컬 DNS 항목을 생성하는 데도 사용되기 때문에 이런 오류가 발생합니다.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 # necessary for docker run
- traefik/whoami:latest
variables:
FF_NETWORK_PER_BUILD: "true" # activate container-to-container networking
script: |
docker run --rm --name curl \
--volume "$(pwd)":"$(pwd)" \
--workdir "$(pwd)" \
--network=host \
curlimages/curl:latest curl "http://traefik-whoami"
이 방법이 동작하려면 다음을 수행해야 합니다.
- job마다 새 네트워크를 생성하는 네트워킹 모드를 사용합니다.
- Docker 소켓 바인딩과 함께 Docker 실행자를 사용하지 않습니다.
부득이하게 사용해야 한다면, 앞의 예시에서
host대신 해당 job을 위해 생성된 동적 네트워크 이름을 사용합니다.
서비스 컨테이너 로그 캡처하기#
서비스 컨테이너에서 실행 중인 애플리케이션이 생성하는 로그는 이후 검토와 디버깅을 위해 캡처할 수 있습니다. 서비스 컨테이너가 정상적으로 시작되었지만 예상치 못한 동작으로 job이 실패하는 경우 서비스 컨테이너 로그를 확인합니다. 로그를 통해 컨테이너 내 서비스 설정이 누락되었거나 잘못되었는지 파악할 수 있습니다.
서비스 컨테이너 로그를 캡처하면 저장 공간과 성능 양쪽에 영향을
미치므로, CI_DEBUG_SERVICES는 서비스 컨테이너를 실제로 디버깅하는
동안에만 활성화해야 합니다.
CI_DEBUG_SERVICES를 활성화하면 마스킹된 변수가 노출될 수 있습니다. CI_DEBUG_SERVICES가
활성화되면 서비스 컨테이너 로그와 CI job 로그가 동시에 job의 추적 로그로
스트리밍됩니다. 이로 인해 서비스 컨테이너 로그가 job의 마스킹된 로그
안에 삽입될 수 있습니다. 그 결과 변수 마스킹 메커니즘이 무력화되어
마스킹된 변수가 노출될 수 있습니다.
서비스 로깅을 활성화하려면 프로젝트의 .gitlab-ci.yml 파일에
CI_DEBUG_SERVICES 변수를 추가합니다.
variables:
CI_DEBUG_SERVICES: "true"
사용 가능한 값은 다음과 같습니다.
- 활성화:
TRUE,true,True - 비활성화:
FALSE,false,False
다른 값을 사용하면 오류 메시지가 발생하며 기능이 사실상 비활성화됩니다.
활성화하면 모든 서비스 컨테이너의 로그가 캡처되어 다른 로그와 동시에 job의 추적 로그로 스트리밍됩니다. 각 컨테이너의 로그에는 해당 컨테이너의 별칭이 접두어로 붙고, 서로 다른 색상으로 표시됩니다.
job 실패를 진단하려면 로그를 캡처하려는 서비스 컨테이너의 로깅 레벨을 조정할 수 있습니다. 기본 로깅 레벨로는 문제 해결에 충분한 정보를 제공하지 못할 수 있습니다.
CI/CD 변수 마스킹하기를 참고합니다.
로컬에서 job 디버깅하기#
다음 명령들은 root 권한 없이 실행됩니다. 사용자 계정으로 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_script는
stdin을 통해 bash 인터프리터로 전달되고, bash 인터프리터는 build
컨테이너 안에서 build_script를 실행합니다.
테스트가 끝난 후 다음 명령으로 컨테이너를 제거합니다.
docker rm -f -v build service-redis
이 명령은 build 컨테이너, 서비스 컨테이너, 그리고 컨테이너 생성 시
만들어진 모든 볼륨(-v)을 강제로(-f) 제거합니다.
서비스 컨테이너 사용 시 보안#
Docker의 특권(privileged) 모드는 서비스에도 적용됩니다. 즉, 서비스 이미지 컨테이너가 호스트 시스템에 접근할 수 있다는 의미입니다. 신뢰할 수 있는 출처의 컨테이너 이미지만 사용해야 합니다.
공유되는 /builds 디렉터리#
빌드 디렉터리는 /builds 아래에 볼륨으로 마운트되며 job과 서비스 간에
공유됩니다. job은 서비스가 실행된 후 프로젝트를 /builds/$CI_PROJECT_PATH에
체크아웃합니다. 서비스가 프로젝트 파일에 접근하거나 아티팩트를 저장해야
할 수도 있습니다. 이런 경우에는 해당 디렉터리가 생성되고
$CI_COMMIT_SHA가 체크아웃될 때까지 기다려야 합니다. job이 체크아웃
과정을 완료하기 전에 이루어진 변경 사항은 체크아웃 과정에서 모두
제거됩니다.
서비스는 job 디렉터리에 데이터가 채워져 처리할 준비가 되었는지를 감지해야 합니다. 예를 들어 특정 파일이 사용 가능해질 때까지 기다리는 방식입니다.
실행되자마자 바로 작업을 시작하는 서비스는 job 데이터가 아직 준비되지
않았을 수 있어 실패하기 쉽습니다. 예를 들어 컨테이너는 docker build
명령을 사용해 DinD 서비스로 네트워크 연결을 만듭니다. 이 서비스는
자신의 API에 컨테이너 이미지 빌드를 시작하라고 지시합니다. Docker
Engine은 Dockerfile에서 참조하는 파일에 접근할 수 있어야 하므로,
서비스에서 CI_PROJECT_DIR에 대한 접근 권한이 필요합니다. 그러나
Docker Engine은 job에서 docker build 명령이 호출되기 전까지는 이
디렉터리에 접근하려고 하지 않습니다. 이 시점에는 /builds 디렉터리에
이미 데이터가 채워져 있습니다. 시작하자마자 바로 CI_PROJECT_DIR에
쓰기를 시도하는 서비스는 No such file or directory 오류로 실패할 수
있습니다.
job 데이터와 상호작용하는 서비스를 job 자체가 제어하지 않는 시나리오에서는 Docker 실행자 워크플로를 검토합니다.