1. 程式人生 > >用 Docker、Gradle 來構建、運行、發布一個 Spring Boot 應用

用 Docker、Gradle 來構建、運行、發布一個 Spring Boot 應用

repo com ase exp base ide 默認 相關 conf

本文演示了如何用 Docker、Gradle 來構建、運行、發布來一個 Spring Boot 應用。
Docker 簡介
Docker 是一個 Linux 容器管理工具包,具備“社交”方面,允許用戶發布容器的 image (鏡像),並使用別人發布的 image。Docker image 是用於運行容器化進程的方案,在本文中,我們將構建一個簡單的 Spring Boot 應用程序。
前置條件
JDK 1.8+
Gradle 2.3+
Docker 最新版。有關 Docker 在的安裝, 如果你的電腦不是 Linux 系統,最好裝個虛擬機,在虛擬機裏面裝個 Linux ,因為 Docker 的依賴 Linux。
用 Gradle 構建項目
創建目錄結構
項目的目錄結構因符合 Gradle 的約定。

在 *nix 系統下執行 mkdir -p src/main/java/docker_spring_boot ,生產如下結構 :

└── src
└── main
└── java
└── com
└── waylau
└── docker_spring_boot

創建 Gradle 構建文件

build.gradle
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath(‘org.springframework.boot:spring-boot-gradle-plugin:1.3.3.RELEASE‘)

// tag::build[]
classpath(‘se.transmode.gradle:gradle-docker:1.2‘)
// end::build[]
}
}

apply plugin: ‘java‘
apply plugin: ‘eclipse‘
apply plugin: ‘idea‘
apply plugin: ‘spring-boot‘
// tag::plugin[]
apply plugin: ‘docker‘
// end::plugin[]

// This is used as the docker image prefix (org)
group = ‘gregturn‘

jar {
baseName = ‘docker-spring-boot-gradle‘

version = ‘1.0.0‘
}

// tag::task[]
task buildDocker(type: Docker, dependsOn: build) {
push = true
applicationName = jar.baseName
dockerfile = file(‘src/main/docker/Dockerfile‘)
doFirst {
copy {
from jar
into stageDir
}
}
}
// end::task[]

repositories {
mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
testCompile("org.springframework.boot:spring-boot-starter-test")
}

task wrapper(type: Wrapper) {
gradleVersion = ‘2.3‘
}
Spring Boot gradle plugin 提供了很多方便的功能:

它收集的類路徑上所有 jar 文件,並構建成一個單一的、可運行的“über-jar”(德語,相關解釋可以移步至 http://stackoverflow.com/questions/11947037/what-is-an-uber-jar),這使得它更方便地執行和傳輸服務。

編寫 Spring Boot 應用
編寫一個簡單的 Spring Boot 應用 :、

src/main/java/com/waylau/docker_spring_boot/Application.java:
package com.waylau.docker_spring_boot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**

  • 主應用入口
  • @author <a href="http://waylau.com">waylau.com</a>
  • @date 2016年3月19日*/
    @SpringBootApplication
    br/>*/
    @SpringBootApplication
    public class Application {

    @RequestMapping("/")
    public String home() {
    return "Hello Docker World."

    • "Welcome to <a href=‘http://waylau.com‘>waylau.com</a></li>";
      }

    public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
    }

}

解釋下上面的代碼:
類用 @SpringBootApplication @RestController 標識,可用 Spring MVC 來處理 Web 請求。
@RequestMapping 將 / 映射到 home() ,並將”Hello Docker World” 文本作為響應。
main() 方法使用 Spring Boot 的 SpringApplication.run() 方法來啟動應用。

運行程序
使用 Gradle
編譯:
gradle build
運行:
java -jar build/libs/docker-spring-boot-gradle-1.0.0.jar
訪問項目

如果程序正確運行,瀏覽器訪問 http://localhost:8080/,可以看到頁面 “Hello Docker World.” 字樣。

將項目容器化
Docker 使用 Dockerfile 文件格式來指定 image 層,

創建文件 src/main/docker/Dockerfile:

FROM frolvlad/alpine-oraclejdk8:slim
VOLUME /tmp
ADD docker-spring-boot-gradle-1.0.0.jar app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]
解釋下這個配置文件:

VOLUME 指定了臨時文件目錄為/tmp。其效果是在主機 /var/lib/docker 目錄下創建了一個臨時文件,並鏈接到容器的/tmp。改步驟是可選的,如果涉及到文件系統的應用就很有必要了。/tmp目錄用來持久化到 Docker 數據文件夾,因為 Spring Boot 使用的內嵌 Tomcat 容器默認使用/tmp作為工作目錄
項目的 jar 文件作為 “app.jar” 添加到容器的
ENTRYPOINT 執行項目 app.jar。為了縮短 Tomcat 啟動時間,添加一個系統屬性指向 “/dev/urandom” 作為 Entropy Source
構建 Docker Image
執行構建成為 docker image:

gradle build buildDocker

運行
運行 Docker Image

docker run -p 8080:8080 -t waylau/docker-spring-boot-gradle

[root@waylau spring-boot]# docker run -p 8080:8080 -t waylau/docker-spring-boot-gradle

. _ _
/\ /
‘_ () \ \ \ \
( ( )\
| ‘ | ‘| | ‘ \/ ` | \ \ \ \
\/ _)| |)| | | | | || (| | ) ) ) )
‘ |__
| .|| ||| |\, | / / / /
=========|_|==============|__/=////
:: Spring Boot :: (v1.3.3.RELEASE)

2016-03-20 08:45:51.276 INFO 1 --- [ main] c.waylau.docker_spring_boot.Application : Starting Application v1.0.0 on 048fb623038f with PID 1 (/app.jar started by root in /)
2016-03-20 08:45:51.289 INFO 1 --- [ main] c.waylau.docker_spring_boot.Application : No active profile set, falling back to default profiles: default
2016-03-20 08:45:51.722 INFO 1 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@669af5fc: startup date [Sun Mar 20 08:45:51 GMT 2016]; root of context hierarchy
2016-03-20 08:45:54.874 INFO 1 --- [ main] o.s.b.f.s.DefaultListableBeanFactory : Overriding bean definition for bean ‘beanNameViewResolver‘ with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/ErrorMvcAutoConfiguration$WhitelabelErrorViewConfiguration.class]] with [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter; factoryMethodName=beanNameViewResolver; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoConfigurationAdapter.class]]
2016-03-20 08:45:57.893 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http)
2016-03-20 08:45:57.982 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
2016-03-20 08:45:57.984 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.0.32
2016-03-20 08:45:58.473 INFO 1 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2016-03-20 08:45:58.473 INFO 1 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 6877 ms
2016-03-20 08:45:59.672 INFO 1 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: ‘dispatcherServlet‘ to [/]
2016-03-20 08:45:59.695 INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ‘characterEncodingFilter‘ to: [/]
2016-03-20 08:45:59.701 INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ‘hiddenHttpMethodFilter‘ to: [/
]
2016-03-20 08:45:59.703 INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ‘httpPutFormContentFilter‘ to: [/]
2016-03-20 08:45:59.703 INFO 1 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: ‘requestContextFilter‘ to: [/
]
2016-03-20 08:46:00.862 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@669af5fc: startup date [Sun Mar 20 08:45:51 GMT 2016]; root of context hierarchy
2016-03-20 08:46:01.166 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/]}" onto public java.lang.String com.waylau.docker_spring_boot.Application.home()
2016-03-20 08:46:01.189 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2016-03-20 08:46:01.190 INFO 1 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2016-03-20 08:46:01.302 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-20 08:46:01.302 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/
] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-20 08:46:01.438 INFO 1 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2016-03-20 08:46:01.833 INFO 1 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2016-03-20 08:46:02.332 INFO 1 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-03-20 08:46:02.343 INFO 1 --- [ main] c.waylau.docker_spring_boot.Application : Started Application in 13.194 seconds (JVM running for 15.828)

訪問項目
如果程序正確運行,瀏覽器訪問 http://localhost:8080/,可以看到頁面 “Hello Docker World.” 字樣。

推送 image 到 Docker Hub
首先,你在 Docker Hub 要有註冊賬號,且創建了相應的庫;

其次,docker 推送前,先要登錄,否則報unauthorized: access to the requested resource is not authorized的錯誤

執行:
docker login
輸出為:
[root@waylau spring-boot]# docker login
Username: waylau
Password:
Email: [email protected]
WARNING: login credentials saved in /root/.docker/config.json
Login Succeeded
執行推送
docker push waylau/docker-spring-boot-gradle
[root@waylau spring-boot]# docker push waylau/docker-spring-boot-gradle
The push refers to a repository [docker.io/waylau/docker-spring-boot-gradle]
751d29eef02e: Layer already exists
4da3741f39c7: Pushed
5f70bf18a086: Layer already exists
7e4d0cb13643: Layer already exists
8f045733649f: Layer already exists
latest: digest: sha256:eb4d5308ba1bb27489d808279e74784bda6761b3574f4298d746abba59b35164 size: 9415
鏡像加速器
Docker Hub 在國外,有時候拉取 Image 極其緩慢,可以使用國內的鏡像來實現加速

阿裏雲
echo "DOCKER_OPTS=\"--registry-mirror=https://yourlocation.mirror.aliyuncs.com\"" | sudo tee -a /etc/default/docker
sudo service docker restart
其中 https://yourlocation.mirror.aliyuncs.com 是您在阿裏雲註冊後的專屬加速器地址:

DaoCloud

sudo echo “DOCKER_OPTS=\”\$DOCKER_OPTS –registry-mirror=http://your-id.m.daocloud.io -d\”” >> /etc/default/docker
sudo service docker restart
其中 http://your-id.m.daocloud.io 是您在 DaoCloud 註冊後的專屬加速器地址:

用 Docker、Gradle 來構建、運行、發布一個 Spring Boot 應用