[AWS][Django] DRF로 만든 웹소켓 채팅서버를 Docker, nginx, daphne를 이용하여 AWS EC2에 배포하기(2)

이민재·2023년 11월 25일
1
post-thumbnail

저번 포스팅에이어 채팅서버를 배포하는 절차를 이어 나가보겠다.

github에 있는 내 프로젝트를 git clone하여 EC2 서버에 끌고와야한다. repository가 public으로 되어있다면 git clone하면 바로 될것이다. 하지만 private로 되어있는 프로젝트는 그냥 바로 git clone하여 가져오려고하면 보안 관련해서 뭐시기뭐시기 뜨면서 원하는대로 되지 않을것이다.

이 포스팅에서는 ssh key를 생성하여 private 프로젝트를 git clone하는 방법을 선택하였다. 그이유는 아래와 같다.

보안 인증: SSH 키는 안전한 인증 방법이다. 비밀번호 기반 인증보다 훨씬 안전하며 키를 가진 사용자만이 서버에 접근할 수 있도록 한다. 이는 특히 private 리포지토리에 중요한데 리포지토리에 대한 접근을 엄격하게 제한하고자 할 때 유용하다.

GitHub와의 보안 연결: GitHub은 SSH 키를 사용하여 리포지토리에 대한 안전한 접근을 제공한다. 이는 GitHub에 저장된 코드가 보안이 유지되면서 접근되도록 보장한다.

개인별/서버별 접근 제어: 개별 서버 또는 사용자에 대해 별도의 SSH 키를 생성함으로써 특정 서버 또는 사용자가 특정 리포지토리에 접근할 수 있도록 세밀하게 제어할 수 있다. 이는 프로젝트의 보안을 강화하는 데 도움이 된다.

이러한 이유로 AWS Ubuntu 서버에서 private GitHub 리포지토리를 클론하려 할 때 SSH 키를 사용하는 것이 권장된다. SSH 키는 안전하고 편리한 접근 방식을 제공하며 프로젝트의 보안을 유지하는 데 중요한 역할을 한다.

이제 아래의 절차를 따르며 ssh key를 생성하고 git clone하여 프로젝트를 EC2에 가져와 보겠다.

📌ssh key 생성 및 등록

📌ssh-keygen -t rsa

먼저 아래의 명령어를 입력한다. 비밀번호를 설정 나오는데 나는 그냥 enter키를 눌러 설정하였다. enter누르다보면 아래의 그림이 나올텐데 성공한 것이다.

ssh-keygen -t rsa

📌cat /home/ubuntu/.ssh/id_rsa.pub

그리고 이제 아래의 명령어를 사용해 키를 추출할것인데 아래의 명령어를 치면 긴 문자들이 나열될것이다. 아래의 파란색으로 된 부분을 전부 복사하여

cat /home/ubuntu/.ssh/id_rsa.pub

📌Deploy keys

github에 접속하여 private로 된 프로젝트의 Settings에 들어가 왼쪽 네비게이션바에 있는 Deploy keys를 클릭하고 오른쪽 상단에 있는 Add deploy key버튼을 누른다

그리고 아까 복사해온 문자들을 입력하여 키를 생성하면 아래의 화면이 나온다면 성공한것이다. 나는 aws-chat-key라는 이름을 ssh key를 생성하였다.

📌git clone

이제 Code화면으로와서 초록색 "Code" 버튼을 누룬후 SSH버튼을 눌러 노란색으로 칠한 Clone할 경로를 복사한다.

이제 복사한 값을 Putty에 git clone을 앞에 붙힌 git clone git@github.com:mimijae/chattingServer.git 이 명령어를 치면 이제 EC2에 프로젝트를 다운받아 올것이다. 명령어로 ls 쳐서 아래와 같이 잘 다운 받아졌는지 확인해보자.

이제 프로젝트를 구동시키기만 하면된다. 이 포스팅에서는 docker를 사용하여 구동시켜 보겠다. 그러기 전에 먼저 저번 [Django] Django rest framework로 웹소켓 채팅 서버 구현하기 (1)(2)(3) 이 포스팅에서 구현한 것에서 docker와 nginx에 관한 코드를 추가 하여야한다.

루트 디렉토리 경로에 아래의 빨간색 동그라미에 있는 파일들을 작성하여야한다.

nginx파일과 Docker는 파일이 보일텐데 설명을 해보겠다.

📌Docker & Nginx

Docker

Docker는 컨테이너화를 통해 어플리케이션을 격리하고 경량화된 가상 환경에서 일관성 있게 실행할 수 있게 하는 도구로 개발 및 배포 프로세스를 간소화하고 환경 간의 차이를 최소화하는 데 중요한 역할을 한다. 이 포스팅에서는 Docker Compose를 이용할 것 이다. 그 이유는 Docker에서는 단일 컨테이너를 실행하는 것은 간단하지만 웹 애플리케이션은 웹 서버, 데이터베이스, 메모리 캐시 등 여러 컨테이너와 같이 여러 서비스가 함께 작동하도록 설계되어 있기때문에 이러한 복잡한 어플리케이션을 Docker Compose는 쉽게 구성하고 관리할 수 있게 해주기 때문이다.

Docker Compose

Docker Compose 여러 컨테이너로 구성된 어플리케이션을 정의하고 실행하기 위한 도구로 YAML 파일을 통해 서비스를 설정하며 단일 명령으로 모든 서비스를 관리할 수 있다. 이를 통해 개발, 테스트, 생산 환경에서의 일관성을 유지하며, 어플리케이션의 구축과 확장을 간편하게 할 수 있다.

Nginx

Django를 서버에 배포할 때 nginx를 사용하는 것은 안정적이고 효율적인 웹 서버 기능, 정적 파일 처리, 높은 동시성 처리 능력, 그리고 보안과 로드 밸런싱을 강화하기 위해서이다. nginx는 Django 어플리케이션 서버 앞에 역방향 프록시로 작동하여 성능을 최적화하고, 웹 트래픽을 관리하는 데 중요한 역할을 한다.

위와 같은 이유로 프로젝트에 작성을 해보겠다.

📌Dockerfile

루트 디렉토리 경로에 Dockerfile이란 이름의 파일을 생성하고 아래의 코드를 작성한다.

# Dockerfile

FROM python:3.11.4

WORKDIR /usr/src/app

ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1

COPY . /usr/src/app/

RUN pip install --upgrade pip
RUN pip install -r requirements.txt

✅FROM python:3.11.4
이 라인은 Docker 이미지를 만들기 위한 기본 이미지로 Python 3.11.4 버전을 사용한다는 것을 의미한다. Docker 이미지는 이 Python 버전을 기반으로 구축된다.

✅WORKDIR /usr/src/app
Docker 컨테이너 내에서 작업 디렉토리를 /usr/src/app으로 설정한다. 이후의 명령어들은 이 디렉토리를 기준으로 실행된다.

✅ENV PYTHONDONTWRITEBYTECODE 1
이 환경 변수는 Python이 .pyc 파일을 생성하지 않도록 한다. 이는 컨테이너의 크기를 줄이는 데 도움이 된다.

✅ENV PYTHONUNBUFFERED 1
이 환경 변수 설정은 Python의 출력을 버퍼링하지 않고 즉시 콘솔에 출력하게 만든다. 이는 Docker 컨테이너에서 로그를 보다 쉽게 추적할 수 있게 해준다.

✅COPY . /usr/src/app/
현재 디렉토리(즉, Dockerfile이 위치한 디렉토리)의 모든 파일을 컨테이너의 /usr/src/app/ 디렉토리로 복사한다. 이는 어플리케이션 코드를 컨테이너 안으로 옮기는 작업이다.

✅RUN pip install --upgrade pip
이 명령어는 컨테이너 내의 pip를 최신 버전으로 업그레이드한다. 이는 종속성 관리를 위해 중요하다.

✅RUN pip install -r requirements.txt
requirements.txt 파일에 나열된 모든 Python 종속성을 설치한다. 이 파일은 보통 프로젝트에서 필요한 외부 Python 라이브러리 목록을 담고 있다.

이 Dockerfile은 Python 기반 어플리케이션을 위한 기본적인 설정을 제공한다. 이미지 구축 시, 이 설정에 따라 컨테이너 환경이 준비되며, 이후 이 환경 위에서 어플리케이션을 실행할 수 있다.

📌docker-compose.yml

#docker-compose.yml

version: '3'

services:
  nginx:
    build: ./nginx
    image: chatting_nginx:latest
    volumes:
      - static_volume:/usr/src/app/_static
    ports:
      - 80:80
      - 443:443
    depends_on:
      - web
  web:
    build: .
    image: chatting_web:latest
    command: daphne config.asgi:application --port 8000 --bind 0.0.0.0 -v2
    volumes:
      - static_volume:/usr/src/app/_static
      - ./:/usr/src/app/
    expose:
      - 8000
    env_file:
      - ./.env.prod
    depends_on:
      - db
      - redis
  db:
    image: postgres:12.0-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    env_file:
      - ./.env.prod.db
  redis:
    image: redis:alpine # 이렇게 해시를 태그로 사용합니다.
    ports:
      - "6379:6379"

volumes:
  postgres_data:
  static_volume:

✅version: '3'
이 라인은 사용하는 Docker Compose 파일의 버전을 명시한다. 여기서는 버전 3를 사용한다.

✅services:
이하의 구성은 서비스를 정의한다. 각 서비스는 별도의 컨테이너에서 실행된다.

✅nginx:

  • build: ./nginx
    ./nginx 디렉토리에 있는 Dockerfile을 사용하여 nginx 서비스의 이미지를 빌드한다.

  • image: chatting_nginx:latest
    빌드된 이미지에 chatting_nginx:latest라는 태그를 지정한다.

  • volumes: - static_volume:/usr/src/app/_static
    static_volume이라는 볼륨을 컨테이너의 /usr/src/app/_static 디렉토리에 마운트한다.

  • ports: - 80:80 - 443:443
    호스트의 80, 433 포트를 컨테이너의 80, 433 포트에 연결한다.

  • depends_on: - web
    web 서비스가 시작된 후에 nginx 서비스가 시작되도록 한다.

✅web:

  • build: .
    현재 디렉토리의 Dockerfile을 사용하여 web 서비스의 이미지를 빌드한다.

  • image: chatting_web:latest
    빌드된 이미지에 chatting_web:latest라는 태그를 지정한다.

  • command: daphne config.asgi:application --port 8000 --bind 0.0.0.0 -v2
    web 서비스 시작 시 실행할 커맨드를 지정한다. 여기서는 Daphne 서버를 사용한다.

  • volumes:
    두 개의 볼륨을 마운트한다. 하나는 정적 파일을 위한 것이고, 다른 하나는 코드를 컨테이너에 마운트한다.

  • expose: - 8000
    컨테이너의 8000 포트를 노출한다.

  • env_file: - ./.env.prod
    환경 변수 파일을 지정한다.

  • depends_on: - db - redis
    db와 redis 서비스가 시작된 후에 web 서비스가 시작되도록 한다.

✅db:

  • image: postgres:12.0-alpine
    PostgreSQL 12.0 알파인 버전을 사용하는 공식 이미지를 지정한다. 이는 db 서비스의 컨테이너에서 실행될 데이터베이스 서버이다.

  • volumes: - postgres_data:/var/lib/postgresql/data/
    postgres_data 볼륨을 컨테이너의 PostgreSQL 데이터 디렉토리에 마운트하여 데이터베이스 파일을 영구적으로 저장한다.

  • env_file: - ./.env.prod.db
    데이터베이스 서버 구성을 위한 환경 변수가 담긴 파일을 지정한다.

✅redis:

  • image: redis:alpine
    Redis의 알파인 버전을 사용하는 공식 이미지를 지정합니다. Redis 서비스의 컨테이너에서 실행될 것이다.

  • ports: - "6379:6379"
    호스트의 6379 포트를 컨테이너의 6379 포트에 매핑하여 Redis 서버에 외부에서 접근할 수 있도록 한다.

✅volumes: 두 개의 볼륨을 정의한다.

  • postgres_data:
    PostgreSQL 데이터를 저장할 볼륨이다. 이를 통해 데이터베이스 파일이 컨테이너 재시작 후에도 유지된다.

  • static_volume:
    정적 파일을 저장하기 위한 볼륨이다. 이는 nginx와 web 서비스에서 공유된다.

📌nginx

Dockerfile

FROM nginx:latest
RUN rm /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/conf.d

✅FROM nginx:latest
Nginx의 최신 공식 이미지를 기반으로 Docker 이미지를 빌드한다.

✅RUN rm /etc/nginx/conf.d/default.conf
Nginx의 기본 설정 파일을 삭제한다. 이는 사용자 정의 설정을 사용하기 위한 준비 단계이다.

✅COPY nginx.conf /etc/nginx/conf.d
사용자 정의 Nginx 설정 파일(nginx.conf)을 Nginx 설정 디렉토리에 복사한다. 이를 통해 Nginx 서버가 이 설정을 사용하게 된다.

nginx.conf

upstream chatting_group {
    server web:8000;
}

server {
    listen 80;
    client_max_body_size 50M;

    location / {
        proxy_pass http://chatting_group;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $host;
        proxy_redirect off;
    }

    location /ws/ {
        proxy_pass http://chatting_group;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";

        proxy_redirect off;
        proxy_set_header Host $host;
    }
    
    location /static/ {
        alias /usr/src/app/_static/;
    }
}

✅upstream chatting_group { server web:8000; }
chatting_group이라는 upstream 그룹을 정의하고, 이 그룹 내에서 web 서비스(8000 포트)로 요청을 전달한다. web은 docker-compose.yml 파일에서 정의된 서비스 이름이다.

✅server { ... }
블록은 웹 서버의 기본 설정을 정의한다:

  • ✅listen 80;
    Nginx가 80 포트에서 수신하도록 설정한다.

  • ✅client_max_body_size 50M;
    클라이언트 요청의 최대 바디 크기를 50MB로 제한한다.

  • ✅location / { ... }
    루트 URL에 대한 요청을 처리한다. 이 요청들은 chatting_group upstream으로 전달된다. 프록시 헤더들은 클라이언트의 원래 IP와 호스트명을 보존한다.

  • ✅location /ws/ { ... }
    WebSocket 연결을 처리하는 설정이다. proxy_http_version 1.1;과 Upgrade, Connection 헤더 설정으로 WebSocket 연결을 가능하게 한다.

  • ✅location /static/ { alias /usr/src/app/_static/; }
    /static/ 경로로 오는 요청을 서버의 /usr/src/app/_static/ 디렉토리에 있는 정적 파일로 처리한다. 이는 Docker Compose 설정에서 정의된 볼륨을 통해 Nginx 컨테이너에 마운트된 것이다.

이 구성을 통해 Nginx는 동적 요청을 web 서비스로 전달하고, 정적 파일을 직접 제공하며, WebSocket 연결을 지원한다. 이런 방식으로 Nginx는 로드 밸런서, 리버스 프록시, 정적 파일 서버로서의 역할을 수행한다.

여기까지 Docker와 nginx관련 코드를 작성하였고 이제 django 프로젝트폴더의 settings.py코드를 수정하고 .env 파일들도 작성을 하는 과정을 다음 포스팅에서 진행을 한 후 배포까지 완료해 보겠다.

0개의 댓글