Skip to content

[FEATURE] Docker 기반 Blue/Gren 무중단배포#18

Merged
youyeon11 merged 6 commits intodevelopfrom
feat/docker-blue-green-17
Sep 2, 2025
Merged

[FEATURE] Docker 기반 Blue/Gren 무중단배포#18
youyeon11 merged 6 commits intodevelopfrom
feat/docker-blue-green-17

Conversation

@youyeon11
Copy link
Contributor

@youyeon11 youyeon11 commented Aug 30, 2025

📌 작업 목적

  • Docker 기반 무중단 자동 배포하도록 배포 전략을 변경하도록 하겠습니다

🗂 작업 유형

  • 기능 추가 (Feature)
  • 빌드/배포/환경 설정 (Chore)

🔨 주요 작업 내용

  • Nginx.conf 파일 설정
    • default.conf가 아닌 nfingx.conf를 변경하여 events와 http 설정이 필요
    • upstream의 이름을 backend로 설정하고 app-blue 컨테이너에 대하여 매핑되도록 설정
    upstream backend {
        server app-blue:8000;
    }
  • deploy.sh 작업
    • 우선은 현재 실행 중인 컨테이너 이름에 따라 다음 컨테이너를 실행하도록 스크립트 작성
    • 제대로 띄워져있는지 확인 후에 Nginx conf 파일 변경
    • GPU 옵션을 추가해야만 Nvidia-container-toolkit이 작동됨
docker run --gpus all -d \
  --name "app-${AFTER_COLOR}" \
  --env-file ./.env \
  -p "${HOST_PORT}:8000" \
  ${ECR_URI}/dearbelly-cv:latest
  • Container 기반으로 작업하는 것이 포트에 대한 처리에 더 안정적일 것 같아 공식문서 docker-compose에서 GPU 환경으로 runtime 설정 변경하는 것을 참고하여 작성하였습니다.

https://docs.docker.com/compose/how-tos/gpu-support/

    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]

📎 관련 이슈


💬 논의 및 고민한 점

  • docker-compose가 아닌 Dockerfile만으로 시도하려고 했는데, 이것이 nginx의 설정에 오류가 갈 것 같아 다시 한 번 확인하겠습니다

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @youyeon11, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

이 PR은 Docker와 Nginx를 활용하여 자동화된 무중단 배포 파이프라인을 구축하는 것을 목표로 합니다. Blue/Green 배포 전략을 도입함으로써, 애플리케이션 업데이트 시 서비스 중단 없이 안정적인 배포가 가능해져 시스템의 신뢰성과 효율성을 크게 향상시킵니다.

Highlights

  • 무중단 배포 시스템 도입: Docker 기반의 Blue/Green 무중단 배포 시스템을 위한 파일들이 추가 및 수정되었습니다.
  • Dockerfile 개선: Python 가상 환경(venv)을 Docker 이미지 내에서 설정하고, pip 설치 경로를 명확히 하여 빌드 효율성과 안정성을 높였습니다.
  • 배포 스크립트(deploy.sh) 구현: 현재 활성화된 컨테이너(Blue 또는 Green)를 감지하여 새로운 컨테이너를 배포하고, Nginx 설정을 동적으로 업데이트하여 트래픽을 전환하는 로직이 추가되었습니다.
  • Nginx 설정 추가: nginx.conf 파일이 새로 추가되어 backend 업스트림을 정의하고, 들어오는 HTTP 요청을 해당 백엔드 컨테이너로 프록시하도록 구성되었습니다.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@youyeon11
Copy link
Contributor Author

🤖 Gemini 코드 리뷰 결과

1. 주요 변경 사항 요약 및 의도 파악

이 PR은 Docker 기반 Blue/Green 배포 방식을 구현하여 무중단 배포를 가능하게 하는 것을 목표로 합니다. deploy.sh 스크립트를 통해 현재 실행 중인 컨테이너(blue 또는 green)를 판별하고, 다른 색상의 컨테이너를 실행한 후 Nginx 설정을 변경하여 트래픽을 전환합니다. 이를 통해 서비스 중단 없이 새로운 버전의 애플리케이션을 배포할 수 있도록 합니다.

2. 코드 품질 및 가독성

  • 코드 스타일: 전반적으로 쉘 스크립트와 Nginx 설정 파일의 스타일은 괜찮습니다. 다만, 쉘 스크립트의 변수명 통일성을 위해 AFTER_COMPOSE_COLOR 대신 AFTER_COLOR를 사용하는 것이 좋겠습니다. (이미 사용 중)
  • 변수/함수명: deploy.sh에서 BEFORE_COLOR, AFTER_COLOR처럼 변수명이 직관적입니다.
  • 주석/문서화: deploy.sh에 각 단계별로 주석이 잘 추가되어 있어 이해하기 쉽습니다. 하지만 nginx.conf에는 주석이 없어 추가하면 좋겠습니다.
  • 중복 코드: 현재까지는 중복 코드가 보이지 않습니다.

3. 잠재적 버그 및 엣지 케이스

  • deploy.sh 스크립트 로직: 현재 스크립트는 blue 또는 green 둘 중 하나만 존재한다고 가정합니다. 만약 둘 다 없거나 둘 다 실행 중인 경우 등의 엣지 케이스를 고려해야 합니다. docker ps 결과를 파싱하여 컨테이너 상태를 더 정확하게 확인하는 로직이 필요합니다.
  • Nginx 설정 변경: sed 명령어를 사용하여 Nginx 설정 파일을 직접 수정하는 방식은 에러 발생 시 복구가 어려울 수 있습니다. 템플릿 파일을 사용하고 변수를 치환하는 방식을 고려해 보는 것이 좋겠습니다.
  • 에러 핸들링: deploy.shset -e와 trap을 사용하여 에러 처리를 하고 있지만, 더 구체적인 에러 메시지를 출력하고 로그를 남기는 것이 좋습니다. 예를 들어, docker 명령어 실행 결과를 확인하고 실패 시 에러 메시지를 출력하도록 개선할 수 있습니다.

4. 성능 및 효율성

  • 시간 복잡도: 배포 스크립트의 성능은 크게 문제되지 않을 것으로 예상됩니다.
  • 자원 사용: docker image prune -af를 통해 사용하지 않는 이미지를 삭제하여 디스크 공간을 효율적으로 관리하고 있습니다.
  • 최적화 제안: 현재로서는 특별한 최적화가 필요해 보이지 않습니다.

5. 보안 및 아키텍처

  • 보안 취약점: 현재 코드에서 특별한 보안 취약점은 보이지 않습니다. .env 파일에 중요한 정보를 저장하지 않도록 주의해야 합니다. Secrets Management를 활용하는 것을 권장합니다.
  • 아키텍처 적합성: Blue/Green 배포 전략은 무중단 배포를 위한 적절한 선택입니다.
  • 확장성: 현재 구현은 두 개의 컨테이너(blue/green)로 제한되어 있습니다. 추후 확장을 고려한다면 컨테이너 orchestration 도구(e.g., Kubernetes) 도입을 고려해 볼 수 있습니다. 또한, Nginx 설정 파일을 직접 수정하는 대신, Consul, etcd와 같은 서비스 디스커버리를 활용하는 것도 장기적인 확장성 측면에서 유리할 수 있습니다.

전반적으로 Blue/Green 배포 방식을 구현하는 좋은 시작입니다. 위에 제시된 피드백을 반영하여 코드의 안정성, 유지보수성, 확장성을 더욱 향상시킬 수 있을 것입니다. 특히 엣지 케이스 처리와 Nginx 설정 변경 방식에 대한 개선을 권장합니다. 수고하셨습니다!

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

이 PR은 Docker 기반 Blue/Green 무중단 배포를 위한 설정을 추가합니다. 전반적인 접근 방식은 좋지만, 배포 스크립트와 Dockerfile에 몇 가지 치명적인 오류와 개선점이 있습니다.

주요 피드백:

  • Dockerfile: 의존성 설치 경로에 오류가 있어 빌드가 실패할 수 있습니다. 또한 Docker 캐시를 더 효율적으로 사용하도록 개선할 수 있습니다.
  • deployment/deploy.sh:
    • 변수 이름 오타로 인해 스크립트가 실패하는 치명적인 버그가 있습니다.
    • 현재 헬스 체크가 애플리케이션의 준비 상태를 보장하지 않아 다운타임의 위험이 있습니다.
    • 불필요한 Nginx 재시작과 과도하게 공격적인 이미지 정리 명령어 등 안정성을 저해할 수 있는 부분들이 있습니다.
  • nginx.conf: 안정성 향상을 위해 타임아웃 설정 추가를 권장합니다.

자세한 내용은 각 파일에 대한 개별 코멘트를 참고해주세요. 이러한 문제들을 수정하면 더 안정적이고 효율적인 배포 파이프라인을 구축할 수 있을 것입니다.

"$VENV_PATH/bin/python" -m pip install --upgrade pip

COPY . .
RUN "$VENV_PATH/bin/pip" install --no-cache-dir -r app/requirements.txt

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

현재 WORKDIR/app으로 설정되어 있고, COPY . . 명령어로 인해 requirements.txt 파일은 /app/requirements.txt에 위치하게 됩니다. 하지만 RUN 명령어에서 -r app/requirements.txt를 사용하고 있어, /app/app/requirements.txt 경로를 찾게 되어 빌드가 실패합니다. 올바른 경로인 requirements.txt로 수정해야 합니다.

추가적으로, Docker 빌드 캐시를 더 효율적으로 활용하기 위해 requirements.txt 파일을 먼저 복사하고 의존성을 설치한 후에 나머지 애플리케이션 코드를 복사하는 것을 고려해보세요. 이렇게 하면 빌드 시간을 단축할 수 있습니다.

RUN "$VENV_PATH/bin/pip" install --no-cache-dir -r requirements.txt


# 새 컨테이너 실행
docker run --gpus all -d \
--name "app-${AFTER_COLOR}" \

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

정의되지 않은 AFTER_COLOR 변수를 사용하고 있습니다. AFTER_COMPOSE_COLOR의 오타로 보입니다. 이 문제는 스크립트가 컨테이너의 이름을 제대로 설정하지 못하게 하여 배포 실패로 이어집니다. 36, 37번째 줄에서도 동일한 실수가 발견되었으니 함께 수정해야 합니다.

Suggested change
--name "app-${AFTER_COLOR}" \
--name "app-${AFTER_COMPOSE_COLOR}" \

Comment on lines +35 to +45
for i in $(seq 1 60); do
if docker ps --filter "name=^/app-${AFTER_COLOR}$" --filter "status=running" --format '{{.Names}}' | grep -q .; then
echo "New app-${AFTER_COLOR} container is running."
break
fi
sleep 1
if [ "$i" -eq 60 ]; then
echo "New container failed to start in time." >&2
exit 1
fi
done

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

현재 헬스 체크는 컨테이너가 running 상태인지만 확인하고 있습니다. 이는 애플리케이션이 실제로 요청을 처리할 준비가 되었음을 보장하지 않습니다. 만약 Nginx가 아직 초기화 중인 컨테이너로 트래픽을 전환하면 다운타임이 발생할 수 있습니다.

애플리케이션에 /health 엔드포인트가 있으므로, curl을 사용하여 해당 엔드포인트가 정상 응답을 반환할 때까지 폴링하는 방식으로 헬스 체크를 강화하는 것이 좋습니다. 이렇게 하면 서비스 안정성을 크게 높일 수 있습니다.

Suggested change
for i in $(seq 1 60); do
if docker ps --filter "name=^/app-${AFTER_COLOR}$" --filter "status=running" --format '{{.Names}}' | grep -q .; then
echo "New app-${AFTER_COLOR} container is running."
break
fi
sleep 1
if [ "$i" -eq 60 ]; then
echo "New container failed to start in time." >&2
exit 1
fi
done
for i in $(seq 1 60); do
echo "Waiting for app-${AFTER_COMPOSE_COLOR} to be ready... ($i/60)"
if curl -fs "http://localhost:${HOST_PORT}/health" | grep -q '"status":"ok"'; then
echo "New app-${AFTER_COMPOSE_COLOR} container is ready."
break
fi
sleep 1
if [ "$i" -eq 60 ]; then
echo "New container failed to start in time." >&2
docker logs "app-${AFTER_COMPOSE_COLOR}"
exit 1
fi
done

Comment on lines +66 to +69
if ! docker compose restart nginx; then
ERR_MSG='Failed to reload Nginx'
exit 1
fi

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

docker exec ... nginx -s reload를 통해 이미 Nginx 설정을 무중단으로 리로드했습니다. docker compose restart nginx는 불필요하며, 오히려 Nginx 컨테이너를 재시작시켜 짧은 다운타임을 유발할 수 있습니다. 또한, 스크립트의 다른 부분에서는 docker run을 사용하고 있어 docker compose 명령어는 일관성을 해칩니다. 이 블록은 삭제하는 것이 좋습니다.

# 이전 컨테이너 종료
docker stop app-${BEFORE_COMPOSE_COLOR}
docker rm app-${BEFORE_COMPOSE_COLOR}
docker image prune -af

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

docker image prune -af 명령어는 사용되지 않는 모든 이미지를 강제로 삭제합니다. 이는 현재 배포와 관련 없는 다른 이미지까지 삭제할 수 있어 위험합니다. 예를 들어, 다른 프로젝트의 이미지가 삭제되거나, 롤백에 필요한 이전 버전 이미지가 사라질 수 있습니다. 이전 컨테이너를 삭제한 후에는 단순히 dangling 이미지(-f 옵션만 사용)만 정리하는 것이 더 안전합니다. -a 플래그는 신중하게 사용해야 합니다.

Suggested change
docker image prune -af
docker image prune -f

Comment on lines +5 to +7
ERR_MSG=''

trap 'echo "Error occured: $ERR_MSG. Exiting deploy script."; exit 1' ERR

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 trap을 사용한 오류 처리 방식이 일부 명령어에 대해서만 ERR_MSG를 설정하고 있습니다. 예를 들어 docker pull이나 docker run이 실패할 경우 set -e에 의해 스크립트는 중단되지만, ERR_MSG가 비어있어 "Error occured: . Exiting deploy script."와 같이 불분명한 메시지가 출력됩니다. 모든 잠재적 실패 지점에서 ERR_MSG를 설정하거나, trap에서 $BASH_COMMAND와 같은 내장 변수를 활용하여 실패한 명령어를 직접 출력하는 방식을 고려해볼 수 있습니다.

Comment on lines +11 to +21
if sudo docker ps --filter "name=app-blue" --quiet | grep -E .; then
echo "Blue down, Green Up "
BEFORE_COMPOSE_COLOR="blue"
AFTER_COMPOSE_COLOR="green"
HOST_PORT="8001"
else
echo "Green down, Blue up"
BEFORE_COMPOSE_COLOR="green"
AFTER_COMPOSE_COLOR="blue"
HOST_PORT="8000"
fi

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

현재 실행 중인 컨테이너 상태를 설명하는 echo 메시지가 혼란스럽습니다. 예를 들어, app-blue가 실행 중일 때 "Blue down"이라고 출력되는데, 이는 실제 상태와 반대입니다. 메시지를 "현재: Blue -> 배포: Green"과 같이 명확하게 수정하여 스크립트의 동작을 더 쉽게 이해할 수 있도록 하는 것이 좋습니다. 또한, grep -E . 대신 grep -q .를 사용하면 불필요한 출력을 줄일 수 있습니다.

Suggested change
if sudo docker ps --filter "name=app-blue" --quiet | grep -E .; then
echo "Blue down, Green Up "
BEFORE_COMPOSE_COLOR="blue"
AFTER_COMPOSE_COLOR="green"
HOST_PORT="8001"
else
echo "Green down, Blue up"
BEFORE_COMPOSE_COLOR="green"
AFTER_COMPOSE_COLOR="blue"
HOST_PORT="8000"
fi
if sudo docker ps --filter "name=app-blue" --quiet | grep -q .; then
echo "Current: Blue -> Deploying: Green"
BEFORE_COMPOSE_COLOR="blue"
AFTER_COMPOSE_COLOR="green"
HOST_PORT="8001"
else
echo "Current: Green -> Deploying: Blue"
BEFORE_COMPOSE_COLOR="green"
AFTER_COMPOSE_COLOR="blue"
HOST_PORT="8000"
fi

Comment on lines +11 to +19
location / {
proxy_pass http://backend/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

기본적인 프록시 설정은 올바릅니다. 하지만 더 안정적인 운영을 위해 프록시 관련 타임아웃 설정을 추가하는 것을 고려해 보세요. 예를 들어, 백엔드 서버의 응답이 느릴 경우를 대비해 proxy_read_timeout 등을 설정하면 예기치 않은 연결 끊김을 방지할 수 있습니다.

location / {
    proxy_pass http://backend/;
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

@youyeon11 youyeon11 merged commit a02453e into develop Sep 2, 2025
@youyeon11 youyeon11 mentioned this pull request Sep 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant