- 서비스가 작업과 연결되는 방법
- 서비스의 헬스 체크 작동 방식
- 서비스 이미지에서 제공되는 소프트웨어 사용하기
.gitlab-ci.yml
파일에서services
정의하기- 서비스 접근
- CI/CD 변수를 서비스에 전달
services
의 사용 가능한 설정- 동일한 이미지에서 여러 서비스 시작
- 서비스에 대한 명령 설정
docker run
과 함께services
사용하기 (Docker-in-Docker)- 서비스 컨테이너 로그 캡처
- 로컬에서 작업 디버깅
- 서비스 컨테이너 사용 시 보안
- 공유된 /builds 디렉터리
서비스
CI/CD를 구성할 때 작업이 실행되는 컨테이너를 생성하는 데 사용되는 이미지를 지정합니다. 이 이미지를 지정하려면 image
키워드를 사용하십시오.
services
키워드를 사용하여 추가 이미지를 지정할 수 있습니다. 이 추가 이미지는 첫 번째 컨테이너에서 사용 가능한 다른 컨테이너를 생성하는 데 사용됩니다. 두 컨테이너는 서로에게 접근할 수 있으며 작업을 실행하는 동안 통신할 수 있습니다.
서비스 이미지는 모든 응용 프로그램을 실행할 수 있지만, 가장 일반적인 사용 사례는 데이터베이스 컨테이너를 실행하는 것입니다. 예를 들어 다음과 같습니다: - MySQL - PostgreSQL - Redis - GitLab - JSON API를 제공하는 마이크로서비스의 예
프로젝트를 빌드할 때마다 예를 들어 mysql
을 설치하는 것보다 기존 이미지를 사용하고 추가로 컨테이너로 실행하는 것이 더 쉽고 빠릅니다.
데이터베이스 서비스에만 제한되지 않습니다. .gitlab-ci.yml
에 필요한 만큼 많은 서비스를 추가하거나 config.toml
을 수동으로 수정할 수 있습니다. Docker Hub나 개인 컨테이너 레지스트리에서 찾을 수 있는 모든 이미지를 서비스로 사용할 수 있습니다.
서비스는 CI 컨테이너 자체와 동일한 DNS 서버, 검색 도메인 및 추가 호스트를 상속합니다.
서비스가 작업과 연결되는 방법
컨테이너를 연결하는 방식을 더 잘 이해하려면 컨테이너를 함께 연결하는 것을 읽어보세요.
애플리케이션에 mysql
을 서비스로 추가하면 이미지가 작업 컨테이너에 연결된 컨테이너를 생성하는 데 사용됩니다.
MySQL을 위한 서비스 컨테이너는 mysql
로 제공되며, 데이터베이스 서비스에 액세스하려면 소켓이나 localhost
대신 호스트인 mysql
에 연결합니다. 서비스에 액세스에서 자세히 읽어보세요.
서비스의 헬스 체크 작동 방식
서비스는 네트워크에서 접근 가능한 추가 기능을 제공하도록 설계되었습니다. MySQL이나 Redis와 같은 데이터베이스, 심지어 Docker-in-Docker를 사용할 수 있는 docker:dind
와 같은 것이 될 수 있습니다. CI/CD 작업이 진행되는 데 필요한 사실상 필요한 모든 것이 될 수 있으며, 네트워크를 통해 액세스됩니다.
이 작동 방식을 확인하려면 러너: 1. 기본적으로 컨테이너에서 노출된 포트를 확인합니다. 1. 이러한 포트에 액세스 가능해질 때까지 기다리는 특별한 컨테이너를 시작합니다.
체크의 두 번째 단계가 실패하면, *** 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
, node
, go
를 사용해야 하는 경우 다음 중 하나를 수행해야 합니다:
- 필요한 모든 도구를 포함하는 기존 Docker 이미지를 선택합니다.
- 모든 필요한 도구가 포함된 자체 Docker 이미지를 만들고 작업에서 해당 이미지를 사용합니다.
.gitlab-ci.yml
파일에서 services
정의하기
또한 직업마다 다른 이미지와 서비스를 정의할 수 있습니다: ```yaml 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 ```
또는 image
와 services
에 대해 일부 확장된 구성 옵션을 전달할 수 있습니다:
```yaml
default:
image:
name: ruby:2.6
entrypoint: [“/bin/bash”]
services:
- name: my-postgres:11.7
alias: db-postgres
entrypoint: [“/usr/local/bin/db-postgres”]
command: [“start”]
before_script:
- bundle install
test: script: - bundle exec rake spec ```
서비스 접근
예를 들어 응용 프로그램과 API 통합을 테스트하기 위해 Wordpress 인스턴스가 필요한 경우, .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
- postgres:14.3
variables:
FF_NETWORK_PER_BUILD: 1
POSTGRES_PASSWORD: supersecretpassword
BACKEND_POSTGRES_HOST: postgres
script:
- npm install
- npm test
이 솔루션이 작동하려면, 각 작업에 대한 새 네트워크를 생성하는 네트워킹 모드를 사용해야 합니다.
CI/CD 변수를 서비스에 전달
또한 .gitlab-ci.yml
파일에서 Docker images
및 services
를 정확하게 조정하기 위해 사용자 정의 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
services
의 사용 가능한 설정
- GitLab 및 GitLab Runner 9.4에서 도입됨.
설정 | 필수 여부 | GitLab 버전 | 설명 |
---|---|---|---|
name
| 사용 시, 다른 옵션과 함께 사용해야 함 | 9.4 | 사용할 이미지의 전체 이름. 전체 이미지 이름에 레지스트리 호스트 이름이 포함된 경우, alias 옵션을 사용하여 더 짧은 서비스 액세스 이름을 정의합니다. 자세한 내용은 서비스 접근을 참조하세요.
|
entrypoint
| 아니요 | 9.4 | 컨테이너의 엔트리포인트로 실행할 명령 또는 스크립트. 이는 컨테이너를 생성하는 동안 Docker --entrypoint 옵션으로 변환됩니다. 구문은 Dockerfile 의 ENTRYPOINT 지시문과 유사하며, 각 쉘 토큰은 배열에서 별도의 문자열로 변환됩니다.
|
command
| 아니요 | 9.4 | 컨테이너의 명령 또는 스크립트. 이미지 이름 뒤에 전달되는 인수로 Docker로 변환됩니다. 구문은 Dockerfile 의 CMD 지시문과 유사하며, 각 쉘 토큰은 배열에서 별도의 문자열로 변환됩니다.
|
alias (1)
| 아니요 | 9.4 | 작업 컨테이너에서 서비스에 액세스하는 데 사용할 수 있는 추가 별칭. 자세한 내용은 서비스 접근을 참조하세요. |
variables (2)
| 아니요 | 14.5 | 서비스에 전용으로 전달되는 추가 환경 변수. 구문은 Job Variables와 동일합니다. 서비스 변수는 자신을 참조할 수 없습니다. |
(1) 별칭 지원은 Kubernetes 실행기의 경우 GitLab Runner 12.8에서 소개되었으며, Kubernetes 버전 1.7 이상에서만 사용할 수 있습니다.
(2) Docker 및 Kubernetes 실행기의 서비스 변수 지원은 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 run
과 함께 services
사용하기 (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"
이 솔루션이 작동하려면 다음을 수행해야 합니다.
- 각 작업을 위한 새 네트워크를 생성하는 네트워킹 모드를 사용합니다.
-
Docker 소켓 바인딩을 사용하지 않습니다.
반드시 사용해야 한다면, 위 예제에서
host
대신에 이 작업용으로 생성된 동적 네트워크 이름을 사용하세요.
서비스 컨테이너 로그 캡처
- GitLab Runner 15.6에서 도입되었습니다.
서비스 컨테이너에서 실행 중인 응용 프로그램에서 생성된 로그는 이후 검사 및 디버깅을 위해 캡처할 수 있습니다. 서비스 컨테이너의 로그를 살펴볼 수도 있습니다. 예를 들어, 서비스 컨테이너가 성공적으로 시작되었지만 예상대로 작동하지 않아 작업 실패로 이어질 경우입니다. 이 로그는 컨테이너 내에서 서비스의 누락되거나 잘못된 구성을 나타낼 수 있습니다.
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 변수 마스킹 참조
로컬에서 작업 디버깅
다음 명령어는 루트 권한 없이 실행됩니다. 사용자 계정으로 Docker를 실행할 수 있어야 합니다.
먼저 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
라는 이름의 컨테이너를 만들고 서비스를 하나 연결하며, stdin
을 통해 build_script
이 bash 해석기에 파이프로 전달되고 이를 통해 build
컨테이너에서 build_script
이 실행됩니다.
테스트를 마치고 더 이상 컨테이너가 필요하지 않은 경우 다음과 같이 제거할 수 있습니다:
docker rm -f -v build service-redis
위 명령은 build
컨테이너, 서비스 컨테이너 및 컨테이너 생성 시 생성된 모든 볼륨(-v
)을 강제로(-f
) 제거합니다.
서비스 컨테이너 사용 시 보안
Docker 권한 모드는 서비스에 적용됩니다. 이는 서비스 이미지 컨테이너가 호스트 시스템에 액세스할 수 있음을 의미합니다. 신뢰할 수 있는 소스의 컨테이너 이미지를 사용해야 합니다.
공유된 /builds 디렉터리
빌드 디렉터리는 /builds
아래의 볼륨으로 마운트되어 작업과 서비스 사이에서 공유됩니다. 서비스가 실행된 후 작업은 프로젝트를 /builds/$CI_PROJECT_PATH
로 체크아웃합니다. 결과적으로, 서비스가 프로젝트의 파일이 필요하거나 예를 들어 해당 디렉터리에서 아티팩트로 사용할 파일이 필요한 경우 해당 디렉터리가 존재하고 $CI_COMMIT_SHA
가 체크아웃되기를 기다려야 합니다. 작업이 체크아웃 프로세스를 완료하기 전에 수행된 모든 변경 사항은 체크아웃 프로세스에 의해 제거됩니다.