M11 ‐ 배포 이후 이슈 리포트 작성 - 100-hours-a-week/KTB4-4th-wiki GitHub Wiki
1. 개요
V1 배포 이후 실제 운영 환경에서 CI/CD, Container Runtime, 환경변수 전달, Nginx, Monitoring, 운영 DB 접근과 관련된 여러 이슈가 발생하였다.
각 이슈에 대해 단순히 오류를 해결하는 데 그치지 않고 로그와 시스템 상태를 기반으로 문제가 발생한 계층을 확인하고 원인을 좁힌 뒤, 재발 방지를 위한 설정 및 모니터링 개선을 함께 수행하였다.
발생한 주요 이슈는 다음과 같다.
| 날짜 | 이슈 | 구분 | 주요 원인 |
|---|---|---|---|
| 2026-09-22 | ECR Docker Image Push 권한 실패 | CI/CD / IAM | IAM Policy의 ECR Resource ARN 불일치 |
| 2026-09-25 | FE Docker Image 런타임 설정 누락 | Container | Runtime Stage에 next.config.ts 누락 |
| 2026-09-25 | Backend 환경변수 전달 실패 | 배포 / 환경변수 | Docker Compose 환경변수 매핑 누락 |
| 2026-09-29 | Nginx 500 오류 | Traffic / Nginx | 순간적인 요청 폭주로 Worker Connection 고갈 |
| 2026-09-30 | HSTS Header 누락 | Security / Nginx | HTTPS Server Block에 HSTS 설정 누락 |
| 2026-10-03 | 운영 DB 접근 인증 오류 | DB / SSM | AWS IAM 인증과 MySQL 인증 체계 혼동 |
2. ECR Docker Image Push 권한 실패
2.1 현상
GitHub Actions에서 AWS OIDC 인증과 ECR Login까지는 정상적으로 수행되었지만, Docker Image Push 단계에서 다음 오류가 발생하였다.
denied: User: arn:aws:sts::12345:assumed-role/NeedU-GitHubActions-CI/GitHubActions
is not authorized to perform:
ecr:InitiateLayerUpload
on resource:
arn:aws:ecr:ap-northeast-2:12345:repository/need-u/backend
2.2 원인
NeedU-GitHubActions-CI Role의 Policy에는 ECR Push에 필요한 Action이 존재했기 때문에 처음에는 Action 권한 자체의 문제로 볼 수 있었다.
하지만 Policy의 Resource ARN을 실제 Repository와 비교한 결과 Repository 이름이 일치하지 않았다.
기존 Policy:
repository/needu-backend
repository/needu-frontend
repository/needu-ai
실제 ECR Repository:
need-u/backend
need-u/frontend
need-u/ai
따라서 ecr:InitiateLayerUpload Action 자체는 허용되어 있었지만, 실제 Push 대상 Repository에는 해당 권한이 적용되지 않고 있었다.
2.3 해결
IAM Policy의 Resource ARN을 실제 ECR Repository 경로와 일치하도록 수정하였다.
"Resource": [
"arn:aws:ecr:ap-northeast-2:12345:repository/need-u/backend",
"arn:aws:ecr:ap-northeast-2:12345:repository/need-u/frontend",
"arn:aws:ecr:ap-northeast-2:12345:repository/need-u/ai"
]
ECR Push에 필요한 다음 Action은 그대로 유지하였다.
ecr:BatchCheckLayerAvailability
ecr:BatchGetImage
ecr:CompleteLayerUpload
ecr:InitiateLayerUpload
ecr:PutImage
ecr:UploadLayerPart
Policy 수정 후 GitHub Actions를 다시 실행하여 Docker Image가 ECR에 정상적으로 Push되는 것을 확인하였다.
2.4 개선점
IAM 권한 오류를 확인할 때는 특정 Action의 허용 여부뿐만 아니라 해당 Action이 어느 Resource에 적용되고 있는지 함께 확인해야 한다.
특히 ECR Repository 이름에 /가 포함되는 경우 IAM Resource ARN에도 실제 Repository 경로가 동일하게 반영되어야 한다.
3. FE Docker Image 런타임 설정 파일 누락
3.1 현상
Docker Image로 실행한 Frontend에서 Kakao Profile Image가 정상적으로 로드되지 않고 400 Bad Request가 발생하였다.
동일 기능이 로컬 환경에서는 정상적으로 동작했지만 Docker 환경에서는 Kakao Image Domain 설정이 적용되지 않았다.
3.2 원인
next.config.ts에서는 Kakao CDN Image Domain을 remotePatterns로 허용하고 있었다.
하지만 Dockerfile의 Runtime Stage에는 next.config.ts가 포함되지 않아 Container 실행 환경에서 필요한 설정이 적용되지 않았다.
즉, 로컬 실행 환경과 실제 Docker Runtime에 포함된 파일의 차이로 인해 발생한 문제였다.
3.3 해결
Dockerfile의 Runtime Stage에 next.config.ts를 포함하도록 수정하였다.
COPY --from=builder /app/next.config.ts ./next.config.ts
Docker Image를 다시 Build하고 배포한 후 Kakao Profile Image가 정상적으로 로드되는 것을 확인하였다.
3.4 개선점
Docker Multi-Stage Build를 사용할 경우 Build 성공 여부뿐만 아니라 최종 Runtime Image에 실제 실행에 필요한 파일이 모두 포함되어 있는지 확인해야 한다.
특히 Framework 설정 파일처럼 Build 결과물 외에 Runtime에서 필요한 파일이 존재하는 경우 Stage별 파일 구성을 확인할 필요가 있다.
4. SSM 환경변수의 Backend Container 전달 실패
4.1 현상
Backend에서 AI Server와 통신하는 과정에서 AICHAT_SERVER_UNAVAILABLE 예외가 반복적으로 발생하였다.
AI Server Mock 기능을 사용하기 위해 SSM Parameter Store에 다음 환경변수를 등록했지만 Backend Container에서는 해당 값을 확인할 수 없었다.
AI_SERVER_MOCK_ENABLED=true
4.2 원인
환경변수 전달 경로를 단계별로 확인하였다.
SSM Parameter Store
↓
deploy-backend.sh
↓
Docker Compose
↓
Backend Container
SSM Parameter Store에는 값이 정상적으로 존재했고 deploy-backend.sh에서도 /needu/prod/backend의 Parameter를 조회하여 환경변수로 export하고 있었다.
하지만 compose.yml의 Backend environment에 AI_SERVER_MOCK_ENABLED가 존재하지 않았다.
따라서 SSM에서 읽은 환경변수가 Host에는 존재했지만 Docker Compose를 통해 Container 내부로 전달되지 않은 것이 원인이었다.
4.3 해결
compose.yml의 Backend 환경변수에 다음 항목을 추가하였다.
environment:
AI_SERVER_BASE_URL: ${AI_SERVER_BASE_URL}
AI_SERVICE_TOKEN: ${AI_SERVICE_TOKEN}
AI_SERVER_MOCK_ENABLED: ${AI_SERVER_MOCK_ENABLED}
이후 기존 deploy-backend.sh를 통해 Backend Container를 재생성하고 Container 내부에서 값이 정상적으로 전달되는 것을 확인하였다.
4.4 개선점
현재 배포 환경에서 환경변수는 다음 경로를 통해 전달된다.
SSM
↓
Deployment Script
↓
Docker Compose
↓
Container
↓
Application
따라서 새로운 환경변수를 추가할 때는 SSM 등록 여부만 확인하는 것이 아니라 환경변수가 전체 전달 경로를 통과하는지 단계별로 확인해야 한다.
현재 deploy-backend.sh가 SSM 값을 자동으로 불러오므로 새로운 변수마다 배포 스크립트를 수정할 필요는 없지만, 해당 변수를 사용하는 Compose Service의 environment에는 명시적인 매핑이 필요하다.
5. 비정상 대량 요청으로 인한 Nginx Worker Connection 고갈 및 보호 정책 개선
5.1 장애 발생
2026년 9월 29일 21시경 특정 클라이언트에서 /api/v1/auth/csrf Endpoint를 대상으로 비정상적인 대량 요청이 발생하였다.
Nginx Access Log를 분석한 결과 동일 IP에서 21:12~21:16 사이 총 89,587건의 요청이 발생하였다.
특히 21:14에는 1분 동안 69,375건의 요청이 집중되었으며, 최대 요청량은 1,773 req/s까지 증가하였다.
| 항목 | 내용 |
|---|---|
| 발생 시간 | 2026-09-29 21:12 ~ 21:16 KST |
| 대상 API | GET /api/v1/auth/csrf |
| 총 요청량 | 89,587건 |
| 최대 분당 요청량 | 69,375건 |
| 최대 초당 요청량 | 1,773 req/s |
| Nginx 500 | 742건 |
| Worker Connection 오류 | 6,564건 |
장애 시간대 Nginx Error Log에서는 다음 오류가 반복적으로 발생하였다.
worker_connections are not enough while connecting to upstream
5.2 원인 분석
장애 발생 당시 요청은 /api/v1/auth/csrf에 집중되어 있었기 때문에 처음에는 Backend의 CSRF 처리 과정에서 발생한 문제일 가능성도 확인하였다.
그러나 동일 시간대 Backend Log에서는 Nginx에서 발생한 500 요청에 대응하는 Application Exception이 확인되지 않았다.
반면 Nginx Error Log에서는 Backend Upstream으로 연결하는 과정에서 worker_connections are not enough 오류가 반복적으로 발생하였다.
당시 Nginx 설정은 다음과 같았다.
worker_processes auto;
events {
worker_connections 768;
}
Nginx가 Reverse Proxy로 동작할 때는 Client Connection뿐만 아니라 Backend Upstream Connection에도 Connection 자원이 사용된다.
비정상적인 요청이 최대 1,773 req/s까지 증가하면서 사용 가능한 Worker Connection이 고갈되었고, Nginx가 Backend의 127.0.0.1:8080으로 새로운 Upstream Connection을 생성하지 못하였다.
/api/v1/auth/csrf 비정상 대량 요청
↓
최대 1,773 req/s
↓
Nginx Connection 사용량 증가
↓
Worker Connection 고갈
↓
Backend Upstream 연결 생성 실패
↓
HTTP 500 발생
따라서 이번 장애의 직접적인 원인은 Spring Boot의 CSRF 처리 로직이 아니라 비정상적인 대량 요청으로 인한 Nginx Worker Connection 고갈로 판단하였다.
또한 하나의 Client가 Nginx의 Connection 자원을 과도하게 사용할 수 있었던 원인으로 다음 문제를 확인하였다.
/api/에 대한 기본 Rate Limit 부재/api/v1/auth/csrf에 대한 별도 요청 제한 부재worker_connections가 768로 설정- Nginx Request / Connection 관련 Monitoring 및 Alarm 부족
특정 API를 대상으로 한 요청이 공유 자원인 Nginx Connection을 고갈시키면서 다른 정상 API 요청까지 영향을 받을 수 있는 구조였다.
5.3 개선
단순히 worker_connections 값만 증가시키는 경우 더 많은 Connection을 처리할 수는 있지만 비정상적인 대량 요청 자체를 제한하지 못한다.
따라서 Connection 처리 한도 개선 + Rate Limit + Monitoring을 함께 적용하였다.
Nginx Connection 설정 개선
worker_connections를 기존 768에서 4096으로 상향하였다.
events {
worker_connections 4096;
}
또한 Nginx Worker가 사용할 수 있는 File Descriptor 한도를 명시하였다.
worker_rlimit_nofile 65535;
API 기본 Rate Limit 적용
전체 /api/ 요청에 동일 IP 기준 기본 Rate Limit을 적용하였다.
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
limit_req zone=api burst=20 nodelay;
정상적인 순간 요청은 burst 범위에서 허용하되, 이를 초과하는 요청은 Backend까지 전달하지 않고 Nginx에서 제한하도록 하였다.
CSRF Endpoint 별도 Rate Limit 적용
장애 당시 집중적으로 호출된 /api/v1/auth/csrf에는 일반 API보다 낮은 별도 Rate Limit을 적용하였다.
limit_req_zone $binary_remote_addr zone=csrf:10m rate=2r/s;
limit_req zone=csrf burst=5 nodelay;
Rate Limit을 초과한 요청에는 다음과 같이 응답하도록 구성하였다.
429 Too Many Requests
따라서 비정상적인 요청이 발생하더라도 Backend까지 모든 요청을 전달하지 않고 Nginx 계층에서 먼저 제한하도록 변경하였다.
5.4 Monitoring 개선
기존 Monitoring은 EC2 CPU, Memory, Disk 및 Service Health 중심으로 구성되어 있어 Nginx 계층에서 발생하는 요청 폭주와 Connection 고갈을 즉시 확인하기 어려웠다.
이에 따라 Nginx Log를 기반으로 다음 Metric과 Alarm을 추가하였다.
- Nginx Request Count
- Nginx 4xx
- Nginx 5xx
- Nginx Worker Connection 부족
- Backend ERROR Log
- 요청량 급증 감지
- 4xx / 5xx 증가 감지
- Worker Connection 부족 감지
CloudWatch Alarm은 Discord Webhook과 연동하여 Alarm 발생 시 운영자가 바로 확인할 수 있도록 하였다.
Discord 알림에는 다음 정보를 포함하였다.
- 발생한 Alarm
- 현재 상태
- 대응 가이드
- CloudWatch Alarm Link
- CloudWatch Dashboard Link
이를 통해 기존의 서버 자원 중심 Monitoring에서 실제 요청 처리 상태를 함께 관측하는 구조로 개선하였다.
5.5 동일 유형 요청 재발 및 개선 효과 확인
2026년 10월 1일 15시경 9월 29일과 동일한 IP에서 다시 비정상적인 대량 요청이 발생하였다.
이번에는 /api/v1/auth/csrf뿐만 아니라 /api/v1/users/48/gift-recommendations?size=20에도 요청이 집중되었다.
특히 CSRF 요청은 최대 5,141 req/s까지 증가하였다.
이는 9월 29일 장애 당시 최대 1,773 req/s보다 약 2.9배 높은 순간 요청량이었다.
그러나 9월 29일 이후 적용한 Nginx Rate Limit이 동작하면서 대부분의 초과 요청이 Backend에 전달되기 전에 429 Too Many Requests로 제한되었다.
15:04~15:16 분석 구간에서 확인된 결과는 다음과 같다.
| Endpoint | 200 | 429 |
|---|---|---|
/api/v1/auth/csrf |
994 | 568,215 |
/api/v1/users/48/gift-recommendations?size=20 |
466 | 412,562 |
해당 구간에서 총 980,785건의 429 응답이 발생하였다.
외부 요청
│
│ 최대 5,141 req/s
▼
Nginx
│
├── Rate Limit 초과
│ ↓
│ 429 반환
│
└── 허용 범위 요청
↓
Backend
즉 외부 요청 자체는 Nginx까지 도달했지만, Rate Limit을 초과한 대량 요청은 Backend의 정상 API 요청으로 처리되지 않고 Nginx 단계에서 제한되었다.
5.6 개선 전후 비교
개선 전
개선 후
| 구분 | 9/29 장애 당시 | 10/01 개선 후 |
|---|---|---|
| 최대 확인 요청량 | 1,773 req/s | 5,141 req/s |
worker_connections |
768 | 4096 |
worker_rlimit_nofile |
별도 설정 없음 | 65535 |
| 일반 API Rate Limit | 없음 | 10 req/s/IP |
| CSRF Rate Limit | 없음 | 2 req/s/IP |
| 초과 요청 처리 | 별도 제한 없음 | 429 반환 |
| Worker Connection 고갈 | 발생 | 확인되지 않음 |
| Access Log 기준 5xx | 발생 | 확인되지 않음 |
| 요청량 / 4xx Alarm | 부족 | 정상 발생 |
개선 전에는 다음과 같은 흐름으로 장애가 발생하였다.
[9/29]
최대 1,773 req/s
↓
Rate Limit 없음
↓
Worker Connection 고갈
↓
Backend Upstream 연결 실패
↓
HTTP 500
개선 후에는 더 높은 요청량에서도 다음과 같이 동작하였다.
[10/01]
최대 5,141 req/s
↓
Nginx Rate Limit
↓
초과 요청 429 반환
↓
Backend 유입 제한
↓
Worker Connection 고갈 미확인
↓
Access Log 기준 5xx 미확인
또한 추가한 NginxRequestCount, Nginx4xxCount 관련 CloudWatch Alarm도 실제 요청 폭주 시점에 발생하고 요청 감소 이후 ALARM → OK로 정상 복구되는 것을 확인하였다.
이를 통해 9월 29일 장애 이후 적용한 Traffic 보호 정책과 Monitoring 정책이 실제 비정상 트래픽 상황에서 동작하는 것을 확인하였다.
6. HSTS Header 누락 및 Nginx 설정 충돌
6.1 현상
운영 서비스의 HTTPS Response Header를 확인한 결과 Strict-Transport-Security Header가 설정되어 있지 않았다.
curl -I https://needu.gift/login
HTTP/1.1 200 OK
Server: nginx/1.28.3 (Ubuntu)
X-Powered-By: Next.js
...
또한 Nginx 설정을 확인하는 과정에서 다음 경고가 발생하는 것을 확인하였다.
conflicting server name "needu.gift" on 0.0.0.0:443, ignored
conflicting server name "needu.gift" on 0.0.0.0:80, ignored
6.2 원인
Nginx의 HTTPS server Block에 Strict-Transport-Security 설정이 존재하지 않았다.
추가로 /etc/nginx/sites-enabled/에 실제 운영 설정 파일과 백업 파일이 함께 존재하였다.
needu
needu.bak
Nginx에서는 다음 설정으로 sites-enabled의 모든 파일을 읽고 있었다.
include /etc/nginx/sites-enabled/*;
따라서 needu.bak 역시 실제 Nginx 설정으로 로드되면서 동일한 needu.gift Server Block이 중복 정의되고 있었다.
6.3 해결
먼저 Backup 설정을 sites-enabled 외부로 이동하였다.
sudo mkdir -p /etc/nginx/backup
sudo mv /etc/nginx/sites-enabled/needu.bak /etc/nginx/backup/
이후 실제 운영 설정의 HTTPS Server Block에 HSTS Header를 추가하였다.
server {
listen 443 ssl;
server_name needu.gift;
add_header Strict-Transport-Security ...;
...
}
설정 변경 후 다음 순서로 검증 및 반영하였다.
sudo nginx -t
sudo systemctl reload nginx
마지막으로 외부에서 다시 Response Header를 조회하여 HSTS가 정상적으로 포함되는 것을 확인하였다.
curl -I https://needu.gift/login
HTTP/1.1 200 OK
...
Strict-Transport-Security: ...
6.4 개선점
HTTPS 인증서를 적용하는 것만으로 Web Security 설정이 모두 완료되는 것은 아니며 실제 Response Header 수준의 보안 설정도 별도로 확인할 필요가 있다.
또한 sites-enabled/*와 같이 Wildcard를 사용하는 경우 Backup 파일도 Nginx가 읽을 수 있기 때문에 운영 설정 Directory와 Backup Directory를 분리해야 한다.
Nginx 설정 변경 시에는 즉시 Reload하지 않고 nginx -t를 통해 문법 및 설정 충돌을 먼저 검증하도록 한다.
7. SSM Port Forwarding 운영 DB 접근 인증 오류
7.1 현상
운영 MySQL의 3306 Port를 외부에 직접 노출하지 않고 Backend 팀원이 DB를 조회할 수 있도록 AWS SSM Port Forwarding 방식으로 접근 환경을 구성하였다.
SSM Session 자체는 정상적으로 연결되었지만 Local 환경에서 MySQL 접속을 시도하면 다음 인증 오류가 발생하였다.
ERROR: Access denied for user
7.2 원인
먼저 AWS IAM User 인증과 SSM Session 상태를 확인하였다.
SSM Session에서 Connection accepted가 확인되었기 때문에 다음 Network 경로는 정상적으로 구성된 상태였다.
Local 127.0.0.1:13306
↓
AWS SSM Port Forwarding
↓
EC2 127.0.0.1:3306
↓
MySQL
따라서 문제 범위를 SSM 연결이 아닌 MySQL 인증 단계로 좁혔다.
확인 결과 SSM 접근에 사용하는 AWS IAM User와 실제 Database에 접근하기 위한 MySQL User를 동일한 인증 체계로 잘못 이해한 것이 원인이었다.
두 인증의 역할은 다음과 같이 구분된다.
| 인증 | 역할 |
|---|---|
| AWS IAM User | SSM Session 생성 권한 확인 |
| AWS CLI Profile | IAM Credential을 이용한 AWS 접근 |
| MySQL User | MySQL 접속 및 DB 권한 확인 |
7.3 해결
AWS 접근과 Database 접근에 사용하는 인증을 분리하였다.
팀원별 IAM User
↓
팀원별 AWS CLI Profile
↓
AWS SSM Port Forwarding
↓
EC2 MySQL :3306
↓
조회 전용 MySQL User
각 팀원은 자신의 IAM User로 구성한 AWS CLI Profile을 이용하여 SSM Port Forwarding Session을 생성한다.
이후 별도의 Terminal에서 조회 전용 MySQL 계정을 이용하여 Database에 접속하도록 구성하였다.
운영 MySQL의 3306 Port를 외부에 직접 공개하지 않고 SSM을 통해 접근하며, Database에서는 SELECT만 가능한 조회용 계정을 사용하도록 하였다.
7.4 개선점
SSM Port Forwarding이 성공했다는 것은 MySQL 인증까지 완료되었다는 의미가 아니다.
운영 DB 접근 과정은 다음과 같이 각 단계를 독립적으로 확인해야 한다.
Network 접근
↓
AWS IAM 인증
↓
SSM Session
↓
MySQL 인증
↓
Database 권한
장애 발생 시 최종 오류 메시지만 확인하는 것이 아니라 요청이 어느 단계까지 정상적으로 전달되었는지를 확인하여 문제 범위를 좁히는 방식이 필요하다.
8. 종합 분석
배포 이후 발생한 이슈를 분석한 결과 문제는 특정 계층에 집중되지 않고 CI/CD부터 Runtime, Network, Monitoring, Security, Database 접근까지 다양한 영역에서 발생하였다.
| 영역 | 발생한 문제 | 개선 내용 |
|---|---|---|
| IAM / CI/CD | ECR Resource ARN 불일치 | 실제 Resource 기준 IAM Policy 검증 |
| Container | Runtime 설정 파일 누락 | Runtime Image 구성 확인 |
| Configuration | 환경변수 전달 누락 | SSM → Script → Compose → Container 전달 경로 확인 |
| Nginx / Traffic | Worker Connection 고갈 | Connection 조정 + Rate Limit |
| Monitoring | Nginx 장애 감지 부족 | Log 기반 Metric / Alarm 추가 |
| Security | HSTS 누락 | HTTPS Response Header 보강 |
| Nginx Configuration | Backup 설정 중복 Load | 운영 / Backup 설정 Directory 분리 |
| DB Access | IAM과 MySQL 인증 혼동 | Network / IAM / DB 인증 단계 분리 |
특히 여러 이슈에서 공통적으로 확인한 점은 오류가 발생한 최종 지점만 보는 것이 아니라 요청과 설정이 전달되는 전체 경로를 단계별로 확인해야 한다는 것이다.
예를 들어 환경변수 문제는 다음과 같이 확인하였다.
SSM
↓
Deployment Script
↓
Docker Compose
↓
Container
↓
Application
DB 접근 문제는 다음과 같이 확인하였다.
Local
↓
AWS IAM
↓
SSM
↓
EC2
↓
MySQL Authentication
↓
Database Permission
Nginx 장애 역시 다음과 같이 요청 경로를 확인하여 Backend가 아닌 Nginx 계층에서 문제가 발생한 것을 확인하였다.
Client
↓
Nginx
X
Backend
이를 통해 운영 장애 분석 시 전체 요청 및 배포 경로를 계층별로 분리하고, 로그와 상태를 이용하여 정상 동작이 확인된 구간부터 하나씩 제외하면서 문제 범위를 좁히는 방식이 효과적임을 확인하였다.
9. 운영 개선 사항
배포 이후 발생한 이슈를 기반으로 다음과 같이 운영 환경을 개선하였다.
CI/CD
- IAM Policy의 Action뿐만 아니라 Resource ARN까지 함께 검증
- 실제 ECR Repository 경로와 IAM Resource 일치 여부 확인
Container / Configuration
- Docker Runtime Stage에 필요한 설정 파일 확인
- 신규 환경변수 추가 시 전체 전달 경로 확인
SSM → Deployment Script → Docker Compose → Container
Monitoring
- Nginx Request / 4xx / 5xx Metric 추가
- Nginx Worker Connection 부족 감지
- Backend ERROR Log 감지
- CloudWatch Alarm 구성
- Discord Webhook을 통한 장애 알림 및 대응 가이드 제공
Nginx
- Worker Connection 허용량 조정
- File Descriptor 한도 명시
- API Rate Limit 적용
- HSTS Header 적용
- 운영 설정과 Backup 설정 파일 분리
- 설정 변경 시
nginx -t검증 후 Reload
운영 DB 접근
- MySQL Port 외부 미노출
- SSM Port Forwarding을 통한 접근
- 팀원별 IAM User 사용
- 조회 전용 MySQL User 사용
- AWS 인증과 Database 인증 분리
10. 회고
V1 배포 이후 발생한 이슈를 통해 개발 환경에서 정상적으로 동작하는 것과 실제 운영 환경에서 안정적으로 동작하는 것은 별개의 문제임을 확인하였다.
CI/CD에서는 IAM Action이 존재하더라도 Resource가 정확하지 않으면 권한이 적용되지 않았고, Container 환경에서는 Local에 존재하는 설정 파일이나 환경변수가 실제 Runtime Container까지 전달되는지를 별도로 확인해야 했다.
또한 운영 중 발생한 대량 요청 문제를 통해 Application뿐만 아니라 Nginx와 같은 요청 진입 계층의 상태도 함께 관측해야 한다는 점을 확인하였다. 이에 따라 Nginx Log 기반 Metric과 Alarm을 추가하고 Rate Limit을 적용하여 장애 감지와 방어 체계를 보강하였다.
운영 DB 접근 환경을 구성하는 과정에서는 Network 연결, AWS IAM 인증, SSM Session, MySQL 인증과 Database 권한이 각각 독립적인 계층이라는 점을 확인하였다.
결과적으로 배포 이후 발생한 이슈를 해결하는 과정에서 문제가 발생한 계층을 추측하기보다 전체 요청 및 설정 전달 경로를 나누고, 로그와 실제 상태를 통해 정상 구간을 하나씩 검증하면서 원인을 좁히는 방식을 운영 장애 대응의 기본 원칙으로 정리할 수 있었다.