资讯专栏INFORMATION COLUMN

Spring Boot 应用 Docker 化 《Spring Boot 2.0极简教程》(陈光剑)

rose / 2274人阅读

摘要:应用化极简教程陈光剑基于的企业级应用开发最佳实践前面的章节中,我们都是在环境中开发运行测试应用程序。关键字是分布式应用微服务容器虚拟化。通常,在企业项目实践中,会实现一套应用部署发布的自动化运维平台工具。

Spring Boot 应用 Docker 化
《Spring Boot 2.0极简教程》(陈光剑)
—— 基于 Gradle + Kotlin的企业级应用开发最佳实践

前面的章节中,我们都是在IDE环境中开发运行测试 Spring Boot 应用程序。在开发测试发布整个软件生命周期的过程中,我们通常需要完成打包部署发布到日常、预发、线上机器运行等运维相关工作。
本章前半部分介绍 Spring Boot 应用的打包和部署,后半部分重点介绍如何使用 Docker 来构建部署运行 Spring Boot 应用。

1.1 准备工作

首先,使用http://start.spring.io/ 创建一个打包方式为 war 的 Spring Boot Kotlin 应用,采用 Gradle 构建。点击 Generate Project 等待创建完毕,下载 zip 包,导入 IDEA 中。可以看到,相比于项目打成jar 包方式,打成 war 包的项目中多了一个用于初始化Servlet的ServletInitializer类。代码如下

class ServletInitializer : SpringBootServletInitializer() {

    override fun configure(application: SpringApplicationBuilder) : SpringApplicationBuilder {
        return application.sources(DemoPackageAndDeployApplication::class.java)
    }

}

我们知道Spring Boot 默认集成了内嵌web容器(例如 Tomcat、Jetty 等),这个时候,Spring Boot 应用支持“一键启动”,像一个普通Java程序一样,从main函数入口开始启动。现在,我们是将项目打包成war包,放到独立的web容器中。
而如果我们这个 war 包中没有配置Spring MVC 的 DispatcherServlet 的 web.xml 文件或者初始化 Servlet的类,那么这个 war 包就不会被 Tomcat识别启动 。这个时候,我们需要告诉 Tomcat 这个 war 包的启动入口。而SpringBootServletInitializer就是来完成这件事情的。
通过重写configure (SpringApplicationBuilder) 方法,使用SpringApplicationBuilder 来配置应用程序的sources类。为了测试应用运行的效果,我们在DemoPackageAndDeployApplication.kt 中添加HelloWorld REST接口方便测试

@SpringBootApplication
open class DemoPackageAndDeployApplication

fun main(args: Array) {
    runApplication(*args)
}

@RestController
class HelloWorld {
    @GetMapping(value = ["", "/"])
    fun hello(): Map {
        val result = mutableMapOf()
        result["msg"] = "Hello,World"
        result["time"] = Date()
        return result
    }
}
1.2 项目打包成可执行 jar

在 IDEA 的右边的 Gradle 工具栏中列出了 Gradle 构建项目的命令,如下图

图16-1 Gradle 构建项目的命令
我们可以直接点击 bootJar 把项目打成 jar 包。当然,在运维部署脚本中通常使用命令行: gradle bootJar 。执行日志如下

17:44:21: Executing task "bootJar"...

:compileKotlin UP-TO-DATE
:compileJava NO-SOURCE
:processResources UP-TO-DATE
:classes UP-TO-DATE
:bootJar UP-TO-DATE

BUILD SUCCESSFUL in 1s
3 actionable tasks: 3 up-to-date
17:44:22: Task execution finished "bootJar".

执行完毕,我们可以在项目的build/libs 目录下看到打好的 jar 包,如下图所示

图16-2 项目的build/libs 目录下打好的 jar 包
然后,我们就可以直接使用 java –jar 命令执行该 jar 包了
$ java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar
此时,我们浏览器访问 http://127.0.0.1:8080/ , 可以看到输出

{
  "msg": "Hello,World",
  "time": "2018-02-09T09:38:31.933+0000"
}

不过,使用java –jar 命令行来启动系统的这种方式
java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar
只要控制台关闭,服务就不能访问了。我们可以使用nohup 与 & 命令让进程在后台运行:
nohup java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar &

1.3 定制配置文件启动应用

我们也可以在启动的时候选择读取不同的配置文件。例如,在项目src/main/resources 目录下面有不同环境下的配置文件。如下图所示:

图16-3 不同环境的属性配置文件
其中,application-dev.properties中配置服务器端口号为9000:

server.port=9000

执行 bootJar重新打jar 包,执行下面的命令:

java -jar 
build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar       
--spring.profiles.active=dev

可以看到应用成功启动,并监听9000端口:

…
o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 9000 (http) with context path ""
2018-02-09 18:18:47.336  INFO 69156 --- [           main] .e.s.d.DemoPackageAndDeployApplicationKt : Started DemoPackageAndDeployApplicationKt in 6.493 seconds (JVM running for 7.589)
1.4 项目打包成 war 包

在上面创建的项目中,Gradle 构建配置文件 build.gradle 内容如下:

buildscript {
    …
}
…
apply plugin: "war"
…
configurations {
    providedRuntime
}
dependencies {
    …
    providedRuntime("org.springframework.boot:spring-boot-starter-tomcat")
}

其中,apply plugin: "war" 是使用 war 插件来完成项目的打包工作。
直接使用 gradle bootWar,即可把项目打成 war包。然后,就可以像普通J2EE项目一样部署到web容器。同样的,war 包的路径默认也是放在 build/libs 下面。
另外,如果下面这行代码还在:

@SpringBootApplication
open class DemoPackageAndDeployApplication

fun main(args: Array) {
    runApplication(*args)
}

项目打成的war包,依然支持java –jar 运行:

$ java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.war 

这个 war 包很不错,既可以直接扔到 Tomcat 容器中执行,也可以直接命令行启动运行。
提示:项目打 war包的示例项目源代码:https://github.com/EasySpring...

1.5 Spring Boot应用运维

本节简单介绍一些 Spring Boot 应用的生产运维的一些内容。

1.5.1 查看JVM参数的值

使用命令:

ps -ef|grep java 

拿到对于Java程序的pid (第2列):

  501 69156 68678   0  6:18PM ttys002    0:21.59 /usr/bin/java -jar build/libs/demo_package_and_deploy-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev

可以根据java自带的jinfo命令:

jinfo -flags 69156

来查看jar 启动后使用的是什么gc、新生代、老年代,分批的内存都是多少,示例如下:

$ jinfo -flags 69156
Attaching to process ID 69156, please wait...
Debugger attached successfully.
Server compiler detected.
JVM version is 25.40-b25
Non-default VM flags: -XX:CICompilerCount=3 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=2147483648 -XX:MaxNewSize=715653120 -XX:MinHeapDeltaBytes=524288 -XX:NewSize=44564480 -XX:OldSize=89653248 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseFastUnorderedTimeStamps -XX:+UseParallelGC 

其中的参数简单说明如表16-1所示。

表16-1 JVM参数
参数说明
-XX:CICompilerCount
最大的并行编译数
-XX:InitialHeapSize 和 -XX:MaxHeapSize
指定JVM的初始堆内存和最大堆内存大小
-XX:MaxNewSize
JVM堆区域新生代内存的最大可分配大小
-XX:+UseParallelGC
垃圾回收使用Parallel收集器
我们可以在 Java 命令行中配置我们需要的JVM参数指标。

提示:更多关于 JVM 选项参数配置参考:http://www.oracle.com/technet... 。

1.5.2 应用重启

要想重启应用,要首先找到该应用的 java进程,然后kill掉 java 进程。完成这个逻辑的shell 脚本如下:

kill -9 $(ps -ef|grep java|awk "{print $2}")

然后,再使用命令行重新启动应用即可。

1.6 使用 Docker 构建部署运行Spring Boot应用

本节介绍如何使用 Docker 来构建部署 Spring Boot 应用。

1.6.1 Docker 简介

Docker 是一个Go语言开发的开源的轻量级应用容器引擎,诞生与2013年。Docker的核心概念是:镜像、容器、仓库。关键字是: 分布式应用(distributed applications), 微服务( microservices), 容器( containers ), 虚拟化(docker virtualization)。
Docker容器“轻量级”的含义主要是跟传统的虚拟机方式的对比而言。如下图所示:

图16-4 Docker “轻量级”容器VS.传统的虚拟机方式
传统的虚拟机技术是在硬件层面实现虚拟化,需要额外的虚拟机管理软件跟虚拟机操作系统这层。而 Docker 是在操作系统层面上的虚拟化,直接使用的是本地操作系统资源,因此更加轻量级。
Docker 的主要目标是通过对应用组件的封装、分发、部署、运行等生命周期的管理,做到“一次封装,到处运行”。
Docker 是实现微服务( microservices )应用程序开发的理想选择。开发、部署和回滚都将变成“一键操作”。传统的在服务器上进行各种软件包的安装、环境配置、应用程序的打包部署、启动进程等零散的运维操作——被更高层次的“抽象”,放到了一个“集装箱”中,我们只是“开箱即用”。Docker把交付运行环境比作“海运”:OS如同一个货轮,每一个在OS上运行的软件都如同一个集装箱,用户可以通过标准化手段自由组装运行环境,同时集装箱的内容可以由用户自定义,也可以由专业人员制造——这样交付一个软件,就是一系列标准化组件集的交付,如同乐高积木,用户只需要选择合适的积木组合,最后个标准化组件就是给用户的应用程序。这就是基于docker的PaaS()产品的原型。
一个完整的Docker有以下几个部分组成:

 DockerClient客户端
 Docker Daemon守护进程
 Docker Image镜像
 DockerContainer容器
 在docker的网站上介绍了使用docker的典型场景:
 Automating the packaging and deployment of applications(应用打包部署自动化)
 Creation of lightweight, private PAAS environments(创建轻量、私有的PaaS环境)
 Automated testing and continuous integration/deployment(实现自动化测试和持续的集成/部署)
 Deploying and scaling web apps, databases and backend services(部署与扩展web app、数据库和后端服务)

由于Docker 基于LXC的轻量级虚拟化的特点,相比 KVM 之类虚拟机而言,最明显的特点就是启动快,资源占用小(轻量级)——这正是构建隔离的标准化的运行环境,轻量级的PaaS,构建自动化测试和持续集成环境,以及一切可以横向扩展的应用等场景的最佳选择。
提示:更多关于 Docker 的介绍参考: https://docs.docker.com 。Dockers Github 项目空间是:https://github.com/docker

1.6.2 环境搭建

本小节介绍如何搭建 Docker 环境。
安装 Docker
去 docker 官网 https://docs.docker.com/install/ 下载对应的操作系统上的安装包。安装完毕,打开Docker运行,可以看到Mac 系统菜单栏上的显示的 Docker 应用信息如下

图16-5 Mac 系统菜单栏上的 Docker 图标
想知道 docker 提供了哪些命令行操作吗?执行docker help即可看到一个详细的命令说明。例如,在命令行查看 Docker 版本信息:

$ docker version
Client:
 Version: 17.12.0-ce
 API version: 1.35
 Go version:  go1.9.2
 Git commit:  c97c6d6
 Built: Wed Dec 27 20:03:51 2017
 OS/Arch: darwin/amd64

Server:
 Engine:
  Version:  17.12.0-ce
  API version:  1.35 (minimum version 1.12)
  Go version: go1.9.2
  Git commit: c97c6d6
  Built:  Wed Dec 27 20:12:29 2017
  OS/Arch:  linux/amd64
  Experimental: false

查看详细的 docker 信息

$ docker info
Containers: 0
 Running: 0
 Paused: 0
 Stopped: 0
Images: 1
Server Version: 17.12.0-ce
…

从仓库 pull Java 环境镜像
使用sudo docker pull java命令从 Docker 官方仓库获取 Java 运行环境镜像:

$ sudo docker pull java
Password:
Using default tag: latest
latest: Pulling from library/java
...
bb9cdec9c7f3: Pull complete 
Digest: sha256:c1ff613e8ba25833d2e1940da0940c3824f03f802c449f3d1815a66b7f8c0e9d
Status: Downloaded newer image for java:latest

下载完毕之后,可以通过docker images命令查看镜像列表:

$ docker images
REPOSITORY TAG     IMAGE ID            CREATED         SIZE
Java       latest  d23bdf5b1b1b        12 months ago   643MB

可以看到,本地镜像中已经有了 java 运行环境。

1.7 Spring Boot 项目 Docker化实战

本节介绍如何把上面的 Spring Boot 项目 Docker 容器化。过程主要分为如下3步:
1)添加 docker构建插件。
2)配置Dockerfile文件创建自定义的镜像。
3)构建Docker镜像。
下面我们就来分别详细介绍。

1.7.1 添加 docker 构建插件

在 Gradle 项目构建配置文件build.gradle 中添加com.palantir.docker插件:

buildscript {
    ext {
        kotlinVersion = "1.2.20"
        springBootVersion = "2.0.0.RC1"
    }
    repositories {
        // gradle-docker plugin repo
        maven { url "https://plugins.gradle.org/m2/" }
        ...
    }
    dependencies {
        ...
        classpath("gradle.plugin.com.palantir.gradle.docker:gradle-docker:0.17.2")
    }
}


apply plugin: "com.palantir.docker"

...

docker {
    name "${project.group}/${jar.baseName}"
    files jar.archivePath
    buildArgs(["JAR_FILE": "${jar.archiveName}"])
}

其中,buildArgs(["JAR_FILE": "${jar.archiveName}"]) 中配置的"JAR_FILE": "${jar.archiveName}" 是我们的 Spring Boot 项目打成 jar包的名称,会传递到Dockerfile文件中使用(下一步骤中将会看到)。
提示:关于Docker 插件com.palantir.docker的介绍参考文档: https://github.com/palantir/g...

这个插件发布在https://plugins.gradle.org/m2...,所以我们添加 maven 仓库的依赖

    repositories {
        // gradle-docker plugin repo
        maven { url "https://plugins.gradle.org/m2/" }
        ...
    }

gradle-docker提供的版本有:
https://plugins.gradle.org/m2...

1.7.2 配置 Dockerfile 文件创建自定义的镜像

Dockerfile文件放置在项目根目录:

图16-6 Dockerfile文件放置在项目根目录
Dockerfile文件内容如下:

FROM java:latest
VOLUME /tmp
ARG JAR_FILE
ADD ${JAR_FILE} app.jar
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app.jar"]

配置构建参数JAR_FILE,这里的JAR_FILE是在 build.gradle 中buildArgs中配置的

docker {
    name "${project.group}/${jar.baseName}"
    files jar.archivePath
    buildArgs(["JAR_FILE": "${jar.archiveName}"])
} 
ADD ${JAR_FILE} app.jar

将文件${JAR_FILE}拷贝到docker container的文件系统对应的路径app.jar

ENTRYPOINT ["java",
"-Djava.security.egd=
file:/dev/./urandom",
"-jar",
"/app.jar"]

Docker container启动时执行的命令。注意:一个Dockerfile中只能有一条ENTRYPOINT命令。如果多条,则只执行最后一条。

-Djava.security.egd=file:/dev/./urandom

配置 JRE 使用非阻塞的 Entropy Source。SecureRandom generateSeed 使用 /dev/random 生成种子。但是 /dev/random 是一个阻塞数字生成器,如果它没有足够的随机数据提供,它就一直等,这迫使 JVM 等待。通过在 JVM 启动参数中配置这么一行:-Djava.security.egd=file:/dev/./urandom 解决这个阻塞问题。

Dockerfile是一个文本格式的配置文件,我们可以使用Dockerfile文件快速创建自定义的镜像。Dockerfile支持的丰富的运维指令。这些指令分为4部分:

 基础镜像信息
 维护者信息
 镜像操作指令
 容器启动时的执行指令
...

1.7.5 启动 Docker 应用镜像运行

直接在命令行执行:

$ docker run -p 8080:9000 -t com.easy.springboot/demo_package_and_deploy

即可启动我们构建发布在 Docker 镜像仓库中的Spring Boot 应用镜像了。

1.7.6 端口映射

我们的 Spring Boot 应用镜像运行在 Docker容器沙箱环境中,端口号是9000,作为外部Host OS环境要访问这个服务, 需要添加TCP端口映射:把本机8080端口映射到 Docker 容器端口9000,如下图所示:

图16-7 把本机8080端口映射到 Docker 容器端口9000
其中:
 -p 是将容器的端口9000映射到 docker 所在操作系统的端口8080;
 -t 是打开一个伪终端,以便后续可以进入查看控制台 log。
使用 docker ps 命令查看运行中的容器:

$ docker ps
CONTAINER ID        IMAGE                                         COMMAND                  CREATED             STATUS              PORTS                    NAMES
36fbfaf05359        com.easy.springboot/demo_package_and_deploy   "java -Djava.securit…"   25 minutes ago      Up 25 minutes       0.0.0.0:8080->9000/tcp   infallible_kare

……
然后,执行 push 命令即可:

$ docker push com.easy.springboot/demo_package_and_deploy

提示:本节项目源代码:https://github.com/EasySpring...

1.8 本章小结

本章简单介绍了Spring Boot项目的打包、分环境运行、生产运维等操作。通常,在企业项目实践中,会实现一套 Spring Boot应用部署发布的自动化运维平台工具。本章还给出了一个完整的 Spring Boot项目 Docker 化的实战案例。
经过前面的学习,相信您已经对如何使用基于 Kotlin 编程语言的 Spring Boot项目开发有了一个比较好的掌握。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69070.html

相关文章

  • Spring Boot 应用 Docker Spring Boot 2.0极简教程》(陈光)

    摘要:应用化极简教程陈光剑基于的企业级应用开发最佳实践前面的章节中,我们都是在环境中开发运行测试应用程序。关键字是分布式应用微服务容器虚拟化。通常,在企业项目实践中,会实现一套应用部署发布的自动化运维平台工具。 Spring Boot 应用 Docker 化 《Spring Boot 2.0极简教程》(陈光剑)—— 基于 Gradle + Kotlin的企业级应用开发最佳实践 前面的章节中,...

    Donne 评论0 收藏0
  • 初探Kotlin+SpringBoot联合编程

    摘要:是一门最近比较流行的静态类型编程语言,而且和一样同属系。这个生成的构造函数是合成的,因此不能从或中直接调用,但可以使用反射调用。 showImg(https://segmentfault.com/img/remote/1460000012958496); Kotlin是一门最近比较流行的静态类型编程语言,而且和Groovy、Scala一样同属Java系。Kotlin具有的很多静态语言...

    xiaokai 评论0 收藏0
  • Spring Boot Admin 2.0开箱体验

    摘要:概述在我之前的应用监控实战一文中,讲述了如何利用版本来可视化地监控应用。接下来我们就来创建一个待监控的示例。 showImg(https://segmentfault.com/img/remote/1460000015671446); 概述 在我之前的 《Spring Boot应用监控实战》 一文中,讲述了如何利用 Spring Boot Admin 1.5.X 版本来可视化地监控 ...

    CastlePeaK 评论0 收藏0
  • Spring笔记1——极简入门教程

    摘要:创建工程将框架代码包解压后放到工作目录。方便起见,本教程使用为例。添加创建一个,负责响应相关的业务请求。添加标注在中,对输入参数进行校验通常使用标注。在本教程,我们将实现的增和查的工作。创建用户用户名重启并提交创建请求。 环境准备 系统:MacOS 开发:IntelliJ IDEA 语言:Java8 其它:Mysql、Redis 脚手架代码 Spring提供了一个创建项目脚手架的官...

    ChristmasBoy 评论0 收藏0
  • 写这么多系列博客,怪不得找不到女朋友

    摘要:前提好几周没更新博客了,对不断支持我博客的童鞋们说声抱歉了。熟悉我的人都知道我写博客的时间比较早,而且坚持的时间也比较久,一直到现在也是一直保持着更新状态。 showImg(https://segmentfault.com/img/remote/1460000014076586?w=1920&h=1080); 前提 好几周没更新博客了,对不断支持我博客的童鞋们说声:抱歉了!。自己这段时...

    JerryWangSAP 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<