[DevOps] Gitlab Runner(Shell Executor)를 통한 Nginx의 Blue/Green 배포
Azure VM에서 GitLab + Nginx로 무중단 블루/그린 배포 구축
- GitLab(Omnibus)은 80/443 그대로, 앱용 edge Nginx는 8081로 분리.
- Blue/Green은 각각 컨테이너(app-blue, app-green)로 항상 떠 있고, 심볼릭 링크 교체 + nginx -s reload로 즉시 전환.
- GitLab Runner(shell) + Docker Compose v2:
- 태그 푸시 → idle 배포 → 검증 → 전환 → (지연) 정리 자동화.
번호 | 주제 |
---|---|
1 | 구조 (스크린샷) |
2 | 환경 |
3 | 디렉터리 구조 |
4 | 📷 구현(스크린샷) |
5 | 주요 코드 (Nginx) |
6 | 주요 코드 (GitLab CI) |
7 | Trouble Shooting |
[Client] → http://<VM>:8081 → (edge-nginx)
├─ / → app_active (blue or green)
├─ /_blue/ → app-blue (nginx 컨테이너)
└─ /_green/ → app-green (apache 컨테이너)
- GitLab(Omnibus): 80/443 (자체 UI/레지스트리)
- Edge Nginx: 8081 (애플리케이션 트래픽 전용)
- 두 슬롯(blue/green) 동시 구동 → 전환은 edge에서 처리
- Ubuntu 기반 Azure VM (root/sudo) - Standard_B4ms(4vCPU / 16GB)
- Docker/Docker Compose v2
- GitLab(Omnibus)
- GitLab Runner(shell) - (동일 VM)
- Nginx + Apache2
azureuser@gitlab-vm:~/nginx-apache-blue-green$ tree
.
├── README.md
├── app
│ ├── blue
│ │ └── www
│ │ └── index.html
│ ├── docker-compose.blue.yml
│ ├── docker-compose.green.yml
│ └── green
│ └── www
│ └── index.html
├── nginx
│ ├── conf.d
│ │ ├── app_active.conf -> app_blue.conf
│ │ ├── app_blue.conf
│ │ └── app_green.conf
│ ├── docker-compose.nginx.yml
│ └── nginx.conf
└── scripts
├── deploy_green.sh
├── retire_old.sh
├── rollback.sh
└── switch_traffic.sh
8 directories, 14 files
nginx Blue/Green 배포
GitLab Runner 등록
shell executor 설정
Job 트리거
태그 추가
->git tag v0.0.1
or수동
bootstrap running
bootstrap -> deploy -> verify -> switch 성공 ( cleanup은 스케줄링(30분 후))
Job 트리거 자동화 ( 버튼 )
Manual Switch Jobs를 통한 스위칭 버튼 추가
전 / 후
1. 활성 슬롯을 심볼릭 링크로 include
# nginx.conf (발췌)
upstream app_blue { server app-blue:80; keepalive 64; }
upstream app_green { server app-green:80; keepalive 64; }
# ★ 전환의 핵심: active 대상만 링크로 교체
include /etc/nginx/conf.d/app_active.conf;
-
app_active.conf만 app_blue.conf ↔ app_green.conf로 갈아끼우면 루트(/) 트래픽이 즉시 전환됨.
-
주의: 컨테이너 안에서는 상대링크여야 함. 절대경로 링크면 “파일 없음”으로 죽음.
2. 볼륨 마운트 기준(Compose 관점)
# docker-compose.nginx.yml (발췌)
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./conf.d:/etc/nginx/conf.d
- Compose의 상대경로는 compose 파일이 위치한 폴더 기준. 엣지 Nginx가 호스트의 링크/설정을 올바르게 읽게 하는 핵심.
1. GitLab Runner가 쓰는 툴을 사전 점검
bootstrap 파이프라인 실패
# .gitlab-ci.yml (발췌)
before_script:
- set -euo pipefail
- docker --version
- docker compose version
tags: [bluegreen]
- 파이프라인 초반에 환경 이상(Compose v2 미설치, runner 태그 미일치)을 즉시 발견.
2. 활성 링크 없을 때 기본 링크 보정
# bootstrap (발췌)
- cd /opt/bluegreen/nginx/conf.d && [ -e app_active.conf ] || ln -s app_blue.conf app_active.conf
- 첫 부팅/초기화 시 루트(/)가 사라지는걸 방지.
3. blue/green을 서로 다른 Compose 프로젝트로 띄우기
# bootstrap (발췌)
- docker compose -p bg-blue -f /opt/bluegreen/app/docker-compose.blue.yml up -d
- docker compose -p bg-green -f /opt/bluegreen/app/docker-compose.green.yml up -d
- 두 compose 파일을 같은 프로젝트로 올리면 orphan 처리로 서로 지워버림.
- -p bg-blue / -p bg-green으로 분리해 충돌/삭제를 막음.
4. Nginx 설정은 테스트 후 리로드
# bootstrap (발췌)
- docker exec edge-nginx nginx -t || (docker exec edge-nginx nginx -T; false)
- docker exec edge-nginx nginx -s reload
- 오타/링크 오류로 전체 엣지가 죽는 걸 방지.
- 실패 시 nginx -T로 전체 설정 덤프를 로그에 남겨 디버깅 용이.
5. idle 슬롯에 배포할 때 프로젝트명 일치
deploy 파이프라인 실패
# scripts/deploy_green.sh (발췌)
# active가 blue면 idle=green → PROJECT="bg-green", 반대면 "bg-blue"
docker compose -p "$PROJECT" -f "$COMPOSE" pull
docker compose -p "$PROJECT" -f "$COMPOSE" up -d
- bootstrap에서 -p로 올렸으면, deploy/cleanup에서도 반드시 같은 -p를 써야 컨테이너 이름 충돌이 안 남.
6. 전환은 원자적 링크 교체 + 사전 검사
# scripts/switch_traffic.sh (발췌)
ln -sfn app_green.conf app_active.conf.new # 또는 app_blue.conf.new
mv -Tf app_active.conf.new app_active.conf # 원자적 교체
docker exec edge-nginx nginx -t && docker exec edge-nginx nginx -s reload
- 링크 교체 중간 상태에서 리로드가 들어가면 “파일 없음” 에러가 날 수 있음 → 원자적 교체로 레이스 제거.
7. 태그 트리거 + 지연 정리
# cleanup (발췌)
when: delayed
start_in: "30 minutes"
only: [tags]
- 새 슬롯 안정화 시간을 준 뒤 이전 슬롯을 자동 정리. GitLab 일부 버전에선 rules + start_in이 충돌하므로 **only: [tags]**로 단순화.
8. GitLab UI 버튼으로 즉시 전환
# manual switch (발췌)
switch_to_green:
stage: switch
when: manual
script: [ "bash /opt/bluegreen/scripts/switch_traffic.sh green" ]
tags: [bluegreen]
only: [branches, tags]
- UI에서 원클릭 전환 가능. (변수 방식으로 TARGET=green을 받는 한 개 짜리 잡으로도 가능)
로컬 테스트명령어
sudo -u gitlab-runner -H bash -lc '/opt/bluegreen/scripts/deploy_green.sh'