컴퓨터 활용/노년에 즐기는 코딩

같은 VPC의 3개 인스턴스(예: Bastion, Web, DB) 를 활용

easyfly 2025. 10. 18. 07:22
반응형

Django + Gunicorn + Nginx + MariaDB 운용 환경을 구축


1. 아키텍처 개요

  • VPC: 10.0.0.0/16 (예시)
  • 서브넷
    • Public Subnet: Bastion, Web(Nginx) 배치, IGW 경유 인터넷 통신
    • Private Subnet: DB(MariaDB) 배치, 공개 IP 없음
  • 보안그룹
    • SG-Bastion: Inbound 22/tcp(관리자 IP), Outbound All
    • SG-Web: Inbound 80,443/tcp(Anywhere 또는 ALB), 22/tcp(Bastion만), Outbound All
    • SG-DB: Inbound 3306/tcp(SG-Web만 참조), Outbound All
      (주의: 3306을 CIDR로 열지 말고 보안그룹 참조로 한정)
  • 접속 흐름
    • 운영자 → Bastion(SSH) → Web(SSH)
    • 외부 사용자 → Web(HTTP/HTTPS, Nginx → Gunicorn → Django)
    • Web → DB : 10.x 프라이빗 통신(3306)

2. 데이터베이스 서버(Private, 10.0.x.y) 준비

2.1 MariaDB 설치 및 기동

sudo dnf update -y
sudo dnf install -y mariadb105-server
sudo systemctl enable --now mariadb
sudo mysql_secure_installation

2.2 데이터베이스/계정 생성

sudo mysql -u root -p
CREATE DATABASE simadang CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
CREATE USER 'simadang'@'10.0.%' IDENTIFIED BY '강한비밀번호!';   -- Web 인스턴스 대역 허용
GRANT ALL PRIVILEGES ON simadang.* TO 'simadang'@'10.0.%';
FLUSH PRIVILEGES;
EXIT;

필요시필요시 CloudShell/S3/Scp로 받은 백업을 복원:

sudo mysql -u root -p simadang < /home/ec2-user/madang_YYYYMMDD.sql

3. 웹 서버(퍼블릭, Nginx + Gunicorn + Django)

3.1 시스템 패키지 및 파이썬 가상환경

sudo dnf update -y
sudo dnf install -y nginx python3.11 python3.11-pip python3.11-venv git
python3.11 -m venv ~/venv
source ~/venv/bin/activate
pip install --upgrade pip wheel

3.2 Django 프로젝트 배포(예시)

# (A) 새 프로젝트
pip install django gunicorn mysqlclient
django-admin startproject config ~/app
# 또는 (B) Git에서 가져오기
# git clone <repo> ~/app && cd ~/app && pip install -r requirements.txt

3.2.1 Django DB 설정 (~/app/config/settings.py)

ALLOWED_HOSTS = ["example.com", "웹서버공인IP", "내부도메인"]
STATIC_URL = "/static/"
STATIC_ROOT = "/var/www/static"

DATABASES = {
  "default": {
    "ENGINE": "django.db.backends.mysql",
    "NAME": "simadang",
    "USER": "simadang",
    "PASSWORD": "강한비밀번호!",
    "HOST": "10.0.2.95",   # DB의 프라이빗 IP
    "PORT": "3306",
    "OPTIONS": {"charset": "utf8mb4"},
  }
}

3.2.2 마이그레이션/정적파일

cd ~/app
python manage.py migrate
python manage.py collectstatic --noinput
sudo mkdir -p /var/www/static && sudo chown -R ec2-user:ec2-user /var/www/static

3.3 Gunicorn 서비스(시스템디)

sudo tee /etc/systemd/system/gunicorn.service > /dev/null <<'EOF'
[Unit]
Description=gunicorn daemon
After=network.target

[Service]
User=ec2-user
Group=nginx
WorkingDirectory=/home/ec2-user/app
Environment="PATH=/home/ec2-user/venv/bin"
ExecStart=/home/ec2-user/venv/bin/gunicorn --workers 3 --bind unix:/run/gunicorn.sock config.wsgi:application
RuntimeDirectory=gunicorn
RuntimeDirectoryMode=0755
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now gunicorn
sudo systemctl status gunicorn --no-pager

3.4 Nginx 리버스 프록시

sudo tee /etc/nginx/conf.d/django.conf > /dev/null <<'EOF'
server {
    listen 80;
    server_name _;

    # 정적 파일
    location /static/ {
        alias /var/www/static/;
        access_log off;
        expires max;
    }

    # 애플리케이션
    location / {
        include proxy_params;
        proxy_pass http://unix:/run/gunicorn.sock;
    }
}
EOF

sudo nginx -t && sudo systemctl enable --now nginx

HTTPS(권장): 퍼블릭 도메인이 있다면 ACM+ALB(또는 Nginx certbot)로 TLS 적용

  • 간단: sudo dnf install -y certbot python3-certbot-nginx && sudo certbot --nginx

4. 배스천 호스트(퍼블릭) 역할

  • 운영자 SSH 진입점.
  • 보안그룹에서 22/tcp를 관리자 고정 IP로만 허용.
  • Bastion → Web/DB로의 SSH는 프라이빗 IP 사용.
  • 파일 반입/반출: scp -i <pem> file ec2-user@10.0.x.x:/path/

5. 네트워킹과 보안 체크리스트

  1. 라우팅
  • 퍼블릭 서브넷: 0.0.0.0/0 → IGW
  • 프라이빗 서브넷: IGW 경로 없음, 내부 통신만
  • 필요시 Private의 외부 업데이트는 NAT GW 경유
  1. 보안그룹
  • Web → DB: SG-Web이 SG-DB의 3306에 접근 가능해야 함(보안그룹 참조 방식)
  • 외부 → Web: 80/443만 개방, 22는 관리자만
  1. 서브넷/ENI
  • Web 인스턴스는 퍼블릭 IP/탄력적 IP(EIP) 부여
  • DB 인스턴스는 퍼블릭 IP 없음
  1. 시계/로캘
sudo timedatectl set-timezone Asia/Seoul
  1. 로그/모니터링
  • journalctl -u gunicorn -f, sudo tail -f /var/log/nginx/access.log
  • CloudWatch Agent로 기본 메트릭/로그 적재 권장

6. 운영 편의(권장 사항)

  • 환경변수 분리: .env(SECRET_KEY/DB PW) → django-environ 사용
  • 정적/미디어 외부화: 이미지/첨부물은 S3 + CloudFront(캐시/HTTPS)
  • 마이그레이션 자동화: 배포 스크립트에 migrate/collectstatic 포함
  • 백업: DB 스냅샷 + 주기적 mysqldump(S3 버킷에 버전닝)
  • 무중단 배포: Nginx proxy_next_upstream, Gunicorn --graceful-timeout 조정
  • 방화벽 추가계층: NACL은 기본 허용/허용 규칙, 차단 정책 시 주의

7. 점검용 퀵 테스트

  • Web ↔ DB 포트 점검
# Web 인스턴스에서
nc -zv 10.0.2.95 3306
  • Django DB 연결 테스트
python - <<'PY'
import MySQLdb
conn = MySQLdb.connect(host="10.0.2.95", user="simadang", passwd="강한비밀번호!", db="simadang", charset="utf8mb4")
cur = conn.cursor(); cur.execute("SELECT 1"); print(cur.fetchone()); conn.close()
PY
  • HTTP 확인
    • curl -I http://웹서버공인IP/
    • 정적: http://웹서버공인IP/static/ 에서 200 응답

8. 장애 시 빠른 체크 순서

  1. systemctl status gunicorn → 비정상 시 journalctl -u gunicorn -e
  2. sudo nginx -t → 설정오류 여부, systemctl restart nginx
  3. mysql -h 10.0.2.95 -u simadang -p → 응답/권한/방화벽 확인
  4. SG 규칙: Web→DB 3306 허용(보안그룹 참조 여부)
  5. 서브넷/라우팅: DB는 퍼블릭 라우트 없어야 함(외부 노출 방지)

마무리

위 절차대로 진행하시면 Bastion(관리), Web(Nginx+Gunicorn), DB(MariaDB)같은 VPC 내에서 안전하게 동작하는 프로덕션 기본 구조를 완성할 수 있습니다.

반응형