Docker와 Docker compose (Spring Boot + Redis + H2)
이 글은 Spring Boot 프로젝트에서 docker compose를 사용해서 ec2에 서버를 띄우는 과정에서 학습한 내용을 적은 글입니다.
다룰 내용
- 도커란 무엇인가?
- 도커를 왜 사용해야 하는가?
- Dockerfile 작성법
- Dockerfile 빌드 최적화 (by Spring Boot)
- docker-compose (H2, Redis, Spring Boot)
도커란 무엇인가?
도커는 리눅스 기반의 컨테이너 기술을 활용하여 어플리케이션의 개발, 배포 등을 효율적으로 할 수 있게 해주는 서비스입니다.
도커를 왜 사용해야 하는가?
도커를 활용할 수 있는 방법은 무수히 많습니다. 그 중에서도 도커의 독립성, 확장성, 이식성을 활용 해야하는 상황에 많이 사용됩니다.
도커를 활용하는 상황
- CI/CD를 활용하여 배포 자동화를 하기 위해 독립된 개발 환경이 필요할 때
- 트래픽 증가에 따라 어플리케이션의 시스템을 확장하거나 축소하고 싶을 때
- 마이크로서비스 아키텍처를 사용할 때
Dockerfile 작성 법
Dokcerfile이란? 이미지를 만들고 프로젝트를 빌드하기 위해서 도커 문법으로 작성한 파일
- FROM: 기본 이미지. 내가 만들 이미지에 기반이 되는 이미지 입니다. 보통 OS 정도가 설치된 이미지 이고 Docker hub에서 해당 이미지를 찾아옵니다.
- COPY: 로컬 파일을 컨테이너로 복사하는 문법입니다. COPY [어떤 파일 올릴지] [이미지 컴퓨터에 어디에 올릴지] 방식으로 사용하면 됩니다.
- WORKDIR: 작업 디렉토리 입니다. 이후 실행되는 명령어는 모두 이 디렉토리 안에서 실행됩니다. (복사된 파일이 생성되는 곳)
- RUN: 이미지를 빌드하는 과정에서 실행할 명령어 입니다. 보통 두가지 방법이 있는데 RUN npm install 또는 RUN ["npm", "install"] 과 같이 사용합니다. RUN npm install은 OS에 있는 기본 쉘을 이용하라는 뜻이어서 실제 실행되는 명령어는 /bin/sh-c npm install 입니다. 그렇기 때문에 RUN ["npm", "install"] 방식으로 하는 것을 추천합니다.
- CMD: 컨테이너가 실행될 때 기본으로 실행할 명령어입니다. RUN에는 많은 명령어를 넣을 수 있는데 마지막 명령어는 RUN 대신 CMD를 사용해야 합니다. 비슷한 명령어로 ENTRYPOINT가 있습니다.
- ENTRYPOINT: CMD와 비슷하지만 CMD 보다 먼저 실행됩니다.
- EXPOSE: 컨테이너에서 사용할 포트 지정
- ENV: 환경 변수
- USER: USER를 설정하지 않으면 기본적으로 명령어가 ROOT 권한으로 실행되기 때문에 보안을 위해 USER를 설정해 줄 수 있습니다.
Dockerfile 빌드 최적화
Spring Boot 프로젝트를 사용하면 라이브러리가 아주 많기 때문에 빌드 하는데 시간이 오래걸리고 성능을 많이 잡아먹습니다.
하지만 Dockerfile을 작성하는 방법에 따라 빌드 시간을 단축시킬 수 있는 방법이 있습니다. 그 방법은 자주 사용하는 것을 밑에다 적고 자주 사용하지 않는 것은 위에다 적는 것입니다. 도커에서는 자동으로 캐싱을 합니다. 그래서 새로 빌드하는 파일이 변경이 없으면 이전에 캐싱했던 결과를 가지고 오게 됩니다.
빌드 시간을 줄일 수 있는 또 다른 방법으로는 multi-stage 방법이 있습니다. Multi-stage 빌드는 Dockerfile을 작성할 때 빌드 단계를 나눠서 이미지를 효율적으로 생성하는 방법입니다 이렇게 하면 최종 이미지에 불필요한 빌드 도구나 파일이 포함되지 않아 이미지 크기를 줄이는 효과를 얻을 수 있습니다.
Spring Boot의 Dockerfile
FROM openjdk:17-jdk-slim AS build
WORKDIR /app
COPY . .
RUN chmod +x ./gradlew
RUN ./gradlew clean build -x test && ls build/libs
FROM openjdk:17-jdk-slim AS runtime
WORKDIR /app
COPY --from=build /app/build/libs/*.jar /app/scholarship.jar
RUN chmod 644 /app/scholarship.jar
CMD ["java", "-jar", "/app/scholarship.jar"]
위의 코드는 Spring Boot 프로젝트에서 작성한 Dockerfile 입니다. 첫번째 빌드할 때는 전체 파일을 복사하고 gradlew build 명령어를 수행하면 jar 파일이 만들어 집니다. 두번째 FROM 에서 다시 만들어지면서 jar 파일만 복사해서 프로젝트를 실행시킵니다. 저는 ec2에서 계속해서 Error: Unable to access jarfile app/scholarship.jar 에러가 뜨면서 jar파일을 찾을 수 없어 빌드 결과를 확인하고 권한을 열어주기 위해서 여러 명령어들을 추가하였습니다. 하지만 결국 원인은 docker hub에 올라간 이미지를 pull 하지 않은 것이었습니다...😂😂
Spring Boot를 사용하는 경우 ./gradlew bootBuildImage 명령어를 사용하면 프로젝트에 맞는 Dockerfile을 자동으로 생성해주는 기능도 있습니다.
Docker Compose
도커의 장점 중 하나가 컨테이너의 확장성이 좋다는 것이었습니다. 하지만 도커의 컨테이너를 여러개 띄우기 위해서는 빌드하고 푸쉬하는 등의 과정을 도커 명령어를 통해서 거쳐야 하고 이 과정을 반복하는 것은 많은 시간이 손실됩니다. Docker compose는 도커 명령어를 한 docker-compose.yml 이라는 파일에 입력해 놓고 이 파일을 실행하면 컨테이너가 실행되게 하는 방법입니다. 또한 docker-compose.yml 안에 있는 이미지 들은 자동으로 같은 네트워크에 속하게 됩니다. 도커 네트워크란 짧게 설명하자면 컨테이너간 통신을 위해서 같은 네트워크로 묶는 것으로 같은 네트워크 안에 있어야만 서로 통신이 가능합니다.
저는 Spring Boot, Redis, H2 를 모두 하나의 ec2 인스턴스에서 띄우고 싶었기 때문에 docker compose를 사용했습니다.
EC2에서 작성한 docker-compose.yml
version: '3.8'
services:
app:
image: 51taek/finding-scholarships:latest
ports:
- "8080:8080"
environment:
- SPRING_DATASOURCE_USERNAME=sa
- SPRING_DATASOURCE_PASSWORD=
- SPRING_DATA_REDIS_HOST=redis
- SPRING_DATA_REDIS_PORT=6379
depends_on:
- redis
- h2
redis:
image: redis:latest
container_name: redis
ports:
- "6379:6379"
h2:
image: oscarfonts/h2
container_name: h2
environment:
- H2_OPTIONS=-tcp -tcpAllowOthers -ifNotExists
- H2_TCP_PORT=1521
ports:
- "1521:1521"
volumes:
- ./h2/:/opt/h2-data
restart: always
다음은 제 docker-compose.yml 코드입니다. 스프링부트를 사용해보셨다면 한번쯤 봤을 만한 yml 파일 형식입니다. services에 app, redis, h2 이렇게 3개의 컨테이너를 띄우고 있습니다. 그 아래에 적혀있는것은 해당 컨테이너의 옵션입니다.
- app (spring boot) : Docker hub에 올려둔 레포에서 이미지를 가져옵니다. Github Action으로 항상 최신화된 이미지를 가져오게 하였습니다. 포트는 8080:8080을 사용합니다. 스프링부트는 항상 redis와 h2가 먼저 실행된 상태여야지만 실행 가능합니다. depends_on을 사용하면 해당 컨테이너를 먼저 실행한 후에 자기 컨테이너를 실행합니다.
- h2 : 스프링부트 프로젝트가 업데이트 될 때마다 컨테이너를 내렸다가 올려야 하는데 h2 컨테이너가 내려가면 그 안에 있던 데이터가 모두 손실되게 됩니다. volume을 이용하면 해당 경로에 데이터를 저장할 수 있습니다.
이렇게 하면 EC2에서 Docker와 Docker compose를 이용할 수 있습니다. 참고로 ec2에서 docker와 docker compose를 다운로드 한 후 docker hub에 로그인 해야 위의 기능들을 사용할 수 있습니다. 이 방법들은 인터넷에 나와있고 어렵지 않으니 쉽게 하실 수 있습니다.