서론
캠스터디 프로젝트를 진행하면서 초기에 빠른 개발을 위해 Supabase로 백엔드를 구축했으나, 실제 서비스 운영 하는 방식과 가장 가깝게 설정하고 싶어서 API를 따로 분리해서 만들기로 결정하였습니다. 백엔드 코드를 GPT의 도움으로 작성하였습니다.
본 게시물에서는 EC2와 Docker를 활용한 서버 배포 과정과 배포 후 지속적으로 발생한 SSH 접속 불가 문제를 해결한 과정을 기록했습니다.
사용한 기술 스택 및 환경
- AWS EC2
- Docker
- Docker Compose
- MySQL
- Spring Boot
배포 과정
기존의 배포 방식과 달리 Docker를 이용한 컨테이너 기반의 배포를 진행했습니다. 아래 블로그 글을 참고하여 진행했습니다.
📘 참고 문서: [Docker] 도커로 EC2에 서버 배포하기
주요 Dockerfile 설정:
dockerfile
FROM openjdk:17-jdk-slim AS buildWORKDIR /appARG JAR_FILE=build/libs/*.jarCOPY ${JAR_FILE} app.jar FROM openjdk:17-jdk-slimWORKDIR /appCOPY src/main/resources/application.yml /app/application.ymlCOPY --from=build /app/app.jar app.jarEXPOSE 8080ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-Dotel.resource.attributes=service.name=auth-server", "-jar", "app.jar"]Docker Hub에 이미지 등록 후, EC2에서 아래의 Docker Compose 설정으로 배포했습니다:
yaml
version: "3.9" services: mysql: image: mysql:8.0 platform: linux/amd64 container_name: mysql ports: - "3306:3306" environment: MYSQL_DATABASE: ${DB_NAME} MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} MYSQL_USER: ${DB_USER} MYSQL_PASSWORD: ${DB_PASSWORD} volumes: - mysql-vl:/var/lib/mysql command: > --default-authentication-plugin=mysql_native_password --innodb_buffer_pool_size=128M --max_connections=50 --table_open_cache=200 --innodb_log_buffer_size=16M restart: always app: image: yejiii1997/cmas:latest platform: linux/amd64 container_name: camstudy-backend depends_on: - mysql ports: - "8080:8080" environment: SPRING_PROFILES_ACTIVE: prod DB_HOST: mysql DB_PORT: 3306 DB_NAME: ${DB_NAME} DB_USER: ${DB_USER} DB_PASSWORD: ${DB_PASSWORD} restart: always volumes: mysql-vl: driver: local🔥 발생한 문제 및 원인 분석
배포 후 일정 시간이 지나면 서버가 정상적으로 동작하지 않았고, 심지어 SSH 접속조차 불가능했습니다. EC2의 상태 검사는 2개 중 1개만 통과하는 상태였습니다. 로그 확인 결과 다음의 에러가 나타났습니다:
bash
[18167.348589] Out of memory: Killed process 1971 (mysqld) total-vm:1294692kB, anon-rss:369024kB...이는 MySQL 프로세스가 메모리 부족으로 인해 커널에서 강제 종료된(OOM Kill) 현상이었습니다.
당시 EC2 메모리 상태:
- 총 메모리: 957.4 MiB
- MySQL 사용: 370MB (%MEM 37.8)
- Java 애플리케이션 사용: 207MB (%MEM 21.2)
- 남은 여유 메모리: 69.6 MiB
해결 방안
Swap 메모리 설정
메모리 부족 문제를 해결하기 위해 Swap 메모리를 추가했습니다.
bash
sudo fallocate -l 1G /swapfilesudo chmod 600 /swapfilesudo mkswap /swapfilesudo swapon /swapfile echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstabMySQL 메모리 제한
MySQL 컨테이너의 메모리 사용량을 제한했습니다.
yaml
command: > --default-authentication-plugin=mysql_native_password --innodb_buffer_pool_size=128M --max_connections=50 --table_open_cache=200 --innodb_log_buffer_size=16M| 설정 항목 | 설명 |
|---|---|
| innodb_buffer_pool_size=128M | InnoDB 캐시 메모리 크기 |
| max_connections=50 | 최대 동시 접속 수 제한 |
| table_open_cache=200 | 테이블 캐시 개수 |
| innodb_log_buffer_size=16M | 로그 버퍼 크기 |
설정 변경 후에는 반드시 Docker 컨테이너를 재시작해야 합니다.
bash
docker-compose downdocker-compose up -d개선된 결과
| 컨테이너 | 변경 전 | 변경 후 | 차이 |
|---|---|---|---|
| MySQL | 370MiB | 248.6MiB | ↓121MiB |
| Backend | 거의 동일 | 거의 동일 | 유지됨 |
현재 안정적으로 서버가 운영 중입니다. 다시는 이런 문제로 글을 쓰지 않기를 바랍니다... 제발~!