시리즈: gcloud CLI 자동화 운영 플레이북 (총 11편) | 9회
gcloud 자동화 완성: –quiet·–format·종료 코드와 키리스 CI/CD 실전 가이드
gcloud 명령어를 CI/CD 파이프라인에 넣었더니 “확인하시겠습니까?” 프롬프트에서 멈춘 적 있지 않아요? 이 글에서는 비대화형 실행의 3대 표준부터 GitHub Actions 키리스 인증, Secret Manager 중앙화까지 CLI 자동화의 모든 것을 정리했어요.
Summary
- gcloud 자동화의 3대 표준은
--quiet(프롬프트 차단) +--format(구조화 출력) + 종료 코드(성공/실패 판단)야 - CI/CD 인증은 서비스계정 키 대신 Workload Identity Federation(WIF)이 기본이야
- GitHub Actions에서는
google-github-actions/auth액션으로 키리스 인증을 구현할 수 있어 - 비밀(시크릿)은 코드나 환경변수에 하드코딩하지 말고, Secret Manager로 중앙화해서 런타임에 주입해야 해
이 글의 대상
- gcloud 명령어를 CI/CD 파이프라인에 통합하려는 DevOps 엔지니어
- 셸 스크립트로 GCP 리소스 관리를 자동화하고 싶은 인프라 엔지니어
- GitHub Actions에서 GCP와 안전하게 연동하는 방법을 찾는 개발자
목차
- 자동화 3대 표준: –quiet, –format, 종료 코드
- 구조화 출력 마스터하기
- 종료 코드 기반 흐름 제어
- CI 인증: 키리스(WIF)가 기본이야
- GitHub Actions + google-github-actions/auth 실전
- Secret Manager 중앙화와 런타임 주입
- 배치 스크립트 실전 패턴
- 실패 케이스와 트러블슈팅
1. 자동화 3대 표준: –quiet, –format, 종료 코드
“사람이 읽는 CLI”에서 “시스템이 실행하는 CLI”로
gcloud를 터미널에서 직접 쓸 때는 사람이 출력을 읽고 판단해. 하지만 자동화(스크립트, CI/CD)에서는 시스템이 출력을 파싱하고 판단해야 하거든. 이 차이를 해결하는 게 3대 표준이야.
| 표준 | 역할 | 없으면 생기는 문제 |
|---|---|---|
--quiet |
확인 프롬프트 차단 | CI에서 “y/n?” 대기 → 타임아웃 |
--format |
출력을 JSON/CSV/value로 구조화 | 사람용 텍스트를 정규식으로 파싱 → 깨지기 쉬움 |
| 종료 코드 | 성공(0)/실패(비0)로 판단 | 출력 문자열로 성공 여부 판단 → 언어/버전에 따라 변경 |
–quiet: 프롬프트 차단의 핵심
# 나쁜 예: CI에서 프롬프트에 걸림
gcloud compute instances delete my-vm
# → "The following instances will be deleted... Do you want to continue (Y/n)?"
# → CI에서 입력 불가 → 타임아웃 → 실패
# 좋은 예: --quiet로 확인 없이 실행
gcloud compute instances delete my-vm --quiet
# → 확인 없이 즉시 삭제
--quiet가 하는 일:
- 확인 프롬프트 비활성화
- 진행 표시줄(progress bar) 비활성화
- 기본값으로 자동 응답
- 오류 메시지는 정상 출력됨 (–quiet가 오류를 숨기지는 않아)
자동화 스크립트 기본 헤더
모든 자동화 스크립트는 이 헤더로 시작하는 게 좋아:
#!/bin/bash
set -euo pipefail
# set -e: 명령어 실패 시 즉시 종료
# set -u: 미정의 변수 사용 시 오류
# set -o pipefail: 파이프 중간 실패도 감지
PROJECT_ID="${PROJECT_ID:?프로젝트 ID가 설정되지 않았어}"
REGION="${REGION:-asia-northeast3}" # 기본값: 서울
# 프로젝트 설정
gcloud config set project "$PROJECT_ID" --quiet
2. 구조화 출력 마스터하기
–format 옵션 완전 가이드
gcloud의 --format 옵션은 생각보다 훨씬 강력해. 자동화에서 가장 많이 쓰는 3가지 형식을 알아보자.
JSON 출력 (가장 범용적):
# 인스턴스 목록을 JSON으로
gcloud compute instances list \
--format=json \
--quiet
# jq로 특정 필드 추출
gcloud compute instances list \
--format=json \
--quiet | jq -r '.[].name'
value 출력 (특정 필드만 깔끔하게):
# 인스턴스 이름만 추출
gcloud compute instances list \
--format='value(name)'
# 여러 필드를 탭으로 구분
gcloud compute instances list \
--format='value(name, zone, status)'
# 결과 예시:
# my-vm-1 asia-northeast3-a RUNNING
# my-vm-2 asia-northeast3-b TERMINATED
csv 출력 (스프레드시트 연동):
# CSV 형식으로 출력
gcloud compute instances list \
--format='csv(name, zone, machineType, status)'
# 헤더 없이 출력
gcloud compute instances list \
--format='csv[no-heading](name, zone, status)'
format 실전 활용 패턴
| 패턴 | 명령어 | 용도 |
|---|---|---|
| ID만 추출 | --format='value(name)' |
루프 입력용 |
| JSON 파싱 | --format=json \| jq '.field' |
복잡한 데이터 추출 |
| 조건 필터 | --filter='status=RUNNING' |
서버사이드 필터 |
| 정렬 | --sort-by=name |
정렬된 결과 |
| 개수 제한 | --limit=10 |
대량 결과 제한 |
filter와 format 조합
# 실행 중인 인스턴스의 이름과 IP만 추출
gcloud compute instances list \
--filter='status=RUNNING' \
--format='value(name, networkInterfaces[0].accessConfigs[0].natIP)' \
--quiet
# 특정 라벨이 있는 리소스만
gcloud compute instances list \
--filter='labels.env=production' \
--format='table(name, zone, machineType.basename(), status)' \
--quiet
3. 종료 코드 기반 흐름 제어
왜 출력 문자열이 아닌 종료 코드로 판단해야 해?
# 나쁜 예: 출력 문자열로 성공 여부 판단
RESULT=$(gcloud compute instances describe my-vm 2>&1)
if echo "$RESULT" | grep -q "RUNNING"; then
echo "실행 중"
fi
# → 문제: gcloud 출력 형식이 바뀌면 깨져
# 좋은 예: 종료 코드로 판단
if gcloud compute instances describe my-vm --quiet > /dev/null 2>&1; then
echo "인스턴스 존재"
else
echo "인스턴스 없음 (또는 접근 불가)"
fi
gcloud 종료 코드 규칙
| 종료 코드 | 의미 | 예시 |
|---|---|---|
| 0 | 성공 | 리소스 생성/조회 성공 |
| 1 | 일반 오류 | 잘못된 인자, 권한 부족 |
| 2 | 사용법 오류 | 잘못된 명령어 구문 |
실전 흐름 제어 패턴
리소스 존재 확인 후 생성 (멱등성):
# 인스턴스가 없으면 생성, 있으면 스킵
if ! gcloud compute instances describe my-vm \
--zone=asia-northeast3-a \
--quiet > /dev/null 2>&1; then
echo "인스턴스 생성 중..."
gcloud compute instances create my-vm \
--zone=asia-northeast3-a \
--machine-type=e2-medium \
--quiet
else
echo "인스턴스가 이미 존재해. 스킵."
fi
재시도 패턴:
# 최대 3회 재시도
MAX_RETRIES=3
RETRY_COUNT=0
while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
if gcloud compute instances start my-vm \
--zone=asia-northeast3-a \
--quiet; then
echo "인스턴스 시작 성공"
break
fi
RETRY_COUNT=$((RETRY_COUNT + 1))
echo "실패. 재시도 $RETRY_COUNT/$MAX_RETRIES (30초 대기)"
sleep 30
done
if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
echo "최대 재시도 횟수 초과. 수동 확인 필요."
exit 1
fi
병렬 실행 + 결과 수집:
# 여러 인스턴스 동시 삭제 (백그라운드)
INSTANCES=("vm-1" "vm-2" "vm-3")
PIDS=()
FAILED=0
for INSTANCE in "${INSTANCES[@]}"; do
gcloud compute instances delete "$INSTANCE" \
--zone=asia-northeast3-a \
--quiet &
PIDS+=($!)
done
# 모든 작업 완료 대기
for PID in "${PIDS[@]}"; do
if ! wait $PID; then
FAILED=$((FAILED + 1))
fi
done
echo "완료: ${#INSTANCES[@]}개 중 $((${#INSTANCES[@]} - FAILED))개 성공, ${FAILED}개 실패"
[ $FAILED -eq 0 ] || exit 1
4. CI 인증: 키리스(WIF)가 기본이야
왜 키리스인가
8편에서 서비스계정 키의 위험성을 다뤘는데, CI/CD에서는 이 문제가 더 심각해:
| 방식 | 키 파일 인증 | WIF (키리스) |
|---|---|---|
| 보안 | 키 유출 위험 상시 존재 | 키 파일 자체가 없음 |
| 관리 | 키 회전 주기 관리 필요 | 자동, 단기 토큰 (1시간) |
| 감사 | 키 사용처 추적 어려움 | Cloud Audit Logs에 자동 기록 |
| 확장 | 리포마다 키 복사/배포 | Pool 하나로 여러 리포 커버 |
WIF 작동 원리 (GitHub Actions 기준)
GitHub Actions 워크플로 실행
│
▼
GitHub OIDC Provider가 토큰 발급
│
▼
google-github-actions/auth가 토큰을 GCP에 전달
│
▼
GCP Workload Identity Pool이 토큰 검증
│
▼
검증 통과 → GCP 단기 토큰 발급 (1시간)
│
▼
gcloud/bq/gsutil 명령어 인증 완료
5. GitHub Actions + google-github-actions/auth 실전
기본 설정 (한 번만 하면 돼)
GCP 측에서 WIF Pool과 Provider를 설정해야 해. 8편에서 다뤘던 명령어를 다시 정리할게:
# 1. Workload Identity Pool 생성
gcloud iam workload-identity-pools create "github-pool" \
--location="global" \
--display-name="GitHub Actions Pool" \
--project=my-project
# 2. OIDC Provider 추가
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
--location="global" \
--workload-identity-pool="github-pool" \
--display-name="GitHub Provider" \
--attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
--issuer-uri="https://token.actions.githubusercontent.com" \
--project=my-project
# 3. 서비스계정 생성 + 권한 부여
gcloud iam service-accounts create github-actions-sa \
--display-name="GitHub Actions SA" \
--project=my-project
# 4. WIF → 서비스계정 연결
gcloud iam service-accounts add-iam-policy-binding \
github-actions-sa@my-project.iam.gserviceaccount.com \
--role="roles/iam.workloadIdentityUser" \
--member="principalSet://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/attribute.repository/my-org/my-repo" \
--project=my-project
GitHub Actions 워크플로 예시
# .github/workflows/deploy.yml
name: Deploy to GCP
on:
push:
branches: [main]
# WIF에 필요한 권한
permissions:
contents: read
id-token: write # OIDC 토큰 발급에 필요
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# 1. 코드 체크아웃
- uses: actions/checkout@v4
# 2. GCP 인증 (키리스!)
- id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/github-pool/providers/github-provider'
service_account: 'github-actions-sa@my-project.iam.gserviceaccount.com'
# 3. gcloud CLI 설정
- uses: google-github-actions/setup-gcloud@v2
with:
project_id: my-project
# 4. 배포 (gcloud 명령어 사용)
- name: Deploy to Cloud Run
run: |
gcloud run deploy my-service \
--source=. \
--region=asia-northeast3 \
--quiet \
--format=json
핵심 포인트
워크플로에서 반드시 확인해야 할 것들:
# 이것들을 빠뜨리면 WIF가 작동 안 해!
permissions:
id-token: write # 이거 없으면 OIDC 토큰 발급 불가
# auth 액션 버전은 v2 사용
- uses: google-github-actions/auth@v2
# v1은 deprecated
멀티 프로젝트 배포
여러 GCP 프로젝트에 배포해야 할 때:
jobs:
deploy-staging:
runs-on: ubuntu-latest
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/STAGING_NUMBER/locations/global/...'
service_account: 'deploy-sa@staging-project.iam.gserviceaccount.com'
- run: gcloud run deploy my-service --project=staging-project --quiet
deploy-production:
needs: deploy-staging # 스테이징 성공 후에만
runs-on: ubuntu-latest
environment: production # 수동 승인 게이트
steps:
- uses: google-github-actions/auth@v2
with:
workload_identity_provider: 'projects/PROD_NUMBER/locations/global/...'
service_account: 'deploy-sa@prod-project.iam.gserviceaccount.com'
- run: gcloud run deploy my-service --project=prod-project --quiet
6. Secret Manager 중앙화와 런타임 주입
왜 Secret Manager인가
비밀(API 키, DB 비밀번호, 인증 토큰)을 관리하는 잘못된 방법들:
| 방법 | 왜 안 돼? |
|---|---|
| 코드에 하드코딩 | Git에 커밋 → 영원히 히스토리에 남음 |
| 환경변수 파일(.env) | 서버에 파일로 남음 → 유출 위험 |
| CI/CD 시크릿 변수 | 플랫폼 종속 → 중앙 관리 불가 |
| 설정 파일 암호화 | 복호화 키 관리 문제 → 결국 같은 문제 |
Secret Manager는 이 모든 문제를 해결해:
- 중앙 저장소에서 통합 관리
- IAM으로 접근 통제
- 버전 관리 자동
- 감사 로그 자동 기록
- API/CLI로 런타임 주입
Secret Manager CLI 기본 명령어
# 시크릿 생성
gcloud secrets create db-password \
--replication-policy=automatic \
--quiet
# 시크릿 값 추가 (버전 생성)
echo -n "MySecurePassword123!" | \
gcloud secrets versions add db-password \
--data-file=- \
--quiet
# 시크릿 값 조회 (최신 버전)
gcloud secrets versions access latest \
--secret=db-password \
--quiet
# 시크릿 목록
gcloud secrets list --format='table(name, createTime)' --quiet
# 특정 버전 조회
gcloud secrets versions access 2 \
--secret=db-password \
--quiet
# 이전 버전 비활성화
gcloud secrets versions disable 1 \
--secret=db-password \
--quiet
스크립트에서 시크릿 주입
#!/bin/bash
set -euo pipefail
# 런타임에 Secret Manager에서 값 가져오기
DB_PASSWORD=$(gcloud secrets versions access latest \
--secret=db-password --quiet)
API_KEY=$(gcloud secrets versions access latest \
--secret=api-key --quiet)
# 환경변수로 주입 (프로세스 내에서만 유효)
export DB_PASSWORD
export API_KEY
# 애플리케이션 실행
python app.py
Cloud Build에서 시크릿 주입
# cloudbuild.yaml
steps:
- name: 'gcr.io/cloud-builders/gcloud'
entrypoint: 'bash'
args:
- '-c'
- |
echo "배포 시작..."
gcloud run deploy my-service \
--set-env-vars="DB_HOST=$$DB_HOST" \
--set-secrets="DB_PASSWORD=db-password:latest" \
--region=asia-northeast3 \
--quiet
secretEnv: ['DB_HOST']
availableSecrets:
secretManager:
- versionName: projects/my-project/secrets/db-host/versions/latest
env: 'DB_HOST'
GitHub Actions에서 시크릿 주입
steps:
- id: auth
uses: google-github-actions/auth@v2
with:
workload_identity_provider: '...'
service_account: '...'
- id: secrets
uses: google-github-actions/get-secretmanager-secrets@v2
with:
secrets: |-
db-password:my-project/db-password
api-key:my-project/api-key
- name: Deploy
run: |
gcloud run deploy my-service \
--set-env-vars="API_KEY=${{ steps.secrets.outputs.api-key }}" \
--set-secrets="DB_PASSWORD=db-password:latest" \
--quiet
7. 배치 스크립트 실전 패턴
패턴 1: 리소스 일괄 정리 스크립트
#!/bin/bash
# 사용하지 않는 리소스 일괄 정리
set -euo pipefail
PROJECT_ID="${PROJECT_ID:?프로젝트 ID를 설정해줘}"
DRY_RUN="${DRY_RUN:-true}" # 기본은 드라이런
echo "=== 미사용 디스크 정리 ==="
UNUSED_DISKS=$(gcloud compute disks list \
--project="$PROJECT_ID" \
--filter='NOT users:*' \
--format='value(name, zone)' \
--quiet)
if [ -z "$UNUSED_DISKS" ]; then
echo "미사용 디스크 없음"
else
echo "$UNUSED_DISKS" | while read -r NAME ZONE; do
if [ "$DRY_RUN" = "true" ]; then
echo "[드라이런] 삭제 예정: $NAME ($ZONE)"
else
echo "삭제 중: $NAME ($ZONE)"
gcloud compute disks delete "$NAME" \
--zone="$ZONE" \
--project="$PROJECT_ID" \
--quiet
fi
done
fi
echo "=== 중지된 인스턴스 목록 ==="
gcloud compute instances list \
--project="$PROJECT_ID" \
--filter='status=TERMINATED' \
--format='table(name, zone, lastStartTimestamp)' \
--quiet
패턴 2: 환경별 배포 스크립트
#!/bin/bash
# Cloud Run 환경별 배포
set -euo pipefail
ENVIRONMENT="${1:?사용법: $0 <staging|production>}"
SERVICE_NAME="my-api"
REGION="asia-northeast3"
IMAGE="gcr.io/my-project/$SERVICE_NAME"
case "$ENVIRONMENT" in
staging)
PROJECT="my-project-staging"
MIN_INSTANCES=0
MAX_INSTANCES=5
;;
production)
PROJECT="my-project-prod"
MIN_INSTANCES=2
MAX_INSTANCES=20
;;
*)
echo "잘못된 환경: $ENVIRONMENT (staging 또는 production)"
exit 1
;;
esac
echo "=== $ENVIRONMENT 환경 배포 ==="
echo "프로젝트: $PROJECT"
echo "서비스: $SERVICE_NAME"
echo "리전: $REGION"
# 빌드
gcloud builds submit . \
--tag="$IMAGE:latest" \
--project="$PROJECT" \
--quiet
# 배포
gcloud run deploy "$SERVICE_NAME" \
--image="$IMAGE:latest" \
--region="$REGION" \
--min-instances="$MIN_INSTANCES" \
--max-instances="$MAX_INSTANCES" \
--set-secrets="DB_PASSWORD=db-password:latest" \
--project="$PROJECT" \
--format=json \
--quiet
# 배포 확인
URL=$(gcloud run services describe "$SERVICE_NAME" \
--region="$REGION" \
--project="$PROJECT" \
--format='value(status.url)' \
--quiet)
echo "배포 완료: $URL"
# 헬스체크
if curl -sf "$URL/health" > /dev/null; then
echo "헬스체크 통과"
else
echo "헬스체크 실패! 롤백 필요"
exit 1
fi
패턴 3: 로그 수집 + 알림 스크립트
#!/bin/bash
# 에러 로그 수집 및 Slack 알림
set -euo pipefail
PROJECT_ID="${PROJECT_ID:?프로젝트 ID를 설정해줘}"
SLACK_WEBHOOK="${SLACK_WEBHOOK:?Slack Webhook URL을 설정해줘}"
LOOKBACK_MINUTES=30
# 최근 30분 에러 로그 수집
ERROR_COUNT=$(gcloud logging read \
"severity>=ERROR AND timestamp>=\"$(date -u -d "$LOOKBACK_MINUTES minutes ago" +%Y-%m-%dT%H:%M:%SZ)\"" \
--project="$PROJECT_ID" \
--format=json \
--quiet | jq 'length')
echo "최근 ${LOOKBACK_MINUTES}분 에러 수: $ERROR_COUNT"
# 임계값 초과 시 알림
if [ "$ERROR_COUNT" -gt 10 ]; then
# 대표 에러 메시지 추출
TOP_ERRORS=$(gcloud logging read \
"severity>=ERROR AND timestamp>=\"$(date -u -d "$LOOKBACK_MINUTES minutes ago" +%Y-%m-%dT%H:%M:%SZ)\"" \
--project="$PROJECT_ID" \
--format='value(textPayload)' \
--limit=3 \
--quiet)
# Slack 알림 발송
curl -sf -X POST "$SLACK_WEBHOOK" \
-H 'Content-type: application/json' \
-d "{
\"text\": \"경고: 최근 ${LOOKBACK_MINUTES}분간 에러 ${ERROR_COUNT}건 발생\\n프로젝트: ${PROJECT_ID}\\n대표 에러:\\n${TOP_ERRORS}\"
}" > /dev/null
echo "Slack 알림 발송 완료"
fi
8. 실패 케이스와 트러블슈팅
케이스 1: CI에서 “You do not have permission” 오류
상황: GitHub Actions에서 gcloud 명령어 실행 시 권한 오류
확인 순서:
# 1. WIF 설정 확인
gcloud iam workload-identity-pools providers describe github-provider \
--workload-identity-pool=github-pool \
--location=global \
--project=my-project
# 2. 서비스계정 IAM 바인딩 확인
gcloud iam service-accounts get-iam-policy \
github-actions-sa@my-project.iam.gserviceaccount.com
# 3. 서비스계정 역할 확인
gcloud projects get-iam-policy my-project \
--flatten="bindings[].members" \
--filter="bindings.members:github-actions-sa@" \
--format='table(bindings.role)'
흔한 원인:
- permissions.id-token: write가 워크플로에 없음
- attribute.repository 매핑의 org/repo 이름이 틀림
- 서비스계정에 필요한 역할이 부여되지 않음
케이스 2: –format=json 출력이 비어 있음
상황: gcloud ... --format=json 실행 결과가 [] 빈 배열
원인 분석:
# 필터가 너무 엄격한 경우 - 필터 없이 먼저 확인
gcloud compute instances list --format=json --quiet
# 프로젝트가 잘못된 경우
gcloud config get-value project
# 리전/존이 다른 경우
gcloud compute instances list \
--zones=asia-northeast3-a,asia-northeast3-b,asia-northeast3-c \
--format=json \
--quiet
케이스 3: Secret Manager에서 “NOT_FOUND” 오류
상황: gcloud secrets versions access latest 실행 시 404
확인:
# 시크릿 존재 여부 확인
gcloud secrets describe db-password \
--project=my-project --quiet
# 프로젝트가 맞는지 확인
gcloud config get-value project
# 활성 버전이 있는지 확인
gcloud secrets versions list db-password \
--project=my-project \
--filter='state=ENABLED' \
--format='table(name, state, createTime)' \
--quiet
흔한 원인:
- 시크릿 이름 오타 (하이픈 vs 밑줄)
- 다른 프로젝트에 시크릿이 있음
- 모든 버전이 disabled/destroyed 상태
케이스 4: 배치 스크립트가 중간에 멈춤
상황: for 루프로 100개 리소스를 처리하는데 30번째에서 멈춤
해결:
# API Rate Limit에 걸린 경우 - 딜레이 추가
for INSTANCE in $(gcloud compute instances list \
--format='value(name)' --quiet); do
gcloud compute instances stop "$INSTANCE" --quiet || {
echo "실패: $INSTANCE - 10초 후 재시도"
sleep 10
gcloud compute instances stop "$INSTANCE" --quiet || echo "재시도도 실패: $INSTANCE"
}
# Rate Limit 방지용 딜레이
sleep 1
done
핵심 정리
1. 자동화 3대 표준: --quiet(프롬프트 차단) + --format(구조화 출력) + 종료 코드(성공/실패 판단)
2. CI/CD 인증은 서비스계정 키가 아니라 WIF(Workload Identity Federation)가 기본이야
3. GitHub Actions는 google-github-actions/auth v2 + permissions.id-token: write 조합으로 키리스 인증해
4. 비밀은 Secret Manager에 중앙화하고, 런타임에 주입하는 패턴이 표준이야
FAQ
Q. –quiet를 붙이면 오류 메시지도 안 나와?
A. 아니야, --quiet는 확인 프롬프트와 진행 표시줄만 비활성화해. 오류 메시지는 정상적으로 stderr에 출력돼. 그래서 CI에서 --quiet를 붙여도 실패 시 로그에서 원인을 확인할 수 있어.
Q. –format=json과 –format=’value(…)’ 중 뭘 쓰는 게 좋아?
A. 단일 필드만 필요하면 value()가 깔끔하고, 여러 필드를 복잡하게 다뤄야 하면 json + jq 조합이 유연해. CI 파이프라인에서 다음 단계에 값을 전달할 때는 value()가 파싱이 필요 없어서 편해.
Q. WIF 설정이 너무 복잡한데, 그냥 키 파일 쓰면 안 돼?
A. 처음 설정은 WIF가 확실히 번거로워. 하지만 한 번 설정하면 키 회전, 유출 걱정, 감사 이슈가 전부 사라져. 키 파일은 단기 프로토타입에서만 쓰고, 정식 파이프라인은 반드시 WIF로 전환하는 걸 추천해.
Q. Secret Manager에 저장하면 비용이 들어?
A. 시크릿당 월 $0.06, 10,000회 접근당 $0.03 정도야. 시크릿 10개를 매일 1,000회 접근해도 월 $1도 안 돼. 비용 대비 보안 이점이 압도적이라 사실상 무료나 다름없어.
Q. set -euo pipefail은 왜 꼭 써야 해?
A. set -e는 명령어 실패 시 즉시 스크립트를 종료하고, set -u는 미정의 변수 사용을 막고, set -o pipefail은 파이프 중간 명령어의 실패도 감지해. 이 3개 없이 스크립트를 돌리면, 중간에 실패해도 다음 명령어가 실행돼서 예측 불가능한 상태가 만들어져.
Q. Cloud Build와 GitHub Actions 중 뭘 쓰는 게 좋아?
A. GCP 생태계에 올인이면 Cloud Build가 통합이 좋고 비용도 저렴해(일 120분 무료). 하지만 GitHub 중심 워크플로라면 GitHub Actions + WIF가 개발자 경험이 더 나아. 둘 다 쓰는 하이브리드도 가능해 — 빌드는 Cloud Build, 배포 승인은 GitHub Actions 같은 조합이지.
Q. gcloud 명령어의 API Rate Limit은 어느 정도야?
A. 서비스마다 다른데, 대부분 분당 수백~수천 요청 정도야. 배치 스크립트에서 수백 개 리소스를 루프로 처리할 때는 sleep 1 정도의 딜레이를 넣는 게 안전해. Rate Limit에 걸리면 429 에러가 나오고, 이때는 지수 백오프(exponential backoff)로 재시도하면 돼.
Q. –format에서 nested 필드를 어떻게 접근해?
A. 점(.)으로 중첩 필드에 접근할 수 있어. 예를 들면 --format='value(networkInterfaces[0].accessConfigs[0].natIP)'처럼. 배열은 인덱스로, 객체는 필드명으로 접근해. 구조를 모르겠으면 먼저 --format=json으로 전체 구조를 확인하는 게 빨라.
Q. Secret Manager 버전 관리는 어떻게 해?
A. 새 값을 추가하면 자동으로 버전이 올라가. gcloud secrets versions add로 새 버전을 만들고, 이전 버전은 disable로 비활성화하면 돼. 코드에서는 latest를 쓰면 항상 최신 버전을 가져오지만, 운영 환경에서는 특정 버전 번호를 고정하는 게 더 안전해.
Q. 여러 프로젝트에 동시에 배포하는 스크립트는 어떻게 만들어?
A. 환경별 설정을 변수로 분리하고, 스크립트 인자로 환경을 받는 패턴이 제일 깔끔해. 7번 섹션의 “패턴 2: 환경별 배포 스크립트”처럼 case 문으로 환경별 변수를 분기하면 돼. 프로젝트가 3개 이상이면 설정 파일(JSON/YAML)에서 읽어오는 것도 방법이야.
참고 자료 (References)
데이터 출처
| 출처 | 설명 | 링크 |
|---|---|---|
| Scripting gcloud CLI | gcloud 비대화형 실행 공식 가이드 | Scripting gcloud |
| Workload Identity Federation | 외부 워크로드 키리스 인증 문서 | WIF 문서 |
| google-github-actions/auth | GitHub Actions용 GCP 인증 액션 | GitHub |
| 서비스 계정 키 베스트프랙티스 | 키 최소화 가이드 | IAM 문서 |
| Secret Manager 베스트프랙티스 | 시크릿 관리 가이드 | Secret Manager |
| Cloud Build 시크릿 사용 | Cloud Build에서 시크릿 주입 | Cloud Build 시크릿 |
핵심 인용
“gcloud 스크립팅 가이드는 비대화형 실행과 구조화 출력, 종료 코드 기반 제어를 핵심 원칙으로 제시한다. 이 3가지만 지켜도 사람이 읽는 CLI에서 시스템이 실행하는 CLI로 바뀐다.” — Google Cloud SDK 문서
다음 편 예고
[10편] 교육 커리큘럼과 실습 과제: 2일/5일 온보딩 패키지 완전 가이드
- 2일 압축형 vs 5일 심화형 커리큘럼 설계
- 실습 과제 패키지: 프로젝트 생성부터 배포까지
- 평가 체크리스트와 수료 기준 설정법
