运行中的 SpringBoot
认识 Spring Boot 的各类 Actuator Endpoint
Actuator
目的:监控并管理应用程序
访问方式
HTTP
JMX
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
一些常用的 Endpoint
如何访问 Actuator Endpoint
HTTP 访问
/actuator/<Actuator ID>
端口与路径(配置 Actuator 发布的端口 和 应用程序分开,实现应用与管理的隔离,例如:应用发布在 8080 端口,Actuator 相关的所有 Endpoint 发布在 8090 端口,前段 nginx 请求转发给 8080 端口,外部请求不允许直接访问 8090 端口,起到了保护作用。)
• management.server.address=
• management.server.port=
• management.endpoints.web.base-path=/actuator
• management.endpoints.web.path-mapping.<id>=路径
开启 Endpoint
• management.endpoint.<Actuator ID>.enabled=true
• management.endpoints.enabled-by-default=false
暴露 Endpoint (按需发布 Endpoints)
• management.endpoints.jmx.exposure.exclude=
• management.endpoints.jmx.exposure.include=*
• management.endpoints.web.exposure.exclude=
• management.endpoints.web.exposure.include=info, health
手动定制自己的 Health Indicator
目的
- 检查应用程序的运行状态
状态
• DOWN - 503
• OUT_OF_SERVICE - 503
• UP - 200
• UNKNOWN - 200
机制
- 通过 HealthIndicatorRegistry 收集信息
- HealthIndicator 实现具体检查逻辑
配置项
• management.health.defaults.enabled=true|false
• management.health.<id>.enabled=true
• management.endpoint.health.show-details=never|when-authorized|always
Spring Boot 自带的 Health Indicator
自定义 Health Indicator
步骤
• 实现 HealthIndicator 接口
• 根据自定义检查逻辑返回对应 Health 状态: Health 中包含状态和详细描述信息
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
application.properties
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
# 发布所有endpoints
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
info.app.author=DigitalSonic
info.app.encoding=@project.build.sourceEncoding@
CoffeeIndicator
package geektime.spring.springbucks.waiter.support;
import geektime.spring.springbucks.waiter.service.CoffeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;
@Component
public class CoffeeIndicator implements HealthIndicator {
@Autowired
private CoffeeService coffeeService;
@Override
public Health health() {
long count = coffeeService.getCoffeeCount();
Health health;
if (count > 0) {
health = Health.up()
.withDetail("count", count)
.withDetail("message", "We have enough coffee.")
.build();
} else {
health = Health.down()
.withDetail("count", 0)
.withDetail("message", "We are out of coffee.")
.build();
}
return health;
}
}
测试
// http://localhost:8080/actuator
{
"_links": {
"self": {
"href": "http://localhost:8080/actuator",
"templated": false
},
"auditevents": {
"href": "http://localhost:8080/actuator/auditevents",
"templated": false
},
"beans": {
"href": "http://localhost:8080/actuator/beans",
"templated": false
},
"caches-cache": {
"href": "http://localhost:8080/actuator/caches/{cache}",
"templated": true
},
"caches": {
"href": "http://localhost:8080/actuator/caches",
"templated": false
},
"health-component-instance": {
"href": "http://localhost:8080/actuator/health/{component}/{instance}",
"templated": true
},
"health": {
"href": "http://localhost:8080/actuator/health",
"templated": false
},
"health-component": {
"href": "http://localhost:8080/actuator/health/{component}",
"templated": true
},
"conditions": {
"href": "http://localhost:8080/actuator/conditions",
"templated": false
},
"configprops": {
"href": "http://localhost:8080/actuator/configprops",
"templated": false
},
"env": {
"href": "http://localhost:8080/actuator/env",
"templated": false
},
"env-toMatch": {
"href": "http://localhost:8080/actuator/env/{toMatch}",
"templated": true
},
"info": {
"href": "http://localhost:8080/actuator/info",
"templated": false
},
"loggers": {
"href": "http://localhost:8080/actuator/loggers",
"templated": false
},
"loggers-name": {
"href": "http://localhost:8080/actuator/loggers/{name}",
"templated": true
},
"heapdump": {
"href": "http://localhost:8080/actuator/heapdump",
"templated": false
},
"threaddump": {
"href": "http://localhost:8080/actuator/threaddump",
"templated": false
},
"prometheus": {
"href": "http://localhost:8080/actuator/prometheus",
"templated": false
},
"metrics-requiredMetricName": {
"href": "http://localhost:8080/actuator/metrics/{requiredMetricName}",
"templated": true
},
"metrics": {
"href": "http://localhost:8080/actuator/metrics",
"templated": false
},
"scheduledtasks": {
"href": "http://localhost:8080/actuator/scheduledtasks",
"templated": false
},
"httptrace": {
"href": "http://localhost:8080/actuator/httptrace",
"templated": false
},
"mappings": {
"href": "http://localhost:8080/actuator/mappings",
"templated": false
}
}
}
// http://localhost:8080/actuator/info
{
"app": {
"author": "DigitalSonic",
"encoding": "UTF-8"
}
}
// http://localhost:8080/actuator/health
{
"status": "UP",
"details": {
"coffeeIndicator": {
"status": "UP",
"details": {
"count": 5,
"message": "We have enough coffee."
}
},
"db": {
"status": "UP",
"details": {
"database": "H2",
"hello": 1
}
},
"diskSpace": {
"status": "UP",
"details": {
"total": 256059109376,
"free": 175957565440,
"threshold": 10485760
}
}
}
}
通过 Micrometer 获取运行数据
在应用运行过程中需要收集一些度量指标:操作系统、JVM、应用业务指标 等……
SpringBoot2.X 中的 Micrometer 可以完成度量指标收集。Micrometer 为主流的监控系统提供了一些 instrumentation clients (探针客户端),允许向 JVM 中插入这些探针。同时它做了一层抽象,所以并不需要关心底层的这些探针的具体实现,可以把 Micrometer 看作是 度量界 的 SLF4J。
认识 Micrometer
特性
- 多维度度量
• 支持 Tag
- 预置大量探针
• 缓存、类加载器、GC、CPU 利用率、线程池……
- 与 Spring 深度整合
• webMVC
• webFlux
• restTemplate
• webClient
支持多种监控系统
- Dimensional (维度)
AppOptics, Atlas, Azure Monitor, Cloudwatch, Datadog, Datadog StatsD, Dynatrace, Elastic, Humio, Inflflux, KairosDB,New Relic, Prometheus, SignalFx, Sysdig StatsD, Telegraf,StatsD, Wavefront
- Hierarchical(分层)
Graphite, Ganglia, JMX, Etsy StatsD
一些核心度量指标
核心接口
- Meter
内置实现
- Gauge, TimeGauge
- Timer, LongTaskTimer, FunctionTimer
- Counter, FunctionCounter
- DistributionSummary
Micrometer in Spring Boot 2.x
⼀些 URL
• /actuator/metrics
• /actuator/prometheus
⼀些配置项
• management.metrics.export.*
• management.metrics.tags.*
• management.metrics.enable.*
• management.metrics.distribution.*
• management.metrics.web.server.auto-time-requests
核心度量项
JVM、CPU、⽂件句柄数、⽇志、启动时间
其他度量项
• Spring MVC、Spring WebFlux
• Tomcat、Jersey JAX-RS
• RestTemplate、WebClient
• 缓存、数据源、Hibernate
• Kafka、RabbitMQ
自定义度量指标
• 通过 MeterRegistry 注册 Meter
• 提供 MeterBinder Bean 让 Spring Boot 自动绑定
• 通过 MeterFilter 进行定制
CoffeeOrderService
package geektime.spring.springbucks.waiter.service;
import geektime.spring.springbucks.waiter.model.Coffee;
import geektime.spring.springbucks.waiter.model.CoffeeOrder;
import geektime.spring.springbucks.waiter.model.OrderState;
import geektime.spring.springbucks.waiter.repository.CoffeeOrderRepository;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.binder.MeterBinder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Arrays;
@Service
@Transactional
@Slf4j
public class CoffeeOrderService implements MeterBinder {
@Autowired
private CoffeeOrderRepository orderRepository;
//计数器(监控订单总量)
private Counter orderCounter = null;
public CoffeeOrder get(Long id) {
return orderRepository.getOne(id);
}
public CoffeeOrder createOrder(String customer, Coffee...coffee) {
CoffeeOrder order = CoffeeOrder.builder()
.customer(customer)
.items(new ArrayList<>(Arrays.asList(coffee)))
.state(OrderState.INIT)
.build();
CoffeeOrder saved = orderRepository.save(order);
log.info("New Order: {}", saved);
//每次创建订单 +1
orderCounter.increment();
return saved;
}
public boolean updateState(CoffeeOrder order, OrderState state) {
if (state.compareTo(order.getState()) <= 0) {
log.warn("Wrong State order: {}, {}", state, order.getState());
return false;
}
order.setState(state);
orderRepository.save(order);
log.info("Updated Order: {}", order);
return true;
}
@Override
public void bindTo(MeterRegistry meterRegistry) {
this.orderCounter = meterRegistry.counter("order.count");
}
}
测试
// http://localhost:8080/actuator/metrics/order.count
{
"name": "order.count",
"description": null,
"baseUnit": null,
"measurements": [
{
"statistic": "COUNT",
"value": 2.0
}
],
"availableTags": [
]
}
//访问prometheus
http://localhost:8080/actuator/prometheus
order_count_total 2.0
//Micrometer 对 spring 有很好的支持
// http://localhost:8080/actuator/metrics
{
"names": [
"jvm.threads.states",
"http.server.requests",
"jdbc.connections.active",
"jvm.gc.memory.promoted",
"jvm.memory.max",
"jvm.memory.used",
"jvm.gc.max.data.size",
"jdbc.connections.max",
"jdbc.connections.min",
"jvm.memory.committed",
"system.cpu.count",
"logback.events",
"tomcat.global.sent",
"jvm.gc.pause",
"jvm.buffer.memory.used",
"tomcat.sessions.created",
"jvm.threads.daemon",
"system.cpu.usage",
"jvm.gc.memory.allocated",
"tomcat.global.request.max",
"hikaricp.connections.idle",
"hikaricp.connections.pending",
"tomcat.global.request",
"tomcat.sessions.expired",
"hikaricp.connections",
"jvm.threads.live",
"jvm.threads.peak",
"tomcat.global.received",
"hikaricp.connections.active",
"hikaricp.connections.creation",
"process.uptime",
"order.count",
"tomcat.sessions.rejected",
"process.cpu.usage",
"tomcat.threads.config.max",
"jvm.classes.loaded",
"hikaricp.connections.max",
"hikaricp.connections.min",
"jvm.classes.unloaded",
"tomcat.global.error",
"tomcat.sessions.active.current",
"tomcat.sessions.alive.max",
"jvm.gc.live.data.size",
"hikaricp.connections.usage",
"tomcat.threads.current",
"hikaricp.connections.timeout",
"jvm.buffer.count",
"jvm.buffer.total.capacity",
"tomcat.sessions.active.max",
"hikaricp.connections.acquire",
"tomcat.threads.busy",
"process.start.time"
]
}
//SpringMVC 请求统计 (SpringBoot 和 Micrometer 一起实现的)
//http://localhost:8080/actuator/metrics/http.server.requests
{
"name": "http.server.requests",
"description": null,
"baseUnit": "seconds",
"measurements": [
{
"statistic": "COUNT",
"value": 9.0
},
{
"statistic": "TOTAL_TIME",
"value": 0.707585
},
{
"statistic": "MAX",
"value": 0.0015951
}
],
"availableTags": [
{
"tag": "exception",
"values": [
"None"
]
},
{
"tag": "method",
"values": [
"GET"
]
},
{
"tag": "uri",
"values": [
"/actuator/metrics/{requiredMetricName}",
"/actuator",
"/actuator/prometheus",
"/actuator/metrics"
]
},
{
"tag": "outcome",
"values": [
"CLIENT_ERROR",
"SUCCESS"
]
},
{
"tag": "status",
"values": [
"404",
"200"
]
}
]
}
Spring Boot Admin
目的:为 Spring Boot 应用程序提供⼀套管理界面
主要功能
- 集中展示应用程序 Actuator 相关的内容
- 变更通知
服务端
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>geektime.spring.boot</groupId>
<artifactId>sba-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sba-server</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-boot-admin.version>2.1.3</spring-boot-admin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.application.name=sba-server
server.port=8080
spring.security.user.name=geektime
spring.security.user.password=sba-server-password
SbaServerApplication(启动类)
package geektime.spring.boot.sba;
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import de.codecentric.boot.admin.server.config.EnableAdminServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
@SpringBootApplication
@EnableAdminServer
public class SbaServerApplication extends WebSecurityConfigurerAdapter {
@Autowired
private AdminServerProperties adminServerProperties;
public static void main(String[] args) {
SpringApplication.run(SbaServerApplication.class, args);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
String adminContextPath = adminServerProperties.getContextPath();
SavedRequestAwareAuthenticationSuccessHandler successHandler = new SavedRequestAwareAuthenticationSuccessHandler();
successHandler.setTargetUrlParameter("redirectTo");
successHandler.setDefaultTargetUrl(adminContextPath + "/");
http.authorizeRequests()
.antMatchers(adminContextPath + "/assets/**").permitAll()
.antMatchers(adminContextPath + "/login").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler).and()
.logout().logoutUrl(adminContextPath + "/logout").and()
.httpBasic().and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringAntMatchers(
adminContextPath + "/instances",
adminContextPath + "/actuator/**"
);
}
}
测试
http://localhost:8080/login
geektime
sba-server-password
客户端
依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>geektime.spring.boot</groupId>
<artifactId>sba-client</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sba-client</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-boot-admin.version>2.1.3</spring-boot-admin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-dependencies</artifactId>
<version>${spring-boot-admin.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.application.name=sba-client
server.port=8081
management.endpoints.web.exposure.include=*
info.demo.name=Spring Boot Admin Client Demo
spring.security.user.name=geektime
spring.security.user.password=sba-client-password
spring.boot.admin.client.url=http://localhost:8080
spring.boot.admin.client.username=geektime
spring.boot.admin.client.password=sba-server-password
spring.boot.admin.client.instance.metadata.user.name=${spring.security.user.name}
spring.boot.admin.client.instance.metadata.user.password=${spring.security.user.password}
如何定制 Web 容器的运行参数
内嵌 Web 容器
• spring-boot-starter-tomcat
• spring-boot-starter-jetty
• spring-boot-starter-undertow
• spring-boot-starter-reactor-netty
修改容器配置
# 端口
server.port
server.address
# 压缩
server.compression.enabled
server.compression.min-response-size
server.compression.mime-types
# Tomcat 特定配置
server.tomcat.max-connections=10000
server.tomcat.max-http-post-size=2MB
server.tomcat.max-swallow-size=2MB
server.tomcat.max-threads=200
server.tomcat.min-spare-threads=10
# 修改容器配置
server.error.path=/error
server.error.include-exception=false
server.error.include-stacktrace=never
server.error.whitelabel.enabled=true
# 其他
server.use-forward-headers
server.servlet.session.timeout
配置方式示例:
application.properties
spring.jpa.hibernate.ddl-auto=none
spring.jpa.properties.hibernate.show_sql=true
spring.jpa.properties.hibernate.format_sql=true
#server.compression.enabled=true
#server.compression.min-response-size=512B
server.error.include-stacktrace=always
server.error.include-exception=true
编程方式修改容器配置
WebServerFactoryCustomizer
• TomcatServletWebServerFactory
• JettyServletWebServerFactory
• UndertowServletWebServerFactory
变成方式示例:
WaiterServiceApplication
package geektime.spring.springbucks.waiter;
import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module;
import geektime.spring.springbucks.waiter.controller.PerformanceInteceptor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.Compression;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.util.unit.DataSize;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.TimeZone;
@SpringBootApplication
@EnableJpaRepositories
@EnableCaching
public class WaiterServiceApplication implements WebMvcConfigurer,
WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
public static void main(String[] args) {
SpringApplication.run(WaiterServiceApplication.class, args);
}
/**
* 编程方式配置 GZIP压缩
* @param factory
*/
@Override
public void customize(TomcatServletWebServerFactory factory) {
Compression compression = new Compression();
compression.setEnabled(true);
compression.setMinResponseSize(DataSize.ofBytes(512));
factory.setCompression(compression);
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PerformanceInteceptor())
.addPathPatterns("/coffee/**").addPathPatterns("/order/**");
}
@Bean
public Hibernate5Module hibernate5Module() {
return new Hibernate5Module();
}
@Bean
public Jackson2ObjectMapperBuilderCustomizer jacksonBuilderCustomizer() {
return builder ->
builder.indentOutput(true)
.timeZone(TimeZone.getTimeZone("Asia/Shanghai"));
}
}
构建 Docker 镜像
Docker镜像
- 镜像是静态的只读模版
- 镜像中包含构建 Docker 容器的指令
- 镜像是分层的
- 通过 Dockerfile 来创建镜像
Dockerfile
通过Maven构建Docker镜像
准备工作
- 提供一个 Dockerfile
FROM java:8
EXPOSE 8080
ARG JAR_FILE
ADD target/${JAR_FILE} /waiter-service.jar
ENTRYPOINT ["java", "-jar","/waiter-service.jar"]
- 配置 dockerfile-maven-plugin 插件
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>geektime.spring.springbucks</groupId>
<artifactId>waiter-service</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>waiter-service</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<docker.image.prefix>springbucks</docker.image.prefix>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.joda</groupId>
<artifactId>joda-money</artifactId>
<version>1.0.1</version>
</dependency>
<dependency>
<groupId>org.jadira.usertype</groupId>
<artifactId>usertype.core</artifactId>
<version>6.0.1.GA</version>
</dependency>
<!-- 增加Jackson的Hibernate类型支持 -->
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-hibernate5</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.10</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>build</goal>
<goal>push</goal>
</goals>
</execution>
</executions>
<configuration>
<repository>${docker.image.prefix}/${project.artifactId}</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
</project>
执行构建
mvn package
或
mvn dockerfile:build
检查结果
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
springbucks/waiter-service 0.0.1-SNAPSHOT 61568abfa910 About a minute ago 683MB
java 8 d23bdf5b1b1b 3 years ago 643MB