수백만 명의 사용자가 학습, 엔터테인먼트, 뉴스, 친구들과의 삶의 진행 상황’ 공유 등 다양한 목적으로 정보에 액세스하기 위해 인터넷에 접속합니다. 따라서 앱을 배포할 때 애플리케이션을 위해 고도로 안전하고 확장 가능한 인프라를 구현하는 것이 가장 좋습니다. 클라우드는 Django 애플리케이션을 안전하게 보호하고 확장할 수 있는 다양한 방법을 제공합니다. 수평 확장은 앱의 여러 복사본을 실행할 수 있는 한 가지 방법입니다. 이를 통해 내결함성이 향상되고 가용성이 높아집니다. 또한 동시에 여러 요청을 처리할 수 있도록 성능을 향상시킵니다.
Django 애플리케이션 수평 확장하기
Django 애플리케이션과 해당 WSGI HTTP 서버(예: Gunicorn 또는 uWSGI)를 실행하는 여러 앱 서버를 프로비저닝하여 Django 애플리케이션을 수평으로 확장할 수 있습니다. 그런 다음 이러한 앱 서버에 들어오는 요청을 분산하는 인프라를 설정해야 합니다. 로드 밸런서와 Nginx와 같은 역방향 프록시가 인프라의 트래픽 분산에 도움이 될 수 있습니다. Nginx는 SSL 인증서를 배포하여 HTTPS를 통해 앱에 대한 안전한 연결을 보장할 수 있습니다. 마지막으로 Nginx는 정적 콘텐츠의 캐싱을 제공하여 서버의 부하를 최소화할 수도 있습니다.
이러한 다양한 구성 요소를 개별적으로 구성하고 이들이 서로 통신하도록 보장하는 것은 어려운 작업일 수 있습니다. 다행히도 Docker를 사용하면 구성 프로세스가 간소화되고 배포 위치에 관계없이 다양한 구성 요소가 동일하게 작동하도록 보장할 수 있습니다.
이 가이드에서 수행할 작업
이 가이드에서는 Gunicorn WSGI HTTP 서버로 서비스되는 컨테이너화된 Django 애플리케이션을 수평으로 확장하는 방법을 배웁니다. 각각 Docker가 설치되어 있고 동일한 Django 및 Gunicorn 앱 컨테이너 복사본을 실행하는 두 개의 애플리케이션 서버를 프로비저닝합니다.
또한 Let’s Encrypt SSL 인증서로 애플리케이션을 보호하기 위해, 세 번째 프록시 서버를 프로비저닝하고 구성하여 Nginx 역방향 프록시 컨테이너와 Certbot 클라이언트 컨테이너를 실행할 것입니다. Certbot은 Let’s Encrypt 인증 기관의 SSL 인증서 관리를 돕는 패키지입니다. 인증서를 가져오고, 인증서 위치로 Nginx 서버 블록을 구성하며, 자동 갱신을 관리합니다. 이는 크론(cron) 작업을 구성하여 인증서가 만료될 예정이어서 갱신이 필요한지 주기적으로 확인하는 방식으로 수행됩니다. SSL 인증서를 최신 상태로 유지하면 웹사이트는 항상 SSL Labs.
에서 높은 보안 등급을 유지하게 됩니다. 이 세 번째 프록시 서버는 분산 아키텍처의 맨 앞에 위치하여 들어오는 모든 외부 트래픽을 수신합니다. 그런 다음 트래픽을 앱 서버로 분산합니다. 앱 서버는 방화벽 뒤에 위치하여 프록시 서버만 액세스할 수 있도록 허용합니다.
이 튜토리얼은 Django, Docker 및 Kubernetes를 사용하는 3부작 튜토리얼 시리즈 중 두 번째입니다. 먼저 Ubuntu에서 Docker로 Django 및 Gunicorn 애플리케이션 빌드하기 튜토리얼에 설명된 단계를 따라야 합니다. 해당 튜토리얼에서는 기본 프로젝트 코드와 Dockerfile을 설정하고 앱을 MinIo Simple Storage Service (S3)에 연결하여 정적 파일을 제공합니다.
사전 요구 사항
이 튜토리얼을 진행하려면 다음이 필요합니다.
- 네 개의 Ubuntu 20.04 서버:
사전 요구 사항 튜토리얼인 Ubuntu에서 Docker로 Django 및 Gunicorn 애플리케이션 빌드하기의 단계를 따랐다면, 네 개의 서버 중 두 개를 이미 보유하고 있는 것입니다.
-
첫 번째 서버는 PostgreSQL 데이터베이스 인스턴스를 실행합니다. 데이터베이스를 설정하려면 Ubuntu에서 Docker로 Django 및 Gunicorn 애플리케이션 빌드하기 튜토리얼의 1단계와 2단계를 따르세요. Postgres 구성은 앱 서버 IP에서만 외부 연결을 허용하도록 수정해야 합니다.
-
The 두 번째 및 세 번째 서버는 애플리케이션 코드용 컨테이너를 호스팅합니다. 사전 요구 사항 튜토리얼을 통해 두 번째 서버가 이미 실행 중이어야 합니다. 프록시 서버 IP에서만 외부 연결을 허용하도록 방화벽을 수정할 것입니다. Ubuntu 서버를 설정하는 데 도움이 되는 이 단계별 튜토리얼의 1~4단계를 따르실 수 있습니다. CloudSigma에서.
-
그 네 번째 서버는 프록시 서버가 될 것이며, 두 개의 애플리케이션 서버 컨테이너로의 부하 분산 및 트래픽 분배를 처리합니다.
-
두 개의 앱 서버와 프록시 서버에 Docker가 설치되어 있어야 합니다.
선행 학습 튜토리얼의 단계를 따른 후, 선행 학습 튜토리얼을 완료했다면 서버 중 하나에 이미 Docker가 설치되어 있어야 합니다. 다음의 1, 2, 3단계를 따르셔도 됩니다. Docker 설치 및 작동에 관한 튜토리얼. 위에서 생성한 sudo 사용자를 Docker 그룹에 추가하는 것을 잊지 마세요.
- 등록된 도메인 이름을 확보하고 DNS 레코드가 프록시 서버의 공인 IP 주소를 가리키도록 설정합니다. 데모 목적으로는 다음을 사용합니다. example_domain.com.
-
S3 객체 스토리지 서비스 설정. 선행 학습 튜토리얼에서는 스토리지 서비스로 MinIO를 사용했습니다. 따라서, 5단계의 설명을 따라 선행 학습 튜토리얼의 설명을 따라 귀하의 MinIO 스토리지 버킷을 설정하십시오.
1단계: 첫 번째 Django 애플리케이션 서버가 작동하는지 확인
선행 조건에서 설명했듯이, 선행 조건의 설명대로, 이 가이드는 Ubuntu에서 Docker를 사용하여 Django 및 Gunicorn 애플리케이션 빌드하기 튜토리얼 이후에 진행됩니다. 해당 튜토리얼을 마치고 단계를 이미 실행했다면 첫 번째 서버가 실행 중이어야 합니다. 애플리케이션 코드는 Django 문서의 설문조사(Polls) 애플리케이션 튜토리얼을 기반으로 합니다. 초기 설정을 이해하려면 해당 단계를 자세히 읽어보는 것이 중요합니다. 튜토리얼의 단계를 이미 실행했다면 이 첫 번째 단계는 건너뛰어도 됩니다.
그렇지 않은 경우, Docker화된 브랜치를 서버에 복제하면 됩니다. 첫 번째 앱 서버에 로그인한 후 다음의 git 명령을 실행하여 django-polls-docker 브랜치 (대상: django-polls 저장소):
|
1 |
git clone --single-branch --branch django-polls-docker --depth 1 https://github.com/jaymoh/django-polls.git |
다음으로, 다음 디렉토리로 이동합니다: django-polls 디렉토리:
cd django-polls
이 디렉토리에는 애플리케이션 이미지를 빌드하는 데 Docker가 사용하는 Dockerfile, Python 애플리케이션 코드가 포함된 django-polls 디렉토리, 그리고 시작 시 컨테이너에 전달되어 동작을 수정할 환경 변수 목록이 포함된 env 파일이 있습니다. Dockerfile에서는 requirements.txt 파일을 통해 Django 패키지 의존성을 정의합니다. 또한, 들어오는 트래픽을 수신하는 데 사용할 포트 8000를 선언하고, 3개의 워커를 가진 gunicorn 서버를 실행하도록 설정해야 합니다. Dockerfile 지시어에 대해 자세히 알아보려면 다음 튜토리얼의 7단계를 참조하십시오: Ubuntu에서 Docker를 사용하여 Django 및 Gunicorn 애플리케이션 빌드하기.
다음 명령을 사용하여 Docker 이미지를 빌드할 수 있습니다:
docker build -t django-polls:v1 .
Docker가 이미지를 빌드한 후, 다음 명령으로 서버에서 사용 가능한 이미지 목록을 확인할 수 있습니다:
docker images
명령을 실행했을 때의 출력 결과는 다음과 같습니다:
다음으로, 런타임 환경을 구성하는 데 사용되는 env 파일을 수정해야 합니다. 이 파일은 컨테이너를 시작할 때 Docker 실행 컨테이너로 전달됩니다. nano 편집기로 env 파일을 여십시오:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
DJANGO_SECRET_KEY=your_secret_key DEBUG= DJANGO_LOGLEVEL=info DJANGO_ALLOWED_HOSTS=your_server_IP_address DB_ENGINE=postgresql_psycopg2 DB_DATABASE=polls_db DB_USERNAME=hackins DB_PASSWORD=your_database_password DB_HOST=your_database_host DB_PORT=your_database_port STATIC_DEFAULT_FILE_STORAGE=storages.backends.s3boto3.S3Boto3Storage STATIC_MINIO_BUCKET_NAME=test-bucket MINIO_ACCESS_KEY=your_minio_access_key MINIO_SECRET_KEY=your_minio_secret_key MINIO_URL=your_minio_url:your_minio_port |
이 env 파일에는 수정하고 올바른 값으로 채워야 하는 몇 가지 자리 표시자 텍스트가 있습니다:
-
DJANGO_SECRET_KEY: 다음에서 설명하는 대로 고유하고 예측할 수 없는 값을 생성합니다: Django docs. 이 명령을 사용하여 무작위 문자열을 생성하고 이를 변수에 설정할 수 있습니다: python -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())'.
-
DJANGO_ALLOWED_HOSTS: 이 값은 HTTP Host Header 공격으로부터 앱을 보호하는 데 사용됩니다. 다음과 같이 설정할 수 있습니다: *, 개발 모드인 경우 모든 호스트와 일치하는 와일드카드입니다. 앱을 프로덕션에 배포할 때는 이를 등록된 도메인 이름으로 설정하세요. 이 데모의 경우 example_domain.com.
-
DB_DATABASE: 이를 다음 섹션에서 생성한 PostgreSQL 데이터베이스의 이름으로 설정합니다: Prerequisites 섹션, 우리의 경우는 polls_db.
-
DB_USERNAME: 이를 데이터베이스에 대해 선택한 사용자 이름으로 설정합니다.
-
DB_PASSWORD: 이를 데이터베이스에 대해 선택한 비밀번호로 설정합니다.
-
DB_HOST: 이를 다음 섹션에서 설정한 대로 데이터베이스 인스턴스를 실행하는 호스트로 설정합니다: Prerequisites 섹션. 이는 다음 튜토리얼의 1단계와 2단계에 설명되어 있습니다: Building a Django and Gunicorn Application with Docker on Ubuntu (데이터베이스 설정 관련).
-
DB_PORT: 이를 데이터베이스의 포트로 설정합니다.
편집을 마치면 파일을 저장하고 닫습니다. 데이터베이스 자격 증명이 준비되었으므로, 컨테이너를 실행하고 Dockerfile에 설정된 CMD 명령을 재정의하여 데이터베이스 스키마를 생성할 수 있습니다. 자세한 정보는 다음에서 확인할 수 있습니다: Dockerfile entry point from official Docs. 다음으로, 아래 명령을 실행합니다:
|
1 |
docker run --env-file env django-polls:v1 sh -c "python manage.py makemigrations && python manage.py migrate" |
이 명령에서는 다음 이미지를 실행하고 django-polls:v1 이전에 수정한 env 파일을 전달합니다. 다음 부분은: sh -c "python manage.py makemigrations && python manage.py migrate” 앱 코드에 정의된 데이터베이스 스키마를 생성합니다. 이 명령을 처음 실행하는 경우, 데이터베이스 스키마 생성을 나타내는 다음과 유사한 출력이 표시되어야 합니다:
스키마가 생성되면 Django 수퍼유저를 생성할 수 있습니다. 대화형 셸로 컨테이너를 시작하려면 다음 명령을 실행하세요:
|
1 |
docker run -i -t --env-file env django-polls:v1 sh |
이 명령은 Python 셸과 상호 작용하는 데 사용할 수 있는 셸 프롬프트와 함께 컨테이너를 시작합니다. 다음 명령으로 사용자를 생성해 보겠습니다:
|
1 |
python manage.py createsuperuser |
프롬프트에 따라 사용자 이름, 이메일 주소, 비밀번호를 입력합니다. 비밀번호를 다시 입력하고 엔터를 누르면 사용자가 생성됩니다. 다음을 눌러 셸을 종료하고 컨테이너를 종료합니다: CTRL+D.
다음으로, 기본 명령을 다음으로 재정의하여 컨테이너를 다시 실행해야 합니다: collectstatic Django 명령. 이 명령은 앱의 정적 파일을 생성하고 MinIO 클라우드 스토리지에 업로드합니다:
|
1 |
docker run --env-file env django-polls:v1 sh -c "python manage.py collectstatic --noinput" |
이 명령은 파일을 생성하고 구성된 오브젝트 스토리지 서비스에 업로드합니다. 출력 결과는 다음과 같습니다:
이제 Dockerfile에 정의된 기본 CMD 명령을 재정의하기 위한 추가 명령을 지정하지 않고도 애플리케이션을 실행할 수 있습니다:
|
1 |
docker run --env-file env -p 80:8000 django-polls:v1 |
Docker는 Dockerfile에 정의된 기본 명령을 실행하고, 다음 서버로 컨테이너를 구동하며, gunicorn 서버를 구동하고, 컨테이너 포트 8000 를 노출하며, 이를 Ubuntu의 포트 80 에 매핑합니다. 이제 주소 표시줄에서 첫 번째 서버의 IP 주소에 액세스하여 브라우저에서 애플리케이션 인터페이스를 볼 수 있습니다: http://FIRST_SERVER_IP.
다음을 받게 됩니다: 404 Page Not Found 왜냐하면 다음에 대해 아무것도 정의하지 않았기 때문입니다: / 경로. 다음으로 이동하십시오: http://FIRST_SERVER_IP/polls 로 이동하여 Polls 인터페이스를 확인합니다:
관리자 인터페이스를 방문하여 설문조사를 생성합니다: http://FIRST_SERVER_IP/admin:
관리자 인터페이스에 액세스하려면 위에서 createsuperuser 명령어로 설정한 자격 증명을 입력하십시오:
페이지 소스를 보면 정의된 대로 스토리지 버킷에서 정적 파일을 가져오는 것을 확인할 수 있습니다. 컨테이너가 예상대로 앱을 서비스하는지 확인한 후, 터미널에서 CTRL+C를 눌러 컨테이너를 종료할 수 있습니다.
다음으로, 첫 번째 서버의 SSH 세션을 종료할 수 있도록 컨테이너를 detached 모드로 계속 실행해야 합니다. 이렇게 하면 컨테이너가 백그라운드에서 계속 실행됩니다. 다음 명령을 실행하십시오:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
The -d 플래그는 컨테이너를 detached 모드로 실행하여 백그라운드에서 계속 실행되도록 합니다. --rm 플래그는 컨테이너가 종료된 후 컨테이너의 파일 시스템을 정리합니다. 컨테이너 목록을 볼 때 확인할 수 있도록 컨테이너 이름을 polls(으)로 지정합니다.
첫 번째 서버의 SSH 세션을 종료하고 브라우저에서 http://FIRST_SERVER_IP/polls로 이동하여 예상대로 실행 중인지 확인합니다. 설문조사(polls) 인터페이스가 표시되면 첫 번째 앱 서버가 성공적으로 설정된 것입니다. 다음 단계에서 두 번째 애플리케이션 서버를 설정해 보겠습니다.
Step 2: Setting up the Second Application Server
이전 Building a Django and Gunicorn Application with Docker on Ubuntu 튜토리얼에서 생성한 애플리케이션의 Docker화된 브랜치를 복제할 것입니다. 여기서 사용할 명령에 대한 자세한 내용은 해당 튜토리얼을 참조하거나, Step 1.
두 번째 서버가 실행 중이어야 하며, Prerequisites 섹션에 설명된 대로 루트가 아닌 sudo 사용자를 추가하고 Docker를 설치해야 합니다.
다음 단계는 이 서버가 PostgreSQL 서버 인스턴스에 연결되도록 구성하는 것입니다. Building a Django and Gunicorn Application with Docker on Ubuntu 튜토리얼의 1단계에서 설명한 대로, ufw 및 PostgreSQL 구성에서 두 번째 서버의 IP 주소를 허용해야 합니다.
먼저, 루트가 아닌 sudo 사용자로 PostgreSQL 데이터베이스 서버 인스턴스에 로그인합니다. ufw 규칙을 추가하려면 다음 명령을 실행하십시오:
|
1 |
sudo ufw allow from SECOND_SERVER_IP to any port 5432 |
다음으로, 이 명령을 실행하고 PostgreSQL 클라이언트 인증 파일에 두 번째 서버의 IP 주소를 추가합니다:
|
1 |
sudo nano /etc/postgresql/12/main/pg_hba.conf |
주석을 읽어보며 구성에 대해 자세히 알아보십시오. 다음으로, hosts 섹션 아래에 IP 주소를 지정하여 이 줄을 추가합니다:
|
1 |
host all all SECOND_SERVER_IP/24 md5 |
편집을 마치면 파일을 저장하고 닫습니다.
그런 다음 변경 사항을 적용하기 위해 PostgreSQL 서비스를 재시작합니다:
|
1 |
sudo service postgresql restart |
PostgreSQL 데이터베이스 서버 인스턴스에서 로그아웃하고 두 번째 앱 서버 인스턴스 구성을 진행합니다.
다음 명령을 사용하여 second app server에 ssh로 로그인합니다. 그런 다음 django-polls- 리포지토리의 of the django-polls 브랜치를 다음 명령으로 복제합니다:
|
1 2 |
git clone --single-branch --branch django-polls-docker --depth 1 https://github.com/jaymoh/django-polls.git |
Move into the django-polls 디렉터리로 이동합니다:
|
1 |
cd django-polls |
그 후, 다음 명령을 사용하여 이미지를 빌드합니다:
|
1 |
docker build -t django-polls:v1 . |
이미지 빌드 프로세스가 완료되면, env 파일의 구성 값을 Step 1에서 설명한 대로 수정합니다. nano로 파일을 엽니다:
|
1 |
nano env |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
DJANGO_SECRET_KEY=your_secret_key DEBUG= DJANGO_LOGLEVEL=info DJANGO_ALLOWED_HOSTS=your_server_IP_address DB_ENGINE=postgresql_psycopg2 DB_DATABASE=polls_db DB_USERNAME=hackins DB_PASSWORD=your_database_password DB_HOST=your_database_host DB_PORT=your_database_port STATIC_DEFAULT_FILE_STORAGE=storages.backends.s3boto3.S3Boto3Storage STATIC_MINIO_BUCKET_NAME=test-bucket MINIO_ACCESS_KEY=your_minio_access_key MINIO_SECRET_KEY=your_minio_secret_key MINIO_URL=your_minio_url:your_minio_port |
자리 표시자 텍스트를 1단계에서 추가한 실제 값으로 바꿉니다. DJANGO_ALLOWED_HOSTS 변수를 적절히 수정해야 함을 기억하세요. 완료되면 파일을 저장하고 닫습니다. 이전 단계에서 했던 것처럼 env 파일에서 MinIO 자격 증명을 업데이트합니다.
이제 다음 명령어를 사용하여 백그라운드(detached) 모드로 앱 컨테이너를 실행할 수 있습니다:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
이 명령은 컨테이너를 시작하고 백그라운드에서 계속 실행되도록 합니다. 두 번째 앱 서버의 ssh 세션을 종료하고 브라우저에서 http://SECOND_SERVER_IP/polls로 이동하여 예상대로 작동하는지 확인합니다. 모든 것이 예상대로 진행되었다면 설문조사(polls) 인터페이스를 볼 수 있을 것입니다.
이제 동일한 애플리케이션 복사본을 실행하는 두 개의 앱 서버가 준비되었습니다. 다음 단계에서는 Nginx 컨테이너를 리버스 프록시로 작동하도록 구성합니다.
3단계: Nginx Docker 컨테이너 설정
Nginx은 세계에서 가장 인기 있는 오픈 소스 웹 서버 소프트웨어 중 하나입니다. 인터넷에서 트래픽이 가장 많은 사이트의 가용성과 확장성을 보장하는 역할을 합니다. 또한 보안을 보장하며 매우 다재다능합니다. 리버스 프록시, 캐싱 및 로드 밸런싱에 사용할 수 있습니다. 당사는 정적 파일과 미디어 파일을 처리하기 위해 별도의 오브젝트 스토리지 서비스를 사용하도록 애플리케이션을 설정했습니다. 따라서 Nginx 캐싱 기능은 사용하지 않습니다. 대신 Nginx 리버스 프록시 및 로드 밸런싱 기능을 사용합니다. 전면에 위치한 Nginx 서버는 들어오는 트래픽을 수신하여 백엔드 애플리케이션 서버로 분산합니다. 그런 다음, 다음에서 발급받은 SSL 인증서를 사용하여 트래픽을 보호함으로써 클라이언트와 서버 간의 안전한 통신을 보장합니다: Let’s Encrypt.
Nginx 리버스 프록시 및 로드 밸런싱을 구현하는 방법에는 여러 가지가 있습니다. 한 가지 방법은 이 튜토리얼에서 한 것처럼 Nginx 리버스 프록시를 백엔드 애플리케이션 서버와 분리하여 설정하는 것입니다. 이 설정은 유연하며 Nginx 프록시 레이어와 애플리케이션 레이어를 모두 확장할 수 있습니다. 여러 Nginx 프록시를 추가하거나 클라우드 로드 밸런서를 구현할 수 있습니다. 리버스 프록시를 구현하는 또 다른 방법은 백엔드 앱 서버 중 하나를 Nginx 프록시로 사용하는 것입니다. 그런 다음 들어오는 요청을 로컬 및 다른 앱 서버로 프록시할 수 있습니다. 선택적으로 모든 백엔드 앱 서버에 Nginx 컨테이너를 구성하고 전면에 클라우드 로드 밸런서를 설정하여 들어오는 트래픽을 수신하고 백엔드 앱 서버로 분산할 수도 있습니다.
프록시 서버 구성을 시작해 보겠습니다. Nginx 프록시로 사용하도록 설정한 네 번째 Ubuntu 서버에 로그인하고 구성 디렉터리를 생성합니다:
|
1 |
mkdir conf |
디렉터리 내부에서 nano로 구성 파일을 엽니다:
|
1 |
nano conf/nginx.conf |
다음으로 파일에 아래 구성을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
upstream django { server FIRST_SERVER_IP; server SECOND_SERVER_IP; } server { listen 80 default_server; return 444; } server { listen 80; listen [::]:80; server_name example_domain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name example_domain.com; # SSL ssl_certificate /etc/letsencrypt/live/example_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example_domain.com/privkey.pem; ssl_session_cache shared:le_nginx_SSL:10m; ssl_session_timeout 1440m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; client_max_body_size 4G; keepalive_timeout 5; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://django; } location ^~ /.well-known/acme-challenge/ { root /var/www/html; } } |
이 설정 파일에서는 server, upstream 및 location 블록을 지정하여 Nginx가 HTTP 요청을 HTTPS로 리디렉션하고, 1단계 및 2단계에서 설정한 두 개의 앱 서버 간에 요청을 분산하도록 지시합니다. 이에 대한 일반적인 정보는 다음을 참고하세요: 공식 문서의 Nginx 설정 파일 구조.
우리는 다음에서 제공하는 설정 파일을 참고했습니다: Docker Hub Nginx 이미지 문서, Certbot 및 Gunicorn 이 최소한의 Nginx 설정 파일을 작성하게 되었습니다. 이는 데모 목적 및 설정을 실행하기 위한 것일 뿐이므로, 자유롭게 탐색하고 실험해 보셔도 좋습니다. Nginx 가이드를 따라 다른 설정들을.
upstream 블록은 들어오는 요청을 처리할 서버 그룹을 정의하는 데 사용됩니다. 이름이 그룹에 부여되며, proxy_pass 지시문에 의해 호출됩니다. 우리는 이 블록의 이름을 django로 지정하고 두 개의 백엔드 앱 서버의 IP 주소를 명시했습니다:
|
1 2 3 4 |
upstream django { server FIRST_SERVER_IP; server SECOND_SERVER_IP; } |
또한 우리는 3개의 server 블록을 정의했습니다. 첫 번째 server 블록은 도메인과 일치하지 않는 모든 요청을 캡처하고 444 코드를 반환합니다(클라이언트에 응답을 보내지 않고 연결을 닫으므로 악성 또는 잘못된 형식의 요청을 거부합니다). 서버의 IP 주소로 직접 들어오는 HTTP 요청은 이 블록이 default_server:
|
1 2 3 4 |
server { listen 80 default_server; return 444; } |
The 두 번째 server 블록은 들어오는 HTTP(포트 80) 요청을 처리하고, 443)로 리다이렉트합니다. 이때 사용되는 것은 HTTP 301 리다이렉트:
|
1 2 3 4 5 6 |
server { listen 80; listen [::]:80; server_name example_domain.com; return 301 https://$server_name$request_uri; } |
이제 세 번째 server 블록이 요청을 처리합니다. 여기에는 여러 지시문이 있으며, 아래에서 그 중요성을 정의하겠습니다.
Certbot에서 제공하는 TLS 인증서 및 키의 경로를 정의하는 두 개의 지시문이 있습니다. 인증서는 Nginx 컨테이너를 시작할 때 컨테이너 내부로 마운트됩니다:
|
1 2 3 |
# SSL ssl_certificate /etc/letsencrypt/live/example_domain.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example_domain.com/privkey.pem; |
다음으로, Certbot에서 권장하는 SSL 보안 기본 설정이 있습니다. 자세한 내용은 ngx_http_ssl_module에 대한 Nginx 공식 문서에서 확인할 수 있습니다. Mozilla에서도 서버 측 보안에 대한 추가 정보를 제공합니다. ssl_ciphers 파일의 conf 값은 Mozilla의 페이지에서 가져온 것입니다:
|
1 2 3 4 5 6 |
ssl_session_cache shared:le_nginx_SSL:10m; ssl_session_timeout 1440m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384"; |
다음 두 지시문에서는 클라이언트 요청 본문의 최대 허용 크기를 정의하고, 클라이언트와의 keep-alive 연결에 대한 타임아웃을 설정합니다. Nginx는 keepalive_timeout 지시문에 설정한 초(seconds)가 지나면 클라이언트와의 연결을 닫습니다. Gunicorn 배포를 위한 Nginx 설정에 대한 자세한 정보를 공식 문서에서 확인할 수 있습니다:
|
1 2 |
client_max_body_size 4G; keepalive_timeout 5; |
설정 파일에는 두 개의 location 블록도 정의되어 있습니다. 첫 번째 블록은 proxy 지시문으로 정의된 요청의 프록시 처리를 담당합니다. 들어오는 요청은 이전에 정의된 upstream django 서버로 프록시됩니다:
|
1 2 3 4 5 6 7 |
location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_redirect off; proxy_pass http://django; } |
프록시 지시어에 대한 자세한 정보는 Nginx Module ngx_http_proxy_module 및 다음 문서인 Gunicorn 서버 배포에서 확인할 수 있습니다..
두 번째 location 블록에서는 다음과 같은 경로를 정의합니다: /well-known/acme-challenge/. 이는 일반적으로 SSL 인증서를 발급하거나 갱신하기 전에 Certbot이 Let’s Encrypt를 통해 도메인 이름을 확인하는 데 사용됩니다:
|
1 2 3 |
location ^~ /.well-known/acme-challenge/ { root /var/www/html; } |
Nginx 설정 파일에 대한 설명은 이것으로 끝입니다. 편집을 마쳤으면 이제 파일을 저장하고 닫을 수 있습니다.
방금 정의한 설정 파일은 Nginx 컨테이너를 실행하는 데 사용할 수 있습니다. 하지만 Let’s Encrypt에서 SSL 인증서를 발급받지 않았기 때문에 실패할 것입니다. 이 튜토리얼에서는 공식 nginx:1.20.2 Docker 이미지 버전 1.20.2를 사용합니다. 공식 Nginx image repository on Docker Hub.
아래 명령어를 실행하여 이미지를 다운로드하고 모든 것이 올바르게 작동하는지 확인할 수 있습니다:
|
1 2 3 4 |
docker run --rm --name nginx -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
이 명령어는 이름이 nginx인 컨테이너를 생성하고, 호스트 시스템과 컨테이너 간에 포트 80 및 443를 매핑합니다. --rm 플래그는 빌드가 성공한 후 중간 컨테이너를 제거합니다. 우리는 -v 플래그를 사용하여 설정 파일을 컨테이너의 /etc/nginx/conf.d/nginx.conf에 마운트합니다. 이곳은 기본 Nginx 설정 디렉토리입니다. Nginx 컨테이너가 파일을 수정하지 못하도록 ro 플래그를 사용하여 읽기 전용 모드로 마운트합니다. 기본 webroot 디렉토리를 설정하고 이를 /var/www/html로 마운트합니다. 마지막으로 Docker가 이 빌드에 nginx:1.20.2 이미지를 사용하도록 지시합니다. 다음 단계에서 Let’s Encrypt로부터 TLS/SSL 인증서와 키를 획득해 보겠습니다.
4단계: Let’s Encrypt에서 SSL/TLS 인증서 발급 및 Certbot 자동 갱신 설정
Certbot은 Let’s Encrypt로부터 무료 TLS 인증서를 발급받을 수 있도록 도울 뿐만 아니라, 만료되기 전에 자동 갱신되도록 관리해 줍니다. 이는 웹사이트의 보안을 향상시키고 HTTPS를 통해 서비스되도록 보장합니다. 아키텍처를 컨테이너화된 상태로 유지하기 위해, Certbot Docker 이미지를 사용하여 SSL/TLS 인증서를 가져오고 자동 갱신을 설정할 것입니다. 프록시 서버에 Docker가 설치되어 있는지 확인하십시오. 자세한 내용은 사전 요구 사항 안내를 참조하십시오.
또한 등록된 도메인 이름의 DNS A 레코드가 프록시 서버의 IP 주소를 가리키고 있어야 합니다. certbot Docker 이미지를 실행하고 --staging 플래그를 전달하여 확인할 수 있습니다:
|
1 2 3 4 |
docker run -it --rm -p 80:80 --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ certbot/certbot certonly --standalone --staging -d example_domain.com |
이 명령어는 Certbot 이미지를 다운로드하고 대화형 모드로 실행합니다. 즉, 셸이 함께 제공되어 몇 가지 세부 정보를 입력할 수 있습니다. 이 명령어는 호스트의 포트 80을 컨테이너 내부의 포트 80에 매핑합니다. 우리는 -v 플래그를 사용하여 두 개의 호스트 디렉토리를 컨테이너에 마운트합니다: /etc/letsencrypt/ 및 /var/lib/letsencrypt/. --standalone 플래그는 Certbot 이미지가 Nginx를 사용하지 않고 실행되도록 지정합니다. 마지막으로, --staging 플래그가 있으며, 이 플래그는 Certbot이 스테이징 서버에서 실행되고 도메인 이름을 검증하도록 합니다.
이메일 주소를 입력하고 서비스 약관에 동의하십시오. 다음은 성공적인 검증의 출력 결과입니다:
|
1 2 3 4 5 6 7 |
계정 등록됨. 요청 중 a 인증서 대상: example_domain.com 성공적으로 수신함 인증서. 인증서 is 저장됨 위치: : /etc/letsencrypt/live/example_domain.com/fullchain.pem 키 is 저장됨 위치: : /etc/letsencrypt/live/example_domain.com/privkey.pem 이 인증서 만료일: on 2022-04-29. 이 파일들은 will be 업데이트됨 when the 인증서가 갱신될 때. |
다음 단계:
인증서는 만료되기 전에 갱신해야 합니다. Certbot은 백그라운드에서 자동으로 인증서를 갱신할 수 있지만, 해당 기능을 활성화하기 위한 조치를 취해야 할 수도 있습니다. 자세한 안내는 이 링크 를 확인하십시오.
다음 cat 명령어를 사용하여 인증서를 확인할 수 있습니다:
|
1 |
sudo cat /etc/letsencrypt/live/example_domain.com/fullchain.pem |
위 명령어를 실행하면 터미널에 인증서가 표시됩니다. Certbot이 인증서를 성공적으로 발급했음을 확인했다면, 이제 3단계에서 생성한 Nginx 설정을 테스트할 수 있습니다. 아래 Docker 명령어를 실행하여 Nginx 컨테이너를 시작하십시오:
|
1 2 3 4 5 6 |
docker run --rm --name nginx -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /etc/letsencrypt:/etc/letsencrypt \ -v /var/lib/letsencrypt:/var/lib/letsencrypt \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
이 명령어에서는 -v 플래그를 사용하여 Let’s Encrypt SSL/TLS 인증서 디렉터리의 위치를 마운트했습니다.
컨테이너가 실행되면 브라우저에서 웹 페이지를 여십시오: http://example_domain.com. 웹사이트가 안전하지 않다는 경고가 표시될 것입니다:
이는 Let’s Encrypt에서 production 인증서가 아닌 스테이징/테스트 인증서만 발급받았기 때문입니다. 다음 Certbot 명령어에서 --staging 플래그를 제외하고 실행하여 운영 인증서를 가져오겠습니다:
|
1 2 3 4 |
docker run -it --rm -p 80:80 --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ certbot/certbot certonly --standalone -d example_domain.com |
프롬프트에서 renew and replace를 입력하여 기존 인증서를 교체할 것임을 확인하고 2을 입력한 후 ENTER를 누르십시오. 이렇게 하면 운영 환경에 적합한 인증서가 발급됩니다. 이제 Nginx 컨테이너를 실행하면 모든 것이 정상적으로 작동할 것입니다:
|
1 2 3 4 5 6 |
docker run --rm --name nginx -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /etc/letsencrypt:/etc/letsencrypt \ -v /var/lib/letsencrypt:/var/lib/letsencrypt \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
컨테이너가 실행되면 브라우저에서 웹 페이지를 다시 여십시오: http://example_domain.com. HTTP를 입력하더라도 브라우저가 HTTPS로 리디렉션되는 것을 확인할 수 있습니다. 이는 Nginx 설정의 서버와 발급된 SSL/TLS 인증서가 모두 정상적으로 작동하고 있음을 의미합니다. 홈 경로에 대해 정의된 라우트가 없으므로 polls 경로 http://example_domain.com/polls로 이동하십시오. /. 설문조사(polls) 인터페이스가 표시되어야 합니다:
지금까지 프로덕션 준비가 완료된 아키텍처를 성공적으로 구성했습니다. 프록시 서버에서 프록시 처리되어 들어오는 요청을 처리할 두 개의 백엔드 서버를 구현했습니다. 프록시 서버는 프로비저닝된 TLS 인증서를 사용하여 트래픽의 부하 분산 및 보안을 처리합니다.
하지만 Let’s Encrypt 인증서는 90일 후에 만료된다는 점을 명심해야 합니다. 따라서 90일이 되기 전에 갱신해야 합니다. Nginx 컨테이너가 실행 중이므로, 인증서 갱신을 위해 certbot 명령을 실행할 때 webroot 모드 대신 standalone 모드를 사용해야 합니다. 이전에 /var/www/html/.well-known/acme-challenge/ 디렉터리를 Nginx 설정 파일에 지정했던 것을 기억하실 것입니다. 3단계에서 말이죠. Certbot은 이 경로를 사용하여 검증 파일을 저장합니다. 또한 Let’s Encrypt 클라이언트는 인증서 갱신을 시도할 때 검증 요청과 함께 이 경로를 호출합니다. 갱신 명령 실행이 완료되면 Nginx를 다시 로드하여 변경 사항을 적용할 수 있습니다.
터미널에서 CTRL+C를 눌러 컨테이너를 종료하고, -d 플래그를 사용하여 백그라운드(detached) 모드로 다시 실행해 보겠습니다:
|
1 2 3 4 5 6 |
docker run --rm --name nginx -d -p 80:80 -p 443:443 \ -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \ -v /etc/letsencrypt:/etc/letsencrypt \ -v /var/lib/letsencrypt:/var/lib/letsencrypt \ -v /var/www/html:/var/www/html \ nginx:1.20.2 |
이렇게 하면 Nginx 컨테이너가 백그라운드에서 계속 실행됩니다. 아래 명령을 실행하여 --dry-run 플래그로 인증서 갱신 절차를 테스트해 보겠습니다:
|
1 2 3 4 5 |
docker run -it --rm --name certbot \ -v "/etc/letsencrypt:/etc/letsencrypt" \ -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \ -v "/var/www/html:/var/www/html" \ certbot/certbot renew --webroot -w /var/www/html --dry-run |
이 명령에서는 --webroot 플러그인과 함께 검증 요청에 사용할 경로를 -w 플래그로 지정했습니다. 또한 실제로 인증서를 발급받지 않고 자동 갱신 절차를 확인하기 위해 --dry-run 플래그를 지정했습니다.
시뮬레이션이 성공하면 다음과 유사한 출력이 표시됩니다:
|
1 2 3 4 5 6 7 8 |
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Processing /etc/letsencrypt/renewal/example_domain.com.conf - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Simulating renewal of an existing certificate for example_domain.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Congratulations, all simulated renewals succeeded: /etc/letsencrypt/live/hackinroms.com/fullchain.pem (success) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - |
실행 중인 애플리케이션의 인증서를 갱신할 때마다 컨테이너가 새 인증서를 사용하도록 Nginx를 다시 로드해야 합니다. 다음 Docker 명령은 nginx 컨테이너(컨테이너 이름을 nginx로 지정했던 것을 기억하세요)를 다시 로드합니다:
|
1 |
docker kill -s HUP nginx |
이 명령은 HUP Unix 시그널을 nginx Docker 컨테이너 내부에서 실행 중인 Nginx 프로세스로 보냅니다. 이로 인해 Nginx는 구성을 다시 로드하고 갱신된 인증서를 사용하기 시작합니다.
프록시 서버에 TLS/SSL을 설치하고 웹사이트가 HTTPS로 서비스되므로, 이제 백엔드 앱 서버가 프록시 서버의 요청만 허용하도록 보안을 강화해야 합니다.
5단계: 외부 액세스로부터 백엔드 Django 서버 보호
이 튜토리얼에서 구현한 프록시 서버는 SSL 연결을 복호화하고 암호화되지 않은 패킷을 백엔드 앱 서버로 전달하는 SSL 종단을 처리합니다. 백엔드 서버를 외부 액세스로부터 보호할 것이므로 대부분의 경우 이 정도 수준의 보안으로 충분합니다. 하지만 은행 정보나 건강 데이터와 같이 민감한 데이터를 전송하는 애플리케이션을 배포하는 경우에는 다음을 구현해야 합니다. 종단 간 암호화.
이 튜토리얼에서 백엔드의 Gunicorn 서버는 전면에 노출되지 않도록 Nginx에 의해 보호됩니다. Nginx 프록시 서버는 백엔드 서버로 가는 게이트웨이와 같아서 외부 클라이언트가 백엔드 앱 서버에 직접 액세스하는 것을 방지합니다. 모든 요청이 프록시 서버를 거치도록 해야 합니다. 그런데 Docker에는 다음과 같은 ufw를 우회하는 문제 방화벽 규칙이 적용되지 않고 포트를 외부에 열어두어 인프라가 안전하지 않은 상태로 남을 수 있는 문제가 있습니다. 이는 실제로 우리가 1단계 및 2단계에서 포트를 허용하지 않고 80의 ufw 규칙에서 말이죠. 하지만 브라우저에서 서버의 공인 IP 주소 중 하나로 접속하면 여전히 웹 페이지에 액세스할 수 있습니다. 이 문제를 해결하는 한 가지 방법은 iptables를 ufw를 거치지 않고 직접 사용하는 것입니다. 자세한 내용은 Docker 및 iptables 공식 문서를 읽어보시기 바랍니다. 또 다른 권장 방법은 클라우드 방화벽을 사용하는 것입니다.
Docker에 의해 열렸을 수 있는 모든 포트에 대한 외부 액세스를 차단하도록 UFW 구성을 수정해 보겠습니다. 호스트 포트 80를 Docker 컨테이너의 포트 8000에 매핑할 때 Docker 명령의 -p 80:8000 플래그를 사용하면서 호스트 머신의 포트 80도 의도치 않게 열렸습니다. UFW 구성을 수정하여 이 액세스를 비활성화할 수 있으며, 자세한 방법은 ufw-docker 리포지토리 README에 설명되어 있습니다.
첫 번째 Django 앱 서버를 변경해 보겠습니다. 서버에 로그인하고 다음 위치의 파일을 엽니다: /etc/ufw/after.rules (sudo 권한으로 nano 편집기 사용):
|
1 |
sudo nano /etc/ufw/after.rules |
이 파일에는 다음과 같은 ufw 규칙이 포함되어 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# # rules.input-after # # ufw 명령줄에서 추가한 규칙 이후에 실행되어야 하는 규칙입니다. 사용자 정의 # 규칙은 다음 체인 중 하나에 추가되어야 합니다: # ufw-after-input # ufw-after-output # ufw-after-forward # # 이 필수 줄을 삭제하지 마십시오. 그렇지 않으면 오류가 발생합니다 *filter :ufw-after-input - [0:0] :ufw-after-output - [0:0] :ufw-after-forward - [0:0] # 필수 줄 끝 # 기본적으로 노이즈가 많은 서비스는 로그에 기록하지 않음 -A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input -A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input -A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input -A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input -A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input -A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input # 노이즈가 많은 브로드캐스트는 로그에 기록하지 않음 -A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input # 'COMMIT' 줄을 삭제하지 마십시오. 그렇지 않으면 이 규칙들이 처리되지 않습니다 COMMIT |
파일 하단에 다음 UFW 구성 줄 블록을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
# BEGIN UFW AND DOCKER *filter :ufw-user-forward - [0:0] :DOCKER-USER - [0:0] -A DOCKER-USER -j RETURN -s 10.0.0.0/8 -A DOCKER-USER -j RETURN -s 172.16.0.0/12 -A DOCKER-USER -j RETURN -s 192.168.0.0/16 -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN -A DOCKER-USER -j ufw-user-forward -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12 -A DOCKER-USER -j RETURN COMMIT # END UFW AND DOCKER |
추가한 규칙은 Docker가 여는 포트에 대한 외부 접속을 차단합니다. 또한, 다음으로부터의 접속을 허용합니다. 10.0.0.0/8, 172.16.0.0/12, 및 192.168.0.0/16 사설 IP 대역. 규칙에 대한 자세한 내용은 다음에서 확인할 수 있습니다: ufw-docker README. 편집을 마치면 파일을 저장하고 닫으십시오. 이 설정은 가상 사설 클라우드 네트워크(VPC)를 구축하고 세 개의 서버가 모두 VPC에 있는 경우 작동하며, Nginx의 upstream 지시문에 Django 서버의 사설 IP를 지정해야 합니다. 설정 파일.
그러나 우리는 공인 IP를 사용했으며 VPC가 없을 수도 있습니다. 따라서 다음에 규칙을 추가해야 합니다: ufw Nginx 프록시 서버로부터의 트래픽을 포트를 통해 허용하기 위해 80 (두 Django 앱 서버 모두의). 다음에 허용 규칙을 추가할 수 있습니다: ufw 발신 서버 IP를 포트로 지정하여 80 다음 명령어를 사용합니다:
|
1 |
sudo ufw allow from NGINX_PROXY_IP to any port 80 |
수정을 완료하면 변경 사항을 적용하기 위해 Django 앱 서버를 재부팅하십시오. 다음을 실행하는 것은 sudo ufw reload 변경 사항을 적용하지 못하는 것으로 보이기 때문입니다:
|
1 |
sudo reboot |
서버가 재부팅되면 다음에서 했던 것처럼 컨테이너를 실행하십시오: 1단계 또는 2단계:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
다음으로, 브라우저에서 첫 번째 Django 서버의 IP에 접속하여 Polls 인터페이스가 표시되는지 확인하십시오: http://FIRST_SERVER_IP/polls. 실패할 것입니다. 이제 첫 번째 서버에서 로그아웃하고 두 번째 서버에 대해 여기서 수행한 단계를 반복하십시오. 다음을 여십시오: /etc/ufw/after.rules sudo 사용자로서 nano로:
|
1 |
sudo nano /etc/ufw/after.rules |
이전에 했던 것처럼 맨 아래로 스크롤하여 UFW 구성 블록을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# BEGIN UFW AND DOCKER *filter :ufw-user-forward - [0:0] :DOCKER-USER - [0:0] -A DOCKER-USER -j RETURN -s 10.0.0.0/8 -A DOCKER-USER -j RETURN -s 172.16.0.0/12 -A DOCKER-USER -j RETURN -s 192.168.0.0/16 -A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN -A DOCKER-USER -j ufw-user-forward -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8 -A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12 -A DOCKER-USER -j RETURN COMMIT # END UFW AND DOCKER |
위의 블록을 추가한 후 파일을 저장하고 닫습니다.
다음으로, 다음 명령을 사용하여 ufw에 발신 server IP를 포트 80로 지정하는 허용 규칙을 추가합니다:
|
1 |
sudo ufw allow from NGINX_PROXY_IP to any port 80 |
변경 사항을 적용하려면 서버를 재부팅하십시오:
|
1 |
sudo reboot |
서버가 다시 켜지면 다음 명령으로 컨테이너를 다시 실행합니다:
|
1 |
docker run -d --rm --name polls --env-file env -p 80:8000 django-polls:v1 |
두 번째 서버의 IP 주소로 직접 이동하여 투표(polls) 인터페이스를 볼 수 있는지 테스트합니다: http://SECOND_SERVER_IP/polls. 이 역시 실패해야 합니다.
이제 이 아키텍처를 테스트할 준비가 되었습니다. https://example_domain_here/polls에 방문하여 브라우저에서 기본 Polls 인터페이스를 볼 수 있습니다. 이는 Nginx 프록시 서버가 여전히 Django 백엔드 서버에 액세스할 수 있음을 의미합니다.
결론
이 가이드에서는 Docker 컨테이너를 사용하여 확장 가능한 인프라를 구현하는 방법을 보여주었습니다. 이 인프라에는 별도의 PostgreSQL 데이터베이스 서버, 두 개의 백엔드 애플리케이션 서버, 그리고 두 서버 간의 트래픽을 로드 밸런싱하고 분산하는 Nginx 프록시 서버가 포함됩니다. 여기서는 애플리케이션의 기준으로 Django Polls 애플리케이션을 사용했지만, 다음과 같이 다양한 프레임워크를 사용하는 여러 애플리케이션에 맞게 이 아키텍처를 맞춤 설정할 수 있습니다: Node.js, Laravel 등.
이것은 시작하기 위한 기본적인 가이드라인입니다. 추가할 수 있는 몇 가지 개선 사항으로는 이미지를 Docker Hub와 같은 이미지 저장소에 호스팅하여 여러 서버에 이미지를 쉽게 배포할 수 있도록 하는 것이 있습니다. 또한 이벤트가 발생할 때마다 이미지를 자동으로 빌드, 테스트 및 앱 서버에 배포하도록 지속적 통합 및 배포 파이프라인을 추가할 수도 있습니다. 예를 들어, 이벤트는 git 저장소의 특정 브랜치에 새 코드를 푸시하는 것일 수 있습니다. 컨테이너에 오류가 발생했을 때의 동작을 자동화할 수도 있습니다. Docker 공식 문서에서는 오류가 발생하거나 시스템이 재부팅되는 경우 컨테이너 자동 시작에 대한 좋은 가이드라인을 제공합니다.
즐거운 컴퓨팅 되세요!








댓글
아직 댓글이 없습니다. 첫 번째로 작성해 보세요.