- 서비스가 작업에 연결되는 방법
- 서비스의 헬스 체크가 작동하는 방법
- 서비스 이미지로 제공되는 소프트웨어 사용하기
.gitlab-ci.yml
파일에services
정의하기- 서비스 접근하기
- CI/CD 변수를 서비스에 전달하기
서비스
에 대한 사용 가능한 설정- 동일한 이미지에서 여러 서비스 시작하기
- 서비스의 명령 설정하기
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:dind
를 사용하여 Docker-in-Docker를 허용할 수도 있습니다. CI/CD 작업이 진행되기 위해 필요한 거의 모든 것이 네트워크를 통해 접근됩니다.
이것이 제대로 작동하는지 확인하기 위해, 러너는:
- 컨테이너에서 기본적으로 노출된 포트를 확인합니다.
- 이러한 포트가 접근 가능할 때까지 기다리는 특별한 컨테이너를 시작합니다.
체크의 두 번째 단계가 실패하면 경고를 출력합니다: *** 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
정의하기
작업마다 서로 다른 이미지와 서비스를 정의하는 것도 가능합니다:
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
에 대한 확장 구성 옵션을 전달할 수 있습니다:
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 옵션으로 변환됩니다. 구문은 Dockerfile 의 ENTRYPOINT 지시어와 유사하며, 각 쉘 토큰은 배열의 개별 문자열입니다. |
command |
아니요 | 9.4 | 컨테이너의 명령으로 사용해야 하는 명령이나 스크립트입니다. 이는 이미지 이름 뒤에 Docker에 전달되는 인수로 변환됩니다. 구문은 Dockerfile 의 CMD 지시어와 유사하며, 각 쉘 토큰은 배열의 개별 문자열입니다. |
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 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 소켓 바인딩을 사용하는 Docker 실행자 사용 안 함.
만약 꼭 사용해야 한다면, 위의 예에서
host
대신 이 작업을 위해 생성된 동적 네트워크 이름을 사용하세요.
도커 통합 작동 방식
아래는 작업 시간 동안 도커가 수행하는 단계에 대한 개요입니다.
- 서비스 컨테이너 생성:
mysql
,postgresql
,mongodb
,redis
. -
config.toml
및 빌드 이미지(ruby:2.6
와 같은)의Dockerfile
에 정의된 모든 볼륨을 저장할 캐시 컨테이너를 생성합니다. - 빌드 컨테이너를 생성하고 모든 서비스 컨테이너를 빌드 컨테이너에 연결합니다.
- 빌드 컨테이너를 시작하고 컨테이너에 작업 스크립트를 전송합니다.
- 작업 스크립트를 실행합니다.
- 코드 체크아웃:
/builds/group-name/project-name/
. -
.gitlab-ci.yml
에 정의된 모든 단계를 실행합니다. - 빌드 스크립트의 종료 상태를 확인합니다.
- 빌드 컨테이너와 생성된 모든 서비스 컨테이너를 제거합니다.
서비스 컨테이너 로그 캡처
- 소개됨 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 변수 마스크하기를 참조하십시오.
로컬에서 작업 디버그하기
다음 명령은 루트 권한 없이 실행됩니다. 사용자의 계정으로 도커를 실행할 수 있어야 합니다.
먼저 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 변경 사항은 체크아웃 프로세스에 의해 제거됩니다.