目录

Life in Flow

知不知,尚矣;不知知,病矣。
不知不知,殆矣。

X

运行中的 SpringBoot

认识 Spring Boot 的各类 Actuator Endpoint

Actuator
 目的:监控并管理应用程序
 访问方式

HTTP
JMX

依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

一些常用的 Endpoint

一些常用的 Endpoint
一些常用的 Endpoint 2

如何访问 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

自定义 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
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

作者:Soulboy