GitLab CI/CD 구축

임기호·2025년 8월 15일

DevOps

목록 보기
1/2

CI/CD 환경 구축(feat. GitLab Runner) – 테스트 중심 배포 가드

1) 도입 이유

  • 🚨 실제 사건: 테스트하려고 중간에 심어둔 return이 그대로 커밋·배포되어 이후 단계가 실행되지 않음.
  • 수동 배포 흐름에서는 이런 임시 코드/주석이 리뷰나 체크리스트에서 누락될 수 있음.
  • 대응: GitLab CI/CDtest → build → deploy 표준화
    • 테스트 실패 시 자동 차단
    • 태그 기반 트리거(예: sandbox-*)
    • 운영과 분리된 DEPLOY_PATH / PM2 앱으로 안전 실험 루프 확보

2) CI/CD를 위한 준비물

  • GitLab Runner: 배포 서버에 Docker + gitlab/gitlab-runner(executor: docker)
  • SSH 배포 키
    • GitLab Variables: SSH_PRIVATE_KEY(Masked/Protected 권장)
    • 서버: ~/.ssh/authorized_keys 등록, 권한(~/.ssh 700, authorized_keys 600)
  • Next.js 빌드 방식: output: 'standalone' (서버에서 pnpm install 없이 실행 가능)
  • PM2 에코시스템: 샌드박스 전용 앱 project-sandbox (예: 포트 4400)

3) CI/CD 구현 계획

(3-1) 파이프라인 단계

  1. unit_test: Vitest 단위 테스트 → 실패 시 배포 차단
  2. build_project_sandbox: Next standalone 빌드 → 산출물 아티팩트
  3. deploy_project_sandbox: 러너 컨테이너에서 서버로 rsync/scp 반영pm2 reload --only project-sandbox

트리거 정책: sandbox-* 태그 푸시 시 개인 샌드박스 파이프라인만 작동

(3-2) GitLab Variables (예시)

  • SSH_PRIVATE_KEY, DEPLOY_HOST, DEPLOY_USER, DEPLOY_PATH
  • Environment scope: project_sandbox 로 제한(내 잡에서만 노출)

(3-3) PM2 에코시스템(샌드박스)

module.exports = {
  apps: [
    // ...기존 앱들
    {
      name: 'project-sandbox',
      cwd: '/home/project/sandbox/.next/standalone',
      script: 'server.js',
      exec_mode: 'cluster',
      instances: 1, // 처음엔 1로 안정화 후 scale
      env: {
        NODE_ENV: 'production',
        PORT: 4400,
        HOST: '0.0.0.0',
        HOSTNAME: '0.0.0.0',
      },
    },
  ],
};

(3-4) CI 구성 요약

unit_test:
  stage: test
  image: node:20
  rules:
    - if: '$CI_COMMIT_TAG =~ /^sandbox-/'
  before_script:
    - corepack enable
    - corepack prepare pnpm@8.14.1 --activate
    - pnpm install --frozen-lockfile
  script:
    - pnpm --filter kream run test -- --reporter=default

build_project_sandbox:
  stage: build
  image: node:20
  needs: ["unit_test"]
  rules:
    - if: '$CI_COMMIT_TAG =~ /^sandbox-/'
  before_script:
    - corepack enable
    - corepack prepare pnpm@8.14.1 --activate
    - pnpm install --frozen-lockfile --prod=false
  script:
    - pnpm -w build --filter kream
  artifacts:
    expire_in: 3 days
    paths:
      - project/.next/standalone
      - project/.next/static
      - project/public

deploy_project_sandbox:
  stage: deploy
  image: alpine:3.20
  needs: ["build_project_sandbox"]
  dependencies: ["build_project_sandbox"]
  environment:
    name: project_sandbox
  rules:
    - if: '$CI_COMMIT_TAG =~ /^project-/'
  before_script:
    - apk add --no-cache openssh-client rsync
    - mkdir -p ~/.ssh && echo "$SSH_PRIVATE_KEY" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
    - ssh-keyscan -H "$DEPLOY_HOST" >> ~/.ssh/known_hosts
  script:
    - ssh $DEPLOY_USER@$DEPLOY_HOST "mkdir -p $DEPLOY_PATH/.next/standalone $DEPLOY_PATH/.next/static $DEPLOY_PATH/public"
    - rsync -az --delete project/.next/standalone/ $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/project/.next/standalone/
    - rsync -az --delete project/.next/static/     $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/project/.next/static/
    - rsync -az --delete project/public/           $DEPLOY_USER@$DEPLOY_HOST:$DEPLOY_PATH/project/public/
    # 중앙 관리 에코시스템 파일 재로드
    - ssh $DEPLOY_USER@$DEPLOY_HOST "pm2 reload /home/project/source/ecosystem.config.js --only project_sandbox || pm2 start /home/sdd_it/KREAM_Web/source/ecosystem.config.js --only project_sandbox"

4) 부록 – 실제 구현 과정(요약)

  1. Docker 설치(Rocky/RHEL): 저장소 추가 → docker-ce 설치 → 서비스 활성화
  2. Runner 컨테이너 기동: /srv/gitlab-runner/config, /var/run/docker.sock 마운트
  3. Runner 등록: GitLab URL/토큰, executor=docker, 기본 이미지=node:20
  4. Variables 등록: SSH_PRIVATE_KEY, DEPLOY_HOST, DEPLOY_USER, DEPLOY_PATH (scope=project_sandbox)
  5. 서버 SSH 준비: 공개키 등록 + 권한 세팅
  6. Next Standalone 설정: output: 'standalone'
  7. PM2 앱 추가: project-sandbox 등록, pm2 reload --only project-sandbox
  8. 태그 트리거: git tag sandbox-YYYY.MM.DD-Ngit push origin sandbox-YYYY.MM.DD-N
  9. 검증 체크: pm2 status/logs, 포트 리슨(예: 4400), curl -I http://127.0.0.1:4400/
  10. 자주 겪는 이슈
    • SSH 실패: 변수 스코프/개행/키 형식 문제 → File 변수나 Base64 전략 고려
    • PM2 파일 경로 혼선: 중앙 ecosystem.config.js vs DEPLOY_PATH 혼동
    • EADDRINUSE: 중복 기동 → scale 1/reload 전략
    • 무한 리다이렉트: 절대 경로, undefined 필터, 정적 경로 matcher 제외

5) 향후 개선 계획

  • 배포 방식 전환: rsync/scp 파일 복사 → Docker 이미지 기반(레지스트리 Push/Pull, 표준 롤백)
  • 배포 알림: 결과를 팀즈 팀 채널/이메일로 자동 발송(커밋/태그/파이프라인 링크/변경 요약)
  • 백엔드 CI/CD: NestJS에도 동일 파이프라인 도입(테스트 → 빌드 → 이미지 → 스테이징/프로덕션)

0개의 댓글