CI에서 Apollo 제품 릴리스 게시
CI 파이프라인에 Apollo CLI를 추가하면 소프트웨어 변경을 빌드한 뒤 새 제품 릴리스를 제품 카탈로그에 등록할 수 있습니다. 컨테이너 이미지와 Helm 차트를 저장소에 게시하는 단계, Apollo에 제품 릴리스 메타데이터를 등록하는 단계는 구분합니다. product-release create는 메타데이터를 등록하며 실제 차트나 컨테이너 이미지 자체를 게시하지 않습니다.
필요한 접근과 인증 정보
CI 실행 환경에서 Apollo 허브와 아티팩트 레지스트리에 접근할 수 있어야 합니다. 조직 내부 네트워크에서 CI를 실행한다면 허브 접근을 위한 외부 통신용 IP 허용 목록도 준비합니다.
CLI 인증은 다음 중 하나를 사용합니다.
| 방식 | CI에서 준비할 정보 | CLI 인증 옵션 |
|---|---|---|
| Bearer 토큰(Bearer token) | 비밀 정보 APOLLO_TOKEN | --apollo-token |
| 서비스 계정 | 클라이언트 ID(client ID)와 클라이언트 비밀값(client secret) | --apollo-client-id, --apollo-client-secret |
두 방식을 동시에 설정하지 않습니다. CI의 계정에는 대상 제품의 릴리스를 생성할 권한이 필요하며, 새 제품을 만들려면 제품 생성에 필요한 권한도 있어야 합니다.
허브 주소는 --apollo-url로 전달합니다. 제품 릴리스의 Maven 좌표는 groupID:artifactID:version 형식이며, Helm 차트 제품을 초기화할 때 --maven-coordinate로 지정합니다.
CI에서 CLI를 준비하는 방법
CLI를 사용할 수 있도록 만드는 방법은 네 가지입니다.
- CI 작업 중 허브에서 직접 다운로드합니다. 권장되는 방법입니다.
- 허브에서 받은 바이너리를 CI 실행 머신이 접근할 수 있는 위치에 보관합니다.
- 코드 저장소에 CLI 바이너리를 포함합니다.
- CLI가 포함된 Docker 이미지를 CI 작업 이미지로 사용합니다.
직접 다운로드할 때는 허브의 운영체제별 바이너리 경로를 사용합니다. 아래 명령은 APOLLO_URL에 끝의 /가 없는 허브 URL을, OS_DISTRIBUTION에 대상 배포판 식별자를 지정한다고 가정합니다. 이후 GitLab 예시는 Linux AMD64 실행 환경을 대상으로 linux-amd64를 사용합니다.
Bearer 토큰으로 다운로드
CLI_DOWNLOAD_TOKEN="${APOLLO_TOKEN}"
서비스 계정으로 다운로드
서비스 계정의 경우 Multipass에 클라이언트 인증 정보 방식(client credentials grant)으로 요청하여 다운로드에 사용할 Bearer 토큰을 얻습니다. 이 단계에는 curl과 jq가 필요합니다.
CLI_DOWNLOAD_TOKEN="$(
curl --fail --silent --show-error \
"${APOLLO_URL}/multipass/api/oauth2/token" \
--header 'content-type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode "client_id=${CLIENT_ID}" \
--data-urlencode "client_secret=${CLIENT_SECRET}" \
| jq -er '.access_token'
)"
다운로드용 토큰과 이후 CLI의 인증 방식을 구분하여 관리합니다. 서비스 계정으로 CLI를 실행할 때는 클라이언트 ID와 클라이언트 비밀값을 전달하고 Bearer 토큰 옵션을 함께 전달하지 않습니다.
바이너리 받기
위 두 방식 중 하나로 CLI_DOWNLOAD_TOKEN을 준비한 뒤 다음 명령을 실행합니다.
curl --fail --silent --show-error --location \
--output apollo-cli \
--header "authorization: Bearer ${CLI_DOWNLOAD_TOKEN}" \
"${APOLLO_URL}/assets/dyn/apollo-cli/bin/${OS_DISTRIBUTION}/apollo-cli"
chmod 700 apollo-cli
차트를 제품 릴리스로 등록하기
Helm 차트는 허브의 아티팩트 저장소나 외부 Artifactory, Harbor 등의 저장소에 게시할 수 있습니다. helm-chart init의 --repository-url은 차트가 있는 저장소를 지정합니다. 원격 OCI 차트의 --name에는 차트까지 포함한 전체 OCI URL을 사용합니다.
다음 Bash 예시는 이미 저장소에 게시된 가상의 apollo-demo-chart를 제품 com.example:apollo-demo-app으로 등록합니다. RELEASE_VERSION은 게시한 차트 버전이며, 레지스트리 사용자 이름과 비밀번호는 CI의 비밀 정보로 제공한다고 가정합니다.
먼저 선택한 인증 방식에 맞는 인수를 준비합니다. Bearer 토큰 방식은 다음과 같습니다.
apollo_auth=(
--apollo-url "${APOLLO_URL}"
--apollo-token "${APOLLO_TOKEN}"
)
서비스 계정 방식에서는 위 배열 대신 다음 배열을 사용합니다.
apollo_auth=(
--apollo-url "${APOLLO_URL}"
--apollo-client-id "${CLIENT_ID}"
--apollo-client-secret "${CLIENT_SECRET}"
)
이후 차트 정보를 초기화하고 제품 릴리스를 등록합니다.
./apollo-cli product-release helm-chart init \
"${apollo_auth[@]}" \
--maven-coordinate "com.example:apollo-demo-app:${RELEASE_VERSION}" \
--name "oci://${HUB_ARTIFACT_STORE_HOST}/apollo-demo-chart" \
--version "${RELEASE_VERSION}" \
--repository-url "oci://${HUB_ARTIFACT_STORE_HOST}/apollo-demo-chart" \
--username "${HUB_ARTIFACT_STORE_USER}" \
--password "${HUB_ARTIFACT_STORE_PASS}" \
--output-dir "./chart"
./apollo-cli product-release create \
"${apollo_auth[@]}" \
--manifest "./chart/manifest.yml"
두 CLI 실행에 허브 URL과 선택한 인증 정보를 명시적으로 전달합니다. 레지스트리의 --username, --password는 차트를 가져오기 위한 인증 정보이며 Apollo 허브에 접근할 Bearer 토큰 또는 서비스 계정과 용도가 다릅니다.
기존 CI에 통합할 때는 차트를 게시한 작업 뒤에 이 초기화와 등록 작업을 추가할 수 있습니다.
전체 GitLab CI 예시
아래는 컨테이너 이미지 게시, Helm 차트 게시, Apollo 릴리스 등록을 연결한 예시입니다. 입력 조건은 다음과 같습니다.
- 저장소 루트에 애플리케이션의
Dockerfile이 있습니다. helm-chart/Chart.yaml의 차트 이름은apollo-demo-chart입니다.- 차트가
values.yaml의imageVersion을 컨테이너 이미지 태그로 사용합니다. 이미지 저장소는 게시할apollo-demo-app경로로 구성되어 있어야 합니다. - Git 태그는
2.0.0처럼 차트 버전으로 사용할 수 있는 Semantic Versioning 값입니다. apollo-ci태그가 있는 Linux AMD64 GitLab Docker executor Runner를 사용합니다.- Docker-in-Docker를 실행할 Runner에는
privileged = true와 서비스 및 작업 컨테이너가 공유할/certs/client볼륨이 설정되어 있습니다. 권한이 높은 컨테이너를 실행하므로 조직이 허용한 Runner에서 사용합니다.
다음 값을 CI 변수로 등록합니다. 인증 정보는 비밀 정보로 관리하며 저장소의 YAML에 직접 넣지 않습니다.
| 변수 | 값 |
|---|---|
APOLLO_URL | 끝의 /가 없는 Apollo 허브 URL |
APOLLO_TOKEN | Apollo API에 접근할 Bearer 토큰 |
HUB_ARTIFACT_STORE_HOST | OCI 레지스트리 호스트. 예를 들어 허브의 컨테이너 저장소 호스트 |
HUB_ARTIFACT_STORE_USER | 레지스트리 사용자 이름 |
HUB_ARTIFACT_STORE_PASS | 레지스트리 비밀번호 |
예시는 Docker CLI와 Docker-in-Docker의 버전을 29.9.1로 맞추고, 차트 작업에는 alpine/helm:4.3.1을 사용합니다. Helm 이미지에는 curl, Bash, Git과 Go 기반 yq가 포함되어 있습니다. Helm 이미지의 기본 진입점은 helm이므로 GitLab 작업 스크립트를 실행하도록 entrypoint를 비웁니다.
stages:
- build_and_push_container
- build_and_push_helm_chart
- publish_helm_chart_apollo
default:
tags:
- apollo-ci
.release_job:
rules:
- if: '$CI_COMMIT_TAG'
build_and_push_container:
extends: .release_job
stage: build_and_push_container
image: docker:29.9.1-cli
services:
- name: docker:29.9.1-dind
alias: docker
variables:
HEALTHCHECK_TCP_PORT: "2376"
variables:
DOCKER_HOST: tcp://docker:2376
DOCKER_TLS_CERTDIR: /certs
DOCKER_TLS_VERIFY: "1"
DOCKER_CERT_PATH: /certs/client
before_script:
- docker info
- |
set -eu
printf '%s' "${HUB_ARTIFACT_STORE_PASS}" | docker login \
"${HUB_ARTIFACT_STORE_HOST}" \
--username "${HUB_ARTIFACT_STORE_USER}" --password-stdin
script:
- |
set -eu
export RELEASE_VERSION="${CI_COMMIT_TAG}"
docker build \
--tag "${HUB_ARTIFACT_STORE_HOST}/apollo-demo-app:${RELEASE_VERSION}" .
docker push "${HUB_ARTIFACT_STORE_HOST}/apollo-demo-app:${RELEASE_VERSION}"
build_and_push_helm_chart:
extends: .release_job
stage: build_and_push_helm_chart
image:
name: alpine/helm:4.3.1
entrypoint: [""]
script:
- |
set -eu
export RELEASE_VERSION="${CI_COMMIT_TAG}"
yq -i '.imageVersion = strenv(RELEASE_VERSION)' helm-chart/values.yaml
cd helm-chart
helm package . \
--version "${RELEASE_VERSION}" \
--app-version "${RELEASE_VERSION}"
printf '%s' "${HUB_ARTIFACT_STORE_PASS}" | helm registry login \
"${HUB_ARTIFACT_STORE_HOST}" \
--username "${HUB_ARTIFACT_STORE_USER}" --password-stdin
helm push "apollo-demo-chart-${RELEASE_VERSION}.tgz" \
"oci://${HUB_ARTIFACT_STORE_HOST}"
artifacts:
paths:
- helm-chart/*.tgz
- helm-chart/values.yaml
publish_helm_chart_apollo:
extends: .release_job
stage: publish_helm_chart_apollo
image:
name: alpine/helm:4.3.1
entrypoint: [""]
dependencies:
- build_and_push_helm_chart
before_script:
- |
set -eu
curl --fail --silent --show-error --location \
--output apollo-cli \
--header "authorization: Bearer ${APOLLO_TOKEN}" \
"${APOLLO_URL}/assets/dyn/apollo-cli/bin/linux-amd64/apollo-cli"
chmod 700 apollo-cli
script:
- |
set -eu
export RELEASE_VERSION="${CI_COMMIT_TAG}"
./apollo-cli product-release helm-chart init \
--apollo-url "${APOLLO_URL}" \
--apollo-token "${APOLLO_TOKEN}" \
--chart-path "helm-chart/apollo-demo-chart-${RELEASE_VERSION}.tgz" \
--repository-url "oci://${HUB_ARTIFACT_STORE_HOST}" \
--maven-coordinate "com.example:apollo-demo-app:${RELEASE_VERSION}" \
--output-dir "./chart"
set -- \
--apollo-url "${APOLLO_URL}" \
--apollo-token "${APOLLO_TOKEN}" \
--manifest "./chart/manifest.yml" \
--default-config "helm-chart/values.yaml"
if [ -f helm-chart/values.schema.json ]; then
set -- "$@" --config-schema "helm-chart/values.schema.json"
fi
./apollo-cli product-release create "$@"
CI_COMMIT_TAG를 세 단계에서 같은 RELEASE_VERSION으로 사용하므로 컨테이너 이미지 태그, 차트 버전, appVersion과 Apollo 제품 릴리스 버전이 연결됩니다. imageVersion은 문자열로 갱신하며, 태그 값을 숫자로 해석하지 않습니다.
dependencies는 차트 게시 작업의 아티팩트만 가져오도록 지정합니다. 마지막 작업은 앞 단계에서 패키징한 차트를 --chart-path로 읽으므로 이름과 버전을 그 패키지에서 얻습니다. 함께 전달한 values.yaml은 같은 빌드에서 갱신한 기본 구성값입니다. 차트의 선택적 values.schema.json이 있으면 기본 구성값과 함께 등록합니다.
rules는 태그 파이프라인에서만 세 작업을 실행합니다. 모든 커밋에서 게시하려면 실행 규칙을 바꾸고, 각 빌드에 사용할 유효하고 고유한 릴리스 버전도 정해야 합니다.
게시가 완료되면 Apollo 왼쪽 메뉴의 Products에서 제품 카탈로그를 열고 해당 제품 홈에서 새 릴리스를 확인할 수 있습니다. 실제 배포 환경이 아티팩트를 가져오려면 그 환경에 필요한 레지스트리 접근도 설정되어 있어야 합니다.