ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링 부트(Spring Boot) + 몽고디비(Mongo DB) 도커(Docker)에 올리기
    웹 어플리케이션 2019. 3. 5. 15:44

    이번 포스트에서는 docker-compose.yml을 이용해 스프링부트와 몽고디비를 도커에 올려보도록 하겠다. 이번 포스트는 이전 포스트에 많이 의존하므로 스프링 부트에 도커 올리기부터 따라 오는 것을 추천한다. 이제 드디어 임베디드 몽고디비에서 벗어나, docker compose를 이용해 실제 몽고디비를 도커에 올리고, 스프링 부트가 이 몽고디비를 이용하도록 설정 해 보도록 한다.

    예상 독자

    목표

    • docker-compose.yml
    • Spring Boot - build.gradle
    • Spring Boot - application.properties
    • docker-compose up

    docker-compose.yml

    하나의 웹 어플리케이션을 만들기 위해서는 여러가지의 컴포넌트들이 필요하다. 우리가 만든 To Do App의 경우 일단 데이터베이스가 필요하고, backend API서비스가 필요하고, Frontend 서비스가 필요하다. 그리고 이런 웹 앱을 전부 deploy 하기 위해서는 데이터베이스, API, frontend 서버를 각각 환경설정하고 코드를 deploy해야 한다. 

    도커의 compose는 각 컴포넌트의 Configuration과 dependency등을 yml이라는 하나의 파일에 정의하고 이 파일에 작성된대로 도커에 올려주는(deploy해 주는) 툴이다. 포트는 몇이고, 컨테이너의 이름은 뭐이고, 로그 파일은 어디에 저장하고 이런 설정들을 yml파일 안에서 명시하면 그 내용(stack이라고 부른다) 그대로 docker compose를 이용해 한번에 docker에 올릴 수 있다. 

    docker-compose.yml파일을 MySpringApp (또는 스프링 부트 Dockerfile이 있는 디렉토리 경로)에 추가하도록 하자.

    docker-compose.yml

    version: "3"
    services:
    mongodb:
    image: mongo:3.2.4
    environment:
    - MONGO_DATA_DIR=/data/db
    - MONGO_LOG_DIR=/dev/null
    volumes:
    - ./data/db:/data/db
    command: mongod --smallfiles --logpath=/dev/null # --quiet
    container_name: "mongodb"
    ports:
    - 27017:27017
    command: --smallfiles
    app:
    image: to-do-springboot # 우리가 Dockerfile에서 지정했던 app 이름
    ports:
    - 5000:5000
    links:
    - mongodb

    위를 보면 services아래에 mongodb가 정의된 것을 확인 할 수 있다. 내부에는 이 컨테이너(mongodb)가 어떤 이미지를 사용 할 것인지, 데이터(db)는 어느 경로에, 로그는 어느 경로에 저장할 건지, 실행 커맨드, 컨테이너 이름, 포트등을 정의한다. 그리고 그 아래에 보면 app이 있다. 이 app이 바로 우리가 만들던 스프링 부트 앱이다. 따라서 이 app은 image로 to-do-springboot를 사용하고 포트는 5000을 사용한다. 주의 할 것은 links에 mongodb가 있다는 것이다. links로 mongodb를 해 주어야 도커 컨테이너가 "아 이 컨테이너(to-do-springboot)는 옆 컨테이너인 mongodb를 사용하니까 이 컨테이너에서 오는 요청은 mongodb가 받아들여도 괜찮아."라고 알아듣는다.

    참고를 위해 스프링 부트 프로젝트의 Dockerfile도 올린다.

    Dockerfile

    # Start with a base image containing Java runtime
    FROM java:8

    # Add Author info
    LABEL maintainer="f.softwareengineer@gmail.com"

    # Add a volume to /tmp
    VOLUME /tmp

    # Make port 8080 available to the world outside this container
    EXPOSE 5000

    # The application's jar file
    ARG JAR_FILE=build/libs/MySpringApp-0.0.1-SNAPSHOT.jar

    # Add the application's jar to the container
    ADD ${JAR_FILE} to-do-springboot.jar

    # Run the jar file
    ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/to-do-springboot.jar"]

    Spring Boot - build.gradle

    다음으로 임베디드 몽고디비 관련 설정을 지우고 실제 몽고디비를 사용하기 위해 build.gradle을 수정해야 한다. 아래처럼 embeded mongo관련된 부분을 모두 주석처리하기 바란다.

    plugins {
    id 'org.springframework.boot' version '2.1.3.RELEASE'
    id 'java'
    }

    apply plugin: 'io.spring.dependency-management'

    group = 'com.fsoftwareengineer'
    version = '0.0.1-SNAPSHOT'
    sourceCompatibility = '1.8'

    repositories {
    mavenCentral()
    }

    dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    compile 'org.springframework.boot:spring-boot-starter-logging'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.projectlombok:lombok:1.18.4'
    runtimeOnly 'org.springframework.boot:spring-boot-devtools'
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
    // mongo db related
    implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
    // compile('cz.jirutka.spring:embedmongo-spring:1.3.1')
    // compile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
    }

    Spring Boot - application.properties

    그 다음으로 application.properties에서 스프링 부트가 사용할 몽고디비의 경로를 지정 해 주어야 한다.

    application.properties

    server.port = 5000
    spring.data.mongodb.uri= mongodb://mongodb:27017/ToDoRepository

    주의 할 점은 보통 로컬 몽고디비에 연결 할 때 mongodb://localhost:27017/xxxx이렇게 연결하는데, 도커를 이용하면 그렇게 할 수 없다는 점이다. 도커 컨테이너는 컨테이너 자체가 분리된 하나의 환경으로 인식되기 때문에 도커 컨테이너의 localhost과 스프링부트의 localhost는 같은 localhost가 아니다. 따라서 localhost를 사용하는 부분은 컨테이너 이름으로 대체 해 줘야 도커가 이를 해석해 몽고디비가 있는 도커로 연결 해 줄 수 있다.

    docker-compose up

    여기까지 다 됐으면 이제 도커에 올려 볼 차례이다. 터미널 또는 파워쉘을 켜서 도커가 이미 실행중인지 확인 하라. 또 도커에 이미 to-do-springboot가 실행중인지 확인하라. 실행중이라면 종료하라.

    실행중인 도커 컨테이너를 모두 보여주는 명령어

    docker ps

    실행중인 컨테이너를 멈추는 명령어

    docker stop <container_id>

    도커 컨테이너에 to-do-springboot가 돌지 않는다면 이제 스프링부트가 있는 디렉토리로 cd해 들어간다. ls를 이용해 Dockerfile과 docker-compose.yml이 있는지 확인해라. 나의 프로젝트는 IdeaProjects/MySpringApp이므로 이 디렉토리로 들어갔지만 여러분의 경로는 다를 수 있음을 유의해라.

    ➜ cd ~/IdeaProjects/MySpringAppls
    Dockerfile data gradlew.bat
    bin docker-compose.yml out
    build gradle settings.gradle
    build.gradle gradlew src

    일단 우리가 build.gradle과 application.properties를 수정 했으므로 빌드를 다시 해야 한다.

    gradle clean build

    BUILD SUCCESSFUL in 6s
    6 actionable tasks: 6 executed

    빌드가 성공적으로 끝나면 위와 같은 결과가 나올 것이다. 소스코드 빌드가 끝났다면 이제 도커 이미지를 빌드 해 보자.

    docker build -t to-do-springboot .

    ...
    ---> 3c534005d2ae
    Successfully built 3c534005d2ae
    Successfully tagged to-do-springboot:latest

    뭐라뭐라 하고 이렇게 마지막에 Successfully tagged to-do-springboot:latest라고 뜨면 새 이미지가 빌드 된 것이다. docker images로 확인 해 보면 CREATED컬럼을 통해 방금 빌드 된 이미지가 맞음을 확인 할 수 있다.

    docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    to-do-springboot latest 3c534005d2ae 58 seconds ago 665MB

    이제 docker compose를 이용해 몽고디비와 스프링부트를 도커에 올려보자.

    docker-compose up
    Starting mongodb ... done
    Recreating myspringapp_app_1 ... done
    Attaching to mongodb, myspringapp_app_1
    ...
    mongodb | 2019-03-05T06:13:50.658+0000 I NETWORK [initandlisten] waiting for connections \ on port 27017
    mongodb | 2019-03-05T06:13:51.009+0000 I FTDC \
    [ftdc] Unclean full-time diagnostic data capture shutdown detected, found interim file,\
    some metrics may have been lost. OK
    app_1 |
    app_1 | . ____ _ __ _ _
    app_1 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
    app_1 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
    app_1 | \\/ ___)| |_)| | | | | || (_| | ) ) ) )
    app_1 | ' |____| .__|_| |_|_| |_\__, | / / / /
    app_1 | =========|_|==============|___/=/_/_/_/
    app_1 | :: Spring Boot :: (v2.1.3.RELEASE)
    app_1 |
    app_1 | 2019-03-05 06:13:52.336 INFO 1 --- [main] c.f.MySpringApp.MySpringAppApplication ... Started MySpringAppApplication in 7.465 seconds (JVM running for 8.954)

    위처럼 뜨기 시작하고 아무런 에러도 나지 않는다면 성공 한 것이다. 혹시 모르니 다른 터미널을 켜 docker ps를 이용해 컨테이너들이 실행중인지 확인 해 보자.

    docker ps
    CONTAINER ID IMAGE COMMAND CREATED
    48505d97ce1f to-do-springboot "java -Djava.securit…" 13 minutes ago
    172790540fe8 mongo:3.2.4 "/entrypoint.sh --sm…" 2 hours ago

    이제 node.js쪽의 프론트엔드 서버를 실행시켜보자. 나는 to-do-frontend가 nodeJsProjects아래 있지만 여러분의 경로는 다를 수 있음을 유의해라.

    cd ~/nodeJsProjects/to-do-frontend npm run dev

    이제 브라우저 상에서 to do item을 몇개 넣어 보자. 그 후 Spring Boot 창 부분으로 돌아가 ctrl + c를 눌러 종료 해 보아라. 그리고 docker-compose up을 이용해 다시 도커에 올려보아라. 기존 임베디드 몽고디비에서는 스프링부트가 실행 될 때 마다 몽고디비가 새로 만들어지기 때문에 아무리 to do item을 저장해도 스프링 부트를 껐다 키면 모든게 사라졌다. 하지만 이제는 실제 몽고디비를 사용하므로 스프링부트와 몽고디비를 껐다 켜도 이전에 저장했던 to do item이 사라지지 않을 것이다. 만약 껐다 켰는데 아까 만든 to do item이 사라졌다면 문제가 있는 것이다.

    이번 포스트를 이용해 스프링부트와 몽고디비를 도커에 올려보았다. 로컬 환경에서 개발하는데 왜 도커를 깔고 그 위에 스프링부트 서버와 몽고디비를 올리는걸까? 로드맵을 잠깐 설명하자면 우리는 프론트엔드 서버, 백엔드 서버, 몽고디비 이렇게 세 컴포넌트를 모두 도커 이미지로 만들어 도커에 올릴 것이다. 그리고 이 이미지들을 Docker Repository(Registry)에 업로드 할 것이다. 이후 아마존 EC2 서버위에 도커를 설치하고, EC2에서 이전에 업로드 한 도커 이미지를 내려받아 docker-compose up을 이용해 도커에 올릴 것이다. 이렇게 하면 어려운 환경설정 없이 손쉽게 production환경을 구축 가능하다.

    도커가 없었다면 어떻게 해야 하는가? 일단 서버에 접속해서 같은 버전의 몽고디비를 설치해야한다. 그리고 스프링부트 코드를 받아와 빌드 이 서버에서 빌드해야한다. 마찬가지로 frontend를 deploy하기 위해 node.js를 설치하고, 우리가 설치했던 모든 패키지를 다시 설치해야한다. 이것이 바로 도커의 도드라지는 장점이다. 도커를 이용하면 이런 Operational Overhead를 줄일 수 있다. 그래서 로컬 환경임에도 불구하고 이후 프로덕션 deployment를 위해 이런 실습을 하는 것이다. 부차적으로는 내 컴퓨터에 디비같은 걸 설치하고 싶지 않다는 이유가 있다. 

    댓글

f.software engineer @ All Right Reserved