Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
d9f1d2c
🔨calc(101)
Sep 13, 2025
4e22689
🔨views추기
Sep 13, 2025
aa2c323
서비스
Sep 13, 2025
af868f8
🐛models.py 변경
Sep 20, 2025
e21dc38
🐛pays model 수정(101)
Sep 20, 2025
33fede7
🚀배포_환경설정(103)
Sep 22, 2025
a72a4f2
🚀deploy.yml 수정(103)
Sep 22, 2025
88688e4
🚀docker-compose.prod.yml env_file 로드설정(103)
Sep 22, 2025
1e69b0e
🚀config->configs로 수정(103)
Sep 22, 2025
bac0585
env_vars 수정
Sep 22, 2025
3757b83
🐛env_vars에 ncloud 추가(103)
Sep 22, 2025
b625d06
🐛STATIC_ROOT가 설정(103)
Sep 22, 2025
757053e
🐛EC2 디스크 용량비우기(103)
Sep 22, 2025
c338b26
🐛deploy.sh변경(103)
Sep 22, 2025
6eeac65
🐛deploy.yml 마지막 스크립트 변경(103)
Sep 22, 2025
b875bc4
nginx, web 설정변경
Sep 22, 2025
888ae14
timeout 방지
Sep 22, 2025
995c5c3
::bug::보안그룹 수정(103)
Sep 22, 2025
6c82622
🐛env_vars 수정(103)
Sep 22, 2025
0a5d6ad
🐛entrypoint 변경(103)
Sep 22, 2025
3b37c2e
🐛commend 수정(103)
Sep 22, 2025
c3aee7c
🐛docker-compose env_file .env로 변경(103)
Sep 22, 2025
566659f
🐛Dockerfile.prod COPY . (103)
Sep 22, 2025
8233da1
🐛compose에 SECRET_KEY 넣기(103)
Sep 22, 2025
6babd90
🐛web에도 .env 불러오기(103)
Sep 22, 2025
6b330b4
🐛docker-compose.yml db 수정(103)
Sep 22, 2025
e7ed8df
용량 지우기(103)
Sep 22, 2025
fdfc383
🐛rsync오류(103)
Sep 22, 2025
5d6bb85
🐛rsync오류2(103)
Sep 22, 2025
9b44c07
🐛rds_name을 초기데이터베이스 이름으로 변경(103)
Sep 23, 2025
df8272c
docker-compose.yml env_file 수정(103)
Sep 23, 2025
d12cde5
🐛settings.py .env파일을 읽도록 변경(103)
Sep 23, 2025
e3e3b00
🐛settings/production.py 수정&database_url 수정(103)
Sep 23, 2025
05537c3
🐛SECRET_KEY 교체:env 값 파싱 문제(103)
Sep 23, 2025
62f8a4d
🐛SECRET_KEY =과 따옴표 없게 교체(103)
Sep 23, 2025
8b3af53
🐛SECRET_KEY docker-compose에 넣기(103)
Sep 23, 2025
a3c89a0
🐛SECRET_KEY 따옴표로 감싸기(103)
Sep 23, 2025
c156f5b
🐛작은따옴표 heredoc로 만들기(103)
Sep 23, 2025
3cb1681
🐛settings_init_.py 삭제(103)
Sep 23, 2025
ec4ee84
🐛development.py 로드(103)
Sep 23, 2025
48decd9
.
Sep 23, 2025
16d29de
🐛secretkey 에러로그 출력코드추가(103)
Sep 23, 2025
88623c7
🐛dev용 compose에 .env 확실히 주입(103)
Sep 23, 2025
7b0413a
🐛프로젝트명 고정(103)
Sep 23, 2025
c6787c0
🐛코드 초기화(103)
Sep 23, 2025
ba61b08
🐛base.py 다시 고침(103)
Sep 23, 2025
4cd2ec0
🐛공간부족오류(103)
Sep 23, 2025
75d1263
v1에서 name지우기(103)
Sep 23, 2025
758a467
deploy.sh가 cd추가(103)
Sep 23, 2025
919314a
공간부족(103)
Sep 23, 2025
e8c9a38
🐛env파일생성 경로변경(103)
Sep 23, 2025
aaf0079
🐛env파일생성 경로변경2(103)
Sep 23, 2025
dc99fdf
공간부족(103)
Sep 23, 2025
bca653e
🐛env 파일참조 추가(103)
Sep 23, 2025
3871cf9
🐛rds 엔드포인트 주소 오류(103)
Sep 23, 2025
0a9810e
공간부족(103)
Sep 23, 2025
c75af32
env의 db이름 수정(103)
Sep 23, 2025
1db184b
dns 수정(103)
Sep 23, 2025
5dbf0b3
공간부족(103)
Sep 23, 2025
7f73a1d
확인용(103)
Sep 24, 2025
1acc15f
🐛 ko모델(103)
Sep 24, 2025
f6d794c
🐛 ko모델 관련 수정(103)
Sep 24, 2025
351791d
🐛 host 값 수정
Sep 24, 2025
242a38e
🚀 deploy
Sep 25, 2025
e201368
🐛 configS로 수정(103)
Sep 25, 2025
9d8558e
🐛 bash로 deploy.yml 바꿈(103)
Sep 25, 2025
d078722
🐛 bash 실행권한 추가(103)
Sep 25, 2025
0517f37
🐛 env_vars에 secret_key 추가(103)
Sep 25, 2025
ae71b12
🐛 N관련 env 추가(103)
Sep 25, 2025
900377c
🐛 env(103)
Sep 25, 2025
1eb8880
🐛 env 완전 수정(103)
Sep 25, 2025
a1b3dd6
🐛 env(103)
Sep 25, 2025
9f97e23
🐛 인스턴스 재시작
Sep 26, 2025
96eb2f6
🐛 dns 수정(103)
Sep 26, 2025
7b14a47
🐛 인스턴스다시시작(103)
Sep 26, 2025
d380ade
🐛 ko 모델(103)
Sep 26, 2025
992ff16
🐛 ko 모델 설치 부분 직접 넣기
Sep 26, 2025
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
59 changes: 59 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Deploy to EC2

on:
push:
branches:
- main # main브랜치에 push되었을 때
pull_request:
branches:
- main # main브랜치에 pr 날렸을 때

jobs:
deploy:
name: Build & Deploy
runs-on: ubuntu-latest

steps:
# 1) 레포 체크아웃
- name: Checkout
uses: actions/checkout@v4

# Action Secret에 설정한 ENV_VARS 값을 env 폴더안의 .env.production으로 만듦
- name: create env file
run: |
mkdir -p env
echo "${{ secrets.ENV_VARS }}" > env/.env.production

# EC2에 접속해 원격디렉토리 생성
- name: create remote directory
uses: appleboy/[email protected]
with:
host: ${{ secrets.HOST }}
username: ubuntu
key: ${{ secrets.KEY }}
script: mkdir -p /home/ubuntu/srv/ubuntu

# SSH 키를 통해 소스 코드를 EC2 인스턴스(서버)로 복사
- name: copy source via ssh key
uses: burnett01/[email protected]
with:
switches: -avzr --delete
remote_path: /home/ubuntu/srv/ubuntu/
remote_host: ${{ secrets.HOST }}
remote_user: ubuntu
remote_key: ${{ secrets.KEY }}

# 서버에 접속한 뒤 deploy.sh(배포 스크립트)를 실행
- name: executing remote ssh commands using password
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.HOST }}
username: ubuntu
key: ${{ secrets.KEY }}
script: |
set -e
# 권한/개행 정리
sed -i 's/\r$//' /home/ubuntu/srv/ubuntu/configs/scripts/deploy.sh
chmod +x /home/ubuntu/srv/ubuntu/configs/scripts/deploy.sh
# 실행 (실행비트가 없어도 확실히 하려면 bash로 읽어 실행)
bash /home/ubuntu/srv/ubuntu/configs/scripts/deploy.sh
47 changes: 47 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Debian slim 기반: 과학 스택(NumPy/Scipy/Sklearn/kiwi/fasttext) 빌드 호환성↑
FROM python:3.12-slim

# 파이썬 런타임 편의/일관성
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1 \
TZ=Asia/Seoul

# 작업 디렉토리
WORKDIR /app

# 시스템 의존성
# - libpq-dev: (psycopg2-binary는 없어도 되지만 있어도 무방)
# - libjpeg-dev, zlib1g-dev: Pillow
# - libgomp1, libstdc++6: fasttext, sklearn/numba 등 OpenMP/CPP 런타임
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
gcc g++ \
libpq-dev \
libjpeg-dev zlib1g-dev \
cmake ninja-build \
libgomp1 libstdc++6 \
tzdata \
&& rm -rf /var/lib/apt/lists/*

# pip / setuptools / wheel 버전 고정(네 요구사항에 맞춤)
RUN python -m pip install --upgrade "pip==25.2" "setuptools==80.9.0" wheel

# 의존성 설치: 대규모 빌드 가속 위해 numpy 먼저
COPY requirements.txt /app/requirements.txt
RUN pip install "numpy==1.26.4"
RUN pip install --no-build-isolation -r requirements.txt

# 비루트 유저
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser

# 앱 소스 복사
COPY . /app/

# (옵션) 기본 포트 노출 — gunicorn 8000 사용 시
EXPOSE 8000

# 실행 명령은 docker-compose나 ECS에서 지정
# 예) gunicorn configs.wsgi:application --bind 0.0.0.0:8000
82 changes: 82 additions & 0 deletions Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# ======================
# BUILDER
# ======================
FROM python:3.12-slim AS builder

WORKDIR /usr/src/app

ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
DEBIAN_FRONTEND=noninteractive


# psycopg2, Pillow, 과학 스택 빌드에 필요한 헤더/툴
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential gcc g++ \
libpq-dev \
libjpeg-dev zlib1g-dev \
tzdata \
&& rm -rf /var/lib/apt/lists/*

# 최신 pip
RUN python -m pip install --upgrade "pip==25.2" "setuptools==80.9.0" wheel

# 의존성 wheel 미리 빌드
COPY ./requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels "numpy==1.26.4"
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /usr/src/app/wheels -r requirements.txt


# ======================
# FINAL
# ======================
FROM python:3.12-slim


ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PIP_NO_CACHE_DIR=1 \
DEBIAN_FRONTEND=noninteractive \
TZ=Asia/Seoul \
HOME=/home/app \
APP_HOME=/home/app/web

# 런타임 의존성만 최소 설치
# - libgomp1 / libstdc++6: scikit-learn/fasttext OpenMP/CPP 런타임
# - libjpeg/zlib: Pillow
# - libpq5: psycopg2-binary 실행 시 필요할 수 있음(안전 차원)
# - ca-certificates: 외부 요청(https) 안정화
RUN apt-get update && apt-get install -y --no-install-recommends \
libgomp1 libstdc++6 \
libjpeg62-turbo zlib1g \
libpq5 \
tzdata ca-certificates \
&& rm -rf /var/lib/apt/lists/*

# 앱 사용자/디렉터리
RUN mkdir -p /home/app && \
addgroup --system app && adduser --system --ingroup app app && \
mkdir -p $APP_HOME/static $APP_HOME/media
WORKDIR $APP_HOME

# wheel 설치
COPY --from=builder /usr/src/app/wheels /wheels
COPY --from=builder /usr/src/app/requirements.txt .
RUN pip install --no-cache-dir --no-index --find-links=/wheels -r requirements.txt

# 소스 코드 전체를 이미지에 복사
COPY . $APP_HOME

# 엔트리포인트 복사 + 윈도 개행 제거 + 실행권한
COPY ./configs/docker/entrypoint.prod.sh /home/app/web/entrypoint.prod.sh
RUN sed -i 's/\r$//' /home/app/web/entrypoint.prod.sh \
&& chmod +x /home/app/web/entrypoint.prod.sh


# 권한
RUN chown -R app:app $APP_HOME
USER app

# 서비스 포트 (gunicorn 8000 가정)
EXPOSE 8000
Empty file added __init__.py
Empty file.
2 changes: 1 addition & 1 deletion configs/asgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'configs.settings')

application = get_asgi_application()
application = get_asgi_application()
6 changes: 6 additions & 0 deletions configs/docker/entrypoint.prod.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
# python manage.py collectstatic --no-input echo "Apply database migrations" python manage.py migrate
python manage.py collectstatic --no-input
python manage.py makemigrations
python manage.py migrate
exec "$@"
5 changes: 5 additions & 0 deletions configs/nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FROM nginx:1.19.0-alpine


RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d
29 changes: 29 additions & 0 deletions configs/nginx/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# 프로젝트명이라는 업스트림(백엔드 서버 그룹) 정의
upstream configs {
# web이라는 이름의 서비스에서 8000 포트로 요청을 전달하도록 설정
server web:8000;
}

# Nginx 서버 블록 정의
server {
# Nginx가 80번 포트에서 HTTP 요청을 수신하도록 설정
listen 80;

location / {
# 클라이언트의 요청을 configs 업스트림으로 전달
proxy_pass http://configs;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_redirect off;
}

location /static/ {
alias /home/app/web/static/;
}

location /media/ {
alias /home/app/web/media/;
}
# 클라이언트가 업로드할 수 있는 최대 요청 본문 크기 설정 가능
# client_max_body_size 10M;
}
65 changes: 65 additions & 0 deletions configs/scripts/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#!/bin/bash
set -euo pipefail

# Docker 설치 여부 확인, 없다면 설치
if ! type docker > /dev/null
then
echo "docker does not exist"
echo "Start installing docker"
sudo apt-get update
sudo apt install -y apt-transport-https ca-certificates curl software-properties-common
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu bionic stable"
sudo apt update
apt-cache policy docker-ce
sudo apt install -y docker-ce
fi

# Docker Compose 설치 여부 확인, 없다면 설치
if ! type docker-compose > /dev/null
then
echo "docker-compose does not exist"
echo "Start installing docker-compose"
sudo curl -L "https://github.com/docker/compose/releases/download/1.27.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
fi

# fastText ko 모델 호스트에 사전 설치 (inline)
############################################
MODEL_DIR="/home/ubuntu/ft_models"
MODEL_BIN="${MODEL_DIR}/cc.ko.300.bin"
MODEL_GZ="${MODEL_BIN}.gz"
MODEL_URL="https://dl.fbaipublicfiles.com/fasttext/vectors-crawl/cc.ko.300.bin.gz"

# 도구 준비
if ! command -v wget >/dev/null 2>&1; then
sudo apt-get update -y
sudo apt-get install -y wget
fi
if ! command -v gunzip >/dev/null 2>&1; then
sudo apt-get update -y
sudo apt-get install -y gzip
fi

# 디렉토리/권한
sudo mkdir -p "${MODEL_DIR}"
sudo chown -R ubuntu:ubuntu "${MODEL_DIR}"

# 없으면 다운로드+압축해제, 있으면 스킵
if [ ! -f "${MODEL_BIN}" ]; then
echo "[INFO] fastText ko 모델 다운로드 시작..."
tmp="${MODEL_GZ}.part"
wget -O "${tmp}" "${MODEL_URL}"
mv "${tmp}" "${MODEL_GZ}"
gunzip -f "${MODEL_GZ}"
sudo chmod 644 "${MODEL_BIN}"
echo "[INFO] fastText 모델 준비 완료: ${MODEL_BIN}"
else
echo "[INFO] fastText 모델 이미 존재: ${MODEL_BIN}"
fi

ls -lh "${MODEL_BIN}" || true

# Docker Compose로 서버 빌드 및 실행 (docker-compose.prod.yml 사용)
echo "start docker-compose up: ubuntu"
sudo docker-compose -f /home/ubuntu/srv/ubuntu/docker-compose.prod.yml up --build -d
8 changes: 2 additions & 6 deletions configs/settings/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,2 @@
from .base import DEBUG

if DEBUG:
from .development import *
else:
from .production import *
# 패키지 초기화 단계에서 __init__.py가 from .base import DEBUG 를 실행하면서 .base를 무조건 임포트해버림.
# .env/환경변수 준비 전이라도 SECRET_KEY = env('SECRET_KEY')가 바로 실행되어 실패한다.
16 changes: 5 additions & 11 deletions configs/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,12 @@


# env

env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env(os.path.join(BASE_DIR, 'env', '.env.base'))

SECRET_KEY = env('SECRET_KEY')

DEBUG = env('DEBUG')

NCLOUD_CLIENT_ID = env('NCLOUD_CLIENT_ID')
NCLOUD_CLIENT_SECRET = env('NCLOUD_CLIENT_SECRET')
environ.Env.read_env(os.path.join(BASE_DIR, 'env', '.env.production'))

SECRET_KEY = env("SECRET_KEY")
DEBUG = env.bool("DEBUG", default=True)

# Application definition

Expand Down Expand Up @@ -111,13 +106,12 @@
# https://docs.djangoproject.com/en/5.2/howto/static-files/

STATIC_URL = 'static/'

STATIC_ROOT = os.getenv("STATIC_ROOT", "/home/app/web/static")

# Media files

MEDIA_URL = '/media/'

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_ROOT = os.getenv("MEDIA_ROOT", "/home/app/web/media")


# Default primary key field type
Expand Down
7 changes: 5 additions & 2 deletions configs/settings/development.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
from .base import *
import environ

environ.Env.read_env(os.path.join(BASE_DIR, 'env', '.env.development'))
DEBUG = True

DATABASES = {
'default': env.db(),
}


CACHES = {
'default': env.cache(),
}

ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])

CORS_ALLOWED_ORIGINS = env.list('CORS_ALLOWED_ORIGINS', default=[])

NCLOUD_CLIENT_ID = env('NCLOUD_CLIENT_ID')
NCLOUD_CLIENT_SECRET = env('NCLOUD_CLIENT_SECRET')
6 changes: 4 additions & 2 deletions configs/settings/production.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from .base import *
import environ

environ.Env.read_env(os.path.join(BASE_DIR, 'env', '.env.production'))
DEBUG = False

DATABASES = {
'default': env.db(),
Expand All @@ -14,3 +13,6 @@
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS', default=[])

CORS_ORIGIN_ALLOW_ALL = True

NCLOUD_CLIENT_ID = env('NCLOUD_CLIENT_ID')
NCLOUD_CLIENT_SECRET = env('NCLOUD_CLIENT_SECRET')
Loading