저번 시간에 이어 Dockerfile 작성법과 CI/CD에 대해 알아보겠습니다.
도커 이미지를 생성하기 위한 스크립트(설정파일)이다. 도커는 Dockerfile에 작성된 명령문을 순서대로 수행하며 도커 이미지를 생성한다.
파일의 이름은 무조건 Dockerfile
로 해야 한다.
# 이미지 설정
FROM python:3.11
# 작성자
LABEL authors="이름"
# 작업 디렉토리 설정
WORKDIR /app
# 프로젝트의 의존성 파일을 컨테이너 내부로 복사한다.
COPY requirements.txt /app/
# 의존성 설치
RUN pip install -r requirements.txt
# 나머지 프로젝트 파일들을 컨테이너 내부로 복사한다.
COPY . /app/
# 8000포트로 노출
EXPOSE 8000
# Docker 컨테이너가 시작될 때 실행될 명령어를 지정한다. 여기서는 Django 개발 서버를 실행한다.
CMD ["python3", "manage.py", "runserver", "0.0.0.0:8000"]
위 코드를 통해 외부에서 8000번 포트로 접속할 수 있다.
실제로 개발을 하는 모습을 상상해 보자. 기능을 개발하고, 깃 푸시, 풀, 테스트, 다시 기능 개발.. 푸시, 풀.. 을 반복하게 된다.
정상적으로만 돌아간다면 상관이 없겠지만 그 과정에서 분명히 에러는 발생할 것이다. 이때, 테스트시 에러가 발생하면 수정하고 깃 푸시.. 풀.. 테스트.. 또 에러발생하면 수정 후 푸시.. 라는 작업을 반복적으로 하게 된다.
굉장히 번거롭다. 이를 해결하기 위해 나온 개념이다.
번역하면 지속적인 통합이라는 뜻으로, 애플리케이션의 새로운 코드 변경 사항이 정기적으로 빌드 및 테스트 되어 공유 레포지토리에 통합하는 것을 의미한다.
Git과 같은 형상관리 도구를 사용함을 의미한다.
코드 변경사항이 테스트를 통과하면 자동적으로 생산 환경에 배포되는 과정이다. 우리나라의 경우는 Deployment
라는 뜻으로 주로 쓰인다.
다양한 도구들이 있지만 간단한 프로젝트에서는 GitHub의 Actions를 사용하고, 더 많은 기능이 필요하다면 Jenkins 등의 도구를 사용한다.
Web Server Gateway Interface
의 약자로, 파이썬 기반의 웹 개발에서 자주 사용되며, 동기 방식의 처리를 기본으로 한다.
Django
애플리케이션을 웹 서버에 연결하는 방식을 정의한 것이라고 볼 수도 있다.
Asynchronous Server Gateway Interface
의 약자로, 비동기식 웹 서버 및 애플리케이션과의 통신을 위한 인터페이스 규격이다.
채팅 서비스와 같은 실시간 양방향 데이터 통신에 사용한다고 한다.
파이썬 기반의 애플리케이션을 위한 WSGI
웹 서버이다. 메모리 자원 사용이 적고 CPU 사용량이 낮아 성능이 좋다.
웹 브라우저나 다른 클라이언트의 HTTP 요청을 받아, 해당 요청을 파이썬 웹 애플리케이션에 전달하고, 애플리케이션의 응답을 클라이언트에 다시 전송한다.
파이썬 프로그램을 호출하는 서버이다.
Django
애플리케이션 배포 시 Gunicorn
과의 조합으로 사용한다. Nginx
는 정적 데이터(이미지, CSS, JS, 기타 미디어 파일 등)를 처리하고 리버스 프록시(Reverse Proxy)역할을 하며 Gunicorn
은 WSGI 서버로 동작한다.
리버스 프록시는 클라이언트와 서버 중간에 대리자 역할을 하는 서버를 말한다. 부하를 분산시킨다고 볼 수 있다.
보통 Nginx와 Gunicorn을 한 프로젝트 내에 위치시켜 Unix 소켓 방식으로 Ip를 사용하지 않고 통신을 한다.
Django 프로젝트(DRF)에서 배포하는 예시이다.
# settings.py
...
WSGI_APPLICATION = "{프로젝트이름}.wsgi.application"
DEBUG = False # 배포환경에서는 DEBUG 옵션을 해제해야합니다.
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": DB_NAME, # env파일
"USER": DB_USER, # env파일
"PASSWORD": DB_PASSWORD, # env파일
"HOST": DB_HOST, # env파일
"PORT": DB_PORT, # env파일
}
}
### postgresql을 사용하려면 psycopg2 설치가 수행되어야합니다.
git clone {장고 레포지토리 주소}
# 가상환경 적용
python3 -m venv venv
source {가상환경이름}/bin/activate
# 설치
pip install -r requirements.txt
# 설치
pip install gunicorn
# 경로
cd /home/{사용자명}/{프로젝트 루트 디렉터리}
gunicorn --bind unix:/tmp/gunicorn.sock {프로젝트명}.wsgi:application
# 일반적인 수행방법(동일하지 않은 시스템)
# gunicorn --bind 0:5000 {프로젝트이름}.wsgi:application
위 코드는 유닉스(우분투) 소켓을 활용한 방법인데 이렇게 실행하면 반드시 Nginx
와 같은 웹서버가 필요하다고 한다.
# 편집기 실행
sudo vim /etc/systemd/system/{프로젝트명}.service
# 파일 작성
[Unit]
Description=gunicorn daemon
After=network.target
[Service]
User=ubuntu # 유저이름 확인해주세요.
Group=ubuntu
WorkingDirectory=/home/ubuntu/{프로젝트 디렉토리} # 프로젝트 루트 디렉터리
EnvironmentFile=/home/ubuntu/{프로젝트 디렉토리}/.env # 환경변수 파일
ExecStart=/home/ubuntu/{프로젝트 디렉토리}/venv/bin/gunicorn \ #가상환경에 설치된 gunicorn
--workers 2 \ #워커 갯수
--bind unix:/tmp/gunicorn.sock \ # WSGI실행 명령
{프로젝트명}.wsgi:application # WSGI(Django) 애플리케이션
[Install]
WantedBy=multi-user.target
# 실행
sudo systemctl start {위에서 작성했던 파일명}
# 확인
sudo systemctl status {위에서 작성했던 파일명} # .service 빼고 작성
# 서버가 재실행 될 때 Gunicorn 서비스가 자동 실행되게 만들어 줍니다.
sudo systemctl enable {위에서 작성했던 파일명} # .service 빼고입니다.
# 서비스 재실행 명령
sudo systemctl restart {위에서 작성했던 파일명} # .service 빼고입니다.
Nginx 세팅
일반적인 Nginx를 포함한 웹 아키텍쳐는 클라이언트 -> Nginx -> WSGI 서버(Gunicorn 등) -> Django로 구성된다.
이러한 Nginx를 프록시 서버라고 한다.
# nginx 설정파일 편집 실행
sudo vim /etc/nginx/sites-enabled/default
server{
root /var/www/html;
server_name # 도메인 주소 없으면 IPV4 주소(IP)
location /api/v1/{ # 클라이언트에서 오는 /api/v1/ 으로 시작되는 모든 요청 핸들링
include proxy_params;
proxy_pass http://unix:/tmp/gunicorn.sock;
}
}
proxy_pass
: 프론트의 요청을 전달해야 하는 백엔드 서버의 주소를 지정proxy_params
: 클라이언트 IP 주소, 요청 스키마, 호스트 이름 등을 전달하기 위한 내용들이 포함된다. 이를 통해 백엔드 서버에서도 클라이언트의 정보를 알 수 있다.
해당하는 항목을 클릭하여 트리거 파일을 생성한다. 그 안에 다음과 같은 내용을 집어넣는다.
name: Django CI/CD
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: 체크아웃 레포지토리
uses: actions/checkout@v3
- name: 파이썬 설정
uses: actions/setup-python@v3
with:
python-version: '3.11'
- name: 의존성 설치
run: |
pip install --upgrade pip
pip install -r requirements.txt
- name: 서버 배포
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
password: ${{ secrets.SERVER_PASSWORD }}
script: |
set -e
cd /home/ubuntu/{레포지토리주소}
git pull origin main
echo "SECRET_KEY=\"${{ secrets.SECRET_KEY }}\"" > .env
echo "DEBUG='${{ secrets.DEBUG }}'" >> .env
source venv/bin/activate
pip install -r requirements.txt
sudo systemctl restart {아까등록한서비스이름}.service
on
으로 시작하는 부분은 main
브랜치에 코드가 push
혹은 pull_request
가 발생할 때 수행된다는 뜻이다.
jobs
는 실행할 작업들을 정의하는 부분이다.
runs-on
은 작업이 실행될 가상 환경의 유형을 지정한다.
steps
는 워크플로우가 진행되는 각 단계(action)을 의미한다.
$
로 시작하는 부분은 이전에 정의했던 환경 변수들이다.
set -e
스크립트는 어느 부분에서든 명령어가 실패하면 즉시 중단되고, 작업이 실패 상태로 표시됨을 의미한다.
yaml 파일은 아주 예민한 파일이기에 띄어쓰기, 탭 등을 매우매우 중요하게 봐야 한다.
성공시 위와 같은 화면이 나오게 된다.
아키텍쳐 그림은 위니브 김승주 강사님 자료를 제공받아 작성하였습니다.