Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/pr-auto-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Install Python Packages
run: |
python -m pip install --upgrade pip
pip install google-generativeai PyGithub
pip install --no-cache-dir google-generativeai PyGithub

- name: Get Git Diff
id: git_diff
Expand Down
18 changes: 13 additions & 5 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:

- name: Build, tag, and push image to Amazon ECR
run: |
docker build -t dearbelly-cv .
docker build --no-cache -t dearbelly-cv .
docker tag dearbelly-cv:latest ${{ secrets.ECR_URI }}/dearbelly-cv
docker push ${{ secrets.ECR_URI }}/dearbelly-cv:latest

Expand All @@ -37,6 +37,14 @@ jobs:
- name: Checkout Code
uses: actions/checkout@v4

- name: Transfer deploy file to EC2
uses: appleboy/scp-action@master
with:
host: ${{ secrets.REMOTE_HOST }}
username: ${{ secrets.REMOTE_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
source: ./deployment/
target: /home/ubuntu/deployment

deploy:
name: Deploy
Expand All @@ -52,8 +60,8 @@ jobs:
port: 22
script: |
# 1. cd
mkdir -p ~/dearbelly
cd ~/dearbelly
mkdir -p ~/dearbelly/deployment
cd ~/dearbelly/deployment

# 2. .env file
echo "${{ secrets.ENV }}" > .env
Expand All @@ -69,5 +77,5 @@ jobs:
docker pull ${{ secrets.ECR_URI }}/dearbelly-cv:latest

# 6. docker start
# TODO : 스크립트 작성
docker run -d --name app-blue -p 8000:8000 ${{ secrets.ECR_URI }}/dearbelly-cv:latest
chmod +x deploy.sh
source deploy.sh
13 changes: 9 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,18 @@ FROM nvidia/cuda:12.6.0-runtime-ubuntu24.04
WORKDIR /app

RUN apt-get update && \
apt-get install -y python3 python3-venv python3-pip git && \
ln -s /usr/bin/python3 /usr/bin/python && \
apt-get clean && rm -rf /var/lib/apt/lists/*
apt-get install -y --no-install-recommends \
python3 python3-pip python3-venv git ca-certificates && \
rm -rf /var/lib/apt/lists/*

ENV VENV_PATH=/opt/venv
RUN python3 -m venv "$VENV_PATH" && \
"$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


RUN pip install --no-cache-dir -r app/requirements.txt
ENV PATH="$VENV_PATH/bin:$PATH"

EXPOSE 8000

Expand Down
73 changes: 73 additions & 0 deletions deployment/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#!/bin/bash

set -e

ERR_MSG=''

trap 'echo "Error occured: $ERR_MSG. Exiting deploy script."; exit 1' ERR
Comment on lines +5 to +7

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와 같은 내장 변수를 활용하여 실패한 명령어를 직접 출력하는 방식을 고려해볼 수 있습니다.



# 현재 포트 파악
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
Comment on lines +11 to +21

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


echo "Pulling image: ${IMAGE}"
# docker pull
docker compose pull ${ECR_URI}/dearbelly-cv:latest
docker compose up -d --no-deps --force-recreate app-${AFTER_COMPOSE_COLOR}


# 새 컨테이너가 running 될 때까지 대기
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
Comment on lines +30 to +40

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


# 새로운 컨테이너 확인 후 Nginx 설정 변경
if docker ps --filter "name=app-${AFTER_COMPOSE_COLOR}" --filter "status=running" --format '{{.Names}}' | grep -q .; then
echo "New app-${AFTER_COMPOSE_COLOR} container is running."
# reload nginx
NGINX_ID=$(sudo docker ps --filter "name=nginx" --quiet)
NGINX_CONFIG="/home/ubuntu/deployment/nginx.conf"

echo "Switching Nginx upstream config..."
if ! sed -i "s/app-${BEFORE_COMPOSE_COLOR}:8000/app-${AFTER_COMPOSE_COLOR}:8000/" $NGINX_CONFIG; then
echo "Error occured: Failed to update Nginx config. Exiting deploy script."
exit 1
fi

echo "Reloding Nginx in Container"
if ! docker exec $NGINX_ID nginx -s reload; then
ERR_MSG='Failed to update Nginx config'
exit 1
fi

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

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

fi

echo "Deployment success."
exit 0
30 changes: 30 additions & 0 deletions deployment/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
services:
app-blue:
image: ${ECR_URI}/dearbelly-dv:latest
ports:
- "8000:8000"
env_file:
- /home/ubuntu/dearbelly/deployment/.env
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [gpu]
container_name: app-blue

app-green:
image: ${ECR_URI}/dearbelly-dv:latest
ports:
- "8001:8000"
env_file:
- /home/ubuntu/dearbelly/deployment/.env
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities: [ gpu ]
container_name: app-green
21 changes: 21 additions & 0 deletions deployment/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
events {}

http {
upstream backend {
server app-blue:8000;
}

server {
listen 80;

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;

}
Comment on lines +11 to +19

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;
}

}
}