Overview
이번 글에서는 GitLab CI를 활용하여 Docker 이미지를 빌드하고,
Harbor, GCP Artifact Registry, AWS ECR 등 세 가지 컨테이너 레지스트리에 환경별로 동적 업로드하는 방법을 알아본다.
빌드 도구로는 Kaniko를 사용하며, Namespace에 따라 GCP, AWS ECR, 또는 Harbor로 동적으로 업로드 대상이 전환되도록 구성한다.
또한, Kubernetes 환경에서 사용할 수 있도록 dockerconfigjson 기반의 Secret 생성 방식도 다루며,
CI Pipeline 안에서 GCP 인증, AWS ECR 인증, 비공개 레지스트리 인증,
그리고 이미지 캐시 전략까지 포함한 실전 수준의 GitLab CI 구성법을 설명한다.

📅 관련 글
2023.04.20 - [IaC/CI CD Tool] - 1. GitLab이란? / 개념 및 설치
2023.04.23 - [IaC/CI CD Tool] - 2. GitLab이란? / GitLab Runner 개념 및 설치
2023.04.24 - [IaC/CI CD Tool] - 3. GitLab이란? / GitLab CI/CD
2023.08.08 - [IaC/CI CD Tool] - 4. GitLab 버전 업그레이드
2023.08.08 - [IaC/CI CD Tool] - 5. GitLab ArgoCD 연동
2024.05.28 - [IaC/CI CD Tool] - 6. Gitlab CI Build(with GCP Artifact Registry, Harbor)
2024.06.18 - [IaC/CI CD Tool] - 7. Gitlab CI Template 활용
2025.01.09 - [IaC/CI CD Tool] - 8. Gitlab Repository Mirroring 방법
Gitlab CI Build and Push(with GCP Artifact Registry, Harbor)
dockerConfigJson 작성
Harbor
- 사용자 이름과 비밀번호 인코딩: `echo -n` 을 사용하여 사용자 이름과 비밀번호를 한 줄로 출력하고, `base64` 명령으로 인코딩한다. `n` 옵션은 라인 끝의 개행문자를 제거한다.
- JSON 파일 생성: `cat <<EOF > config.json` 를 사용하여 다중 라인의 텍스트를 `config.json` 파일로 리다이렉션한다. 변수 `$AUTH` 는 이전 단계에서 생성된 인코딩된 문자열을 사용한다.
- 전체 JSON 파일 인코딩: `cat config.json` 으로 파일을 읽고, ` base64` 로 인코딩한 다음, `tr -d '\\n'` 으로 인코딩된 문자열에서 개행 문자를 제거한다.
- Kubernetes Secret 생성용 YAML 파일 준비: 위와 비슷한 방식으로 `YAML` 파일을 생성하며, 이 파일은 Kubernetes에서 사용할 수 있다.
사용자 이름과 비밀번호를 인코딩한다.
AUTH=$(echo -n 'somaz:somaz@2024' | base64)
JSON 파일을 생성한다.
cat <<EOF > config.json
{
"auths": {
"your.harbor.domain": {
"username": "somaz",
"password": "somaz@2024",
"email": "somaz.link",
"auth": "$AUTH"
}
}
}
EOF
# 한줄작성
echo '{"auths":{"your.harbor.domain":{"username":"somaz","password":"somaz@2024","email":"somaz.link","auth":"'"$AUTH"'"}}}' > config.json
전체 JSON 파일을 BASE64로 한번더 인코딩한다.
# JSON 파일을 BASE64로 인코딩
ENCODED_JSON=$(cat config.json | base64 -w 0)
or
# 둘중 하나 선택
ENCODED_JSON=$(cat config.json | base64 -w 0 | tr -d '\\n')
# 확인
echo $ENCODED_JSON
eyJhdXRocyI6eyJ5b3VyLmhhcmJvci5kb21haW4iOnsidXNlcm5hbWUiOiJzb21heiIsInBhc3N3b3JkIjoic29tYXpAMjAyNCIsImVtYWlsIjoic29tYXoubGluayIsImF1dGgiOiJjMjl0WVhvNmMyOXRZWHBBTWpBeU5BPT0ifX19Cg==
echo "eyJhdXRocyI6eyJ5b3VyLmhhcmJvci5kb21haW4iOnsidXNlcm5hbWUiOiJzb21heiIsInBhc3N3b3JkIjoic29tYXpAMjAyNCIsImVtYWlsIjoic29tYXoubGluayIsImF1dGgiOiJjMjl0WVhvNmMyOXRZWHBBTWpBeU5BPT0ifX19Cg==" |base64 -d -w 0
{"auths":{"your.harbor.domain":{"username":"somaz","password":"somaz@2024","email":"somaz.link","auth":"c29tYXo6c29tYXpAMjAyNA=="}}}
마지막으로 Kubernetes Secret 생성용 YAML 파일 준비한다.
cat <<EOF > k8s-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: myregistrykey
namespace: default
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: $ENCODED_JSON
EOF
Google Artifact Registry
- Docker 로그인 실행 : 로컬에서 `serviceaccount.json` 파일을 사용하여 Docker 로그인을 수행하고, 결과로 생성된 `config.json` 파일을 생성한다.
- dockerconfigjson 파일 준비 : 로그인 후 생성된 `~/.docker/config.json` 파일을 `base64` 로 인코딩한다. 인코딩된 결과는 Helm의 values 파일에 사용된다.
- Kubernetes Secret 생성 : 인코딩된 `dockerconfigjson`를 Helm 차트의 values 파일에 삽입하여 Kubernetes에서 사용할 수 있는 Secret을 생성한다.
`serviceaccount.json` 파일을 사용하여 로컬에서 `dockerconfigjson` 을 생성해본다.
export REGION=<YOUR-REGION>
cat serviceaccount.json | docker login -u _json_key --password-stdin https://$REGION.pkg.dev
생성된 `config.json` 파일의 내용을 복사하여 Helm values 파일의 `artifactregistry.dockerConfigJson` 필드에 `base64` 인코딩된 형태로 삽입한다.
cat ~/.docker/config.json | base64 -w 0
Kubernetes Secret을 생성한다. 이번에는 Helm Chart를 사용해본다. template을 먼저 생성한다.
`gcp-ar-secret.yaml`
{{- if .Values.artifactregistry.enabled }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "somaz.fullname" . }}
namespace: {{ .Release.Namespace }}
type: kubernetes.io/dockerconfigjson
data:
.dockerconfigjson: {{ .Values.artifactregistry.dockerConfigJson }}
{{- end }}
`values.yaml`
image:
repository: <region>-docker.pkg.dev/mgmt-2023/<repo name>/<image name>
pullPolicy: IfNotPresent
# Overrides the image tag whose default is the chart appVersion.
tag: "<image Tag>"
imagePullSecrets:
- name: <image pull secret name>
artifactregistry:
enabled: false
dockerConfigJson: "BASE64_ENCODED_JSON_HERE"
AWS ECR
AWS ECR의 경우 Access Token을 기반으로 한 인증이 필요하다.
# AWS CLI를 통한 ECR 로그인 토큰 획득
ECR_TOKEN=$(aws ecr get-login-password --region $AWS_REGION)
ECR_AUTH=$(echo "AWS:$ECR_TOKEN" | base64 -w 0)
# dockerconfigjson 생성
echo "{\"auths\":{\"$AWS_ECR_REGISTRY\":{\"auth\":\"$ECR_AUTH\"}}}" > ~/.docker/config.json
# base64 인코딩
cat ~/.docker/config.json | base64 -w 0
실전 GitLab CI 구성
Build에는 Kaniko를 사용한다. 환경별 레지스트리 분기 처리가 포함된 완전한 GitLab CI 구성을 살펴보자.
Gitlab Runner 관련해서는 아래의 게시물 참고하면 된다.
2023.04.18 - [IaC/CI CD Tool] - 2. GitLab이란? / GitLab Runner 개념 및 설치
변수 설정
stages:
- prepare_admin
- aws-auth
- gcloud
- build
- build_aws
- update
variables:
NAMESPACE:
value: 'dev'
description: 'Select the namespace: dev1 or dev2 or staging or alpha'
options:
- 'dev'
- 'dev2'
- 'staging'
- 'alpha'
SERVICE:
value: 'game'
description: 'Select game or admin'
options:
- 'game'
- 'admin'
# 공통 변수
IMAGE_PROJECT: projectm
CI_REGISTRY_IMAGE: $CI_REGISTRY/$IMAGE_PROJECT/$SERVICE
BUILD_TAG: $CI_COMMIT_SHORT_SHA
IMAGE_URL: '${CI_REGISTRY_IMAGE}:${BUILD_TAG}'
BUILD_TAG_LATEST: latest
IMAGE_URL_LATEST: '${CI_REGISTRY_IMAGE}:${BUILD_TAG_LATEST}'
# GCP Artifact Registry 변수
GCP_CI_REGISTRY_IMAGE: '$GCP_CI_REGISTRY/${SERVICE}-fgn/${SERVICE}-fgn'
GCP_IMAGE_URL: '${GCP_CI_REGISTRY_IMAGE}:${BUILD_TAG}'
GCP_IMAGE_URL_LATEST: '${GCP_CI_REGISTRY_IMAGE}:${BUILD_TAG_LATEST}'
# AWS ECR 변수
AWS_REGION: eu-central-1
AWS_ACCOUNT_ID: 027438161209 # 예시
AWS_ECR_REGISTRY: '$AWS_ACCOUNT_ID.dkr.ecr.eu-central-1.amazonaws.com'
AWS_ECR_IMAGE_URL: '${AWS_ECR_REGISTRY}/luckyday/${SERVICE}:${BUILD_TAG}'
AWS_ECR_IMAGE_URL_LATEST: '${AWS_ECR_REGISTRY}/luckyday/${SERVICE}:${BUILD_TAG_LATEST}'
AWS_ECR_CACHE_REPO: '${AWS_ECR_REGISTRY}/luckyday/${SERVICE}-cache'
AWS ECR 인증 템플릿
방법 1: 기존 Access Key 방식 (Legacy)
.templates:
.aws_auth_template_legacy: &aws_auth_template_legacy
stage: aws-auth
image: amazon/aws-cli:latest
before_script:
- echo "[INFO] Start AWS ECR auth config (Legacy method)"
- export AWS_ACCESS_KEY_ID="${LUCKYDAY_AWS_ACCESS_KEY}"
- export AWS_SECRET_ACCESS_KEY="${LUCKYDAY_AWS_SECRET_KEY}"
- export AWS_DEFAULT_REGION="$AWS_REGION"
# Role ARN이 있으면 assume role 수행
- |
if [ -n "${LUCKYDAY_AWS_ROLE_ARN}" ]; then
CREDENTIALS=$(aws sts assume-role --role-arn "${LUCKYDAY_AWS_ROLE_ARN}" --role-session-name "gitlab-ci-$CI_JOB_ID")
export AWS_ACCESS_KEY_ID=$(echo $CREDENTIALS | jq -r '.Credentials.AccessKeyId')
export AWS_SECRET_ACCESS_KEY=$(echo $CREDENTIALS | jq -r '.Credentials.SecretAccessKey')
export AWS_SESSION_TOKEN=$(echo $CREDENTIALS | jq -r '.Credentials.SessionToken')
fi
- mkdir -p aws
script:
# Docker 없이 ECR 인증 토큰으로 config.json 직접 생성
- ECR_TOKEN=$(aws ecr get-login-password --region $AWS_REGION)
- ECR_AUTH=$(echo "AWS:$ECR_TOKEN" | base64 -w 0)
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$AWS_ECR_REGISTRY\":{\"auth\":\"$ECR_AUTH\"}}}" > ~/.docker/config.json
- cp ~/.docker/config.json aws/config.json
- echo "[INFO] ECR auth config created successfully"
artifacts:
paths:
- aws/config.json
expire_in: 1 hour
tags:
- build-image
방법 2: OIDC Provider 방식 (권장)
OIDC를 사용하면 장기 자격증명 없이도 AWS 리소스에 안전하게 접근할 수 있다.
AWS 설정
# 1. OIDC Provider 생성
aws iam create-open-id-connect-provider \
--url https://gitlab.example.com \
--client-id-list project_path:group/project:ref_type:branch:ref:main \
--thumbprint-list a031c46782e6e6c662c2c87c76da9aa62ccabd8e
# 2. IAM Role 생성
cat > trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::ACCOUNT-ID:oidc-provider/gitlab.example.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"gitlab.example.com:sub": "project_path:group/project:ref_type:branch:ref:main"
}
}
}
]
}
EOF
aws iam create-role \
--role-name GitLabCIRole \
--assume-role-policy-document file://trust-policy.json
# 3. ECR 권한 정책 연결
aws iam attach-role-policy \
--role-name GitLabCIRole \
--policy-arn arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryPowerUser
GitLab CI 구성
.aws_auth_oidc_template: &aws_auth_oidc_template
stage: aws-auth
image: amazon/aws-cli:latest
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.example.com
before_script:
- echo "[INFO] Start AWS ECR OIDC auth config"
- echo "[INFO] Using OIDC token for authentication"
- mkdir -p aws
script:
# OIDC 토큰으로 AWS 역할 assume
- |
export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" \
$(aws sts assume-role-with-web-identity \
--role-arn ${AWS_ROLE_ARN} \
--role-session-name "GitLabRunner-${CI_PROJECT_ID}-${CI_PIPELINE_ID}" \
--web-identity-token ${GITLAB_OIDC_TOKEN} \
--duration-seconds 3600 \
--query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' \
--output text))
# ECR 로그인 토큰 생성
- ECR_TOKEN=$(aws ecr get-login-password --region $AWS_REGION)
- ECR_AUTH=$(echo "AWS:$ECR_TOKEN" | base64 -w 0)
- mkdir -p ~/.docker
- echo "{\"auths\":{\"$AWS_ECR_REGISTRY\":{\"auth\":\"$ECR_AUTH\"}}}" > ~/.docker/config.json
- cp ~/.docker/config.json aws/config.json
- echo "[INFO] OIDC ECR auth config created successfully"
artifacts:
paths:
- aws/config.json
expire_in: 1 hour
tags:
- build-image
GCP 인증 템플릿
방법 1: Service Account Key 방식 (Legacy)
.gcp_auth_template_legacy: &gcp_auth_template_legacy
stage: gcloud
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:latest
variables:
CLOUDSDK_CONFIG: $CI_PROJECT_DIR/gcloud
before_script:
- echo "[INFO] Start gcloud config setting (Legacy method)"
- echo "[INFO] GCP_CI_REGISTRY is $GCP_CI_REGISTRY"
- mkdir -p ${CLOUDSDK_CONFIG}
script:
- echo $GCP_SERVICE_ACCOUNT_KEY_BASE64 | base64 --decode > ${CLOUDSDK_CONFIG}/gcloud-service-key.json
- gcloud auth activate-service-account --key-file=${CLOUDSDK_CONFIG}/gcloud-service-key.json
- token=$(gcloud auth print-access-token)
- docker_token=$(echo -n "gclouddockertoken:$token" | base64 | tr -d "\\n")
- echo "{\"auths\":{\"$GCP_CI_REGISTRY\":{\"auth\":\"$docker_token\",\"email\":\"admin@company.com\"}}}" > gcloud/config.json
artifacts:
paths:
- gcloud/config.json
expire_in: 1 hour
tags:
- build-image
방법 2: Workload Identity Federation 방식 (권장)
Workload Identity Federation을 사용하면 Service Account JSON 키 없이도 안전하게 GCP에 인증할 수 있다.
GCP 설정
# 1. Workload Identity Pool 생성
gcloud iam workload-identity-pools create "gitlab-pool" \
--project="${PROJECT_ID}" \
--location="global" \
--display-name="GitLab CI Pool"
# 2. Workload Identity Provider 생성 (GitLab용)
gcloud iam workload-identity-pools providers create-oidc "gitlab-provider" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="gitlab-pool" \
--display-name="GitLab Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.actor=assertion.actor,attribute.repository=assertion.project_path" \
--issuer-uri="https://gitlab.example.com" \
--attribute-condition="assertion.project_path == 'group/project'"
# 3. Service Account 생성 및 권한 부여
gcloud iam service-accounts create gitlab-ci-sa \
--display-name="GitLab CI Service Account"
# 4. Artifact Registry 권한 부여
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:gitlab-ci-sa@${PROJECT_ID}.iam.gserviceaccount.com" \
--role="roles/artifactregistry.writer"
# 5. Workload Identity 바인딩
gcloud iam service-accounts add-iam-policy-binding \
--role roles/iam.workloadIdentityUser \
--member "principalSet://iam.googleapis.com/projects/${PROJECT_NUMBER}/locations/global/workloadIdentityPools/gitlab-pool/attribute.repository/group/project" \
gitlab-ci-sa@${PROJECT_ID}.iam.gserviceaccount.com
# 6. Provider 정보 확인
gcloud iam workload-identity-pools providers describe "gitlab-provider" \
--project="${PROJECT_ID}" \
--location="global" \
--workload-identity-pool="gitlab-pool" \
--format="value(name)"
GitLab CI 구성
.gcp_auth_wif_template: &gcp_auth_wif_template
stage: gcloud
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:latest
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.example.com
variables:
CLOUDSDK_CONFIG: $CI_PROJECT_DIR/gcloud
before_script:
- echo "[INFO] Start gcloud Workload Identity Federation config"
- echo "[INFO] GCP_CI_REGISTRY is $GCP_CI_REGISTRY"
- echo "[INFO] Using Workload Identity Federation"
- mkdir -p ${CLOUDSDK_CONFIG}
script:
# Workload Identity Federation을 통한 인증
- |
gcloud iam workload-identity-pools create-cred-config \
projects/${GCP_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${GCP_WIF_POOL_ID}/providers/${GCP_WIF_PROVIDER_ID} \
--service-account=${GCP_SERVICE_ACCOUNT}@${GCP_PROJECT_ID}.iam.gserviceaccount.com \
--output-file=${CLOUDSDK_CONFIG}/wif-config.json \
--credential-source-file=${CI_PROJECT_DIR}/gitlab-oidc-token
# OIDC 토큰을 파일로 저장
- echo ${GITLAB_OIDC_TOKEN} > ${CI_PROJECT_DIR}/gitlab-oidc-token
# WIF 자격증명으로 gcloud 인증
- gcloud auth login --cred-file=${CLOUDSDK_CONFIG}/wif-config.json
- gcloud config set project ${GCP_PROJECT_ID}
# Docker 인증을 위한 토큰 생성
- token=$(gcloud auth print-access-token)
- docker_token=$(echo -n "_json_key:$(gcloud auth print-access-token)" | base64 | tr -d "\\n")
- echo "{\"auths\":{\"$GCP_CI_REGISTRY\":{\"auth\":\"$docker_token\",\"email\":\"gitlab-ci@${GCP_PROJECT_ID}.iam.gserviceaccount.com\"}}}" > gcloud/config.json
- echo "[INFO] Workload Identity Federation auth completed"
artifacts:
paths:
- gcloud/config.json
expire_in: 1 hour
tags:
- build-image
# 간소화된 WIF 템플릿 (gcloud auth configure-docker 사용)
.gcp_auth_wif_simple_template: &gcp_auth_wif_simple_template
stage: gcloud
image: gcr.io/google.com/cloudsdktool/google-cloud-cli:latest
id_tokens:
GITLAB_OIDC_TOKEN:
aud: https://gitlab.example.com
script:
# 환경변수 설정으로 간소화
- export GOOGLE_APPLICATION_CREDENTIALS="/tmp/wif-config.json"
- |
cat > /tmp/wif-config.json << EOF
{
"type": "external_account",
"audience": "//iam.googleapis.com/projects/${GCP_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${GCP_WIF_POOL_ID}/providers/${GCP_WIF_PROVIDER_ID}",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"token_url": "https://sts.googleapis.com/v1/token",
"credential_source": {
"file": "/tmp/gitlab-oidc-token"
},
"service_account_impersonation_url": "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/${GCP_SERVICE_ACCOUNT}@${GCP_PROJECT_ID}.iam.gserviceaccount.com:generateAccessToken"
}
EOF
- echo ${GITLAB_OIDC_TOKEN} > /tmp/gitlab-oidc-token
- gcloud auth login --cred-file=/tmp/wif-config.json
- gcloud auth configure-docker ${GCP_CI_REGISTRY} --quiet
- cp ~/.docker/config.json gcloud/config.json
artifacts:
paths:
- gcloud/config.json
expire_in: 1 hour
tags:
- build-image
빌드 템플릿 (멀티 레지스트리 지원)
.build_template: &build_template
stage: build
image:
name: harbor.concrit.us/library/kaniko-project/executor:v1.23.0-debug
entrypoint: ['']
before_script:
- echo "[INFO] Start build image"
- echo "[INFO] SERVICE is $SERVICE"
- echo "[INFO] NAMESPACE is $NAMESPACE"
- echo "[INFO] BUILD_TAG is $BUILD_TAG"
- mkdir -p /kaniko/.docker
# 환경별 레지스트리 설정
- |
if [[ "$NAMESPACE" == "alpha" ]]; then
echo "Using AWS ECR for alpha environment"
cp aws/config.json /kaniko/.docker/config.json
elif [[ "$NAMESPACE" =~ ^somaz- ]]; then
echo "Using GCP Artifact Registry for somaz environments"
cp gcloud/config.json /kaniko/.docker/config.json
else
echo "Using Harbor for standard environments"
echo "{\"auths\":{\"$CI_REGISTRY\":{\"auth\":\"$(echo -n ${CI_REGISTRY_USER}:${CI_REGISTRY_PASSWORD} | base64)\"}}}" > /kaniko/.docker/config.json
fi
script:
- |
if [[ "$NAMESPACE" == "alpha" ]]; then
# AWS ECR 빌드
/kaniko/executor \
--cache=true \
--cache-ttl=24h \
--cache-repo $AWS_ECR_CACHE_REPO \
--snapshot-mode=redo \
--context $CI_PROJECT_DIR \
--dockerfile $CI_PROJECT_DIR/$SERVICE.Dockerfile \
--destination $AWS_ECR_IMAGE_URL \
--destination $AWS_ECR_IMAGE_URL_LATEST \
--build-arg NODE_ENV=$NAMESPACE \
--skip-tls-verify
elif [[ "$NAMESPACE" =~ ^somaz- ]]; then
# GCP Artifact Registry 빌드
/kaniko/executor \
--cache=true \
--cache-ttl=24h \
--snapshot-mode=redo \
--context $CI_PROJECT_DIR \
--dockerfile $CI_PROJECT_DIR/$SERVICE.Dockerfile \
--destination $GCP_IMAGE_URL \
--destination $GCP_IMAGE_URL_LATEST \
--build-arg NODE_ENV=$NAMESPACE \
--skip-tls-verify
else
# Harbor 빌드
/kaniko/executor \
--cache=true \
--cache-ttl=24h \
--snapshot-mode=redo \
--context $CI_PROJECT_DIR \
--dockerfile $CI_PROJECT_DIR/$SERVICE.Dockerfile \
--destination $IMAGE_URL \
--destination $IMAGE_URL_LATEST \
--build-arg NODE_ENV=$NAMESPACE \
--skip-tls-verify \
--insecure-pull
fi
tags:
- build-image
실제 Job 정의
# AWS ECR 인증 (alpha 환경용)
aws_auth_manual:
<<: *aws_auth_template
rules:
- if: '($CI_PIPELINE_SOURCE == "web" && $NAMESPACE == "alpha")'
aws_auth_auto:
<<: *aws_auth_template
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME == "alpha"'
# GCP 인증 (somaz- 환경용)
gcp_auth_manual:
<<: *gcp_auth_template
rules:
- if: '($CI_PIPELINE_SOURCE == "web" && $NAMESPACE =~ /^somaz-/)'
gcp_auth_auto:
<<: *gcp_auth_template
rules:
- if: '$CI_PIPELINE_SOURCE == "push" && $CI_COMMIT_REF_NAME =~ /^somaz-/'
# 빌드 Job들
build_manual_image:
<<: *build_template
needs:
- job: aws_auth_manual
artifacts: true
optional: true
- job: gcp_auth_manual
artifacts: true
optional: true
rules:
- if: '($CI_PIPELINE_SOURCE == "web" && $SERVICE != "admin")'
build_auto_image_game:
<<: *build_template
needs:
- job: aws_auth_auto
artifacts: true
optional: true
- job: gcp_auth_auto
artifacts: true
optional: true
variables:
SERVICE: game
NAMESPACE: $CI_COMMIT_REF_NAME
rules:
- if: '$CI_PIPELINE_SOURCE == "push"'
changes:
- apps/game/**/*
- game.Dockerfile
- libs/**/*
환경별 분기 처리 로직
1. Alpha 환경 → AWS ECR
- NAMESPACE == "alpha"인 경우
- AWS ECR 캐시 레포지토리 활용
- IAM Role 기반 인증 지원
2. Somaz 환경 → GCP Artifact Registry
- NAMESPACE =~ /^somaz-/인 경우
- Service Account 키 기반 인증
- GCP 토큰 방식 사용
3. 일반 환경 → Harbor
- 기타 모든 환경 (dev, dev2, staging 등)
- 기본 username/password 인증
- --insecure-pull 옵션 활용
이미지 정리 정책(Retention Policy) 고려하기
장기 운영 시 Docker 이미지가 계속 누적되므로,
Artifact Registry나 Harbor에서 제공하는 Retention Policy 설정도 병행해야 한다.
- GCP Artifact Registry:
Retention policy 설정 문서를 참고하여 일정 기간 지난 태그 없는 이미지를 자동 삭제하도록 구성 가능하다. - Harbor:
Harbor 관리 페이지에서 Tag Retention Rule을 설정하여,
"Latest 5개 태그만 보관" 등 규칙 기반으로 이미지 정리를 자동화할 수 있다.
불필요한 이미지가 지속적으로 쌓이면 저장소 용량 부족이나 비용 문제가 발생할 수 있으므로,
CI/CD 파이프라인과 함께 이미지 정리 전략도 함께 고려하자.
`CI_JOB_TOKEN` 을 활용한 인증 (옵션)
GitLab 자체 레지스트리(`registry.gitlab.com` 또는 `self-hosted GitLab Registry`)를 사용하는 경우
별도 `docker login` 없이 `CI_JOB_TOKEN` 을 활용한 인증도 가능하다.
docker login -u gitlab-ci-token -p $CI_JOB_TOKEN registry.gitlab.com
해당 방식은 GitLab CI의 Job 안에서 보안적으로 인증 정보를 따로 노출하지 않고도
이미지 Push/Pull이 가능하여 내부 프로젝트에는 유용하다.
단, 외부 레지스트리(GCP, Harbor 등)에는 해당 토큰을 사용할 수 없다.
Kaniko의 --build-arg, --cache 전략 고도화
`--build-arg` 를 통해 동적으로 환경 변수를 주입하거나 `--cache` 세부 설정을 조절해 최적화할 수 있습니다.
Kaniko 캐시 및 빌드 인자 최적화 팁
- `--cache-repo`: 특정 이미지 저장소에 캐시를 유지할 수 있어 멀티 파이프라인에서 공용 캐시가 가능함
- `--build-arg`: Dockerfile에 정의된 ARG를 외부에서 주입 가능
/kaniko/executor \
--cache=true \
--cache-ttl=24h \
--cache-repo=gcr.io/my-project/kaniko-cache \
--build-arg ENV=production \
...
- 빌드 환경에 따라 다양한 ARG를 주입해보자.
- 예: `NODE_ENV, VERSION, BASE_URL`
마무리
GitLab CI를 기반으로 Harbor, GCP Artifact Registry, AWS ECR 등 세 가지 레지스트리를 환경별로 활용하는 파이프라인을 구성하면 다음과 같은 이점을 얻을 수 있다.
- 유연한 배포 전략: 환경별 최적화된 레지스트리 선택
- 하이브리드 인프라 지원: 온프레미스와 멀티 클라우드 동시 운영
- 보안 강화: 환경별 격리된 인증 체계
- 비용 최적화: 각 클라우드별 최적 요금 정책 활용
- 장애 대응: 레지스트리 장애시 대안 경로 확보
실제 프로덕션 환경에서는 각 레지스트리의 특성을 고려하여
- 개발/테스트: Harbor (온프레미스 비용 절약)
- 스테이징: GCP Artifact Registry (클라우드 네이티브)
- 프로덕션: AWS ECR (AWS 인프라와 통합)
과 같은 전략적 배치도 고려할 수 있다.
네임스페이스 기반의 동적 분기 처리 방식은 향후 추가 클라우드 플랫폼(Azure Container Registry 등) 확장시에도 쉽게 적용할 수 있는 확장 가능한 아키텍처를 제공한다.
이제 단순히 GitLab CI로 이미지를 빌드하는 것을 넘어,
환경에 따라 전략적으로 이미지를 저장하고 배포하는 파이프라인 설계가 중요해진 시점이다.
실제 프로젝트에서 이번 구성을 어떻게 응용할 수 있을지 고민해보는 것도 좋은 연습이 될 것이다.
Reference
'IaC > CI CD Tool' 카테고리의 다른 글
| 8. Github Action Template 생성후 MarketPlace 등록하기 (4) | 2024.07.01 |
|---|---|
| 7. Gitlab CI Template 활용 (0) | 2024.06.27 |
| 7. Github Action Build and Push(with GCP Artifact Registry) (0) | 2024.06.19 |
| ArgoCD SSO 구성 가이드(GCP Oauth) (0) | 2024.04.14 |
| 6. Github Action (With Using Concurrency) (0) | 2024.03.20 |