[AWS] Docker Compose로 Spring Boot 애플리케이션 실행하기

2026. 2. 16. 06:56카테고리 없음

저번 포스팅에서는 EC2 인스턴스를 생성하고, Docker Docker Compose 설치하며, 컨테이너 기반으로 애플리케이션을 실행할 있는 환경을 구성했습니다.

 

이번 포스팅에서는 실제로 Spring Boot 애플리케이션을 Docker 이미지로 빌드한 , AWS ECR 업로드하고, EC2 서버에서 해당 이미지를 내려받아 실행하려합니다. 시작에 앞서, 주요 개념을 한 번 더 짚고 넘어가겠습니다.

 


 

Docker 란?


Docker는 애플리케이션을 컨테이너(Container) 라는 단위로 실행할 수 있게 해주는 플랫폼입니다.

애플리케이션을 실행하기 위해서는 소스코드 이외의 여러가지 것들이 필요한데요. 도커는 실행에 필요한 모든 것들을 이미지(Image)라는 하나의 실행 패키지로 묶어 어떤 환경에서도 동일하게 실행될 수 있도록 만들어줍니다.

 

Docker 이미지(Image) 란?

도커 이미지는 애플리케이션을 실행하기 위한 패키지입니다.

실행에 필요한 베이스 운영체제, 런타임 환경, 애플리케이션 코드, 실행 명령어 등의 정보를 모두 담고 있습니다.

Dockerfile이라는 파일에 이 모든 설정을 작성하고 docker build를 실행하면 이미지가 생성됩니다.

컨테이너(Container) 란?

컨테이너는 도커 이미지를 기반으로 실제 실행중인 애플리케이션, 결과물을 의미합니다. 

 

 

 

 

Docker Compose 란?


여러 개의 컨테이너를 하나의 서비스처럼 정의하고 실행할 수 있게 해주는 도구입니다.

실제 애플리케이션은 단일 컨테이너만으로 구성되기보다, 여러 컨테이너가 함께 동작해야 완성되는 경우가 많은데요.

저 역시도 프로젝트를 실행하기 위해 Spring Boot 애플리케이션, MySQL 데이터베이스, Redis 캐시 서버를 각각 분리된 컨테이너로 구성하려고 합니다. 이때 컨테이너들을 하나씩 docker run으로 실행하고 관리하기에는 번거롭고, 설정도 복잡해지는 문제가 있는데요.

 

Docker Compose는 docker-compose.yml 파일에 여러 컨테이너의 설정을 미리 정의해두면 단 한 줄의 명령어로 모든 컨테이너를 동시에 실행할 수 있게 해줍니다. 이번시간에는 함께 docker-compose.yml 파일을 작성하고, 어떻게 실행되는지 확인하보겠습니다.  

 

 


 

 

1.  Dockerfile 작성


Dockerfile은 Docker 이미지를 생성하기 위한 설정 파일로, 애플리케이션이 어떤 환경에서 어떻게 실행될지를 정의하는 파일입니다. 

 

Dockerfile은 반드시 Spring Boot 프로젝트 루트 디렉토리(프로젝트 최상위 경로) 에 생성해야 합니다.

 

아래와 같이 Dockerfile을 작성해주세요.

FROM eclipse-temurin:17-jdk

LABEL maintainer="yesul"
LABEL version="0.0.1"

ARG JAR_FILE_PATH=build/libs/*.jar
COPY ${JAR_FILE_PATH} app.jar

# JVM 타임존 설정 추가
ENTRYPOINT ["java", "-Duser.timezone=Asia/Seoul", "-jar", "app.jar"]

 

 

 

각 항목의 의미를 간단히 살펴보면 다음과 같습니다.

  • FROM eclipse-temurin:17-jdk  → Java 17이 설치된 베이스 이미지 사용
  • LABEL maintainer, LABEL version  → 이미지의 작성자와 버전 정보
  • ARG JAR_FILE_PATH=build/libs/*.jar  → 빌드된 jar 파일 경로를 변수로 지정
  • COPY ${JAR_FILE_PATH} app.jar  → 빌드 결과물인 jar 파일을 컨테이너 내부로 복사하고 app.jar로 지정
  • ENTRYPOINT  → 컨테이너 실행 시 수행할 명령어
    여기서는 JVM 타임존을 Asia/Seoul로 설정한 뒤 Spring Boot 애플리케이션을 실행합니다.

이제 Docker 이미지를 실제로 빌드해보겠습니다.

 

 

2.  Docker 이미지 빌드


도커를 사용하기 위해서는 우선 Docker Desktop 앱을 실행시켜주세요. (그래야 도커 엔진이 돌아갑니다.)

Docker Engine Running...

 

그리고 다시 프로젝트 터미널로 돌아와서 명령어를 통해 프로젝트를 빌드를 진행하겠습니다.

./gradlew clean build

 

그러면 build/libs 경로에 jar 파일이 생성되게 됩니다.

 

 

이제 아래 명령어를 통해 Docker 이미지를 생성하겠습니다.

(참고로 로컬 컴퓨터 cpu가 intel이라면 그냥 docker build -t yesul-server . 로 치셔도 됩니다.)

docker build --platform linux/amd64 -t yesul-server .

 

💡 맥과 윈도우가 명령어가 다른 이유

사실 맥과 윈도에 따라 명령어가 다른 것은 아닙니다. 사용하는 cpu가 기준이 되는데요. 도커 이미지는 기본적으로 빌드하는 환경의 CPU 아키텍처를 따라갑니다. 하지만 우리가 EC2 t2 인스턴스는 x86_64(amd64) 기반이고 저는 애플 칩을 사용하고 있어서, 단순히 빌드하면 아키텍처 불일치로 인해 서버에서 이미지가 실행되지 않습니다. 따라서 저는 --platform linux/amd64 옵션을 통해 ec2에 맞는 이미지를 만들어줬습니다. 

 

그러면 도커는 build/libs 경로에 생성된 jar 파일을 COPY하여 이미지 안으로 복사해와서 이를 기반으로 도커 이미지를 생성합니다.

이제 로컬에서 이미지 빌드는 완료되었고, EC2에 우리가 생성한 이미지를 사용할 수 있도록 전달해야합니다. 그렇다면, 생성한 이미지를 EC2로 어떻게 옮길 수 있을까요?

 

 

3.  ECR 이미지 Push 하기


가장 간단하고 안전한 방법은 이미지 저장소(Registry)를 활용하는 것입니다.

 

로컬에서 빌드한 Docker 이미지를 곧바로 EC2로 복사하는 방식도 가능하지만, 실제 운영 환경에서는 이미지를 중앙 저장소에 업로드한 뒤 필요한 서버에서 내려받아 사용하는 방식을 주로 사용합니다.

 

마치 소스코드를 GitHub에 올려두고 어디서든 clone 받아 사용하는 것처럼, Docker 이미지 역시 컨테이너 레지스트리에 push 한 뒤 EC2에서 pull 받아 사용하는 구조입니다.

 

이미지 저장소는 Docker Hub, GitHub Container Registry 등 다양한 선택지가 있습니다만 이번 프로젝트에서는 AWS 환경을 사용하고 있으므로, AWS에서 제공하는 컨테이너 이미지 저장소인 Amazon ECR(Elastic Container Registry) 를 활용해보겠습니다.

 

먼저 AWS 콘솔에서 ECR 저장소를 생성하겠습니다.

 

 

 

AWS ECR 생성


AWS에 ECR을 검색한 후, 리포지토리 생성을 눌러주세요.

 

리포지토리 이름을 생성해주고 [ 생성 ] 을 눌러 완료하겠습니다.

 

 

 

 

 

 

IAM 사용자 설정


로컬에서 빌드한 도커 이미지를 AWS ECR에 푸시하려면,

마치 GitHub에 코드를 올릴때, 로그인하는 것처럼, AWS에서도 본인을 인증할 수 있는 계정을 통해 로그인을 해야합니다.

 

AWS에서는 사용자의 권한을 IAM이라는 서비스로 관리하는데요.

각 사용자에게 전용 액세스 키(Access Key) 를 발급해주고,이 키를 통해 권한을 인증합니다.

 

이번 단계에서는 ECR에 이미지를 push 할 수 있는 IAM 사용자를 만들고, 내 로컬 컴퓨터에 그 권한을 사용할 수 있도록 설정하는 과정을 알아보겠습니다.

 

 

IAM (사용자계정) 생성하기 

 

사용자 이름을 작성해주세요.

 

 

사용자 권한 설정하기

 

[권한 옵션] : 직접 정책 연결 

  • AmazonEC2ContainerRegistryFullAccess (필수)
  • AmazonS3FullAccess (선택)

저는 이미지를 S3에 저장하려고 하기 때문에 AmazonS3FullAccess 권한을 추가해주었습니다. 

필요없으시다면 선택하지 않아도 됩니다.

 

이후, [ 사용자 생성 ] 을 눌러 완료해주세요.

이때 발급된 Secret Access Key는 생성 시에만 확인 가능하기 때문에 반드시 안전한 곳에 저장하세요.

코드에 직접 작성하여 GitHub에 올리셔도 안됩니다.

 

ECR에 push, pull 권한을 가진 IAM 사용자가 생성되었습니다.

이제 발급받은 Access Key를 로컬환경에 등록하고 이를 통해서 빌드한 Docker 이미지를 ECR로 푸시하도록 하겠습니다. 

 


 

 

AWS CLI 설치하기 


계정 생성을 마쳤다면, 이제 명령을 내릴 도구를 설치해야합니다.

Github에 코드를 올리려면 Git이 깔려 있어야 하는 것처럼, AWS ECR에 이미지를 올리려면 AWS CLI라는 도구가 설치되어 있어야 합니다.

 

로컬에 AWS CLI 설치하기

저는 macOS를 사용하고 있어 아래 명령어로 설치 진행하겠습니다. 

brew install awscli

 

로컬에 인증정보 등록하기 

앞서 발급받았던 IAM 사용자 Access Key를 로컬에 등록하겠습니다.

aws configure

 

이는 Git에서 git configure 명령어로 사용자 정보를 등록하던 것과 비슷한 과정입니다.

 

명령이 실행되면 아래와 같은 정보를 요청하는데요. 발급정보를 그대로 복사하여 작성해주시면 됩니다.

 

  • AWS Access Key ID  발급받은 액세스 키 입력
  • AWS Secret Access Key  발급받은 비밀 액세스 키 입력
  • Default region name  본인의 리전 
  • Default output format  (엔터)


 

4.  도커 이미지 push


이제 앞서 빌드했던 이미지를 AWS ECR에 푸시해보겠습니다.

푸시하는 방법은 AWS ECR 푸시 명령 보기에 나와있고 명령어를 복사하여 그때로 따라하면 됩니다.

 

 

프로젝트 루트 경로에서 터미널을 열어주세요.

 

아래 명령어로 AWS ECR에 로그인하겠습니다. aws configure로 저장한 정보대로 진행됩니다. 

aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com

 

 

도커 이미지를 ECR에 저장하기 위해서는 레지스토리 주소 형식의 이름으로 바꿔주겠습니다. 

각 명령어는 AWS ECR 리포지토리 '푸시 명령 보기'에서 복사해주세요. 

docker tag yesul-server:latest 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com/yesul-server:latest

 

그리고 이미지를 push 해주겠습니다. 

docker push 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com/yesul-server:latest

 

 

 


 

5.  서버에서 이미지 Pull 받기


이제 올린 도커 이미지를 서버에서 내려받아 애플리케이션을 실행시켜주겠습니다. 

 

 

AWS CLI 설치


 

로컬에 AWS CLI를 설치하여 push했다면, pull 받을 서버에서도 설치해줘야겠죠.

EC2에 접속하여 아래 명령어를 실행시켜주세요. 

sudo apt install unzip
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip awscliv2.zip
sudo ./aws/install
aws --version

이제 로그인해야겠죠?

로컬에서는 IAM 사용자 Access Key를 발급받아 aws configure 명령어로 인증 정보를 저장한 뒤 로그인 진행했습니다.
하지만 EC2 서버에서는 다른 방식을 사용합니다. 서버에 Access Key를 저장하는 방식은 보안상 권장되지않기 때문입니다.

 

대신 EC2 에서는 IAM 역할(Role)을 연결하여, AWS가 제공하는 임시 자격 증명(Temporary Credentials) 을 자동으로 발급받아 인증하는 방식을 사용합니다.

 

 

IAM 역할 설정


IAM Role 생성하기 

[ AWS > IAM > 역할 ] 에서 [ 역할 생성 ]을 눌러주세요.

 

위에서 IAM 사용자 생성했을 때와 마찬가지로 권한 생성을 하고 다음을 눌러주세요.

  • AmazonEC2ContainerRegistryFullAccess (필수)
  • AmazonS3FullAccess (S3 사용하는 경우, 선택)

 

간단하게 역할 이름만 작성해주고 [ 역할 생성 ] 을 눌러 생성완료해주겠습니다. 

 

 

 

 

EC2 IAM Role  연결하기

[ AWS > EC2 > 인스턴스 ] 에서 프로젝트 서버 인스턴스를 선택해주세요.

 

상단 [ 작업 ] 에서 [ 보안 > IAM 역할 수정 ]을 누르겠습니다. 

 

 

 

방금 생성했던 IAM 역할을 선택하고 [ IAM 역할 업데이트 ] 를 눌러주세요.

 

 

 

도커 이미지 pull 받기 

ssh통신으로 EC2에 들어가주세요.

 

아래 명령어를 통해 aws 로그인을 하겠습니다. 

aws ecr get-login-password --region ap-northeast-2 \
| docker login --username AWS --password-stdin 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com

 

 

해당 명령어는 EC2에 연결된 IAM 역할을 자동으로 불러와 로그인을 진행해줍니다. 

 

그리고 pull 해주겠습니다. push 할때 명령어와 똑같이 ECR리포지토리 주소를 사용해 내려받겠습니다. 

docker pull 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com/yesul-server:/latest

 

 

컨테이너 실행

이제 내려받은 도커 이미지를 실행시킬 차례입니다. 아래 명령어으로 애플리케이션을 실행시켜보겠습니다. 

docker run -d -p 8080:8080 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com/yesul-server:latest

 

 

따로 설정한 포트가 있다면 맞춰서 진행해주세요. 저는 8080 기본 포트 사용하고 있습니다. 

 

두둥! spring 이 실행되고 있습니다!

하지만 실제 서버의 ip주소로 접속해보면.. 정상적으로 응답이 오지 않거나 500에러나 발생할겁니다.

 

왜 그럴까요?

 

컨테이너는 정상실행되었지만 애플리케이션이 동작하기 위해 필요한 DB를 연결해주지 않았기 때문입니다..

(Dockerfile에 DB 정보를 포함해 단일 컨테이너로 구성하신 분들은 정상 동작할 수 있습니다. 다만 이 방식은 애플리케이션과 DB가 한 컨테이너에 묶여 있어 운영 및 유지보수 측면에서 불리하기 때문에 권장하지 않습니다.)

 

지금까지 과정이 오래걸렸지 이 이후부터는 간단합니다. 걱정하지마세요!

 

제 서비스가 정상적으로 동작하려면 MySQLRedis가 필요합니다.

(다른 DB나 캐시 서버를 사용하고 계신다면 그에 맞춰 적용해주세요.)

 

저는 MySQL과 Redis를 각각 분리된 컨테이너로 구성하고, Docker Compose를 사용해 여러 컨테이너를 한 번에 관리하고 실행하겠습니다. 이 과정이 끝나면 드디어 브라우저에서 동작하는 화면을 직접 확인하실 수 있습니다.

 


5.  Docker Compose 배포하기


docker-compose.yml 작성 

docker-compose.yml 파일은 여러 개의 컨테이너를 한 번에 관리하고 실행하기 위해 꼭 필요한 설정 파일입니다.

 

저는 이 파일에 Spring Boot 애플리케이션, MySQL, Redis 컨데이너 설정을 작성하겠습니다.

우선 EC2 서버에 접속하여 프로젝트 디렉토리를 생성하고 이동해주세요. 

mkdir yesul && cd yesul

 

 

만든 디렉토리에 docker compose 파일을 작성할건데요. 

저는 vi 텍스트 편집기로 설정파일을 생성하고 편집해주겠습니다. (vi 말고 다른 편집기를 사용하셔도 상관없습니다.)

vi docker-compose.yml

 

 

파일이 열리면 바로 수정이 불가능합니다.

i 를 눌러 편집 모드로 진입한 후, 아래 내용을 작성해주세요. (본인의 인프라 구성에 맞게 내용을 수정해주세요.)

 

 

[ docker-compose.yml ] 

services:
  yesul-db:
    image: mysql
    environment:
      MYSQL_ROOT_PASSWORD: rkdud1717*
      MYSQL_DATABASE: yesul_db
    volumes:
      - ./mysql_data:/var/lib/mysql
    ports:
      - 3306:3306
    healthcheck:
      test: ["CMD", "mysqladmin", "ping" ]
      interval: 5s
      retries: 10

  yesul-redis-server:
    image: redis
    ports:
      - 6379:6379
    healthcheck:
      test: [ "CMD", "redis-cli", "ping" ]
      interval: 5s
      retries: 10

  yesul-server:
    image: 313549093641.dkr.ecr.ap-northeast-2.amazonaws.com/yesul-server
    env_file:
      - .env
    ports:
      - 8080:8080
    depends_on:
      yesul-db:
        condition: service_healthy
      yesul-redis-server:
        condition: service_healthy

 

저의 경우는 위와 같이 설정해주었는데요. 각 항목의 의미가 궁금하면 아래를 참고하시기 바랍니다.

💡 docker-compose 자세히 살펴보기
  • services : 실행할 컨테이너들을 정의하는 항목
  • image : 해당 컨테이너에 사용할 Docker 이미지를 지정
  • environment : 컨테이너 내부에서 사용할 환경 변수 설정
  • volumes : 호스트 디렉토리와 컨테이너 내부 경로를 연결 (컨테이너가 삭제되더라도 데이터가 유지되어 영속성 보장됨)
  • ports : 호스트:컨테이너 포트 매핑
  • healthcheck : 컨테이너가 정상적으로 실행 중인지 주기적으로 확인
                           (주의, DB나 Redis가 완전히 준비되기 전에 Spring Boot가 먼저 실행되면 연결에 실패할 수 있음)
  • depends_on : 작성한 서비스가 준비된 이후에 해당 컨테이너를 실행하도록 순서 지정
  • condition : service_healthy 옵션을 사용하면 healthcheck가 통과된 이후에만 Spring Boot 컨테이너가 실행됨
  • env_file : .env 파일을 읽어 환경 변수를 주입 (민감한 값들을 코드에 직접 작성하지 않고 외부 파일로 분리해 관리)

 

저는 애플리케이션에서 사용하는 설정값들을 .env 파일에 따로 모아뒀는데요. 저처럼 env파일을 사용하시는 분들을 docker-compose.yml 파일과 같은 경로에 .env 파일을 생성해주세요. 마찬가지로 vi 사용해서 작성하시면 됩니다.  

로컬에서 작업을 하셨다면 환경변수의 호스트 주소를 localhost로 사용하고 계셨을텐데요. 이를 docker-compose에 작성하신 서비스 이름으로 바꿔주세요. (ex. yesul-db / yesul-redis-server) Docker Compose로 실행된 컨테이너들은 같은 네트워크 안에 있어서 서비스 이름으로 서로를 찾기 때문입니다. 

 

 

하나 더! 혹시 RDS가 아닌 외부 DB (로컬 DB 포함) 를 사용하고 계시다면 ,  

해당 DB에 대한 접근이 가능하도록 보안 규칙(포트 허용 등)을 추가로 설정해야 합니다.

 

저는 로컬 DB로 사용하고 있어서 인바운드 규칙에 제 집 ip 주소만 추가해주었습니다.  

보안규칙 (인바운드)에 DB 포트에 대한 접근 ip 추가해준 모습

 


 

 

이제 DB까지 연결했으니, Docker Compose로 전체 컨테이너를 실행해보겠습니다. 서버 터미널에 아래 명령어를 입력합니다.

docker compose up -d —-build

 

문제가 없다면 우리가 작성했던 컨테이너들이 모두 실행되었을 것입니다. 

 

이제부터 컨테이너 목록을 확인하고 싶으시면 docker ps로 실행되고 있는 컨테이너를 확인하실 수 있습니다. 

만약, 애플리케이션 서버가 컨테이너 목록에 안보이는 경우에는 어떠한 이유로 컨테이너가 꺼졌을 수도 있습니다. 

 

이럴때는 docker ps -a 명령어로 모든 상태(실행중/종료/실패)의 컨테이너를 확인하실 수 있습니다. 

 

 

잘 안되신다면 체크해보세요

  • .env 파일 위치가 작성한 경로에 제대로 위치하고 있는지
  • 환경변수 누락된건 없는지
  • 호스트 주소 자리에 docker compose에 작성한 이미지명 대로 사용중인지
docker logs --tail 200

 

위 명령어를 통해서 컨테이너 실행 로그를 확인하신 후, 문제를 해결해보시기 바랍니다. 

 

 

제가 겪었던 문제

  • 상황
    docker compose up -d --build 로 생성은 잘 되는데 얼마안가서 애플리케이션 서버가 종료되었음. 심지어 ssh 접속을 할 경우 접속이 오래걸리다가 결국 time out.  AWS 페이지에서 EC2 재실행시킨 후, 다시 해봐도 동일.  
  • 원인
    인스턴스 타입을 t3.micro로  애플리케이션, 디비, 레디스 이렇게 세 컨테이너를 돌리기에는 사양이 너무 낮음
  • 해결
    t3.small로 업그레이드하니 안정적으로 실행되었습니다.

    💡 t3.small 사용 시 주의사항
           t3.small은 프리티어 무료 대상이 아닙니다.  사용하지 않을 때는 반드시 인스턴스를 중지하시는걸 추천합니다.
           저는 깜빡하고 한 달 내내 켜뒀다가 약 4만 원(1,500원 초반대 환율 기준) 이 청구되었습니다...ㅜㅜ

 

 

 


 

여기까지 정말 수고 많으셨습니다!

 

그런데 그거 아세요...? 
지금 이 상태에서 코드를 한 줄이라도 수정하면...

  1. 로컬에서 다시 이미지 빌드
  2. ECR에 push
  3. 서버에서 pull
  4. 컨테이너 재실행

이 과정을 매번 반복해야 합니다....
상상만 해도 슬프죠? 그냥 상상으로만 남겨둡시다. 다 방법이 있으니까요.

 

 

다음 포스팅에서는 코드를 push하면 빌드부터 배포까지 자동으로 처리되는 환경을 구성하는 내용으로 돌아오겠습니다. 고생하셨습니다.