스프링 부트를 도커 이미지로 만드는 작업(dockerize)을 github, travis-ci, dockerhub를 이용하여 자동화 해보았다. 각 서비스에는 가입을 하였고, travis-ci와 github을 연결해두었다.
참고 링크
- 스프링부트 데모 깃헙레파지토리 : https://github.com/voyagerwoo/vw.demo.helloworld
- 스프링 부트 도커 이미지 - 도커 허브 : https://hub.docker.com/r/voyagerwoo/vw.demo.helloworld
- spring boot with docker : https://spring.io/guides/gs/spring-boot-docker/
1. 프로젝트 만들기
intellij를 이용하여 프로젝트를 만들었다. gradle을 사용하여 라이브러리 의존성 관리를 했다. gradle은 따로 설치하지 않고 gradle wrapper(./gradlew
)를 사용했다. 그리고 java나 kotlin이 아닌 groovy 언어를 사용했다. 그 이유는 함수형 사고라는 책을 보고 groovy라는 언어가 개인적으로 참으로 편리하다고 생각했기 때문이다.
그루비로 간단하게 컨트롤러를 작성한다.
package vw.demo.helloworld
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/hello")
class HelloworldController {
@GetMapping
String hello(String name) {
return "Hello, ${name? name: 'world'}!"ㅁ
}
}
참고!
코드를 간결하게 하기 위해서 String hello(String name = "world")
이렇게 파라미터에 default value를 줄 수 있다. 그런데 @GetMapping
같이 RequestMapping 어노테이션이 붙은 메서드의 파라미터에 default value를 주면 java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'helloworldController' method
이런 에러가 난다. 왜냐하면 defalut value가 컴파일 되면 아래처럼 두개의 메서드로 나눠지기 때문이다. 조심하시길.
@GetMapping
String hello(String name = "world") {
return "Hello, ${name}!"
}
@GetMapping
public String hello(String name) {
CallSite[] var2 = $getCallSiteArray();
return (String)ShortTypeHandling.castToString(new GStringImpl(new Object[]{name}, new String[]{"Hello, ", "!"}));
}
@GetMapping
public String hello() {
CallSite[] var1 = $getCallSiteArray();
return !__$stMC && !BytecodeInterface8.disabledStandardMetaClass() ? this.hello((String)"world") : this.hello((String)"world");
}
Dockerfile 만들기
기존에 처음 스프링 부트로 도커이미지를 만들었을 때는 빌드시에 코드를 모두 도커 이미지로 옮기고 도커이미지 내에서 빌드하고 실행했다. jar나 war를 만들지도 않고 그냥 스프링부트의 메이븐 플러그인을 사용해서 mvn spring-boot:run
을 실행했다. 그랬더니 단점이 매번 도커파일이 빌드될 때 라이브러리를 다운로드하고 용량도 매우 커졌다.
그랬는데 이번 spring boot with docker 공식문서를 보니 jar로 빌드한 다음에 해당 jar만 도커 이미지에 넣고 빌드하는 것이었다. 그렇게 되면 용량도 가볍고 빌드 서버에 있는 ~/.m2
디렉토리에 라이브러리들이 캐싱될테니 속도도 빨라지는 것이다. 그래서 나도 이렇게 하기로 했다.
FROM openjdk:8-jdk-alpine
VOLUME /tmp
RUN echo ${JAR_FILE}
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
이 도커파일은 도커를 빌드할때 --build-arg JAR_FILE=jarfile_path/app.jar
이처럼 옵션을 작성해야한다. 옵션 없이도 빌드가 성공한다. 그러나 옵션이 없을 경우에는 도커를 실행할 때 에러가 난다. 조심해야한다.
(-Djava.security.egd
옵션에 대한 설명은 http://lng1982.tistory.com/261 이 링크를 참고)
travis 설정
travis를 github과 연동을 하고 repository를 추가하면 그냥 설정은 끝이다. 뭐가 더 있겠지만은 딱히 필요가 없어서 건드리지 않았다.
이제 부터 해야할 일은 아래 두가지 이다.
1. .travis.yml 코드 작성
travis는 소스가 푸시가 되면 .travis.yml 코드를 가지고 빌드를 진행한다.
language: java
jdk: oraclejdk8
sudo: required
cache:
directories:
- .autoconf
- $HOME/.m2
services:
- docker
script:
# BUILD
- ./gradlew build
# GET NAME
- PRJ_GROUP=$(gradle properties -q | grep "group:" | awk '{print $2}')
- PRJ_NAME=$(gradle properties -q | grep "name:" | awk '{print $2}')
- PRJ_VERSION=$(gradle properties -q | grep "version:" | awk '{print $2}')
- echo "## PROJECT_GROUP - ${PRJ_GROUP}"
- echo "## PROJECT_NAME - ${PRJ_NAME}"
- echo "## PROJECT_VERSION - ${PRJ_VERSION}"
- PRJ_JAR=${PRJ_NAME}-${PRJ_VERSION}.jar
# DOCKER BUILD
- docker build -t ${PRJ_GROUP}.${PRJ_NAME}:latest --build-arg JAR_FILE=build/libs/${PRJ_JAR} ./
# PUSH TO DOCKER HUB
- docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}
- docker tag vw.demo.helloworld:latest voyagerwoo/vw.demo.helloworld:latest
- docker tag vw.demo.helloworld:latest voyagerwoo/vw.demo.helloworld:${TRAVIS_JOB_NUMBER}
- docker push voyagerwoo/vw.demo.helloworld:latest
- docker push voyagerwoo/vw.demo.helloworld:${TRAVIS_JOB_NUMBER}
2. 환경변수 설정
우선 오른쪽 상단에 햄버거 버튼을 누르고 그다음 settings 버튼을 누른다. 그 다음 여기에서 필요한 환경변수를 넣어준다. 여기에 기입하고 Add 버튼을 누르게 되면 자동으로 암호화 되서 저장이 된다. 나 같은 경우는 docker hub에 로그인 하기 위한 환경변수들을 추가했다.
정리
이렇게 github, travis, dockerhub를 이용하여 스프링 부트 도커 이미지 만들기 자동화를 했다.
세 도구 모두 퍼블릭으로 사용할 경우 무료이다.