이번 포스팅에서는 로컬환경에서 작업했던 SpringBoot 팀 프로젝트를 Docker Container 환경에서 구동시키는 과정을 기록을 남기고자 합니다.
먼저 전체적인 프로젝트 아키텍처와 기술 스택은 다음과 같습니다.
OS : Windows 11
Development Tool : IntelliJ IDEA
Programming Language : Java
Framework : Spring Boot, Thymeleaf, Nginx
Database : MariaDB, AWS RDS
Build : Gradle
먼저 Docker 컨테이너 내에서 Java 애플리케이션을 실행하기 전에, 먼저 Gradle 또는 Maven 같은 빌드 도구를 사용하여 애플리케이션을 JAR 파일로 빌드해야합니다.
JAR(Java Archive) 파일은 여러 Java 클래스 파일, 리소스(예: 텍스트, 이미지 등)들을 한 곳에 모아 둔 파일 형식입니다.
이 파일 형식은 ZIP 파일 형식을 기반으로 만들어졌으며, Java 애플리케이션 또는 라이브러리를 쉽게 배포하고 실행할 수 있게 해줍니다.
여러 파일을 하나로 묶어 관리하기 때문에, 프로젝트의 빌드, 배포, 실행 과정이 훨씬 간단해집니다.
JAR 파일 하나만으로 애플리케이션을 배포하고 실행할 수 있으므로, 복잡한 디렉토리 구조나 다수의 파일을 관리할 필요가 없는 이점이 있습니다.
여기선 Gradle 빌드 도구를 이용해서 JAR 파일로 빌드합니다.
다음과 같은 명령어를 터미널에 작성합니다.
// Windows
gradlew.bat build
Linux/Unix 사용자는 ./gradlew build 명령어를 사용!
// Linux/Unix
./gradlew build
성공적으로 빌드가 완료가 되면 build/libs 디렉토리 내에 실행 가능한 JAR 파일을 생성됩니다.
Dockerfile에서 COPY 명령어를 사용하여 앞서 빌드한 JAR 파일을 Docker 이미지 내부로 복사하고, ENTRYPOINT 또는 CMD 명령어를 통해 JAR 파일을 실행합니다.
Dockerfile은 Docker에서 컨테이너를 실행하기 위한 설정과 명령어들을 담고 있는 파일입니다. 마치 요리법처럼, 어떤 애플리케이션이나 서비스를 컨테이너 내에서 어떻게 구동할지에 대한 지침을 제공합니다!
FROM openjdk:17-alpine
EXPOSE 8080
COPY ./build/libs/warehouseProject-0.0.1-SNAPSHOT.jar app.jar
ENTRYPOINT ["java", "-jar", "-Duser.timezone=Asia/Seoul", "/app.jar"]
컨테이너의 기반이 될 이미지를 지정한다. 여기서는 OpenJDK 17이 설치된 Alpine Linux 이미지를 사용합니다.
즉, Java 애플리케이션을 실행하기 위한 Java 런타임과 Linux 시스템을 제공받게 됩니다!
이 명령은 컨테이너가 8080 포트를 사용할 것임을 나타낸다. 웹 애플리케이션이 이 포트를 통해 외부와 통신할 수 있도록 합니다.
즉, 이 포트(8080) 으로 들어오는 요청을 애플리케이션이 받아들일 수 있게 해줍니다!
호스트 컴퓨터(자신의 컴퓨터) 에서 컨테이너 내부로 파일을 복사합니다.
warehouseProject-0.0.1-SNAPSHOT.jar 라는 이름의 Java 애플리케이션 파일을 컨테이너 내 app.jar로 복사합니다.
이렇게 함으로써, 컨테이너 내에서 애플리케이션을 실행할 수 있게 됩니다!
컨테이너가 시작될 때 실행될 명령을 지정합니다.
여기서는 java -jar -Duser.timezone=Asia/Seoul /app.jar 명령을 실행하여, 앞서 복사한 app.jar 파일을 실행합니다.
-Duser.timezone=Asia/Seoul 은 애플리케이션의 시간대를 서울로 설정.
이 명령을 통해 Java 애플리케이션을 컨테이너 안에서 실행시키게 됩니다!
docker-compose.yml 파일은 Docker 컨테이너를 함께 구성하고 관리하기 위한 YAML 형식의 파일입니다.
이 파일을 통해 여러 컨테이너를 동시에 관리할 수 있으며, 각 컨테이너의 설정을 세밀하게 조정할 수 있습니다.
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_URL=jdbc:mariadb://자신의 aws RDS ROOT:3306
- SPRING_DATASOURCE_USERNAME=USERNAME
- SPRING_DATASOURCE_PASSWORD=PASSWORD
nginx:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf
depends_on:
- app
version : '3.8' 은 docker-compose.yml 파일의 버전을 명시합니다.
services 에는 실행할 각 컨테이너를 정의합니다.
여기서는 두 가지 서비스, app과 nginx를 정의하고 있고 DB는 AWS RDS를 사용하고 있기 때문에 따로 정의하지 않습니다.
app: 은 Dockerfile 에서 정의한 Spring Boot 애플리케이션을 실행하는 Java 컨테이너입니다.
build: Dockerfile을 사용하여 이미지를 빌드하는 방법을 정의합니다.
ports: 호스트와 컨테이너 사이의 포트 매핑을 정의합니다.
Docker 컨테이너를 사용하는 경우 호스트와 각각 독립된 실행환경이기 때문에 포트포워딩을 통한 연결 설정이 꼭 필요합니다!
nginx 서비스는 웹 트래픽을 관리하고 정적 콘텐츠를 제공하는 역할을 하는 Nginx 웹 서버입니다.
nginx 웹 서버는 정적(html, css, imgage, javascript) 요소들을 집중적으로 처리할 수 있기 때문에 WAS에 부담을 덜 주게 됩니다.
volumes 설정을 통해 호스트와 컨테이너 사이에 볼륨을 마운트합니다.
Nginx의 설정 파일(default.conf)을 호스트에서 컨테이너의 설정 디렉토리(/etc/nginx/conf.d/)로 복사합니다.
마지막으로, depends_on: nginx 서비스가 app 서비스에 의존한다는 것을 나타냅니다.
app 서비스가 실행된 후에 nginx 서비스가 시작되는걸로 이해하시면 됩니다!
default.conf 파일은 Nginx가 설치된 직후 기본적으로 사용할 수 있는 초기 서버 설정 파일입니다.
이 파일은 일반적으로 기본 웹 서버 설정을 포함하며, Nginx를 처음 사용할 때 바로 웹 서버 기능을 테스트할 수 있게 해줍니다.
Nginx가 80번 포트에서 들어오는 HTTP 요청을 받아들이도록 설정합니다.
웹 서버는 80 port 에서 요청을 받도록 기본적으로 설정되어 있습니다!
특정 URL 패턴에 대한 요청을 어떻게 처리할지 결정합니다.
여기서 사이트의 모든 요청http://로 시작하는 모든 요청을 처리하도록 지정했습니다.
클라이언트에서 들어오는 요청을 http://app:8080으로 전달하라고 Nginx에 지시합니다.
여기서 app 은 docker-compose.yml 에서 정의된 서비스 이름이며, 8080은 해당 서비스가 가지고 있는 포트입니다.
백엔드 서버가 정확한 호스트 정보를 알 수 있도록 클라이언트의 원래 호스트 이름을 프록시 요청에 포함합니다.
로깅이나 분석 등에 사용될 수 있게 클라이언트의 실제 IP 주소를 프록시 요청에 포함합니다.
클라이언트의 원래 IP 주소를 포함한 전달 경로를 나타내는 헤더를 설정합니다.
이 헤더는 리버스 프록시를 통과하는 동안 클라이언트의 IP 주소를 추적하는 데 사용됩니다.
클라이언트가 사용한 프로토콜(예: http 또는 https)을 나타내는 헤더를 설정
이 정보는 애플리케이션이 보안 연결을 요구하는지 여부를 결정하는 데 사용됩니다.
로컬 환경에서 Docker 컨테이너를 빌드하고 실행하기 위한 명령어는 다음과 같습니다!
docker-compose up --build
이 명령어는 docker-compose.yml 파일에 정의된 모든 서비스(컨테이너)를 시작합니다.
-d 옵션(데몬 모드)을 추가하면 백그라운드에서 서비스를 실행할 수 있습니다!
--build 옵션은 Docker 이미지가 존재하지 않거나 Dockerfile이 변경 된 경우 이미지를 새로 빌드하라는 명렁어 입니다.
이 옵션을 사용하면 최신 코드 변경사항이 이미지에 반영되어 실행되도록 보장되는 이점이 있습니다.
정상적으로 실행이 되었다면 위 그림처럼 Spring 로고가 떠야합니다!
Docker는 Linux 에서 동작하기 때문에 Windows에서 진행한 프로젝트에서 설정한 Windows 상대 경로들을 Linux가 읽을 수 있게 수정 작업이 필요합니다.
String uploadDirectory = "C:/work/AcornProject/src/main/resources/static/upload/";
String uploadDirectory = "src/main/resources/static/upload/";
이상으로 Docker와 docker-compose를 사용하여 Spring Boot 애플리케이션과 Nginx를 함께 배포하는 방법에 대해 알아보았습니다.
Docker를 사용함으로써 개발 환경과 운영 환경의 일관성을 보장하고, 애플리케이션의 배포 및 운영을 보다 효율적으로 할 수 있는 장점들이 있습니다.
또한 언제든지 프로젝트의 docker-compose.yml 파일과 Dockerfile을 수정하여 컨테이너 환경을 자유롭게 구성하고 최적화할 수 있습니다.
단! Linux 에서 동작하는 Docker 이기 때문에 윈도우 운영체제에서 작업한 프로젝트에서 설정한 경로를 수정해야하는 작업이 번거로울 수 있습니다.