[Final 17] 서비스 모니터링 기법과 17일간의 로드맵 회고
서비스를 배포한 후 가장 무서운 상황은 "사용자는 안 된다고 하는데, 개발자는 이유를 모를 때"입니다. 이를 방지하기 위해 서버의 심박수를 체크하는 모니터링과 과거의 기록을 살피는 로그 관리가 필수적입니다.
1. 로그 관리의 정석 (Logging)
로그는 서버에서 일어나는 모든 일의 '기록'입니다. Django와 Nginx의 로그를 체계적으로 관리해야 트래픽 급증이나 에러 발생 시 원인을 빠르게 파악할 수 있습니다.
① Django 로깅 설정 (settings.py)
콘솔뿐만 아니라 파일로도 로그를 남기도록 설정합니다.
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/django_error.log',
'maxBytes': 1024 * 1024 * 5, # 5MB
'backupCount': 5,
},
},
'loggers': {
'django': {
'handlers': ['file'],
'level': 'ERROR',
'propagate': True,
},
},
}
② 도커 실시간 로그 확인
컨테이너 환경에서는 아래 명령어가 가장 강력한 무기입니다.
- docker-compose logs -f --tail=100 web: Django 앱의 최근 로그 100줄을 실시간으로 추적합니다.
- docker-compose logs -f nginx: 접속 요청(Access Log)과 서버 에러를 확인합니다.
2. 서버 모니터링 기법
서버가 죽기 전에 미리 징후를 파악하는 것이 중요합니다.
① 리소스 모니터링 (CPU, RAM, Disk)
- htop: 리눅스 터미널에서 CPU와 메모리 점유율을 시각적으로 확인합니다.
- df -h: 디스크 용량을 확인합니다. 로그 파일이 쌓여 용량이 꽉 차면 서버가 멈추므로 주의해야 합니다.
② 외부 모니터링 도구 활용
- UptimeRobot: 5분마다 내 사이트에 접속해보고 응답이 없으면 메일/텔레그램으로 알림을 보내주는 무료 서비스입니다.
- Sentry (에러 트래킹): Django 코드에서 에러가 발생하면 어떤 유저가 어떤 기기에서 에러를 겪었는지 상세히 분석해서 알려줍니다. (실무 필수 도구)
3. 트러블슈팅 체크리스트 (에러 유형별)
| 에러 메시지 | 주요 원인 | 해결 방법 |
| 502 Bad Gateway | Gunicorn이 꺼져 있거나 Nginx 설정 오류 | docker ps로 컨테이너 확인 및 Nginx config 체크 |
| 504 Gateway Timeout | 로직 처리가 너무 오래 걸림 | 쿼리 최적화 또는 Celery(비동기) 도입 검토 |
| 403 Forbidden | 권한 설정 또는 CSRF 토큰 누락 | ALLOWED_HOSTS 및 Django Permission 체크 |
| Connection Refused | DB 서버(RDS) 접속 불가 | 보안 그룹(Security Group) 인바운드 규칙 확인 |
4. 17일간의 로드맵 회고: 우리는 무엇을 배웠는가?
지난 17개의 포스팅을 통해 우리는 한 명의 개발자가 서비스를 세상에 내놓기 위해 필요한 Full-Cycle을 경험했습니다.
- Backend (01~05): Django와 DRF로 탄탄한 비즈니스 로직과 API를 구축했습니다.
- Infrastructure (06~09): Linux와 Docker로 어디서든 돌아가는 컨테이너 환경을 만들었습니다.
- Cloud (10~13): AWS의 넓은 생태계에서 EC2, RDS, Route53을 유기적으로 연결했습니다.
- DevOps (14~17): S3 저장소, HTTPS 보안, CI/CD 자동화와 모니터링으로 서비스의 완성도를 높였습니다.
✍️ 마지막 블로그 포스팅을 마치는 인사말
독자들에게 **"이 17단계는 끝이 아니라 새로운 시작입니다"**라고 격려해 주세요. 이제 여러분은 단순히 코드를 짜는 사람이 아니라, 서비스를 기획하고 운영하며 관리하는 **'엔지니어'**가 되었습니다. 앞으로는 성능 최적화, 마이크로서비스 아키텍처(MSA), 쿠버네티스 등 더 넓은 세상으로 나아가길 응원하며 시리즈를 마무리해 보세요.
vim ~/.bashrc
export AIRFLOW_HOME=/home/ec2-user/airflow
mkdir ~/airflow
pip install apache-airflow==2.11.0
airflow standalone
- airflow 종료 후
cd ~/airflow
ls -al
vim airflow.cfg
43번째 라인
default_timezone = Asia/Seoul
497 라인
sql_alchemy_conn = mysql://airflow:1q2w3e@10.0.0.10:3306/airflow
1425 라인
default_ui_timezone = Asia/Seoul
124 라인
load_examples = False
Host db
HostName 43.201.116.207
User ubuntu
IdentityFile C:/Users/play/.ssh/skn15-scw.pem
sudo mysql -uroot
create database airflow;
create user airflow@'%' identified by '1q2w3e';
grant all privileges on airflow.* to airflow@'%';

추가
sudo dnf install -y mariadb-connector-c-devel gcc
pip install mysqlclient
airflow webserver -p 8080
airflow scheduler
airflow db migrate
airflow users create --username play --firstname seo --lastname woong --role Admin --email afasf@encore.com
mkdir ~/airflow/dags

dag_test.py
from datetime import datetime
from airflow import DAG
from airflow.operators.python import PythonOperator
from pendulum import timezone
kst = timezone("Asia/Seoul")
def hello_world():
print("Hello, Airflow!")
def print_date(execution_date=None, **context):
# execution_date는 논리 실행 시간(Logical Date)
print(f"Execution date: {execution_date}")
with DAG(
dag_id="example_basic_python",
description="A simple DAG with two Python tasks",
start_date=datetime(2023, 1, 1, tzinfo=kst),
schedule_interval="0 9 * * *", # 매일 09:00 KST
catchup=False, # 과거 날짜에 대한 백필 방지
tags=["example", "basic"],
) as dag:
t1 = PythonOperator(
task_id="hello_world",
python_callable=hello_world,
)
t2 = PythonOperator(
task_id="print_date",
python_callable=print_date,
)
t1 >> t2
---
get_stock.py
from airflow import DAG
from airflow.operators.python_operator import PythonOperator
import datetime
import requests
import pandas as pd
args = {
'start_date' : datetime.datetime(2025,9,11),
}
def get_krx():
pass
def get_naver():
pass
dag = DAG(
'stock_crawler',
default_args = args,
description="stock 수집",
schedule_interval= "30 15 * * 1-5",
catchup=False
)
t1 = PythonOperator(
task_id='krx',
python_callable = get_krx,
dag=dag
)
t2 = PythonOperator(
task_id='naver_stock',
python_callable = get_naver,
dag=dag
)
t1 >> t2
sudo dnf install -y mariadb-connector-c-devel gcc
pip install mysqlclient
형제관계여서 오류 x?
import sqlalchemy
user = 'play'
password = '1q2w3e4r!'
port = 3306
host="10.0.0.10"
engine = sqlalchemy.create_engine(f"mysql://{user}:{password}@{host}:{port}")
'웹 > 배포' 카테고리의 다른 글
| 프로젝트 정리 (0) | 2025.09.15 |
|---|---|
| [Final 16] 코드만 push하세요, 배포는 GitHub Actions가 알아서 합니다 (0) | 2025.09.11 |
| [Final 15] 아직도 HTTP? Certbot으로 5분 만에 HTTPS 적용하기 (0) | 2025.09.10 |
| [Final 14] 서버 용량 걱정 끝! S3로 Static & Media 파일 관리하기 (0) | 2025.09.10 |
| [Cloud 13] Nginx 리버스 프록시 설정으로 서버 트래픽 안정화하기 (1) | 2025.09.08 |