오늘은 Docker로 만든 멀티 서비스 애플리케이션을 AWS EC2에 실제로 배포하는 과정을 집중적으로 실습했다. 이전까지는 로컬 환경에서 Docker와 Docker Compose를 사용하는 데 초점을 맞췄다면, 오늘은 한 단계 더 나아가 Docker Hub에 이미지를 올리고 AWS 서버에서 내려받아 실행하는 과정까지 직접 경험했다.
특히 Backend, Frontend, MCP 서버를 각각 컨테이너로 분리하고, 로컬에서 정상 동작하는지 확인한 뒤 AWS에서도 동일한 서비스를 실행해보면서 개발 환경과 운영 환경의 차이를 조금씩 이해할 수 있었다.
오늘 배운 내용
오늘의 전체 흐름은 크게 다음과 같았다.
로컬 개발
↓
Docker Image 생성
↓
Docker Hub Push
↓
AWS EC2 생성
↓
SSH 접속
↓
Docker / Docker Compose 구성
↓
이미지 Pull
↓
Docker Compose 실행
↓
AWS에서 실제 서비스 확인
단순히 Docker 명령어를 몇 개 사용하는 것이 아니라, 개발한 애플리케이션을 실제 서버에서 실행하기 위한 전체적인 배포 흐름을 경험한 것이 오늘 학습의 가장 큰 부분이었다.
1. Docker Image와 Docker Hub의 관계 이해하기
기존 Weather MCP 프로젝트를 통해 먼저 Docker Hub에 이미지를 올리는 과정을 진행했다.
사용한 이미지는 다음과 같다.
jso4603/weather-backend:1.0.0
jso4603/weather-frontend:1.0.0
jso4603/weather-mcp:1.0.0
로컬에서 Docker Image를 만든 다음 Docker Hub에 Push하면 AWS 서버에서는 이 이미지를 다시 내려받아 컨테이너로 실행할 수 있다.
이 과정을 직접 해보면서 Docker Image는 실행 환경을 패키징한 결과물이고, Docker Hub는 그 이미지를 저장하고 공유하는 장소라는 개념이 조금 더 명확해졌다.
2. compose.yml과 compose.release.yml의 역할
이번 실습에서 특히 중요했던 부분은 Docker Compose 파일의 역할이었다.
개발 환경에서 이미지를 만들 때 사용하는 Compose 설정과 실제 서버에서 이미지를 가져와 실행하는 Release 설정을 구분했다.
compose.release.yml에서는 Docker Hub에 올라간 이미지를 직접 지정했다.
backend:
image: jso4603/weather-backend:1.0.0
frontend:
image: jso4603/weather-frontend:1.0.0
weather-mcp:
image: jso4603/weather-mcp:1.0.0
이렇게 해두면 AWS에서는 소스코드를 가지고 다시 이미지를 빌드하지 않아도 Docker Hub에서 이미지를 내려받아 실행할 수 있다.
결국 다음처럼 역할이 나뉜다.
개발 환경
compose.yml
↓
Docker Image Build
↓
Docker Hub Push
배포 환경
compose.release.yml
↓
Docker Hub Pull
↓
Container 실행
처음에는 Compose 파일 하나로 모든 것을 처리한다고 생각했는데, 이번 실습을 통해 이미지를 만드는 과정과 이미지를 가져와 실행하는 과정은 분리해서 생각해야 한다는 것을 이해하게 됐다.
3. 멀티 에이전트 프로젝트를 실제 서비스 구조로 확인
7~8교시에는 mini_agent_03_mcp 프로젝트도 직접 분석하고 배포 준비를 진행했다.
단순한 날씨 조회 프로그램이 아니라 다음과 같은 여행 AI Agent 구조였다.
Browser
↓
Frontend :8501
↓
Backend :8000
↓
Travel MCP :8010
↓
Policy MCP
↓
OpenAI API
MCP는 AI가 외부 기능이나 데이터를 사용할 수 있도록 연결해주는 구조라고 이해하고 있는데, 이번 프로젝트에서는 날씨 조회, 호텔 검색, 호텔 정책 조회 등의 도구를 MCP를 통해 연결했다.
실제로 로컬에서 통합 테스트를 진행했고,
Backend → 정상
MCP → connected
MCP Tool → 3개 발견
Frontend → HTTP 200
OpenAI → 실제 호출 성공
MCP → 실제 호출 성공
까지 확인했다.
실제 질문도 실행해보면서 단순히 컨테이너가 실행되는 것만 확인한 것이 아니라 AI → MCP Tool → 결과 반환까지 전체 흐름이 실제로 동작하는 것을 확인했다.
4. AWS EC2에 실제 배포하기
오늘 가장 큰 변화는 로컬 환경을 벗어나 AWS EC2에 실제 서비스를 배포했다는 것이다.
EC2는 AWS에서 제공하는 가상 서버라고 생각하면 된다.
새로운 EC2는 Ubuntu 24.04 LTS 환경으로 구성했고, SSH를 이용해 서버에 접속했다.
처음에는 Windows에서 .pem 개인키의 권한 문제 때문에 SSH 접속이 거부되는 문제도 있었다.
WARNING: UNPROTECTED PRIVATE KEY FILE!
Permissions for '.\agentkey.pem' are too open.
Windows에서 개인키에 너무 많은 사용자가 접근할 수 있는 상태였기 때문에 OpenSSH가 보안상의 이유로 키 사용을 거부한 것이다.
권한을 수정한 뒤 정상적으로 SSH 접속할 수 있었다.
이 과정에서 단순히 ssh 명령어만 외우는 것보다 왜 SSH가 개인키의 권한을 검사하는지를 이해하는 것이 중요하다는 생각이 들었다.
5. AWS Security Group과 포트 이해
EC2를 생성하면서 Security Group도 설정했다.
Security Group은 쉽게 말하면 AWS 서버로 들어오고 나가는 네트워크 접근을 제어하는 방화벽 역할을 한다.
이번 프로젝트에서는 외부에서 직접 접근할 필요가 있는 Streamlit Frontend만 8501 포트를 열고, Backend와 MCP는 외부에 직접 노출하지 않는 방향으로 구성했다.
8501 → Frontend → 외부 접근
8000 → Backend → 내부/localhost
8010 → MCP → Docker 내부
모든 포트를 외부에 열어놓는 것이 아니라 실제로 외부에 공개할 서비스만 열어야 한다는 점도 배웠다.
6. .env와 보안
배포 과정에서는 API Key 같은 민감한 정보를 코드에 직접 넣지 않고 .env를 사용했다.
그리고 AWS 서버에 전달한 .env 파일의 권한도 제한했다.
chmod 600 .env
또한 프로젝트의 .gitignore에 다음 내용을 추가했다.
*.pem
AWS SSH 개인키인 agentkey.pem이 Git에 올라가지 않도록 하기 위해서였다.
이번 과정을 통해 코드가 정상적으로 실행되는 것만큼 인증키와 환경변수를 어떻게 관리하는지도 중요하다는 것을 다시 확인했다.
7. AWS에서 Docker Compose로 서비스 실행
AWS 서버에서는 Docker Hub에 올라간 이미지를 Pull한 뒤 Compose를 이용해 서비스를 실행했다.
docker compose -f compose.release.yml pull
docker compose -f compose.release.yml up -d
그리고:
docker compose -f compose.release.yml ps
를 이용해 Backend, Frontend, MCP 컨테이너가 정상적으로 실행되고 있는지 확인했다.
이전에는 docker compose up을 실행하면 단순히 컨테이너가 올라간다고만 생각했는데, 이제는 이미지를 어디서 가져오는지, 각 서비스가 어떤 포트를 사용하는지, 컨테이너 간에는 어떻게 연결되는지까지 함께 확인해야 한다는 것을 조금 더 이해하게 됐다.
수동 배포와 자동 배포의 차이
오늘 강의에서 다음 단계인 자동 배포(CD)에 대한 설명도 들었다.
현재까지 진행한 방식은 사람이 직접 명령어를 실행하는 수동 배포에 가깝다.
Docker Build
→ Docker Hub Push
→ AWS 접속
→ Docker Pull
→ Compose 실행
자동 배포는 이후 GitHub Actions 등을 연결해서,
Git Push
↓
GitHub Actions
↓
CI 테스트
↓
Docker Image Build
↓
배포
↓
AWS 반영
과정을 자동화하는 방식이다.
CI(Continuous Integration)는 코드를 합치기 전에 테스트와 검증을 자동으로 수행하는 과정이고, CD(Continuous Delivery/Deployment)는 검증된 결과물을 실제 환경까지 전달하는 과정을 의미한다.
오늘은 수동 배포를 직접 경험했기 때문에 앞으로 자동 배포를 배우더라도 자동화되는 각각의 단계가 무엇인지 이해하면서 볼 수 있을 것 같다.
오늘 새롭게 이해한 점
오늘 가장 크게 달라진 생각은 "Docker를 사용할 줄 안다"와 "Docker를 이용해 서비스를 배포할 수 있다"는 서로 다른 수준이라는 것이다.
이전에는 Docker Compose를 실행해서 컨테이너가 올라오면 일단 성공이라고 생각했다.
하지만 실제 배포 과정에서는 그보다 훨씬 많은 것을 확인해야 했다.
- 어떤 이미지를 사용할 것인지
- 이미지를 어디에 저장할 것인지
- 환경변수는 어떻게 전달할 것인지
- 어떤 포트를 외부에 공개할 것인지
- 서버에 어떻게 접속할 것인지
- 컨테이너가 정상적으로 실행되고 있는지
- MCP와 Backend가 서로 연결되는지
- 실제 AI 요청까지 정상적으로 처리되는지
결국 개발한 프로그램을 만드는 것과 그것을 다른 환경에서 안정적으로 실행할 수 있도록 만드는 것은 별개의 문제라는 것을 체감했다.
오늘의 어려움과 해결
가장 기억에 남았던 것은 AWS 서버에 SSH로 접속하는 과정이었다.
Windows의 .pem 파일 권한이 너무 넓게 설정되어 있어서 처음에는 SSH 접속이 거부됐다.
Bad permissions
UNPROTECTED PRIVATE KEY FILE
라는 메시지를 보고 단순히 IP나 키가 잘못된 것이라고 생각하기 쉬웠지만, 실제 원인은 개인키 파일의 Windows 권한 설정이었다.
권한을 수정한 후 정상적으로 접속할 수 있었고, Linux 서버에서 디렉터리와 파일 권한을 확인하는 과정까지 경험했다.
또 하나 기억에 남은 것은 Docker Compose 명령어에서 파일명을 명시해야 했던 부분이다.
docker compose down --remove-orphans
를 실행했을 때:
no configuration file provided: not found
오류가 발생했다.
현재 사용하는 파일이 기본 이름인 compose.yml이 아니라 compose.release.yml이었기 때문에 발생한 문제였다.
그래서:
docker compose -f compose.release.yml down --remove-orphans
처럼 명시적으로 Compose 파일을 지정해야 했다.
이런 작은 오류도 실제 배포 환경에서는 자주 발생할 수 있기 때문에 현재 내가 어떤 파일과 환경을 사용하고 있는지 확인하는 습관이 중요하다는 것을 배웠다.
현재 나의 상태
오늘 학습을 통해 이제 단순히 Python이나 AI Agent 코드를 작성하는 것에서 한 단계 더 나아가,
- 여러 서비스를 Docker 컨테이너로 분리하고
- Docker Compose로 묶고
- Docker Image를 만들고
- Docker Hub에 업로드하고
- AWS EC2를 생성하고
- SSH로 서버에 접속하고
- 환경변수와 파일 권한을 설정하고
- Docker Hub에서 이미지를 받아
- 실제 AWS 서버에서 AI Agent 서비스를 실행하는
전체적인 흐름을 직접 경험했다.
아직 AWS 인프라나 CI/CD를 깊게 이해했다고 하기는 어렵다. 특히 GitHub Actions를 이용한 자동 배포와 실제 운영 환경에서의 서버 관리는 더 연습할 필요가 있다.
하지만 적어도 이제 Docker → Docker Hub → AWS EC2 → Docker Compose가 각각 따로 존재하는 기술이 아니라 하나의 배포 과정으로 연결되어 있다는 것은 이해할 수 있게 됐다.
오늘의 회고
오늘은 지금까지 배운 Docker와 MCP, AI Agent 기술이 실제 서버 환경에서 어떻게 연결되는지를 확인한 날이었다.
특히 로컬에서 정상적으로 동작하는 프로젝트를 AWS에 올리는 과정에서 "내 컴퓨터에서 잘 된다"와 "서버에서도 실행할 수 있다"는 것은 다르다는 것을 직접 경험했다.
Docker Image를 만들고 Docker Hub에 올리는 이유도 단순히 Docker를 공부하기 위해서가 아니라, 실행 환경을 패키징해서 다른 서버에서도 동일한 서비스를 실행하기 위해서라는 점이 명확해졌다.
이제 다음 단계는 수동으로 하나씩 실행하는 배포 과정을 GitHub Actions와 연결해 자동화하는 것이다.
오늘 직접 수동 배포를 해봤기 때문에, 이후 자동 배포를 배울 때도 단순히 명령어를 따라가기보다는 "지금 이 과정이 기존의 어느 작업을 자동화하는 것인지"를 생각하면서 학습해봐야겠다.
'AI 오케스트레이션 캠프 > 일차별 회고' 카테고리의 다른 글
| 50일차 회고|Docker·AWS 배포·CI/CD|AI 오케스트레이션 개발자 국비지원 (0) | 2026.09.18 |
|---|---|
| 49일차 회고|Docker·AWS 배포·Release 환경 구성|AI 오케스트레이션 개발자 국비지원 (0) | 2026.09.17 |
| 47일차 회고|Docker·CI/CD·멀티모달 Agent 배포 환경 구축|AI 오케스트레이션 개발자 국비지원 (0) | 2026.09.15 |
| 46일차 회고|Docker Compose·멀티 에이전트·포트폴리오 정리|AI 오케스트레이션 개발자 국비지원 (0) | 2026.09.14 |
| 45일차 회고|Docker Compose·MCP HTTP·Docker Hub|AI 오케스트레이션 개발자 국비지원 (0) | 2026.09.11 |
댓글