본문 바로가기
devOps

Spring Boot 프로젝트 AWS EC2 배포하기

by WhoamixZerOne 2025. 2. 2.

 

Spring Boot 프로젝트를 AWS EC2에 가장 쉽고 간단한 방법으로 배포를 해보려고 합니다.

프로젝트를 빌드해서 jar 파일을 만들고 EC2 인스턴스로 옮겨서 jar 파일을 실행만 하면 됩니다.

AWS EC2 구축 전이라면 아래의 링크를 참조해 주세요.
AWS EC2 구축 세팅(프리 티어)

 

저는 스프링에서 제공하는 예제 중 petclinic의 gradle 프로젝트로 배포했습니다. 프로젝트 기본 설정이 h2 database를 사용하고 있어서 "application.properties" 파일에서 "database" 값을 "mysql"로 변경하고, "spring.profiles.active=mysql"을 추가해서 mysql로 사용하도록 변경하고 진행했습니다. petclinic에 대한 예제는 아래의 링크를 참조해 주세요.

👉 spring-petclinic

1. Gradle build

인텔리제이에서 Gradle 탭에서 build를 통해 하거나 프로젝트 루트 경로에서 아래의 명령어를 통해 빌드를 합니다.

./gradlew clean build

빌드를 하면 이미지와 같이 루트 경로에 build 디렉터리가 생기고 하위에 libs에 "spring-petclinic-버전.jar" 파일이 생성됩니다.

2. EC2 Java 설치

jar 파일을 실행시키려면 java가 설치되어 있어야 합니다. 먼저 ec2 인스턴스에 접근해서 아래의 명령어를 통해 확인해 보겠습니다.

  • ec2 인스턴스 접근 명령어
# ssh -i "키페어.pem" 사용자계정@"ec2 IP or DNS"
ssh -i ~/.ssh/my_aws_key.pem ubuntu@3.35.138.211

  • java 버전 확인 명령어
java -version

java가 설치되어 있다면 해당 java 버전을 출력하지만 설치가 안되어 있다면 이미지와 같이 설치가 안되어 있으니까 설치할 방법을 알려줍니다. 로컬에서 17 버전을 사용하고 있으므로 17 버전을 설치하는데, java 프로그램만 실행할 수 있으면 되기 때문에 jre로 설치합니다.

  • apt update & jre 17 설치
sudo apt update && sudo apt install openjdk-17-jre-headless

설치가 완료되면 "java -version"을 통해 설치가 제대로 됐는지 확인합니다.

3. jar 파일 EC2로 전송

ec2로 파일을 전송하기 위해서 "Secure Copy Protocol(SCP)" 명령어를 사용합니다.

scp는 리눅스(Linux)에서 사용되는 명령어인데 윈도우 "PowerShell"에서도 사용할 수 있습니다.

네트워크를 통해 로컬과 원격 시스템 간에 파일을 안전하게 복사하는 데 사용되고 기본적으로 SSH(Secure Shell)을 기반으로 동작하며, 데이터 전송 시 암호화가 이루어져 보안성이 높습니다. 파일을 전송하기 앞서 scp 명령어 사용 방법에 대해 간략히 살펴보겠습니다.

로컬(Local) → 원격지(Remote)

1. 단일 파일 전송

scp [option] [source] [user_id]@[host_ip]:[target]

ex) scp testfile root@192.168.1.10:/home/user

현재 위치에 있는 testfile 파일을 root 계정 ip 192.168.1.10 컴퓨터에 /home/user 경로 하위에 testfile을 복사합니다.

2. 복수 파일 전송

scp [option] [source1][source2] [user_id]@[host_ip]:[target]

ex) scp testfile1 testfile2 root@192.168.1.10:/home/user

현재 위치에 있는 testfile1, testfile2 파일을 root 계정 ip 192.168.1.10 컴퓨터에 /home/user 경로 하위에 testfile1, testfile2를 복사합니다.

3. 디렉터리 전체 전송(재귀적)

scp [option] [directory] [user_id]@[host_ip]:[target]

ex) scp -r testfolder root@192.168.1.10:/home/user

현재 위치에 있는 testfolder 포함, 하위 내용들을 root 계정 ip 192.168.1.10 컴퓨터에 /home/user 경로 하위에 testfolder를 복사합니다.

원격지(Remote) → 로컬(Local)

1. 단일 파일 전송

scp [option] [user_id]@[host_ip]:[source] [target]

ex) scp root@192.168.1.10:/home/user/testfile /home/user

원격지 root 계정 ip 192.168.1.10 컴퓨터에 /home/user/testfile 파일을 로컬에 /home/user 하위에 testfile을 복사합니다.

2. 복수 파일 전송

scp [option] [user_id]@[host_ip]:"[source1][source2]" [target]

ex) scp root@192.168.1.10:"/home/user/testfile1 /home/user/testfile2" /home/user

원격지 root 계정 ip 192.168.1.10 컴퓨터에 /home/user/testfile1, /home/user/testfile2 파일을 로컬에 /home/user 하위에 testfile을 복사합니다.

※로컬에서 원격과 다르게, 원격에서 로컬로 복수 파일을 전송할 때는 " "(큰따옴표)로 묶어줘야 합니다.

3. 디렉터리 전체 전송(재귀적)

scp [option] [user_id]@[host_ip]:[source] [target]

ex) scp -r root@192.168.1.10:/home/user/testfolder /home/user

원격지 root 계정 ip 192.168.1.10 컴퓨터에 /home/user/testfolder 포함, 하위 내용들을 로컬에 /home/user 하위에 testfolder를 복사합니다.

  • jar 파일 ec2로 전송

현재 위치는 프로젝트의 루트 경로입니다. ec2 인스턴스에 접근해야 하기 때문에 ssh를 사용할 때와 마찬가지로 "키페어.pem"을 포함해야 합니다.

scp -i ~/.ssh/my_aws_key.pem ./build/libs/spring-petclinic-3.2.0.jar ubuntu@3.35.138.211:~/

프로젝트 위치에서 build/libs에 있는 spring-petclinic-3.2.0.jar 파일을 ubuntu 계정 3.35.138.211 컴퓨터에 사용자 계정 메인 경로에 복사하도록 지정했습니다.

파일 전송을 완료했습니다. 그러면 ec2에 정상적으로 파일이 있는지 확인해 보겠습니다.

사용자 계정의 메인 경로에 정상적으로 파일이 복사된 것을 볼 수 있습니다.

4. Docker 설치

프로젝트에서 mysql을 사용하기 때문에 docker로 mysql를 설치하겠습니다.

설치 내용은 아래의 링크를 참조해서 진행했습니다.

👉 Docker Engine Install(Ubuntu)

apt 저장소를 사용하여 설치

  • docker apt 저장소 설정
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
  • docker packages 설치
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
  • "hello-world" 이미지를 실행해 설치 확인
sudo docker run hello-world

"Hello from Docker!" 메시지가 있으면 성공적으로 도커를 설치했습니다.

아래의 명령어를 실행해 보면 "hello-world" 이미지의 컨테이너가 실행했다가 종료가 된 것을 볼 수 있고, "hello-world" 이미지를 다운로드한 목록을 볼 수 있습니다.

sudo docker ps -a # 실행중, 종료된 목록 전부 확인
sudo docker images # 이미지 목록 확인

설치를 확인했으니 아래의 명령어로 "hello-world"를 삭제합니다.

컨테이너 아이디는 겹치는 부분이 없으면 일부만 적어도 됩니다.

# sudo docker rm {container_id} or {container_name}
sudo docker rm 4e9
# sudo docker rmi {image_id} or {image_name}
sudo docker rmi 74c

그런데 명령어를 실행할 때 sudo를 빼고 실행하면 "Permission 어쩌고 저쩌고" 권한이 없어서 안된다고 나온다. 그래서 sudo를 붙여서 관리자 권한으로 실행해야 하는데 설정을 통해 변경해 준다.

  • 현재 사용자를 docker 그룹에 추가

기본적으로 docker는 루트 권한이 필요한 작업이 많기 때문에, 사용자가 docker 그룹에 속하면 루트 권한 없이도 docker 명령어를 사용할 수 있게 된다. 단, 명령어 실행 후 현재 세션에서는 바로 적용되지 않을 수도 있기 때문에 새로 로그인하거나 "newgrp docker" 명령어를 실행하여 그룹 변경 사항을 반영할 수 있다.

sudo usermod -aG docker ${USER}

5. Docker에 MySQL 설치

  • mysql 이미지를 다운로드하고 컨테이너를 구동합니다.
docker pull mysql:8.4
docker run -d --name mysql8.4 -e MYSQL_USER=petclinic -e MYSQL_PASSWORD=petclinic -e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=petclinic -p 3306:3306 mysql:8.4

mysql container가 정상적으로 구동된 것을 확인했습니다.

6. 프로젝트 실행(jar 파일)

jar 파일을 복사한 위치에서 다음 명령어를 통해 프로젝트를 실행합니다.

  • jar 실행 명령어(java -jar 파일명.jar)
java -jar spring-petclinic-3.2.0.jar

프로젝트가 정상적으로 실행이 됐고 mysql connection이 된 것도 확인이 됐으니 이제 웹에서 접속해 보겠습니다.

https://aws.amazon.com

"Public IP:8080" 해당 주소로 연결을 하지만 접근이 안 되실 겁니다. 그 이유는 http 8080 port에 대한 접근이 막혀있기 때문입니다.

7. 보안 그룹 설정

Security Groups > 내 Security Groups 선택 > Inbound rules > Edit inbound rules

https://aws.amazon.com

이미지와 같이 아직 SSH에 대한 22 port만 허용된 상태이기 때문에 8080 포트로 접근이 안 되는 상태입니다.

https://aws.amazon.com

Add rule > Custom TCP 선택 > 8080 입력 > Anywhere-IPv4 선택 > Save rules로 저장합니다.

 

다시 접속했을 때 정상적으로 연결이 됐다면 축하드립니다. Spring Boot 프로젝트 배포에 성공하셨습니다!

하지만 저는 연결이 안 돼서 그 얘기를 더 이어가 보겠습니다.

 

TCP 8080 포트도 열어줬기 때문에 접속이 되겠지 하고 접속했지만 여전히 접속이 안되고 계속 로딩되다가 연결이 실패합니다.😭

"왜 안되지 고민하다가" 프로젝트 실행 콘솔 로그를 살펴보니 뭔가 이상합니다! 톰캣이 시작 됐다는 것과 애플리케이션이 시작 됐다는 로그가 안보입니다. 프로젝트 실행을 멈추려고 했는데 반응이 없고 새로운 터미널로 새 접속을 해보려고 했는데 접속이 안 됐습니다. 그때 뇌리에 스치는 게 "메모리가 부족해서 Spring Boot 프로젝트 하나만 실행해도 서버가 죽는다"라는 기억이 났습니다.

 

인스턴스 화면에서 종료했는데, 기존에 안 보이던 강제 종료가 보였고 다시 시작을 해도 아래와 같은 오류가 났습니다.

"The instance is not in a state from which it can be started." 서버가 비정상적으로 종료가 돼서 재구동 되기까지 약간의 시간이 걸렸습니다.

8. 메모리 용량 늘리기

프리 티어로 "t2.micro"를 사용하기 때문에 메모리 제한이 1GB밖에 안됩니다. 스프링 부트 프로젝트를 실행하는 도중에 메모리 인해 서버가 중지된 것을 알 수 있었습니다. 그래서 이 문제를 찾아보다가 해결 방법을 찾았습니다. ec2 인스턴스에서 스왑 공간을 설정하는 방법이었습니다.

 

스왑(Swap) 공간은 시스템의 물리적 RAM이 부족할 때 임시로 디스크 공간을 사용하여 RAM의 역할을 대신하는 공간입니다. 시스템이 메모리가 부족할 때 성능 저하를 막는 데 유용합니다.

 

스왑 공간을 설정하는 방법은 스왑 파일을 만들고 해당 파일을 스왑 영역으로 설정해서 부팅 시 자동으로 스왑 파일을 사용하도록 설정하면 됩니다. 더 자세한 내용은 아래의 링크를 참조해 주세요.

👉 스왑 공간 설정

 

1. 스왑 파일 생성(bs는 블록 크기, count는 블록 수), 4GB(128MB x 32) 파일

sudo dd if=/dev/zero of=/swapfile bs=128M count=32

 

2. 스왑 파일의 읽기 및 쓰기 권한 설정

sudo chmod 600 /swapfile

 

3. Linux 스왑 영역을 설정

sudo mkswap /swapfile

 

4. 스왑 공간에 스왑 파일을 추가하여 스왑 파일을 즉시 사용하도록 지정

sudo swapon /swapfile

 

5. 정상적으로 됐는지 확인

sudo swapon -s

 

6. /etc/fstab 파일을 수정해서 부팅 시 자동으로 스왑 파일을 시작하도록 설정

vi로 파일을 수정

sudo vi /etc/fstab

파일 끝에 다음 새 줄을 추가하고 파일을 저장

/swapfile swap swap defaults 0 0

 

스왑 공간 설정이 끝나면 적용이 되도록 ec2 인스턴스를 재시작해주시기 바랍니다.

재시작 후에 다시 프로젝트를 실행시킵니다.

이번엔 정상적으로 톰캣과 애플리케이션이 시작 됐다는 콘솔 로그를 확인할 수 있습니다. 이제 사이트에 연결해 봅니다.

https://github.com/spring-projects/spring-petclinic

드디어 제대로 된 화면이 나왔습니다. 👏

 

마지막으로 ec2 터미널에서 "java -jar"로 프로젝트를 실행하면 터미널에서 다른 작업을 할 수 없습니다.

다른 작업을 하려면 프로젝트를 종료시키고 작업을 해야 하고, 혹은 ssh로 연결한 터미널 세션이 끊어져도 마찬가지로 프로젝트가 종료됩니다. 이 문제를 해결하기 위해 백그라운드에서 계속 실행할 수 있도록 하는 방법이 있습니다.

9. Java 백그라운드(daemon) 실행

터미널의 세션 연결이 끊어져도 프로세스를 계속 동작할 수 있게 하는 명령어 "nohup"이 있습니다.

  • "nohup java -jar 파일이름.jar &"
nohup java -jar spring-petclinic-3.2.0.jar &

이미지 상태에서 "Enter"를 치면 다음과 같이 터미널에 커서가 생기면서 터미널을 사용할 수 있게 된다.

그리고 "ls" 명령어를 통해 확인해 보면 'nohup.out' 파일이 생성되어 있다.

백그라운드로 실행된 프로그램을 종료시키기 위한 방법은 다음과 같다.

10. 실행 중인 프로세스 확인 및 종료

프로세스를 찾는 방법 2가지를 얘기하겠습니다.

  • ps를 통한 프로세스 찾기(ps -ef | grep "실행시킨 프로세스")

프로세스명은 전체 다 적지 않아도 된다.

ps -ef | grep spring-petclinic
  • lsof(해당 포트에서 동작하는 프로세스)
lsof -i :8080

  • kill -9 {PID}(해당 프로세스 종료)
kill -9 3222

프로세스가 정상적으로 Killed 됐다는 것을 확인할 수 있다.

 

프로세스에 대한 로그를 확인하고 싶은 경우에는 다음 명령어를 통해 확인할 수 있다.

  • tail 명령어
tail -f ./nohup.out

 

 

 

🔗 Reference

'devOps' 카테고리의 다른 글

Spring 프로젝트 AWS EC2 Docker 배포  (0) 2025.02.05
[Docker] Redis 설치  (0) 2023.06.28
Dockerfile & docker-compose 작성  (0) 2022.07.31
Docker 설치 및 명령어 정리  (0) 2022.07.24