Создать класс расширяющий SpringBootServletInitializer,
реализовать метод configure
добавить в builder главный класс ...Application
-
в gradle добавить
apply plugin: 'war'
public class IngredientServiceServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(IngredientServiceApplication.class);
}
}
Важно, что такой war по прежнему можно запускать из командной строки, как jar
java -jar target/ingredient-service-0.0.19-SNAPSHOT.war
Пример Dockerfile
FROM openjdk:8-jdk-alpine
ENV SPRING_PROFILES_ACTIVE docker
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java",\
"-Djava.security.egd=file:/dev/./urandom",\
"-jar",\
"/app.jar"]
Breaking this Docker file down line by line, you see the following:
- The FROM instruction identifies an image to base the new image on. The new image extends the base image. In this case, the base image is openjdk:8-jdkalpine,
a container image based on version 8 of OpenJDK.
- The ENV instruction sets an environment variable. You’re going to override a few of the Spring Boot application configuration properties based on the active profile,
so in this image, you’ll set the environment variable SPRING_PROFILES_ACTIVE to docker to ensure that the Spring Boot application starts with docker as the
active profile.
- The VOLUME instruction creates a mount point in the container. In this case, it creates a mount point at /tmp so that the container can write data, if necessary,
to the /tmp directory.
- The ARG instruction declares an argument that can be passed in at build time. In this case, it declares an argument named JAR_FILE, which is the same as the
argument given in the Maven plugin’s <buildArgs> block.
- The COPY instruction copies a file from a given path to another path. In this case, it copies the JAR file specified in the Maven plugin to a file named app.jar
within the image.
- The ENTRYPOINT instruction describes what should happen when the container starts. Given as an array, it specifies the command line to execute. In this case, it
uses the java command line to run the executable app.jar.
сборка в maven
<build>
<plugins>
...
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.3</version>
<configuration>
<repository>
${docker.image.prefix}/${project.artifactId}
</repository>
<buildArgs>
<JAR_FILE>target/${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>
</plugins>
</build>
mvnw package dockerfile:build
mvnw dockerfile:push (в dockerhub)
инструкции для разворота в разных облаках:
- AWS https://aws.amazon.com/getting-started/tutorials/deploy-dockercontainers/
- Microsoft Azure https://docs.docker.com/docker-for-azure/deploy/
- Google Cloud Platform—https://cloud.google.com/kubernetes-engine/docs/tutorials/hello-app
--------------------------------------------------------------------------------------------------------------------------------
JMX is automatically enabled by default in a Spring Boot application. As a result, all of the Actuator endpoints are exposed as MBeans.
Actuator MBeans listed under the org.springframework.boot domain
JConsole shows MBeans
создание собственных mbean-ов
All you must do is annotate the bean class with @ManagedResource and then annotate any methods or properties
with @ManagedOperation or @ManagedAttribute.
@Service
@ManagedResource
public class TacoCounter {
@ManagedAttribute
public long getTacoCount() { return counter.get();}
@ManagedOperation
public long increment(long delta) {return counter.addAndGet(delta);}
}
Можно создать Notification& который будет отсылаться при изменениях, для этого надо чтобы класс реализовал интерфейс
implements NotificationPublisherAware и реализовал его метод
@Override
public void setNotificationPublisher(NotificationPublisher np) { this.np = np;}
-----------------------------------------------------------------------------------------------------------------------------
The Spring Boot Admin is an administrative frontend web application that makes Actuator endpoints more consumable by humans
starter: spring-boot-admin-starter-server + @EnableAdminServer (then localhost://9090)
Клиенты (которых показывает сервер), должны быть зарегистрированы в AdminServer
Two ways to register Spring Boot Admin clients with the Admin server follow:
- Each application explicitly registers itself with the Admin server (spring-boot-admin-starter-client + настройки)
- The Admin server discovers services through the Eureka service registry (spring-cloud-starter-netflix-eureka-client)
Что показывает:
- General health and information
- Any metrics published through Micrometer and the /metrics endpoint
- Environment properties (look and set)
- Logging levels for packages and classes (look and set)
- Thread tracing details
- HTTP traces for requests
- Audit logs
Можно добавить безопасноти, подключая актуаторы по паролю и собственно входя на сам admin-сервер по паролю
-----------------------------------------------------------------------------------------------------------------------------
По умолчанию в основном все endpoit-ы выключены
Endpoints включают в том числе:
- управление логами и уровнями логирования,
- получение (/httptrace, /threaddump, /heapdump)
- healthcheck (в т.ч. готовый для разных систем)
- получение всей информации о env (/actuator/env) и configurations
- инфо о beans
- /conditions - об autoconfiguration бинах
- runtime metrics (actuator/metrics)
API позволяет получить значение конкретной конфигурационной пременной
@Component
public class TacoCountInfoContributor implements InfoContributor {
@Override
public void contribute(Builder builder) {...}
}
To enable build information to be included in the results of the /info endpoint, add gradle
springBoot {
buildInfo()
}
To create a custom health indicator, all you need to do is create a bean that implements the HealthIndicator interface.
Собственные метрики можно собрать через MeterRegistry (Micrometer)
meterRegistry.counter("tacocloud", "ingredient", ingredient.getId()).increment();
,и затем по имени получить через
/metrics/<имя метрики>
@Component
@Endpoint(id="notes", enableByDefault=true)
public class NotesEndpoint {}
- аннотации для определения действий @ReadOperation, @WriteOperation, and @DeleteOperation
- доступ /actuator/notes
- если только http, то надо использовать @WebEndpoint
Using EndpointRequest, you can apply the same security requirements for Actuator endpoints without hardcoding the /actuator/** path:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatcher(EndpointRequest.toAnyEndpoint())
.authorizeRequests()
.anyRequest().hasRole("ADMIN")
and()
httpBasic();
}
-----------------------------------------------------------------------------------------------------------------------
Описание шаблона Circuit Breaker на русском: https://medium.com/@kirill.sereda/%D1%81%D1%82%D1%80%D0%B0%D1%82%D0%B5%D0%B3%D0%B8%D0%B8-%D0%BE%D0%B1%D1%80%D0%B0%D0%B1%D0%BE%D1%82%D0%BA%D0%B8-%D0%BE%D1%88%D0%B8%D0%B1%D0%BE%D0%BA-circuit-breaker-pattern-650232944e37
- Methods that make REST calls
These could fail due to the remote service being unavailable or returning HTTP 500 responses.
- Methods that perform database queries
These could fail if, for some reason, the database becomes unresponsive, or if the schema changes in ways that break the application.
- (!!!)Methods that are potentially slow
These won’t necessarily fail, but may be considered unhealthy if they're taking too long to do their job.
Подключить стартером.
@EnableHystrix
@HystrixCommand(fallbackMethod="getDefaultIngredients")
public Iterable<Ingredient> getAllIngredients() {
ParameterizedTypeReference<List<Ingredient>> stringList = new ParameterizedTypeReference<List<Ingredient>>() {};
return rest.exchange("http://ingredient-service/ingredients", HttpMethod.GET,HttpEntity.EMPTY, stringList).getBody();
}
private Iterable<Ingredient> getDefaultIngredients() {
List<Ingredient> ingredients = new ArrayList<>();
ingredients.add(new Ingredient("FLTO", "Flour Tortilla", Ingredient.Type.WRAP));
return ingredients;
}
Мониторинг в конкретном сервисе возможен через spring-actuator (/actuator/hystrix.stream), по умолчанию он выключен, т.е. надо включить в свойствах, для использования
Есть собственный dashboard spring-cloud-starter-netflix-hystrix-dashboard
Есть агрегированный dashboard для всех breaker-ов на всех микросервисах (отдельный проект Turbine) : spring-cloud-starter-netflix-turbine
Turbine, offers a way to aggregate all of the Hystrix streams from all the microservices into a single stream that the Hystrix dashboard can monitor.
The turbine.app-config property accepts a comma-delimited list of service names to look up in Eureka and for which it should aggregate Hystrix streams. For Taco Cloud,
you’ll need Turbine to aggregate the streams for the four services registered in Eureka as ingredient-service, taco-service, order-service, and user-service. The following
entry in application.yml shows how to set turbine.app-config:
turbine:
app-config: ingredient-service,taco-service,order-service,user-service
cluster-name-expression: "'default'"
----------------------------------------------------------------------------------------------------------------------------------------
config-сервер - самостоятельное отдельное приложение (сервис)
Пример конфигурации (сервер) с чтением из git-a
spring:
cloud:
config:
server:
git:
uri: http://localhost:10080/tacocloud/tacocloud-config
username: tacocloud
password: s3cr3tP455w0rd
Путь к конфигу (клиент):
http://localhost:8888/application/default/branch-name
application - имя конкретного приложения
default - spring active profile
branch-name (не обязательняй) - ветка/tag в git-е (default 'master')
When the application starts up, the property source provided by the Config Server client
will make a request to the Config Server. Whatever properties it receives will be
made available in the application’s environment. What’s more, those properties will
be effectively cached; they’ll be available even if the Config Server goes down
spring:
data:
mongodb:
password: '{cipher}93912a660a7f3c04e811b5df9a3cf6e1f63850...'
апостроф и префикс {cipher} говорят Config Server, что это зашифрованное значение,
оно будет расшифровано сервером перед отдачей клиенту
(но можно указать свойство (encrypt: enabled: false), чтобы сервер отдавал не расшифровывая,
как есть '{cipher}93...'
interesting side effect of using Vault alongside Git—even Git properties are indirectly
hidden by the Config Server unless a valid token is provided
обязательно нужен токен X-Config-Token в заголовке запроса
В локальной конфигурации клиента настраивается так:
spring:
cloud:
config:
token: roottoken
The spring.cloud.config.token property tells the Config Server client to include
the given token value in all requests it makes to the Config Server. This property
must be set in the application’s local configuration (not stored in Config Server’s
Git or Vault backend) so that Config Server can pass it along to Vault and be able to
serve properties.
- руками
(нужен spring-boot-actuator и вызов endpoint)
- автоматическая (через подписку на очереди RabbitMQ или Kafka)
Each Config Server client application will need the Spring Cloud Bus dependency
Нужно донастроить hook -на git, чтобы при изменениях он "толкал" ConfiggServer,
который в свою очередь предаст в очередь сообщение об изменениях, а клиент должен быть подписан
на эту очередь (! и на сервере (кроме настрйки hook) и на клиенте это всё делается
готовыми spring-starter-ами, например - spring-cloud-starter-stream-kafka(client)
и spring-cloud-config-monitor(server))
---------------------------------------------------------------------------------------------------------------------------------------------
RestTemplate использует Ribbon под капотом
Once you’ve enabled an application as being a Eureka client, however, you have the
option of declaring a load-balanced RestTemplate bean. All you need to do is declare a
regular RestTemplate bean, but annotate the @Bean method with @LoadBalanced:
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
---- или так (в этом случае балансировка будет работать только для конкретного клиента/класса)
private RestTemplate rest;
public IngredientServiceClient(@LoadBalanced RestTemplate rest) {
this.rest = rest;
}
The @LoadBalanced annotation has two purposes: first, and most importantly, it tells
Spring Cloud that this RestTemplate should be instrumented with the ability to look
up services through Ribbon.
Вызов сервиса с Eureka (ribbon разресолвит имя сервиса 'ingredient-service' самостоятельно)
public Ingredient getIngredientById(String ingredientId) {
return rest.getForObject("http://ingredient-service/ingredients/{id}", Ingredient.class, ingredientId); // вместо http://t1host.asd.ru:8080/ingredients/{id}
}
---- тоже для Feign
Internally, this service will be looked up via Ribbon (так же как и RestTemplate)
@FeignClient("ingredient-service") //! не конкретный адрес, а имя сервиса
public interface IngredientClient {
@GetMapping("/ingredients/{id}")
Ingredient getIngredient(@PathVariable("id") String id);
}