← 전체 글로 돌아가기

CI/CD

GitHub Actions에서 secret 누락을 빨리 알아채기

배포 스크립트에서 환경 변수를 빼먹으면, 배포는 성공해도 서비스는 작동하지 않는다.

GitHub Actions로 배포할 때 가장 흔한 실수는 secret이 누락되는 것이다. workflow는 성공했는데, 배포된 애플리케이션이 작동하지 않는다. 원인은 대부분 환경 변수가 없기 때문이다.

문제: 조용한 실패

name: Deploy
on: [push]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Deploy
        run: |
          docker build -t myapp:latest .
          docker push myapp:latest

Workflow는 초록색으로 완료되지만, 배포된 앱을 열면 에러다. 왜? SECRET이나 환경 변수를 정의하지 않았기 때문이다.

CI/CD 시스템은 빌드와 배포만 확인한다. 앱이 실제로 작동하는지는 모른다.

원인 분석

Secret이 누락되는 경우:

1. 개발 중에는 .env 파일을 썼는데, CI에 secret으로 등록하지 않음

# 로컬 실행 (잘 작동)
POST_API_KEY=abc123 npm run build

# CI 실행 (secret 없음)
DOCKER_BUILD_ARGS="--build-arg POST_API_KEY=" docker build .
# → POST_API_KEY가 빈 문자열

2. Secret 이름을 잘못 입력

GitHub UI에서 POST_API_KEY로 저장했는데, workflow에서 API_KEY로 참조했다.

# 정의: POST_API_KEY
# 사용: API_KEY ← 틀림!
run: docker build --build-arg API_KEY=${{ secrets.API_KEY }}

3. 새 repository로 옮기면서 secrets를 복사하지 않음

모든 secrets을 다시 설정해야 한다.

조기 발견 방법

1단계: Workflow에서 환경 변수 검증

name: Deploy
on: [push]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Validate secrets
        run: |
          if [ -z "${{ secrets.POST_API_KEY }}" ]; then
            echo "ERROR: POST_API_KEY is not set"
            exit 1
          fi
          if [ -z "${{ secrets.DB_PASSWORD }}" ]; then
            echo "ERROR: DB_PASSWORD is not set"
            exit 1
          fi
          echo "✓ All required secrets are set"

이 단계가 실패하면, workflow도 실패한다. 환경 변수 누락을 빨리 발견할 수 있다.

2단계: Docker 빌드에 명시적으로 전달

- name: Build Docker image
  run: |
    docker build \
      --build-arg POST_API_KEY=${{ secrets.POST_API_KEY }} \
      --build-arg DB_PASSWORD=${{ secrets.DB_PASSWORD }} \
      -t myapp:latest .

빌드 arg로 전달하면, 나중에 런타임에 누락되는 것과 구분할 수 있다.

3단계: 배포 후 헬스 체크

- name: Health check
  run: |
    sleep 5
    curl -f http://localhost:3000/health || exit 1

배포 직후 앱이 정말 작동하는지 확인한다.

체크리스트 자동화

Python 스크립트로 필요한 secrets을 자동 검증할 수 있다.

#!/usr/bin/env python3
import os
import sys

REQUIRED_SECRETS = [
    'POST_API_KEY',
    'DB_PASSWORD',
    'JWT_SECRET',
    'AWS_ACCESS_KEY_ID',
    'AWS_SECRET_ACCESS_KEY',
]

missing = [s for s in REQUIRED_SECRETS if not os.environ.get(s)]

if missing:
    print(f"ERROR: Missing secrets: {', '.join(missing)}")
    sys.exit(1)

print("✓ All required secrets are present")
- name: Check required secrets
  run: |
    python3 check_secrets.py
  env:
    POST_API_KEY: ${{ secrets.POST_API_KEY }}
    DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
    JWT_SECRET: ${{ secrets.JWT_SECRET }}
    AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
    AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

Repository 복제할 때

다른 repository로 복제하면 모든 secrets을 다시 설정해야 한다.

# 현재 repository의 secrets 확인
gh secret list

# 다른 repo로 복제
gh secret list -R other/repo

# 수동으로 설정
gh secret set POST_API_KEY -b $POST_API_KEY -R other/repo

GitHub CLI를 쓰면 더 쉽게 관리할 수 있다.

로그에서 secret 누수 주의

Secret을 로그에 출력하면 안 된다. GitHub Actions는 자동으로 마스킹하지만, 실수하기 쉽다.

# 나쁜 예
- run: echo "Database password is ${{ secrets.DB_PASSWORD }}"
# → 로그에 password가 노출될 수 있음

# 좋은 예
- run: echo "Database connected"
  # secret은 쓰되, 로그엔 출력하지 않음

만약 실수로 secret을 로그에 출력했다면:

  1. GitHub에서 secret을 즉시 변경
  2. Workflow 실행 로그 삭제
  3. 외부에 노출되지 않았는지 확인

모니터링

배포 후 실제로 앱이 환경 변수를 읽는지 모니터링하자.

// Node.js
if (!process.env.POST_API_KEY) {
  console.error('CRITICAL: POST_API_KEY is not set!');
  process.exit(1);
}

앱 시작 시점에 모든 필수 환경 변수를 검증하면, 누락된 걸 빨리 발견한다.

정리

Secret 누락은 CI 관점에서는 "성공"이지만, 실제로는 배포 실패다. Workflow에 검증 단계를 추가해서 조기에 발견하자. 특히 새 repository를 만들거나 팀원이 추가될 때 secret 설정을 빼먹기 쉬우므로, 체크리스트를 만들어두는 게 좋다. 일단 배포되면 고치기가 훨씬 어렵다.