프로그래밍/DevOps

[DevOps] CI/CD 실무 다듬기 — 브랜치별 환경 분리와 이미지 태그 전략

이슬먹는 개발자 2026. 7. 24. 15:49
728x90
반응형

지난 글에서 push하면 자동으로 배포되는 파이프라인을 완성했다. 잘 돌아간다. 그런데 실무에 쓰기엔 두 가지가 걸린다.

첫째, 배포할 곳이 하나뿐이다. 실무에선 보통 개발 서버(dev)와 운영 서버(prod)를 따로 둔다. 테스트는 개발 서버에서 해보고, 검증되면 운영에 올린다. 지금은 main에 push하면 무조건 그 서버 하나로 나간다.

둘째, 태그가 latest 하나뿐이다. 이러면 지금 서버에 떠 있는 게 어떤 코드인지 알 수 없다. 배포했는데 문제가 생겨서 되돌리고 싶어도, 이전 버전 이미지가 남아있지 않다.

오늘은 이 두 가지를 해결해서 파이프라인을 실무 수준으로 다듬는다.


1. 브랜치별로 다른 환경에 배포하기

가장 흔한 구성은 이렇다.

브랜치 배포 대상 용도
develop 개발 서버 기능 테스트, 내부 확인
main 운영 서버 실제 서비스

develop에 push하면 개발 서버로, main에 push하면 운영 서버로 나가게 만드는 거다.

방법: 조건문(if)으로 분기

GitHub Actions는 스텝이나 잡에 if 조건을 걸 수 있다. 여기에 "지금 어떤 브랜치인지"를 물어보면 된다.

on:
  push:
    branches: [ main, develop ]   # 두 브랜치 모두 감시

jobs:
  deploy:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # ... (빌드 & push 스텝은 동일) ...

      - name: 개발 서버 배포
        if: github.ref == 'refs/heads/develop'
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.DEV_SERVER_HOST }}
          username: ${{ secrets.DEV_SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ghcr.io/${{ github.repository }}:develop
            # ... 재시작 명령 ...

      - name: 운영 서버 배포
        if: github.ref == 'refs/heads/main'
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.PROD_SERVER_HOST }}
          username: ${{ secrets.PROD_SERVER_USER }}
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            docker pull ghcr.io/${{ github.repository }}:latest
            # ... 재시작 명령 ...

github.ref가 현재 브랜치 정보를 담고 있다. refs/heads/develop 형태로 들어온다. 그래서 if: github.ref == 'refs/heads/develop'은 "지금 develop 브랜치일 때만 이 스텝 실행"이 된다.

시크릿도 서버별로 따로 등록해둔다. DEV_SERVER_HOST, PROD_SERVER_HOST 이런 식으로.

더 깔끔한 방법: Environments 사용

GitHub에는 Environments라는 기능이 있다. 저장소 Settings → Environments에서 development, production 같은 환경을 만들고, 환경별로 시크릿을 따로 등록할 수 있다.

jobs:
  deploy-prod:
    if: github.ref == 'refs/heads/main'
    environment: production      # 이 환경의 시크릿을 사용
    runs-on: ubuntu-latest
    steps:
      # secrets.SERVER_HOST 하나로 쓰면
      # production 환경에 등록된 값이 자동으로 들어온다

이러면 시크릿 이름을 DEV_, PROD_로 나눌 필요 없이 같은 이름을 쓰고, 환경만 바꿔 끼우면 된다. 훨씬 깔끔하다.

보너스로 승인 기능도 붙는다. Environments에는 "이 환경으로 배포하려면 지정된 사람이 승인해야 함"을 설정할 수 있다. 운영 배포에 이걸 걸어두면, 자동으로 다 준비되고 마지막 승인 버튼만 사람이 누르게 된다. 첫 CI/CD 글에서 말한 Continuous Delivery가 바로 이 형태다.


2. 이미지 태그 전략 — latest만 쓰면 안 되는 이유

지난 글에서 이미지 태그를 latest 하나로만 썼다. 편하지만 실무에선 위험하다.

문제 1: 지금 뭐가 떠 있는지 모른다. 서버에 latest가 떠 있다는 건 알겠는데, 그게 어제 코드인지 오늘 코드인지 알 수 없다.

문제 2: 롤백이 안 된다. 배포했는데 버그가 터졌다. 이전 버전으로 되돌리고 싶다. 근데 latest는 계속 덮어써져서 이전 이미지가 남아있지 않다. 되돌릴 수가 없다.

해결: 커밋 해시나 버전으로 태그 달기

가장 실용적인 방법은 커밋 SHA를 태그로 쓰는 거다. 커밋마다 고유하니까 이미지와 코드가 1:1로 대응된다.

      - name: 이미지 빌드 & push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.sha }}
            ghcr.io/${{ github.repository }}:latest

tags에 여러 줄을 주면 같은 이미지에 태그를 여러 개 달 수 있다. 여기서는 두 개를 단다.

  • :abc1234... (커밋 SHA) — 고유 식별용. 이건 절대 덮어써지지 않는다.
  • :latest — 편의용. 항상 최신을 가리킨다.

이러면 이전 커밋의 이미지가 레지스트리에 그대로 남아있으니, 문제가 생겼을 때 그 태그로 되돌리면 된다.

# 롤백: 이전 커밋 이미지로 다시 띄우기
docker pull ghcr.io/user/repo:이전커밋SHA
docker stop my-app && docker rm my-app
docker run -d --name my-app ... ghcr.io/user/repo:이전커밋SHA

릴리스는 버전 태그로

정식 릴리스가 있는 프로젝트라면 Git 태그(v1.2.0)를 이미지 태그로 쓰는 것도 좋다.

on:
  push:
    tags: [ 'v*' ]      # v로 시작하는 태그가 push될 때

git tag v1.2.0 && git push --tags 하면 그 버전으로 이미지가 만들어지고 배포된다. 버전 관리가 명확해진다.

태그 전략 정리

태그 언제 쓰나 특징
latest 편의용 계속 덮어써짐. 이것만 쓰면 위험
커밋 SHA 모든 빌드 고유함. 롤백의 핵심
develop 개발 브랜치 개발 서버가 바라보는 태그
v1.2.0 정식 릴리스 사람이 읽기 좋은 버전

실무에서는 보통 커밋 SHA + 브랜치명(또는 latest) 조합으로 시작하면 충분하다.


정리

  • 브랜치별 환경 분리: if: github.ref == '...'로 develop은 개발 서버, main은 운영 서버로 나눈다.
  • Environments를 쓰면 환경별 시크릿을 깔끔하게 관리하고, 운영 배포에 승인 단계도 걸 수 있다.
  • latest만 쓰면 롤백이 불가능하다. 커밋 SHA 태그를 같이 달아서 이전 버전을 남겨두자.
  • 태그는 여러 개를 동시에 달 수 있다. (SHA + latest)

이 두 가지만 적용해도 파이프라인이 훨씬 실무다워진다. "개발 서버에서 먼저 확인하고, 운영은 승인받고 배포하고, 문제 생기면 이전 커밋으로 되돌린다"는 흐름이 완성되는 거다.

이걸로 CI/CD 계열을 마무리한다. 개념부터 시작해서 자동 테스트, 자동 배포, 환경 분리와 롤백까지 왔다. 다음부터는 지난 글에서 예고했던 쿠버네티스 이야기로 넘어간다. 컨테이너가 수십 개로 늘어나고, 무중단 배포가 필요해질 때 등장하는 물건이다.

728x90
반응형