HandDoc 졸업프로젝트에서 평소 건드려보고 싶었던 배포를 맡았다 !!
우선 FastAPI 레포, BE 배포를 진행했다. (가능하면 UMC 경험을 살려 FE 배포도 맡을 예정~!)
일단 최대한 서버 한 대로 버텨보자는 생각에 EC2 t3.micro 한 대에 Nginx 프록시로 라우팅만 잡아 /api → Spring(8080), /ai(/ws) → FastAPI(8000) 구조로 가기로 했다. DB는 MongoDB Atlas로 분리했다.
🎈깃허브 : https://github.com/3-NoPainNoGain
[폴더구조]
/home/ec2-user/
└── apps/
└── .env # Spring에서 사용할 환경 변수 모음
└── deploy.sh # 블루/그린 무중단 배포를 위한 스크립트
└── docker-compose.spring-blue.yml # 블루 컨테이너
└── docker-compose.spring-green.yml # 그린 컨테이너
└── docker-compose.fastapi.yml # fastapi 서비스 컨테이너
/etc/nginx/conf.d # 리버스 프록시 설정
└── handdoc.conf
└── active-upstream.conf # deploy.sh에서 배포 성공 시에 포트 교체하고 nginx reload
처음 AWS배포를 진행해보는 분들께도 도움을 드릴 수 있도록 과정을 최대한 자세히 써 보겠다.
1. AWS 콘솔 설정
먼저 Google에 AWS 콘솔을 검색하여 들어간다.
ㄴ자세한 aws 콘솔 가입 및 설정 과정은 이미 다른 블로그에 많으니 참고하시면 되어요~
1) AWS 콘솔 회원가입 & 비용 알람
- 계정 만들고 결제카드 등록까지 했다. (루트 계정으로 로그인)
- 바로 Billing → Budgets에서 월 사용액 알람 만들어두었다. 잘못되어 과금되면 안 되니까..
내 계정은 t3.micro가 프리티어였다. (t2는 유료였음!!)
2) IAM 사용자 만들기 (루트 로그인 막기)
루트로 계속 작업하면 위험하기 때문에
- IAM 사용자를 만들고 EC2·VPC 필요한 권한만 부여.
- 앞으로는 이 사용자로 콘솔/CLI 에 들어가기로 한다. (루트는 결제/보안만)
3) 리전 고정
서울( ap-northeast-2 )로 고정했다. 리전이 달라지면 EIP/EC2가 달라서 헷갈릴 수도 있다!
4) EC2 만들기 & EIP 연결
- 인스턴스: t3.micro, AMI: Amazon Linux 2023
- 키 페어 생성: handdoc-key.pem (로컬 보관)
- 탄력적 IP(EIP) 할당 및 인스턴스에 연결(Associate)
- (확인) 인스턴스 내부에서 IMDSv2로 공인 IP 조회:
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/public-ipv4
# 결과: 43.200.110.36
트러블슈팅: SG ID 404
- 처음에 SG ID를 /latest/meta-data/security-group-ids로 치다가 404 받음 !! (경로 틀림)
- MAC 경로로 수정해서 해결:
MAC=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/network/interfaces/macs/ | head -n1)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/network/interfaces/macs/${MAC}security-group-ids
# 결과 예: sg-0850234c0d72f2d0e
2. Windows에서 SSH 접속(권한 문제/타임아웃 대응)
pem 권한
$KEY="C:\Users\<me>\Desktop\handdoc-key.pem"
icacls $KEY /inheritance:r
icacls $KEY /grant:r "$($env:USERNAME):R"
접속
ssh -i "C:\Users\<me>\Desktop\handdoc-key.pem" ec2-user@43.200.110.36
트러블슈팅 1: Connection timed out
- SG의 22/tcp가 내 IP로 열려 있지 않았음 → 내 현재 IP/32로 열고 해결.
- 사무실/카페 네트워크에서 막힐 때는 테더링으로 테스트하면 된다고 하네요!
트러블슈팅 2: 호스트 키 경고
- 첫 접속 시 fingerprint 확인 메시지 → yes 입력 후 정상 접속.
3. 기본 OS 설정
sudo timedatectl set-timezone Asia/Seoul
sudo dnf update -y
sudo dnf install -y git unzip htop
4. Docker & Compose 설치 (AL2023 고생포인트..)
잘못된 시도(실패)
- docker-ce CentOS 리포를 붙여보니 404 / 패키지 미존재:
- docker-ce.repo 추가 → Failed to download metadata for repo 'docker-ce-stable'
- docker-compose-plugin No match 오류
해결
- Amazon Linux 2023에서는 기본 docker 패키지로 설치:
sudo dnf install -y docker
sudo systemctl enable --now containerd docker
sudo usermod -aG docker ec2-user && newgrp docker
docker version
docker run --rm hello-world # 성공
docker compose version # v2.27.0 확인
교훈: AL2023에 docker-ce 리포 붙이지 말기. 기본 repo의 docker 패키지로 충분하다.
5. 앱 디렉토리 & .env
mkdir -p ~/apps/handdoc/{be,fastapi,nginx,logs}
cd ~/apps/handdoc
JWT=$(openssl rand -hex 32)
cat > .env <<EOF
SPRING_PROFILES_ACTIVE=prod
ALLOWED_ORIGINS=http://localhost:5173
MONGODB_URI=mongodb+srv://<DB_USER>:<DB_PASS>@<CLUSTER_HOST>/handdoc?retryWrites=true&w=majority
SERVER_PORT=8080
JWT_SECRET=$JWT
AI_PORT=8000
EOF
chmod 600 .env
트러블슈팅: .env에 이상한 텍스트 섞임
- 터미널 붙여넣다가 명령어 조각이 .env 마지막 줄에 붙음.
- 값만 뽑아서 깨끗하게 재작성:
SPR=$(grep -m1 '^SPRING_PROFILES_ACTIVE=' .env | cut -d= -f2-)
ALLOWED=$(grep -m1 '^ALLOWED_ORIGINS=' .env | cut -d= -f2-)
MONGO=$(grep -m1 '^MONGODB_URI=' .env | cut -d= -f2-)
SRV=$(grep -m1 '^SERVER_PORT=' .env | cut -d= -f2-)
JWT=$(grep -m1 '^JWT_SECRET=' .env | cut -d= -f2-)
cat > .env.clean <<EOF
SPRING_PROFILES_ACTIVE=$SPR
ALLOWED_ORIGINS=$ALLOWED
MONGODB_URI=$MONGO
SERVER_PORT=$SRV
JWT_SECRET=$JWT
AI_PORT=8000
EOF
mv -f .env.clean .env && chmod 600 .env
sed -n '1,200p' .env
# 최종 .env 6개 키만 깔끔히 확인
현재 .env는 정상이며 깨끗하다. MONGODB_URI는 Atlas URI로 교체 필요(아직 placeholder).
6. Nginx(호스트) 설치 & 프록시 설정
설치 & 기동
sudo dnf install -y nginx
sudo systemctl enable --now nginx
프록시 설정 파일 존재
- /etc/nginx/conf.d/handdoc.conf 존재 확인(본인이 생성)
- 핵심 라우팅 라인 확인:
location /api/ { proxy_pass http://127.0.0.1:8080/; ... }
location /ai/ { proxy_pass http://127.0.0.1:8000/; ... }
# WebSocket 업그레이드 관련 헤더 라인도 들어 있음
문법 테스트:
sudo nginx -t
트러블슈팅: conflicting server name "_" on 0.0.0.0:80
- nginx -t에서 경고 확인.
- 원인: 기본 server가 어딘가에 하나 더 있어서 _가 중복되었다.
- 해결:
- /etc/nginx/conf.d/default.conf 있으면 비활성화
- /etc/nginx/nginx.conf 안의 기본 server 블록 주석
- 이후 sudo nginx -t && sudo systemctl reload nginx
7. 포트/서비스 상태 확인
# 80만 리슨 중(정상). 8080/8000은 컨테이너 기동 전이라 없음.
sudo ss -ltnp | grep -E ':80|:8080|:8000' || echo "리슨 없음"
# nginx 상태
systemctl is-active nginx && systemctl is-enabled nginx
sudo nginx -t
8. 보안그룹(의도)
- 22/tcp → 내 IP/32 (팀원 접속 시 그 사람 IP/32 추가)
- 80/tcp → 0.0.0.0/0
- 443/tcp → 필요 시(도메인+SSL)
- 8080/8000은 외부 개방 안 함 (컨테이너는 127.0.0.1 바인딩 예정)
내가 맡은 부분은 여기까지이다. 나머지는 백엔드 api를 작성한 팀원이 담당하기로 했다.
AWS 배포 인프라의 앞부분을 맡으며 얻은 교훈
- AL2023에서는 docker-ce 리포 붙이지 말기
- 404/No match로 시간 날림..ㅠ 기본 docker 패키지로 가자.
- .env 붙여넣기 오염 주의
- 값만 추출해 새 파일로 재작성하는 스니펫이 유용하다고 한다!
- NGINX server_name _ 충돌 주의.
- default/welcome 비활성화 + 기본 server 주석처리로 해결
- NGINX에서 server_name _; 설정이 있으면 기본 서버(default server)처럼 동작하는데, 이게 여러 군데 선언되어 있으면 충돌이 난다. 그래서 기본 server 블록을 주석 처리하거나 welcome page(디폴트 페이지)를 꺼야 합니당!
- default/welcome 비활성화 + 기본 server 주석처리로 해결
- 보안그룹 22/tcp는 내 IP/32만!
- 회사/카페 방화벽 이슈 있으면 테더링으로 먼저 점검해보기~
- IMDSv2 메타데이터 경로는 자주 틀린다
- SG ID는 NIC(MAC) 하위 경로에서 가져와야 한다.
[팀원을 위한 최종 핸드오버 문서]
# HandDoc 인프라 핸드오버
[서버]
- EIP: 43.200.110.36
- User: ec2-user
- OS/Spec: Amazon Linux 2023 / t3.micro
- 작업 경로: /home/ec2-user/apps/handdoc
[보안그룹]
- 22/tcp: 팀원 고정 IP/32만 허용
- 80/tcp: 0.0.0.0/0 허용
- 8080/8000: 0.0.0.0/0 허용 - 나중에 닫음
[Nginx(호스트)]
- 파일: /etc/nginx/conf.d/handdoc.conf
- 라우팅:
/api/* → 127.0.0.1:8080 (Spring BE)
/ai/* → 127.0.0.1:8000 (FastAPI)
[환경파일(.env)]
- 위치: /home/ec2-user/apps/handdoc/.env
BE의 나머지 배포는 타 팀원이 맡았고 FastAPI 배포 및 github action 처리는 내가 했다 !! FE도 내가 배포할 예정!
이 과정도 아래에 기록해보았으니 꼬옥 읽어보시길 바란다.
2025.08.27 - [프로젝트/캡스톤 졸프] - [HandDoc] FastAPI 추론 서버 - Docker+GitHub Actions로 EC2에 배포해보자
'프로젝트 > 캡스톤 졸프' 카테고리의 다른 글
[HandDoc] FastAPI 추론 서버 - Docker+GitHub Actions로 EC2에 배포해보자 (3) | 2025.08.27 |
---|---|
[졸프스타트] "HandDoc" AI (2) | 2025.05.14 |
[졸프스타트] "HandDoc" 기획 (1) | 2025.05.14 |