도커 실무 정리
배포 서비스
1. postgrespl 컨테이너 생성
2. gunicorn을 사용해 django 프로젝트 컨테이너 생성
3. nginx를 사용해 웹서버 컨테이너 생성
4. nginx / postgresql / django 연동
5. env를 사용해 중요한 정보 관리
6. slim 이미지로 배포 적용
7. 도메인을 사용해 배포 서버 접속
8. https 적용하기
[목표]
1. docker를 활용해 컨테이너를 자유자재로 다루기.
2. django, nginx, database를 docker 컨테이너로 띄워 배포하기.
[내용]
- gunicorn을 사용해 django 배포 환경 구축하기
- django와 postgresql 데이터베이스 연동하기
- nginx를 활용해 외부에서 내가 만든 서버에 접속할 수 있도록 설정하기
- 도메인 구매 및 네임서버 설정하기
- https 적용하기
1. postgrespl 컨테이너 생성
postgrespl 이란?
오픈 소스 데이터베이스로, Oracle DB, MySQL 등 상용 라이센스를 가지고 있는 데이터베이스와는 다르게 무료로 사용 가능합니다.
Oracle DB, Mysql, Microsoft SQL에 이어 네번째로 사용량이 많은 데이터베이스입니다.
또한 장고에서는 기본 데이터베이스로 postgresql을 사용하는 것을 권장하고 있습니다.
원하는 docker 이미지를 찾는 방법(사이트)
docker에서 사용 가능한 이미지들은 https://hub.docker.com 에서 제공하고 있습니다.
docker-compose.yml
version: '3.8'
volumes:
postgres: {} # postgresql에서 사용 할 볼륨 지정
services:
postgres:
container_name: postgres
image: postgres:14.5
volumes:
- postgres:/var/lib/postgresql/data/
environment: # postgresql 컨테이너에서 사용할 환경변수 지정해주기
- POSTGRES_USER=user # 데이터베이스 사용자 지정
- POSTGRES_PASSWORD=P@ssw0rd # 사용자 비밀번호 지정
- POSTGRES_DB=django # 데이터베이스 이름 지정
restart: always
# sudo docker compose up -d 로 postgresql이 정상적으로 실행되었는지 확인하기.
2. gunicorn을 사용해 django 프로젝트 컨테이너 생성
gunicorn이란?
django 프로젝트를 실행할 때 사용되는 runserver와 같이, 사용자의 요청을 받아 django에 작성한 코드를 실행시켜 주도록 하는 역할을 해줍니다.
단순하게 생각해서 배포용으로 사용되는 runserver라고 생각해도 무관합니다.
runserver가 아닌 gunicorn을 사용해 배포하는 이유
기본적으로 runserver는 배포용이 아닌 개발용으로 사용되는 명령어이며, 공식 문서에서도 runserver로 배포하는 것을 권장하지 않고 있습니다.
또한 runserver는 기본적으로 싱글 스레드에서 동작하지만, gunicorn은 멀티 스레드로 동작하도록 설정할 수 있기 때문에 많은 요청을 더 효율적으로 처리할 수 있습니다.
이외에도 runserver에 비해 속도, 안정성 등 다양한 장점을 가지고 있기 때문에 배포 환경에서는 gunicorn을 사용하는 것을 권장하고 있습니다.
소스코드 다운받기 (예제)
git clone https://github.com/sparta-course/drf-project.git ./django
Django 프로젝트를 배포하기 전 추가 설정
1. settings.py # 컨테이너에 넣기전에 로컬 환경에서 작업하기
ALLOWED_HOSTS = ['*']
STATIC_ROOT = BASE_DIR / "static"
2. timezone 설정하기
sudo ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime
# date 입력하여 변경 사항 확인 하기
3. backend/Dockerfile
# python 3.10.8버전 이미지를 사용해 빌드
FROM python:3.10.8
# .pyc 파일을 생성하지 않도록 설정합니다.
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정합니다.
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성합니다.
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정합니다.
WORKDIR /app/
# requirments.txt를 작업 디렉토리(/app/) 경로로 복사합니다.
COPY ./django/requirements.txt .
# 프로젝트 실행에 필요한 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# gunicorn을 사용하기 위한 패키지를 설치합니다.
RUN pip install gunicorn
4. docker-compose.yml
version: '3.8'
services:
backend:
container_name: backend
build: ./backend/
# drf_project.wsgi는 프로젝트 경로에 맞게 지정해야 합니다.
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
ports:
- 80:8000
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro # host의 timezone 설정을 컨테이너에 적용합니다.
# ro 은 읽기 전용(read only) 속성으로 볼륨을 설정하는 것을 의미합니다.
restart: always
5. entrypoint 명령어
docker-compose.yml을 작성하며 entrypoint에 django 프로젝트를 실행시키기 위한 명령어를 사용했습니다.
sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn project_name.wsgi --workers=5 -b 0.0.0.0:8000"
해당 명령어는 아래와 같이 나눠서 해석할 수 있습니다.
`sh -c` : 컨테이너에서 뒤에 작성한 명령어를 실행시킬 수 있도록 해줍니다.
`&&` : 특정 명령어를 실행한 이후 다음 명령어를 실행시켜 줍니다.
`python manage.py collectstatic --no-input` : 배포를 위해 static 파일을 모아줍니다. 해당 명령어를 실행시키기 위해서는 settings.py에 STATIC_ROOT가 정의되어 있어야 합니다.
`python manage.py migrate` : django에 연결된 db를 migrate 해줍니다.
`gunicorn project_name.wsgi --workers=5 -b 0.0.0.0:8000` : gunicorn을 사용해 django 프로젝트를 실행시킵니다.
- `project_name` : django 프로젝트 이름을 입력합니다. 이름을 다르게 입력할 경우 에러가 발생합니다.
- `--workers=5` : django를 실행시킬 process 갯수를 입력합니다. 일반적으로 cpu 코어 갯수 * 2 + 1만큼 지정해줍니다.(ex - 2코어라면 2*2+1=5)
- `-b 0.0.0.0:8000` : 8000번 포트로 실행시킵니다.
# sudo docker compose up --build -d 입력하여 django 프로젝트가 정상적으로 실행되었는지 확인
3. nginx를 사용해 웹서버 컨테이너 생성
nginx란?
nginx는 클라이언트의 request 요청을 처리해주는 웹 서버(web server)입니다.
reverse proxy, 로드밸런싱, 캐싱 등의 기능을 지원하며, 클라이언트의 요청을 nginx가 받은 후 service(django) 데이터를 넘겨주는 역할을 해줍니다.
nginx를 사용하는 이유
로드밸런싱을 활용해 트래픽을 분산할 수 있습니다.
SSL 기능을 사용해 데이터를 안전하게 전달할 수 있습니다.
reverse proxy 기능을 통해 client에서 서버에 직접적으로 접근하는 것을 막아줍니다.
콘텐츠를 캐싱하여 동일한 요청에 대해 더 빠른 속도로 처리할 수 있게 해줍니다.
docker-compose.yml
version: '3.8'
services:
nginx:
container_name : nginx
image: nginx:1.23.2
ports:
- "80:80" # http 포트포워딩
- "443:443" # https 포트포워딩
restart: always
# sudo docker compose up -d 입력하여 컨테이너가 잘 작동하는지 확인.
4. nginx / postgresql / django 연동
nginx 설정파일 만들기
nginx/default.conf
server {
listen 80;
server_name _; # 모든 도메인 혹은 ip로 들어오는 요청에 대해 처리해 줍니다.
location / { # nginx로 요청이 들어왔을 때
proxy_pass http://backend:8000/; # backend 컨테이의 8000번 포트로 전달합니다.
}
location /static/ { # 브라우저에서 /static/ 경로로 요청이 들어왔을 때
alias /static/; # /static/ 경로에 있는 파일들을 보여줍니다.
}
location /media/ { # 브라우저에서 /media/ 경로로 요청이 들어왔을 때
alias /media/; # /media/ 경로에 있는 파일들을 보여줍니다.
}
}
backend/Dockerfile
# python 3.10.8버전 이미지를 사용해 빌드
FROM python:3.10.8
# .pyc 파일을 생성하지 않도록 설정합니다.
ENV PYTHONDONTWRITEBYTECODE 1
# 파이썬 로그가 버퍼링 없이 즉각적으로 출력하도록 설정합니다.
ENV PYTHONUNBUFFERED 1
# /app/ 디렉토리를 생성합니다.
RUN mkdir /app/
# /app/ 경로를 작업 디렉토리로 설정합니다.
WORKDIR /app/
# requirments.txt를 작업 디렉토리(/app/) 경로로 복사합니다.
COPY ./django/requirements.txt .
# 프로젝트 실행에 필요한 패키지들을 설치합니다.
RUN pip install --no-cache-dir -r requirements.txt
# gunicorn과 postgresql을 사용하기 위한 패키지를 설치합니다.
RUN pip install gunicorn psycopg2
django settings.py 설정
import os
# 환경변수에 따라 DEBUG모드 여부를 결정합니다.
DEBUG = os.environ.get('DEBUG', '0') == '1'
# 접속을 허용할 host를 설정합니다.
ALLOWED_HOSTS = ['backend', ]
# postgres 환경변수가 존재 할 경우에 postgres db에 연결을 시도합니다.
POSTGRES_DB = os.environ.get('POSTGRES_DB', '')
if POSTGRES_DB:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': POSTGRES_DB,
'USER': os.environ.get('POSTGRES_USER', ''),
'PASSWORD': os.environ.get('POSTGRES_PASSWORD', ''),
'HOST': os.environ.get('POSTGRES_HOST', ''),
'PORT': os.environ.get('POSTGRES_PORT', ''),
}
}
# 환경변수가 존재하지 않을 경우 sqlite3을 사용합니다.
else:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# CORS 허용 목록에 ec2 ip를 추가합니다.
CORS_ORIGIN_WHITELIST = ['http://$ec2_public_ip']
# ex) CORS_ORIGIN_WHITELIST = ['http://43.201.72.190']
# CSRF 허용 목록을 CORS와 동일하게 설정합니다.
CSRF_TRUSTED_ORIGINS = CORS_ORIGIN_WHITELIST
# 기존 settings.py 에 위 내용이 없다면 추가하기.
디렉토리 구조
path : /home/ubuntu/
├── backend
│ ├── Dockerfile
│ └── django # project directory
├── docker-compose.yml
└── nginx
└── default.conf
docker-compose.yml
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5
volumes:
- postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=P@ssw0rd
- POSTGRES_DB=django
restart: always
backend:
container_name: backend
build: ./backend/
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro
- django_media:/app/media/ # nginx에서 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/app/static/ # nginx에서 static을 사용할 수 있도록 volume을 지정해줍니다.
environment: # django에서 사용할 설정들을 지정해줍니다.
- DEBUG=1
- POSTGRES_DB=django
- POSTGRES_USER=user
- POSTGRES_PASSWORD=P@ssw0rd
- POSTGRES_HOST=postgres
- POSTGRES_PORT=5432
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- django_media:/media/ # django의 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/static/ # django의 static 사용할 수 있도록 volume을 지정해줍니다.
depends_on:
- backend
restart: always
배포 환경 통신 구조
배포 환경의 구조를 적어보자면 아래와 같습니다.
user ----(request)----> nginx ----> gunicorn ----> django ----> postgresql
가장 먼저 사용자는 EC2의 nginx에 request 요청을 하게 됩니다.
nginx에서는 사용자의 요청을 받아 .conf 파일에서 설정한 서버로 요청을 전달합니다.
이후 gunicorn에서는 django로, django에서는 필요에 따라 데이터베이스에 쿼리를 날려 개발자가 작성한 코드를 실행하게 됩니다.
5. env를 사용해 중요한 정보 관리
.env 작성하기
DEBUG=1
POSTGRES_DB=django
POSTGRES_USER=user
POSTGRES_PASSWORD=P@ssw0rd
POSTGRES_HOST=postgres
POSTGRES_PORT=5432
docker-compose.yml
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5
# 만약 .env 파일을 다른 이름으로 사용할 경우 env_file 옵션을 사용해 불러올 수 있습니다.
# env_file:
# - prod.env
volumes:
- postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_USER # <------ 내용 지우기
- POSTGRES_PASSWORD # <------ 내용 지우기
- POSTGRES_DB # <------ 내용 지우기
restart: always
backend:
container_name: backend
build: ./backend/
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro
- django_media:/app/media/
- django_static:/app/static/
environment: #
- DEBUG
- POSTGRES_DB # <------ 내용 지우기
- POSTGRES_USER # <------ 내용 지우기
- POSTGRES_PASSWORD # <------ 내용 지우기
- POSTGRES_HOST # <------ 내용 지우기
- POSTGRES_PORT # <------ 내용 지우기
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- django_media:/media/ # django의 media를 사용할 수 있도록 volume을 지정해줍니다.
- django_static:/static/ # django의 static 사용할 수 있도록 volume을 지정해줍니다.
depends_on:
- backend
restart: always
# 위에 다른 docker-compose 랑 비교시 뭐가 다른지 확인해보자.
6. slim 이미지로 배포 적용
slim 이미지의 종류와 특징
동일한 docker 이미지라 하더라도 태그에 따라 이미지의 용량이 달라집니다.
가령 python 이미지의 경우 가장 큰 이미지와 가장 작은 이미지의 용량이 300mb 이상 차이납니다.
기본 python : python 3.12.0a1 / 약 344mb
가장 작은 용량의 python : python 3.12.0a1-alpine / 약 18mb
# 다만, 용량이 작은 이미지를 사용할 때는 외부 패키지를 설치할 때 필요한 의존성 파일들이 존재하지 않아 에러가 발생할 수 있습니다.
# 내가 사용하는 이미지에 어떤 종류의 태그가 존재하는지 확인이 필요할 때는 docker hub 이미지 페이지의 tags에서 확인 가능합니다.
이미지의 태그별 특징
- buster, jessie, stretch
- debian에서 만든 linux를 기반으로 만들어진 이미지입니다.
- buster, jessie, stretch는 os의 codename입니다. (링크)
- python:3.8과 python:3.8-buster는 동일합니다. (약 300~350mb)
- slim
- 실행에 필요한 환경만 만들어둔 이미지입니다.
- 이미지가 기본이미지에 비해서는 작습니다. (약 40~50mb)
- 보통 python 실행환경에서 가장 많이 쓰이는 이미지
- alpine
- 용량이 작고, 보안에 집중한 alpine-linux 기반으로 만들어진 이미지입니다.
- 보통 이미지들 중에서 가장 작다는 특징을 가지고 있습니다. (약 15~20mb)
- python기준으로 봤을 때, pip install을 할 때 불리한 점이 있습니다.
backend/Dockerfile
# python 3.10.8-slim버전 이미지를 사용해 빌드
FROM python:3.10.8-slim
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN mkdir /app/
WORKDIR /app/
# slim 이미지에서 postgresql 패키지를 설치하기 위해 필요 명령어 추가
RUN apt update && apt install libpq-dev gcc -y
COPY ./django/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
RUN pip install gunicorn psycopg2
docker-compose.yml
version: '3.8'
volumes:
postgres: {}
django_media: {}
django_static: {}
services:
postgres:
container_name: postgres
image: postgres:14.5-alpine # alpine 이미지를 지정해줍니다.
volumes:
- postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_DB
restart: always
backend:
container_name: backend
build: ./backend/
entrypoint: sh -c "python manage.py collectstatic --no-input && python manage.py migrate && gunicorn drf_project.wsgi --workers=5 -b 0.0.0.0:8000"
volumes:
- ./backend/django/:/app/
- /etc/localtime:/etc/localtime:ro
- django_media:/app/media/
- django_static:/app/static/
environment:
- DEBUG=1
- POSTGRES_DB
- POSTGRES_USER
- POSTGRES_PASSWORD
- POSTGRES_HOST
- POSTGRES_PORT
depends_on:
- postgres
restart: always
nginx:
container_name : nginx
image: nginx:1.23.2-alpine # alpine 이미지를 지정해줍니다.
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
- django_media:/media/
- django_static:/static/
depends_on:
- backend
restart: always
# sudo docker images 입력하여 용량 차이를 확인 해보자.
7. 도메인을 사용해 배포 서버 접속
도메인 구매하기
도메인은 AWS, 가비아 등 다양한 도메인 제공 업체에서 구매할 수 있습니다.
freenom 홈페이지에서 도메인 구매 (무료) : https://www.freenom.com/
주의 사항
freenom 홈페이지는 매우 느립니다. 때문에 검색을 해도 수 초 이상 걸릴 때가 잦으며, 소셜로그인과 같은 기능은 정상적으로 동작하지 않을 때가 많습니다.
때문에 무료 도메인이 목적이 아니라면 다른 도메인 구매 플랫폼을 사용하는 것을 권장드립니다.
또한, 회원가입 후 도메인을 구매하게 되는 일반적인 플랫폼들과는 다르게 freenom은 도메인을 구매 하면서 회원가입을 할 수 있습니다.
즉, 도메인 검색 → 도메인 선택 → 이메일 인증(회원가입) → 구매 내역 확인 순으로 진행해야 합니다.
freenom nameserver 설정하기
freenom에서 구매한 도메인을 AWS의 route53 서비스를 사용해 적용해 보겠습니다.
AWS
1. AWS 접속 및 로그인 후 route53 서비스에 접속합니다.
2. 호스팅 영역 생성 버튼을 클릭합니다.
3. freenum에서 구매한 도메인을 입력해주고, 호스팅 영역 생성 버튼을 클릭합니다.
4. 호스팅 영역을 생성하면 호스팅 영역에서 지정된 도메인을 확인할 수 있습니다.
5. 호스팅 영역에서 도메인을 클릭하고 들어와서 값/트래픽 라우팅 대상에 있는 값들을 확인한 후 freenom 페이지로 이동합니다.
freenom
6. Service → My domains를 눌러 마이페이지에 들어간 후 Manage Domain 버튼을 클릭합니다.
7. Management Tools → Nameserver를 클릭합니다.
8. Use custom nameservers을 클릭하고, AWS의 호스팅 영역(AWS)에서 확인했던 NS 유형의 값/트래픽 라우팅 대상에 있는 값들을 입력해 줍니다.
이후 Change Nameservers 버튼을 누르면 Nameserver 설정이 끝납니다.
※설정 후 도메인이 반영되는데 까지는 짧게는 수십분에서 최대 24시간까지 걸릴 수 있습니다.
route 53 설정하기
route53에서 구매한 도메인에 접속했을 때 지정한 서버로 접속할 수 있도록 설정해 보겠습니다.
1. route53 → 호스팅 영역에서 도메인을 클릭합니다.
2. 레코드 생성 버튼을 클릭합니다.
3. 레코드 이름을 빈칸으로 두고, 값에 ec2의 퍼블릭 ip를 입력한 후 레코드 생성 버튼을 클릭합니다.
4. 동일한 방법으로 레코드 이름에 www를 입력하고, 레코드 유형을 CNAME으로 변경한 후 값에 구매한 도메인을 입력한 후 레코드 생성 버튼을 클릭합니다.
이후 레코드 목록에서 설정 한 레코드들을 확인할 수 있습니다.
# ec2의 퍼블릭 ip 확인 방법
(1) ec2 서비스에서 인스턴스(실행 중) 버튼을 눌러 들어갑니다.
(2) 도메인을 연결해줄 인스턴스 id를 클릭하고 퍼블릭 ip를 확인합니다.
nginx/default.conf
server {
listen 80;
server_name www.spartacodingclub.tk; # www.spartacodingclub.tk 도메인으로 들어오는 요청을 처리해줍니다.
location / {
proxy_pass http://backend:8000/;
}
location /static/ {
alias /static/;
}
location /media/ {
alias /media/;
}
}
server {
listen 80;
server_name spartacodingclub.tk; # www가 없는 url로 요청 했을 때
return 301 http://www.spartacodingclub.tk$request_uri; # www를 붙인 url로 redirection 해줍니다.
}
8. https 적용하기
https 란?
웹 서버에서 데이터를 전송하기 위한 http 프로토콜에 보안을 의미하는 secure를 붙여 http 통신을 더 안전하게 할 수 있도록 해주는 프로토콜입니다.
보안적인 특성 때문에 배포를 하는 서비스에는 https를 필수적으로 적용하게 됩니다.
https를 적용하기 위해서는 SSL 인증서를 발급받고 웹서버에서 인증서를 지정해주는 과정이 필요합니다.
SSL 인증서 발급 받기
https 적용을 위한 SSL 인증서 발급 방법을 알아보겠습니다.
1. aws에서 서비스에 ssl을 검색하고 Certificate Manager에 접속합니다.
2. 서비스에 접속 후 인증서 요청 버튼을 클릭합니다.
3. 퍼블릭 인증서 요청을 체크하고 다음 버튼을 클릭합니다.
4. 도메인 이름에 구매한 도메인 입력
→ 이 인증서에 다른 이름 추가
→ *.구매한 도메인 이름을 입력한 후 DNS 검증을 선택하고 요청 버튼을 클릭합니다.
(1) spartacodingclub.tk
(2) *.spartacodingclub.tk
5. 인증서 목록 화면에서 요청한 인증서의 ID를 클릭합니다.
※ 만약 요청한 인증서가 보이지 않을 경우 페이지를 새로고침 해주세요
6. 도메인의 소유자라는 것을 증명하기 위해 Route 53에서 레코드 생성 버튼을 클릭합니다.
7. 도메인을 확인하고 레코드 생성 버튼을 클릭합니다.
이후 수분에서 수십분정도 기다리면 상태가 성공으로 바뀌게 됩니다. 이제 SSL 인증서 발급이 완료되었습니다.
대상 그룹 설정하기
로드밸런서를 설정하기 전, 어떤 서버에 로드밸런서를 사용할지 대상을 지정해 주는 작업이 필요합니다.
1. AWS에서 ec2 서비스에 접속합니다.
2. 좌측 메뉴에서 로드 밸런싱 - 대상 그룹을 클릭합니다.
3. Create target group 버튼을 클릭합니다.
4. 인스턴스를 선택해줍니다.
5. Target group name을 지정해줍니다.
6. Health check path를 입력한 후 Next 버튼을 클릭합니다.
※Health check는 로드 밸런서에서 서버가 정상 동작 하는지 확인하는 용도로 사용되며, 서버에 정상적으로 접근하고 http status code 200을 리턴해줄 수 있는 경로를 입력해야 합니다.
7. 로드밸런서에 대상이 될 서버를 지정하고 Include as pending below 버튼을 클릭합니다.
8. 추가된 대상을 확인하고 Create target group 버튼을 클릭합니다.
다시 대상 그룹을 들어가 보면서 등록한 대상을 확인할 수 있습니다.
로드밸런서 설정하기
로드밸런서를 위한 사전 작업이 완료되었습니다. 이제 본격적으로 로드밸런서를 생성해 보겠습니다.
1. aws에서 ec2 서비스를 들어갑니다.
2. 좌측 메뉴에서 로드 밸런싱 - 로드밸런서 클릭합니다.
3. 로드 밸런서를 클릭합니다.
4. Application Load Balancer의 Create 버튼을 클릭합니다.
5. Basic Configuration에서 로드밸런서의 이름을 작성하고 Internet-facing, IPv4를 선택합니다.
6. Network mapping에서 ap-northeast-2a, 2b를 선택합니다.
7. Security groupts에서 default 그룹을 선택해 줍니다.
8. Listeners and routing에서 이전에 생성한 대상 그룹을 지정하고 Add listener 버튼을 클릭합니다.
9. Protocol에서 HTTPS를 선택하고 동일하게 대상 그룹을 선택해줍니다.
10. Secure listener settings에서 생성한 SSL 인증서를 선택합니다.
※ Select a certificate에 아무것도 나오지 않는다면 SSL 인증서가 정상적으로 발급 되었는지 확인해야 합니다.
11. 최종적으로 설정을 확인하고 Create load balancer 버튼을 클릭합니다.
이제 로드밸런서 목록에서 생성된 로드밸런서를 확인할 수 있습니다. 생성되자마자 상태를 확인하면 프로비저닝 중 이라 나오며, 수 분 후 활성으로 바뀌게 됩니다.
로드밸런서 설정하기
route53 설정하기
1. AWS → route53 서비스 접속 → 호스팅 영역 → 등록한 도메인 클릭
2. 유형 A로 등록된 도메인을 선택한 후 레코드 편집 버튼을 클릭합니다.
3. 별칭 옵션을 활성화 한 후 Application/Classic Load Balancer에 대한 별칭
→ 아시아 태평양(서울)
→ 생성한 로드밸런서를 순서대로 선택한 후 저장 버튼을 클릭합니다.
설정이 적용되기까지는 수 분 정도 소요되며, 이후 도메인으로 접속했을 때에는 ec2에 직접적으로 접근하는 것이 아는 로드밸런서를 통해 접근하게 됩니다.
http 요청을 https로 redirect 하도록 설정하기
일반적으로 사용자들이 웹 브라우저를 사용해 접근하는 대부분의 사이트들은 http로 접속했을 때 https로 자동으로 리다이렉션 해줍니다.
가령, [http://www.naver.com](http://www.naver.com) 이라는 주소로 접속했을 때 자동으로 [https://www.naver.com](https://www.naver.com으로)으로 리다이렉션 되는 것을 확인할 수 있습니다.
이번에는 로드 밸런서의 리스너 설정을 통해 http 요청을 https로 리다이렉션 해주도록 설정해 보겠습니다.
1. AWS → ec2 서비스 접속 → 로드밸런서 → 생성한 로드밸런서 클릭
2. 리스너 → HTTP : 80에 해당하는 리스너의 규칙 보기/편집 버튼을 클릭합니다.
3. 상단의 추가(더하기 아이콘) 버튼 → 규칙 삽입을 클릭합니다.
4. IF의 조건 추가 → 호스트 헤더 선택 → *.구매한도메인이름을 입력한 후 체크 아이콘을 클릭합니다.
5. THEN의 작업 추가 → 리디리렉션 대상 선택 → HHTPS 선택 및 443 포트 입력 → 기본 호스트, 301을 각각 선택한 후 체크 아이콘을 클릭합니다.
6. 설정을 확인하고 저장 버튼을 클릭합니다.
django settings.py 설정해주기
# CORS 허용 목록에 도메인 서버를 추가합니다.
CORS_ORIGIN_WHITELIST = ['https://www.$domain', ]
# CORS_ORIGIN_WHITELIST = ['https://www.$domain', ]
# CSRF 허용 목록을 CORS와 동일하게 설정합니다.
CSRF_TRUSTED_ORIGINS = CORS_ORIGIN_WHITELIST
< 참고 자료 >
1. PostgreSQL이란?
[ PostgreSQL이란? ]
PostgreSQL은 오픈 소스 객체-관계형 데이터베이스 시스템(ORDBMS)으로, Enterprise급 DBMS의 기능과 차세대 DBMS에서나 볼 수 있을 법한 기능들을 제공한다.약 20여년의 오랜 역사를 갖는 PostgreSQL은 다른 관계형 데이터베이스 시스템과 달리 연산자, 복합 자료형, 집계 함수, 자료형 변환자, 확장 기능 등 다양한 데이터베이스 객체를 사용자가 임의로 만들 수 있는 기능을 제공함으로써 마치 새로운 하나의 프로그래밍 언어처럼 무한한 기능을 손쉽게 구현할 수 있다.
[ PostgreSQL의 구조 ]
PostgreSQL은 클라이언트/서버 모델을 사용한다. 서버는 데이터베이스 파일들을 관리하며, 클라이언트 애플리케이션으로부터 들어오는 연결을 수용하고, 클라이언트를 대신하여 데이터베이스 액션을 수행한다. 서버는 다중 클라이언트 연결을 처리할 수 있는데, 서버는 클라이언트의 연결 요청이 오면 각 커넥션에 대해 새로운 프로세스를 fork한다. 그리고 클라이언트는 기존 서버와의 간섭 없이 새로 생성된 서버 프로세스와 통신하게 된다.
[ PostgreSQL의 기능 ]
PostgreSQL은 관계형 DBMS의 기본적인 기능인 트랜잭션과 ACID(Atomicity, Consistency, Isolation, Durability)를 지원한다. ANSI:2008 구격을 상당 부분 만족시키고 있으며, 전부 지원하는 것을 목표로 계속 기능을 추가하고 있다. PostgreSQL은 기본적인 신뢰도와 안정성을 위한 기능 뿐만 아니라 진보적인 기능이나 학술적 연구를 위한 확장 기능도 많이 가지고 있는데, PostgreSQL의 주요 기능을 열거해보면 아래와 같다.
- Nested transactions (savepoints)
- Point in time recovery
- Online/hot backups, Parallel restore
- Rules system (query rewrite system)
- B-tree, R-tree, hash, GiST method indexes
- Multi-Version Concurrency Control (MVCC)
- Tablespaces
- Procedural Language
- Information Schema
- I18N, L10N
- Database & Column level collation
- Array, XML, UUID type
- Auto-increment (sequences),
- Asynchronous replication
- LIMIT/OFFSET
- Full text search
- SSL, IPv6
- Key/Value storage
- Table inheritance
[ PostgreSQL의 특징 ]
- Portable
- PostgreSQL은 ANSI C로 개발되었으며, 지원하는 플랫폼의 종류로는 Windows, Linux, MAC OS/X, Unix 등 다양한 플랫폼을 지원한다.
- Reliable
- 트랜잭션 속성인 ACID에 대한 구현 및 MVCC
- 로우 레벨 라킹 등이 구현
- Scalable
- PostgreSQL의 멀티 버젼에 대해 사용이 가능
- 대용량 데이터 처리를 위한 Table Partitioning과 Tables Space 기능 구현이 가능
- Secure
- DB 보안은 데이터 암호화, 접근 제어 및 감시의 3가지로 구성됨
- 호스트-기반의 접근 제어, Object-Level 권한, SSL 통신을 통한 클라이언트와 네트워크 구간의 전송 데이터를 암호화 등 지원
- Recovery & Availability
- Streaming Replication을 기본으로, 동기식/비동기식 Hot Standbt 서버를 구축 가능
- WAL Log 아카이빙 및 Hot Back up을 통해 Point in time recovery 가능
- Advanced
- pg_upgrade를 통해 업그레이드를 할 수 있으며, 웹 또는 C/S 기반의 GUI 관리 도구를 제공하여 모니터링 및 관리는 물론 튜닝까지 가능
- 사용자 정의 Procedural로 Perl, Java, Php 등의 스크립트 언어 지원이 가능
출처 : https://mangkyu.tistory.com/71
2. Gunicorn 이란? (g유니콘 이라고도한다.)
[ 구니콘 ]
구니콘이란 파이썬의 Web Server Gateway interface (WSGI)를 구현하는 HTTP server이다. 구니콘은 GreenUnicorn의 약자이며 jee-unicorn 또는 gun-i-corn이라 발음하기도 한다. 구니콘 서버는 다수의 web 프레임워크와 널리 호환되며 구현이 심프랗고, 서버 리소스가 적으며, 빠른 속도로 동작한다.
웹 서버에서 파이썬 장고 애플리케이션을 호출하기 위 필요한 WSGI 서버이다.
[ WSGI ]
Web Server Gateway Interface는 웹 서버 소프트웨어와 파이썬으로 작성된 웹 응용 프로그램 간의 표준 인터페이스이다. 표준 인터페이스는 여러 웹 서버에서 WSGI를 지원하는 응용 프로그램을 쉽게 사용할 수 있도록 한다.
파이썬에서 어플리케이션, 즉 파이썬 스크립트(웹 어플리케이션)가 웹 서버와 통신하기 위한 인터페이스이다. WSGI는 서버와 앱 양단으로 나뉘어져 있다. WSGI 리퀘스트를 처리하려면 서버에서 환경정보와 콜백함수를 앱에 제공해야 한다. 앱은 그 요청을 처리하고 콜백함수를 통해 서버에 응답한다.
요청 -> 웹서버 -> WSGI Server -> WSGI를 지원하는 웹 어플리케이션
예. Gunicorn, uWSGI
uWSGI : WSGI 규칙을 따라서 만들어진 소프트웨어이며 정적인 웹서버와 python으로 작성된 Web Framework(Flask/Django) 사이의 통신을 도와주는 역할을 한다.
[ ASGI ]
WSGI의 경웽는 비동기적인 요청 처리에 단점이 있다. 하나의 동기적인 callable이 요청을 받아 응답을 리턴하는 방식이었는데, 이런 방식은 길게 유지되어야 하는 연결에는 적합하지 않다.
ASGI는 이를 개선하기 위해 만들어졌으며, 파이썬 웹 서버, 프레임워크, 어플리케이션 사이에 비동기적인 표준 인터페이스를 제공한다.
즉, WSGI가 파이썬 앱에 대한 동기성에 대한 표준을 제공했다면 ASGI는 동기성과 비동기성 모두에 대한 표준을 제공한다.
예. Uvicorn
[ CGI (Common Gateway Interface) ]
웹 서버에서 어플리케이션을 작동시키기 위한 인터페이스이다. 정적인 웹서버를 동적으로 기능하게 하기 위해서 등장하였다. 서버 프로그램과 외부 프로그램 간의 인터페이스가 바로 CGI이다.
요청 -> 웹서버(아파치,ngnix 등) -> (웹서버가 직접실행)프로그램
웹서버와 프로그램 사이의 정보를 주고받는 규칙을 의미한다.
웹 어플리케이션이 서버에서 실행되어 어떤 동작을 처리하고 그에 맞는 html을 리턴할 때 이를 위해 서버로 들어온 요청을 웹 어플리케이션으로 넘겨줄 수 있어야 한다. 이때 만약 서버마다 애플리케이션 언어가 다르다면 번거로운 일이 일어나므로 공통의 표준 인터페이스를 제공해준다. 이 인터페이스가 CGI이다.
[ WAS(Web Application Server) ]
웹서버가 동적으로 기능하면 WAS이다. 즉, Web Server + CGI 가 WAS이다. 간단하게 웹서버 위에 서버 어플리케이션을 얹은 것이 WAS이다.
요청 -> 웹서버 -> WAS -> (웹어플리케이션 서버가 실행)프로그램
어플리케이션 서버가 프로그램의 실행결과를 웹 서버에 전달해주며 웹 서버는 전달 받은 결과를 웹 브라우저에 전송한다.
WAS vs. Wev Server
- Static Pages : Web Server 는 파일 경로 이름을 받아 경로와 일치하는 file contents를 반환한다. 항상 동일한 페이지를 반환하며 image,html,css,js 파일과 같이 컴퓨터에 저장되어 있는 파일을 의미한다.
- Dynamic Pages : 인자의 내용에 맞게 동적인 contents를 반환한다. 즉, 웹 서버에 의해서 실행되는 프로그램을 통해서 만들어진 결과물을 말한다.
- Web Server : 하드웨어(Web Server가 설치된 컴퓨터), 소프트웨어(웹 브라우저 클라이언트로부터 HTTP 요청을 받아 **정적인 컨텐츠(.html,.jpeg,.css등)**를 제공하는 프로그램), 정적인 컨텐츠를 제공하거나, 동적인 컨텐츠 제공을 위한 요청을 WAS에 전달하여 WAS가 처리한 결과를 클라이언트에게 전달한다. (예.Apache Server, Nginx, IIS 등)
- Web Application Server : `DB 조회나 다양한 로직 처리를 요구하는 동적인 컨텐츠를 제공하기 위해 만들어진 어플리케이션 서버이다. HTTP를 통해 컴퓨터나 장치에 애플리케이션을 수행해주는 미들웨어 이다. 프로그램 실행 환경과 DB 접속 기능을 제공하여 여러개의 트랜잭션을 관리하고 비즈니스 로직을 수행한다. (예, Tomcat, JBoss, Jeus, Wev Sphere 등)
Web Server와 WAS 분리 이유?
: WAS를 통해 요청에 맞는 데이터를 DB에서 가져와서 비즈니스 로직에 맞게 그때 그때 결과를 만들어서 제공함으로써 자원을 효율적으로 사용할 수 있다. 간단한 정적 파일(이미지)을 Application Server까지 가지 않고 앞단에서 빠르게 보내줄수 있다.
: 기능을 분리하여 서버의 부하를 방지하고 물리적으로 분리하여 보안 강화하는 등의 이유
출처 : https://velog.io/@yoojinjangjang/%EA%B5%AC%EB%8B%88%EC%BD%98Gunicorn%EC%9D%B4%EB%9E%80
3. Nginx 이란?
[ Nginx란? ]
트래픽이 많은 웹사이트의 서버(WAS)를 도와주는 비동기 이벤트 기반구조의 웹 서버 프로그램이다.
공식문서에서는 "NGINX는 고성능, 확장성, 고가용성 웹서버, 역방향 프록시 서버 및 웹 가속기(HTTP 로드밸런서, 콘텐츠 캐시 등의 기능 결합)이다." 라고 소개하고 있다.
Web Server : 단순히 정적 파일 응답
WAS(Web Application Server) : 클라이언트 요청에 대해 동적 처리가 이뤄진 후 응답
[ Nginx가 만들어진 배경 ]
1995년 유닉스 기반으로 만들어진 최초의 웹서버 NCSA HTTPd가 있었다. 하지만 버그가 굉장히 많았다.
그래서 만들어진 것이 아파치 서버(Apache HTTP Server)이다.
Apache HTTP SERVER
아파치 서버는 요청이 들어오면 커넥션을 형성하기 위해 프로세스를 생성한다.
즉, 새로운 요청이 들어올 때마다 프로세스를 새로 만든다.
(이는 유닉스 계열 OS가 네트워크 커넥션을 형성하는 모델을 그대로 적용한 것이다.)
프로세스 생성 시간이 오래 걸리므로 요청이 들어오기 전에 프로세스를 미리 생성하는 PREFORK 방식을 사용했다.
그래서 새로운 클라이언트 요청이 들어오면 미리 만들어 놓은 프로세스를 가져다 사용했다.
만약 만들어 놓은 프로세스가 모두 할당되면 추가로 프로세스를 만들었다.
이런 구조는 개발하기 쉽다는 장점이 있었다.(확장성)
Apache HTTP SERVER 확장성
덕분에 개발자는 다양한 모듈을 만들어서 서버에 빠르게 기능을 추가할 수 있었다.
이런 식으로 아파치 서버는 동적 컨텐츠를 처리할수 있게 되었다. (Q&A 설명 추가)
확장성이 좋다는 장점 덕분에 요청을 받고 응답을 처리하는 과정을 하나의 서버에서 해결하기 좋았다.
C10K 문제
1999년부터는 서버 트래픽량이 높아져서 서버에 동시 연결된 커넥션이 많아졌을 때 더 이상 커넥션을 형성하지 못하는 문제가 생겼다.
이를 C10K 문제(Connection 10000 Problem)라고 한다.
아파치 서버는 다음과 같은 문제점이 있었다.
- 커넥션마다 프로세스를 할당하기에 메모리 부족으로 이어짐.
- 확장성이라는 장점이 프로세스의 리소스 양을 늘려서 무거운 프로그램이 되었음
- 많은 커넥션에서 요청이 들어오면 CPU 부하가 높아짐. (컨텍스트 스위칭을 많이함)
즉, 수많은 동시 커넥션을 감당하기엔 아파치 서버의 구조가 적합하지 않았다.
Nginx + Apache HTTP SERVER
2004년에 새로운 구조를 채택하면서 아파치 서버를 보완하기 위한 소프트웨어가 나왔는데 이것이 NGINX 이다.
초창기에는 아파치 서버와 Nginx는 함께 사용하기 위해 만들어졌다.
수많은 동시 커넥션을 Nginx가 유지하고, 웹서버이기에 정적 파일에 대한 요청은 스스로 처리하여 아파치 서버의 부하를 줄였다.
웹 서버 역할의 Nginx는 클라이언트로부터 동적 파일 요청을 받았을 때만 뒤에 있는 아파치 서버와 커넥션을 형성했다.
[ Nginx의 구조 ]
Nginx 구조
Nginx는 가장 먼저 마스터 프로세스(master process)라는 것을 볼 수 있다.
설정 파일을 읽고 워커 프로세스(worker process)를 생성하는 프로세스이다.
이 워커 프로세스가 실제로 일을 하는데, 워커 프로세스가 생성될 때 각자 지정된 listen 소켓을 배정받는다.
그 소켓에 새로운 클라이언트 요청이 들어오면 커넥션을 형성하고 처리한다.
커넥션은 정해진 Keep Alive 시간만큼 유지되는데, 이렇게 커넥션이 형성되었다고 해서 워커 프로세스가 커넥션 하나만 담당하진 않는다. 형성된 커넥션에 아무런 요청이 없으면 새로운 커넥션을 형성하거나 이미 만들어진 다른 커넥션으로부터 들어온 요청을 처리한다.
Nginx에서는 이런 커넥션 형성과 제거, 새로운 요청을 처리하는 것을 이벤트(event)라고 부른다.
워커 프로세스
이벤트들은 OS 커널이 큐 형식으로 워커 프로세스에게 전달해준다.
이벤트는 큐에 담긴 상태에서 워커 프로세스가 처리할 때까지 비동기 방식으로 대기한다.
그리고 워커 프로세스는 하나의 스레드로 이벤트를 꺼내서 처리해 나간다.
아파치 서버는 커넥션 연결 후 요청이 없다면 방치되는 반면, NGINX는 커넥션 연결 후 요청이 없으면 다른 커넥션의 요청을 처리하거나 새로운 커넥션을 형성하므로 아파치 서버에 비해 서버 자원을 효율적으로 쓰는 것을 알 수 있다.
🧐 만약 큐에 담긴 요청중 하나가 시간이 오래 걸린다?
그럴 경우를 대비해서 스레드 풀(Thread Pool)을 만들어 그 요청은 따로 수행하게 된다.
워커 프로세스는 처리할 요청이 시간이 오래 걸릴 것 같으면 스레드 풀에 이벤트를 위임하고 다른 이벤트를 처리한다.
이런 워커 프로세스는 보통 CPU 코어 수만큼 생성하게 된다.
그래서 코어가 담당하는 프로세스를 바꾸는 횟수를 줄이기에 CPU의 컨텍스트 스위칭을 줄이게 된다.
이게 바로 Nginx가 사용하는 Event-Driven Model(이벤트 기반 구조)이다.
[ Nginx의 장점과 단점 ]
단점은 개발자가 직접 모듈을 만들기가 까다롭다.
하지만 장점은 다음과 같다.
프로세스를 적게 만들다보니 가볍다.
이런 프로세스를 적게 만드는 구조는 Nginx의 설정을 동적으로 바꾸는 것을 가능하게 한다.
- 동시 커넥션 양 최소 10배 증가 (일반적으로 100 ~ 1000배 증가)
- 동일한 커넥션 수일 때 속도 2배 향상
- 동적 설정 변경 (reload)
동적 설정 변경
개발자가 설정 파일을 변경하고 Nginx에 적용하면 마스터 프로세스는 거기에 맞게 워커 프로세스를 새로 생성한다.
기존의 워커 프로세스가 더 이상 커넥션을 형성하지 않도록 하여 처리하는 이벤트가 없으면 해당 프로세스를 종료시킨다.
NGINX 설정을 변경할 때 명령어로는 reload와 restart가 있습니다. 위 동적 설정 변경을 하려면 reload를 사용해야 합니다.
[ NGINX 기능 ]
- 웹 서버
- 로드 밸런서
- 웹 서버 가속기
- SSL 터미네이션 : 클라이언트와 https 통신하고, 서버와 http 통신하는 것
- 캐싱 : http 프로토콜을 사용하여 전달하는 콘텐츠를 캐싱할 수 있음, 한 번 서버에서 응답받은 것을 스스로 보관하고 클라이언트에 전달함.
- HSTS(HTTP Strict Transport Security)
- CORS 처리
- TCP/UDP 커넥션 부하 분산
- HTTP/2 등
출처 : https://ssdragon.tistory.com/60
'Sparta Coding Club > Today I Learned [TIL]' 카테고리의 다른 글
[TIL] #DAY - 054 - (1) 무신사 크롤링? 스크랩핑!(실시간 랭킹) (0) | 2022.11.22 |
---|---|
[TIL] #DAY - 053 - M1 맥북 tensorflow, opencv, dlib 사용하기! (내일배움캠프AI 3기) (0) | 2022.11.17 |
[TIL] #DAY - 051 - (2) 도커 실무 정리 : docker (내일배움캠프AI 3기) (0) | 2022.11.16 |
[TIL] #DAY - 050 - (1) 도커 실무 정리 : 리눅스(내일배움캠프AI 3기) (1) | 2022.11.15 |