- 이번 포스트에서는 EC2 인스턴스에 Docker Compose로 배포하는 과정을 설명합니다.
- Backend, Redis, Frontend 인스턴스 각각에 Docker 환경을 구성하고, Redis는 세션 저장소와 캐시 저장소로 설정하며, Frontend는 Nginx를 리버스 프록시로 활용하여 백엔드와 연동합니다.
- EC2 Ubuntu 서버에서 Docker Compose 설치 및 실행은 아래포스트를 참고해주세요!
[Docker] Ubuntu 기반 AWS EC2 인스턴스에 Docker 및 Docker Compose 구축 및 실행
[Docker] Ubuntu 기반 AWS EC2 인스턴스에 Docker 및 Docker Compose 구축 및 실행
이번 포스트에서는 Ubuntu 기반 AWS EC2 인스턴스에 Docker와 Docker Compose 환경을 설정해 보겠습니다.GUI 기반 Windows와 달리 Ubuntu와 같은 Linux 기반 OS는 명령줄 인터페이스(CLI) 를 통해 작업해야 하므로,
myrail.tistory.com
1. SSH 접근을 위한 보안그룹 생성
- 실습을 위해 모든 인스턴스를 Public Subnet에 배치했습니다.
- 실제 프로덕션 환경에서는 FrontEnd를 제외한 모든 인스턴스를 Private Subent에 배치하는것이 좋습니다.
1) 새로운 보안그룹 생성
- Local 환경에서 SSH 접속을 위해 새로운 보안그룹을 생성하였습니다.
- In Bound :
Local Ip
를 소스로 설정하여 접근을 제한합니다.
2) 새로운 보안규칙 적용
- 모든 EC2 인스턴스에 보안그룹을 적용하여, SSH(22번 포트) 접속을 허용합니다. 이를 통해 인스턴스가 외부에서 관리자의 IP로만 접근할 수 있도록 설정됩니다.
2. Redis 인스턴스 셋팅
1) 보안그룹 설정
- In Bound : BackEnd 인스턴스와 통신하기 위해 Redis의 기본 포트인 6379와 6380을 열고, 소스를 BackEnd 인스턴스의 보안 그룹으로 설정합니다. 또한, SSH 접속을 위해 22번 포트는 오직 Admin IP로만 접근할 수 있도록 제한합니다.
- Out Bound : 사용하지 않으므로 닫아줍니다.
2) docker-compose.yml 작성
- Alpine 이미지를 사용하여 Redis의 공식 이미지를 경량화 버전을 docker hub에서 받아 사용합니다.
- Session storage 는 6379 포트, Cache Storage는 6380 포트를 열어 사용합니다.
- 실행시
redis.conf
파일을 마운트하여 설정합니다.
nano docker-compose.yml
GNU nano 7.2 docker-compose.yml
services:
redis-session:
image: redis:alpine # Redis 이미지
container_name: redis-session
ports:
- "6379:6379" # Redis 포트 노출
networks:
- app-network
volumes:
- redis_session:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf # 설정 파일 마운트
command: ["redis-server", "/usr/local/etc/redis/redis.conf"] # 설정 파일 사용
restart: always
redis-cache:
image: redis:alpine # Redis 이미지
container_name: redis-cache
ports:
- "6380:6379" # Redis 포트 노출
networks:
- app-network
volumes:
- redis_cache:/data
- ./redis.conf:/usr/local/etc/redis/redis.conf
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
restart: always
volumes:
redis_session:
redis_cache:
networks:
app-network:
driver: bridge # 네트워크 드라이버 설정
3) redis.conf
작성
- Redis 는 기본적으로 외부에서 접속이 막혀있어 접속을 위한 설정을 진행합니다.
bind 0.0.0.0
으로 모든 IP에 대해 열어줍니다. 반드시보안그룹 설정
으로 통해 보호해야 합니다.protected-mode no
로 외부접속을 허용합니다.
nano redis.conf
bind 0.0.0.0
protected-mode no
3) 실행
docker-compose up -d
명령어로 백그라운드에서 Redis 컨테이너를 실행하고, docker ps로 상태를 확인합니다.
2. 백엔드 인스턴스
1) 보안그룹 설정
- In Bound : FrontEnd 인스턴스와 통신하기 위해 80(HTTP), 443(HTTPS)를 열고, 소스를 FrontEnd 인스턴스의 보안 그룹으로 설정합니다. 또한, SSH 접속을 위해 22번 포트는 오직 Admin IP로만 접근할 수 있도록 제한합니다.
- Out Bound : Redis 인스턴스와 통신하기 위해 Redis 포트인 6379, 6380을 열고, 소스를 Redis 인스턴스의 보안 그룹으로 설정합니다. 또한, RDS와 통신하기 위해 MySQL의 기본 포트인 3306을 열고, 소스를 RDS 보안 그룹으로 설정합니다.
2) docker-compose.yml 작성
- Docker Hub에 업로드된 이미지를 사용하여 백엔드 애플리케이션을 실행합니다.
applicaiton.properties
필요한 환경변수를 설정하기 위해env_file
을 사용하여.env
파일을 전달합니다.
GNU nano 7.2 docker-compose.yml
services:
backend:
image: <User>/backend:latest # Docker Hub에서 이미지를 가져옴
container_name: backend
ports:
- "8081:8081" # 애플리케이션 포트 설정
env_file:
- .env # .env 파일을 통해 환경변수 설정
networks:
- app-network
networks:
app-network:
driver: bridge
3) .env 파일작성
- 백엔드 애플리케이션이 사용할 환경변수를
application.properties
파일에 전달하기 위한 .env 파일을 자겅샇빈다. .env
파일에 작성된 환경변수들은docker-compose.yml
에서env_file
을 통해 백엔드 컨테이너 내에 전달됩니다.- 환경변수를
.env
파일에 저장함으로써, 중요한 정보(예: 데이터베이스 비밀번호, API 키, 서버 주소 등)가 코드베이스에서 직접 노출되지 않도록 할 수 있습니다.(보안성 강화)
SPRING_PROFILES_ACTIVE=prod
FRONT_END_DOMAIN=http://<EC2 Instance Domain>
#DB Config
DB_URL=jdbc:mysql://<RDS Endpoint>.ap-northeast-2.rds.amazonaws.com:3306/netnovel
DB_USERNAME=<Name>
DB_PASSWORD=<PassWord>
# Naver OAuth Config
NAVER_CLIENT_ID=<Id>
NAVER_CLIENT_SECRET=<Secret>
#Redis Config
REDIS_SESSION_HOST=<Private Subnet IP>
REDIS_SESSION_PORT=6379
REDIS_CACHE_HOST=<Private Subnet IP>
REDIS_CACHE_PORT=6380
4) 실행
docker-compose up -d
로 백그라운드에서 실행한 후docker ps
로 상태를 확인합니다.
4. Frontend 인스턴스
1) 보안그룹 설정
- In Bound : 클라이언트가 직접 접속해야 하므로 80(HTTP)과 443(HTTPS) 포트를 모두 개방합니다. 또한, SSH 접속을 위해 22번 포트는 오직 Admin IP로만 접근할 수 있도록 제한합니다.
- Out Bound : 백엔드 인스턴스와의 통신을 위해 8081 포트를 열고, 소스를 백엔드 인스턴스의 보안 그룹으로 설정합니다.
1) docker-compose.yml 작성
- Docker Hub에 업로드된 이미지를 사용하여 프론트엔드 컨테이너를 실행합니다.
- 환경 변수는 .env 파일을 통해 전달되며, 이를 nginx의 리버스 프록시 설정에 사용합니다.
GNU nano 7.2 docker-compose.yml
services:
frontend:
image: <User>/frontend:latest # Docker Hub에서 nginx 이미지를 가져옴
container_name: frontend
ports:
- "80:80" # 호스트의 80 포트를 컨테이너의 80 포트에 연결
networks:
- app-network
env_file:
- .env # 환경변수 파일 경로 설정
networks:
app-network:
driver: bridge
3) .env
파일 작성
- Nginx를 리버스 프록시로 사용할 예정이므로, 환경변수로 백엔드 인스턴스의 private IP를 설정해야 합니다.
- nginx 에서는 proxy_pass http://$BACKEND_HOST;
로 전달되어 백엔드 API로 요청을 전달합니다.
BACKEND_HOST=10.0.1.66:8081 # 백엔드 인스턴스의 private IP와 포트
docker-file
은 다음과 같습니다.
# Step 1: Node.js 기반 이미지로 작업 시작, 버전 18 사용
FROM node:18-alpine AS build-stage
# Step 2: 작업 디렉토리를 만듦
WORKDIR /app
# Step 3: package.json과 package-lock.json 파일을 복사
COPY package*.json ./
# Step 4: 의존성 설치
RUN npm install
# Step 5: 소스 코드를 복사
COPY . .
# Step 6: 프로젝트 빌드(npm)
RUN npm run build
# Step 7: Nginx 이미지로 전환하여 빌드된 파일을 서비스
FROM nginx:alpine AS production-stage
# Step 8: Step 6의 빌드 결과물을 Nginx로 복사(Nginx가 서빙할 정적 파일 생성)
COPY --from=build-stage /app/dist /usr/share/nginx/html
# Step 9: nginx 설정 파일 복사
# COPY nginx.conf /etc/nginx/conf.d/default.conf
COPY nginx.conf /etc/nginx/templates/default.conf.template
# Step 10: HTTP 포트 80번 오픈
EXPOSE 80
# 환경 변수 설정을 위한 패키지 설치 및 envsubst 사용 설정
RUN apk add --no-cache gettext
# Step 11: Nginx를 시작
CMD ["sh", "-c", "envsubst '$BACKEND_HOST' < /etc/nginx/templates/default.conf.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
- 컨테이너로 전달될
nginx.conf
파일은 다음과 같습니다. docker image
생성시 위의docker file
에 따라 컨테이너 내부로 복사됩니다.- Nginx를 리버스 프록시로 설정하여 프론트엔드 요청을 백엔드로 전달합니다.
server {
listen 80;
server_name localhost;
root /usr/share/nginx/html;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://$BACKEND_HOST;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
client_max_body_size 10M;
}
location /oauth2/ {
proxy_pass http://$BACKEND_HOST;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /login/oauth2/code/naver {
proxy_pass http://$BACKEND_HOST;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
4) Elastic Ip 연결
- 프론트엔드 인스턴스는 클라이언트가 직접 접속 해야 하므로, Elastic IP를 연결하여 고정 IP를 할당 받습니다. EC2 인스턴스는 재부팅 시 Public IP가 변경될 수 있기 때문에, Elastic IP를 사용하여 안정적인 IP를 할당합니다.
- 먼저 Elastic IP를 생성하여 IP를 할당받습니다.
- 생성된 IP에 Fronetend 인스턴스를 연결합니다.
4) 실행
docker-compose up -d
로 백그라운드에서 실행한 후docker ps
로 상태를 확인합니다.Elatic ip
로 접근하여 FrontEnd, BackEnd, Redis, RDS가 정상적으로 연결되고 실행되는지 확인합니다.
5. 결론
- 이번 포스트에서는 AWS EC2 인스턴스에 Docker Compose를 활용하여 Backend, Frontend, Redis 서비스를 배포하는 과정을 살펴보았습니다.
- 각 인스턴스에 Docker 환경을 구축하고, Redis를 세션 저장소와 캐시 저장소로 설정하여, Nginx를 리버스 프록시로 활용한 백엔드와 프론트엔드 간의 연동을 구현했습니다.
배포 과정에서는 다음과 같은 핵심 사항들을 고려했습니다:
- 보안: 인스턴스의 보안 그룹 설정을 통해 외부 접근을 제어하고, 중요한 환경변수들은 .env 파일을 통해 관리하여 보안을 강화했습니다.
- Docker Compose: Docker Compose를 사용함으로써 여러 서비스를 쉽게 관리하고, 환경 변수 파일(
.env
)을 활용해 민감한 정보를 안전하게 처리할 수 있었습니다. - 서비스 분리: 각 서비스를 독립적인 컨테이너로 분리하여, 유지보수와 확장성이 용이한 아키텍처를 구축할 수 있었습니다.
- 리버스 프록시 설정: Nginx를 리버스 프록시로 설정하여, 백엔드가 프론트엔드와 안전하게 통신할 수 있도록 했습니다.
- Elastic IP 사용: 프론트엔드 인스턴스에 Elastic IP를 할당하여, 클라이언트가 안정적으로 접속할 수 있도록 설정했습니다.
- 이와 같은 설정을 통해, EC2 환경에서 Docker와 Docker Compose를 사용한 효율적이고 유연한 배포 구조를 구현할 수 있었습니다. 프로덕션 환경에 배포할 때 고려해야 할 보안과 안정성, 확장성 문제를 해결할 수 있는 좋은 예시가 되었습니다.
'AWS > EC2' 카테고리의 다른 글
[EC2] Load Balancer 기반 Auto Scaling 구현 (1) 도입배경, 이론 (2) | 2024.11.21 |
---|---|
[EC2] EC2 Auto Scaling Group 적용 (3) - 오토스케일링 테스트 (0) | 2024.11.20 |
[EC2] EC2 Auto Scaling Group 적용 (2) - 시스템 구축 (1) | 2024.11.19 |
[EC2] EC2 Auto Scaling Group 적용 (1) - 도입배경, 이론 (0) | 2024.11.19 |
[EC2] AWS EC2 프로젝트 배포 (1) - 도입배경, 설계 (1) | 2024.11.18 |