构建Spring微服务并对其进行Dockerize生产

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

在这篇文章中,您将学习微服务架构以及如何使用Spring Boot来实现它。 在使用该技术创建了一些项目之后,您将把工件部署为Docker容器,并使用Docker Compose进行简化以模拟容器编排器 (例如Kubernetes)。 锦上添花的是使用Spring Profiles进行身份验证集成。 您将了解如何通过生产资料启用它。

但是首先,让我们谈谈微服务。

了解现代微服务架构

与整体架构相反,微服务要求您将应用程序分成逻辑上相关的小块。 这些片段是独立的软件,例如,可以使用HTTP或消息与其他片段进行通信。

有一些关于微型尺寸的讨论。 有人说微服务是可以在单个冲刺中创建的软件。 其他人则说,如果微服务在逻辑上相关(例如,您不能混合使用苹果和橙子),则微服务的规模可能会更大。 我同意马丁·福勒 ( Martin Fowler)的观点,认为尺寸并没有多大关系,它与款式息息相关。

微服务有许多优点:

  • 耦合风险不高 –由于每个应用程序都处于不同的进程中,因此无法创建相互对话的类。
  • 轻松扩展 –如您所知,每项服务都是独立的软件。 因此,它可以按需扩展或缩小。 此外,由于代码比整体代码 ,因此启动速度可能更快。
  • 多个堆栈 –您可以为每个服务使用最佳的软件堆栈。 例如,当Python对您正在构建的东西更好时,就不再需要使用Java。
  • 更少的合并和代码冲突 –由于每个服务都是一个不同的存储库,因此更易于处理和检查提交。

但是,有一些缺点:

  • 您有一个新的敌人- 网络问题 。 服务启动了吗? 如果服务中断,该怎么办?
  • 复杂的部署过程 – OK CI / CD在这里,但是您现在为每个服务只有一个工作流程。 如果他们使用不同的堆栈,则可能甚至无法为每个堆栈复制工作流程。
  • 更复杂且难以理解的体系结构 –它取决于您的设计方式,但请考虑以下问题:如果您不知道方法的作用,则可以阅读其代码。 在微服务体系结构中,此方法可能在另一个项目中,甚至可能没有代码。

如今,通常应该首先避免使用微服务架构 。 经过一些迭代后,代码划分将变得更加清晰,项目的需求也将变得更加清晰。 在您的开发团队开始进行小型项目之前,处理微服务通常过于昂贵。

在Spring使用Docker构建微服务

在本教程中,您将构建两个项目:一个服务(school-service)和一个UI(school_ui)。 该服务提供持久层和业务逻辑,而UI提供图形用户界面。 只需最少的配置即可连接它们。

初始设置后,我将讨论发现和配置服务。 两种服务都是任何大规模分布式体系结构的重要组成部分。 为了证明这一点,您将其与OAuth 2.0集成在一起,并使用配置项目来设置OAuth 2.0密钥。

最后,每个项目都将转换为Docker映像。 Docker Compose将用于模拟容器协调器,因为Compose将使用服务之间的内部网络来管理每个容器。

最后,将介绍Spring配置文件以根据当前适当分配的环境来更改配置。 这样,您将拥有两个OAuth 2.0环境:一个用于开发,另一个用于生产。

更少的单词,更多的代码! 克隆本教程的资源库,并检出start分支。

git clone -b start https://github.com/oktadeveloper/okta-spring-microservices-docker-example.git

pom.xml文件不是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><groupId>com.okta.developer.docker_microservices</groupId><artifactId>parent-pom</artifactId><version>0.0.1-SNAPSHOT</version><packaging>pom</packaging><name>parent-project</name><modules><module>school-service</module><module>school-ui</module>    </modules>
</project>

这称为聚合项目,因为它聚合子项目。 这对于在所有声明的模块上运行相同的Maven任务很有用。 这些模块无需将根模块用作父模块。

有两个模块可用:学校服务和学校UI。

学校服务微服务

school-service目录包含一个Spring Boot项目,该项目充当项目的持久层和业务规则。 在更复杂的情况下,您将拥有更多这样的服务。 该项目是使用始终出色的Spring Initializr创建的,并具有以下配置:

Spring微服务
  • 组– com.okta.developer.docker_microservices
  • 神器– school-service
  • 依赖关系– JPA,Web,Lombok,H2

您可以阅读PostgreSQL,Flyway和JSONB的Spring Boot以获得有关此项目的更多详细信息。 总而言之,它具有实体TeachingClassCourse, Student并使用TeachingClassServiceDBTeachingClassController通过REST API公开一些数据。 要测试它,请打开一个终端,导航到school-service目录,然后运行以下命令:

./mvnw spring-boot:run

该应用程序将从端口8081 (在文件school-service/src/main/resources/application.properties )启动,因此您应该能够导航到http://localhost:8081并查看返回的数据。

> curl http://localhost:8081
[{"classId":13,"teacherName":"Profesor Jirafales","teacherId":1,"courseName":"Mathematics","courseId":3,"numberOfStudents":2,"year":1988},{"classId":14,"teacherName":"Profesor Jirafales","teacherId":1,"courseName":"Spanish","courseId":4,"numberOfStudents":2,"year":1988},{"classId":15,"teacherName":"Professor X","teacherId":2,"courseName":"Dealing with unknown","courseId":5,"numberOfStudents":2,"year":1995},{"classId":16,"teacherName":"Professor X","teacherId":2,"courseName":"Dealing with unknown","courseId":5,"numberOfStudents":1,"year":1996}
]

基于Spring的School UI微服务

顾名思义,学校UI是利用学校服务的用户界面。 它是使用Spring Initializr使用以下选项创建的:

  • 组– com.okta.developer.docker_microservices
  • 神器– school-ui
  • 依存关系-网络,仇恨,胸腺,Lombok

UI是一个单独的网页,列出了数据库上可用的类。 为了获取信息,它通过文件school-ui/src/main/resources/application.properties的配置与school-service连接。

service.host=localhost:8081

SchoolController类具有查询服务的所有逻辑:

package com.okta.developer.docker_microservices.ui.controller;import com.okta.developer.docker_microservices.ui.dto.TeachingClassDto;
import org.springframework.beans.factory.annotation.*;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;
import java.util.List;@Controller
@RequestMapping("/")
public class SchoolController {private final RestTemplate restTemplate;private final String serviceHost;public SchoolController(RestTemplate restTemplate, @Value("${service.host}") String serviceHost) {this.restTemplate = restTemplate;this.serviceHost = serviceHost;}@RequestMapping("")public ModelAndView index() {return new ModelAndView("index");}@GetMapping("/classes")public ResponseEntity<List<TeachingClassDto>> listClasses(){return restTemplate.exchange("http://"+ serviceHost +"/class", HttpMethod.GET, null,new ParameterizedTypeReference<List<TeachingClassDto>>() {});}
}

如您所见,该服务有一个硬编码的位置。 您可以使用-Dservice.host=localhost:9090这样的环境变量来更改属性设置。 尽管如此,它仍必须手动定义。 如何拥有许多学校服务申请实例? 在当前阶段不可能。

启用school-service后 ,启动school-ui ,并在浏览器中浏览至http://localhost:8080

./mvnw spring-boot:run

您应该看到如下页面:

Spring微服务

使用Spring Cloud和Eureka构建发现服务器

现在,您有了一个可以使用的服务,该应用程序使用两种服务将信息提供给最终用户。 怎么了 在现代应用程序中,开发人员(或操作)通常不知道应用程序可能部署在何处或在哪个端口上。 部署应该是自动化的,以便没有人关心服务器名称和物理位置。 (除非您在数据中心内工作。否则,希望您在意!)

但是,必须有一个工具来帮助服务发现其对应对象。 有许多可用的解决方案,对于本教程,我们将使用Netflix的Eureka ,因为它具有出色的Spring支持。

返回start.spring.io并创建一个新项目,如下所示:

  • 组: com.okta.developer.docker_microservices
  • 神器: discovery
  • 依赖项:Eureka Server

编辑主DiscoveryApplication.java类,以添加@EnableEurekaServer批注:

package com.okta.developer.docker_microservices.discovery;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;@SpringBootApplication
@EnableEurekaServer
public class DiscoveryApplication {public static void main(String[] args) {SpringApplication.run(DiscoveryApplication.class, args);}
}

并且,您需要更新其application.properties文件,使其在端口8761上运行,并且不会尝试向其自身注册。

spring.application.name=discovery-server
server.port=8761
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

让我们定义每个属性:

  • spring.application.name –应用程序的名称,发现服务也使用它来发现服务。 您会看到其他所有应用程序也都有一个应用程序名称。
  • server.port –服务器正在运行的端口。 Eureka服务器的默认端口8761
  • eureka.client.register-with-eureka –告诉Spring不要将自己注册到发现服务中。
  • eureka.client .fetch-registry –指示该实例不应从服务器获取发现信息。

现在,运行并访问http://localhost:8761

./mvnw spring-boot:run
Spring微服务

上面的屏幕显示了准备注册新服务的Eureka服务器。 现在,该更改学校服务学校用户界面以使用它了。

注意:如果在启动时收到ClassNotFoundException: javax.xml.bind.JAXBContext错误,那是因为您在Java 11上运行。您可以将JAXB依赖项添加到pom.xml以解决此问题。

<dependency><groupId>javax.xml.bind</groupId><artifactId>jaxb-api</artifactId><version>2.3.1</version>
</dependency>
<dependency><groupId>org.glassfish.jaxb</groupId><artifactId>jaxb-runtime</artifactId><version>2.3.2</version>
</dependency>

使用服务发现在微服务之间进行通信

首先,添加所需的依赖关系很重要。 将以下内容添加到pom.xml文件中(在school-serviceschool-ui项目中):

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

该模块是Spring Cloud计划的一部分,因此,需要一个新的依赖关系管理节点,如下所示(不要忘记将其添加到两个项目中):

<dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-cloud.version}</version><type>pom</type><scope>import</scope></dependency></dependencies>
</dependencyManagement>

现在,您需要配置两个应用程序以向Eureka注册。

在两个项目的application.properties文件中,添加以下行:

eureka.client.serviceUrl.defaultZone=${EUREKA_SERVER:http://localhost:8761/eureka}
spring.application.name=school-service

不要忘了应用程序的名称从改变school-serviceschool-ui学校的UI项目。 注意第一行中有一种新的参数: {EUREKA_SERVER:http://localhost:8761/eureka} 。 这意味着“如果环境变量EUREKA_SERVER存在,请使用其值,否则请使用默认值。” 这在以后的步骤中将很有用。 ;)

你知道吗? 两个应用程序都准备好将自己注册到发现服务中。 您无需执行任何其他操作。 我们的主要目标是学校用户界面项目不需要知道学校服务在哪里 。 因此,您需要更改SchoolController (在school-ui项目中)以在其REST端点中使用school-service 。 您也可以在此类中删除serviceHost变量。

package com.okta.developer.docker_microservices.ui.controller;import com.okta.developer.docker_microservices.ui.dto.TeachingClassDto;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;import java.util.List;@Controller
@RequestMapping("/")
public class SchoolController {private final RestTemplate restTemplate;public SchoolController(RestTemplate restTemplate) {this.restTemplate = restTemplate;}@RequestMapping("")public ModelAndView index() {return new ModelAndView("index");}@GetMapping("/classes")public ResponseEntity<List<TeachingClassDto>> listClasses() {return restTemplate.exchange("http://school-service/classes", HttpMethod.GET, null,new ParameterizedTypeReference<List<TeachingClassDto>>() {});}
}

在集成Eureka之前,您已经进行了配置,指出了学校服务的位置。 现在,您已将服务调用更改为使用其他服务使用的名称:无端口,无主机名。 您需要的服务就在某处,您无需知道在哪里。

学校服务可能具有的多个实例,并且最好在这些实例之间进行负载均衡负载。 幸运的是,Spring有一个简单的解决方案:在创建RestTemplate bean时,如下所示添加@LoadBalanced批注。 每当您向服务器提出问题时,Spring都会管理多个实例调用。

package com.okta.developer.docker_microservices.ui;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.config.annotation.*;@SpringBootApplication
public class UIWebApplication implements WebMvcConfigurer {public static void main(String[] args) {SpringApplication.run(UIWebApplication.class, args);}@Bean@LoadBalancedpublic RestTemplate restTemplate() {return new RestTemplate();}@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {if(!registry.hasMappingForPattern("/static/**")) {registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/", "classpath:/static/js/");}}
}

现在,开始重新启动school-serviceschool-ui (并保持Discovery服务启动)。 再次快速浏览一下http://localhost:8761

Spring微服务

现在,您的服务正在与Discovery服务器共享信息。 您可以再次测试该应用程序,然后查看它是否可以正常运行。 只需在您喜欢的浏览器中转到http://localhost:8080

将配置服务器添加到您的微服务架构

尽管此配置有效,但最好删除项目源代码中任何配置值的痕迹。 首先,配置URL已从项目中删除,并由服务进行管理。 现在,您可以使用Spring Cloud Config对项目中的每个配置执行类似的操作。

首先,使用Spring Initializr和以下参数创建配置项目:

  • 组: com.okta.developer.docker_microservices
  • 工件: config
  • 依赖项:配置服务器,Eureka发现

在主类中,添加@EnableConfigServer

package com.okta.developer.docker_microservices.config;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;@SpringBootApplication
@EnableConfigServer
public class ConfigApplication {...
}

在项目的application.properties添加以下属性和值:

spring.application.name=CONFIGSERVER
server.port=8888
spring.profiles.active=native
spring.cloud.config.server.native.searchLocations=.
eureka.client.serviceUrl.defaultZone=${EUREKA_SERVER:http://localhost:8761/eureka}

有关属性的一些解释:

  • spring.profiles.active=native表示Spring Cloud Config必须使用本机文件系统来获取配置。 通常使用Git存储库,但是为了简单起见,我们将坚持使用本机文件系统。
  • spring.cloud.config.server.native.searchLocations –包含配置文件的路径。 如果将其更改为硬盘驱动器上的特定文件夹,请确保并在其中创建school-ui.properties文件。

现在,您需要一些配置和适用于此示例。 Okta的配置如何? 让我们将school-ui放在授权层后面,并使用配置项目提供的属性值。

您可以注册一个永久免费的开发人员帐户 ,该帐户使您可以创建所需使用的尽可能多的用户和应用程序! 创建帐户后,在Okta的信息中心中创建一个新的Web应用程序(“ 应用程序” >“ 添加应用程序” ):

Spring微服务

并用以下值填写下一个表格:

Spring微服务

该页面将为您返回一个应用程序ID和一个密钥。 确保安全,然后在config项目的根文件夹中创建一个名为school-ui.properties的文件,内容如下。 不要忘记填充变量值:

okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId={yourClientId}
okta.oauth2.clientSecret={yourClientSecret}

现在,运行config项目并检查其是否正确获取了配置数据:

./mvnw spring-boot:run
> curl http://localhost:8888/school-ui.properties
okta.oauth2.clientId: YOUR_CLIENT_ID
okta.oauth2.clientSecret: YOUR_CLIENT_SECRET
okta.oauth2.issuer: https://YOUR_DOMAIN/oauth2/default

更改School UI以使用Spring Cloud Config和OAuth 2.0

现在,您需要对Spring UI项目进行一些更改。

首先,您需要更改school-ui/pom.xml并添加一些新的依赖项:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency><groupId>com.okta.spring</groupId><artifactId>okta-spring-boot-starter</artifactId><version>1.1.0</version>
</dependency>
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>

com.okta...ui.config包中创建一个新的SecurityConfiguration类:

package com.okta.developer.docker_microservices.ui;import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/").permitAll().anyRequest().authenticated().and().logout().logoutSuccessUrl("/").and().oauth2Login();}
}

更改您的SchoolController以便仅允许具有范围profile用户使用(每位经过身份验证的用户都拥有)。

import org.springframework.security.access.prepost.PreAuthorize;....@GetMapping("/classes")
@PreAuthorize("hasAuthority('SCOPE_profile')")
public ResponseEntity<List<TeachingClassDto>> listClasses(){return restTemplate.exchange("http://school-service/class", HttpMethod.GET, null,new ParameterizedTypeReference<List<TeachingClassDto>>() {});
}

一些配置需要在项目启动时定义。 Spring有一个聪明的解决方案,可以上下文启动之前正确定位并提取配置数据。 您需要创建一个文件src/main/resources/bootstrap.yml如下所示:

eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}
spring:application:name: school-uicloud:config:discovery:enabled: trueservice-id: CONFIGSERVER

引导文件会创建一个预启动的Spring Application Context,用于在实际应用程序启动之前提取配置。 您需要将所有属性从application.properties移到该文件,因为Spring需要知道Eureka Server的位置以及如何搜索配置。 在上面的示例中,您启用了通过发现服务进行配置( spring.cloud.config.discovery.enabled )并指定了配置service-id

更改application.properties文件,使其仅具有一个OAuth 2.0属性:

okta.oauth2.redirect-uri=/authorization-code/callback

最后一个要修改的文件是src/main/resources/templates/index.hml 。 对其进行调整,以在用户未通过身份验证时显示登录按钮,在用户登录时显示注销按钮。

<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><!-- Required meta tags --><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><!-- Bootstrap CSS --><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous"><title>Hello, world!</title>
</head>
<body>
<nav class="navbar navbar-default"><form method="post" th:action="@{/logout}" th:if="${#authorization.expression('isAuthenticated()')}" class="navbar-form navbar-right"><input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /><button id="logout-button" type="submit" class="btn btn-danger">Logout</button></form><form method="get" th:action="@{/oauth2/authorization/okta}" th:unless="${#authorization.expression('isAuthenticated()')}"><button id="login-button" class="btn btn-primary" type="submit">Login</button></form>
</nav><div id="content" th:if="${#authorization.expression('isAuthenticated()')}"><h1>School classes</h1><table id="classes"><thead><tr><th>Course</th><th>Teacher</th><th>Year</th><th>Number of students</th></tr></thead><tbody></tbody></table><!-- Optional JavaScript --><!-- jQuery first, then Popper.js, then Bootstrap JS --><script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script><script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script><script src="static/js/school_classes.js"></script>
</div></body>
</html>

您应该在此HTML中了解一些Thymeleaf属性:

  • @{/logout} –返回在后端定义的注销URL
  • th:if="${#authorization.expression('isAuthenticated()')}" –仅在用户登录时打印HTML
  • @{//oauth2/authorization/okta} –这是Spring Security重定向到Okta的URL。 您也可以链接到/login ,但这只是呈现相同的链接,您必须单击它。
  • th:unless="${#authorization.expression('isAuthenticated()')}" –仅在用户注销后才在节点内打印HTML

现在,重新启动配置项目和school-ui。 如果导航到输入http://localhost:8080 ,则应该看到以下屏幕:

Spring微服务

登录后,屏幕应显示如下:

Spring微服务

恭喜,您已经使用Spring Cloud config和Eureka创建了微服务架构来进行服务发现! 现在,让我们更进一步,并对每个服务进行Dockerize。

使用Docker打包Spring应用程序

Docker是一项了不起的技术,它允许创建类似于虚拟机的系统映像,但是共享与主机操作系统相同的内核。 此功能可以提高系统性能和启动时间。 此外,Docker提供了一个精巧的内置系统,该系统可确保一旦创建映像就可以; 它永远不会改变。 换句话说:不再有“它可以在我的机器上工作!”

提示:需要更深的Docker背景吗? 看看我们的《 Docker开发人员指南》 。

您需要为每个项目创建一个Docker映像。 每个映像在每个项目的根文件夹中应具有相同的Maven配置和Dockerfile内容(例如, school-ui/Dockerfile )。

在每个项目的pom中,添加dockerfile-maven-plugin

<plugins>...<plugin><groupId>com.spotify</groupId><artifactId>dockerfile-maven-plugin</artifactId><version>1.4.9</version><executions><execution><id>default</id><goals><goal>build</goal><goal>push</goal></goals></execution></executions><configuration><repository>developer.okta.com/microservice-docker-${project.artifactId}</repository><tag>${project.version}</tag><buildArgs><JAR_FILE>${project.build.finalName}.jar</JAR_FILE></buildArgs></configuration></plugin>
</plugins>

每次运行./mvnw install时,此XML都会配置Dockerfile Maven插件以构建Docker映像。 将使用名称developer.okta.com/microservice-docker-${project.artifactId}创建每个图像,其中project.artifactIdproject.artifactId而异。

在每个项目的根目录中创建一个Dockerfile文件。

FROM openjdk:8-jdk-alpine
VOLUME /tmp
ADD target/*.jar app.jar
ENV JAVA_OPTS="
ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar"

Dockerfile遵循Spring Boot与Docker的建议。

现在,更改school-ui/src/main/resources/bootstrap.yml以添加新的failFast设置:

eureka:client:serviceUrl:defaultZone: ${EUREKA_SERVER:http://localhost:8761/eureka}
spring:application:name: school-uicloud:config:discovery:enabled: trueserviceId: CONFIGSERVERfailFast: true

spring.cloud.failFast: true设置告诉Spring Cloud Config在找不到配置服务器时立即终止应用程序。 这将对下一步很有用。

添加Docker Compose以运行所有内容

创建一个名为docker-compose.yml的新文件,该文件定义每个项目的启动方式:

version: '3'
services:discovery:image: developer.okta.com/microservice-docker-discovery:0.0.1-SNAPSHOTports:- 8761:8761config:image: developer.okta.com/microservice-docker-config:0.0.1-SNAPSHOTvolumes:- ./config-data:/var/config-dataenvironment:- JAVA_OPTS=-DEUREKA_SERVER=http://discovery:8761/eureka-Dspring.cloud.config.server.native.searchLocations=/var/config-datadepends_on:- discoveryports:- 8888:8888school-service:image: developer.okta.com/microservice-docker-school-service:0.0.1-SNAPSHOTenvironment:- JAVA_OPTS=-DEUREKA_SERVER=http://discovery:8761/eurekadepends_on:- discovery- configschool-ui:image: developer.okta.com/microservice-docker-school-ui:0.0.1-SNAPSHOTenvironment:- JAVA_OPTS=-DEUREKA_SERVER=http://discovery:8761/eurekarestart: on-failuredepends_on:- discovery- configports:- 8080:8080

如您所见,每个项目现在都是Docker中声明的服务,用于组成文件。 它将暴露其端口和其他一些属性。

  • 发现外,所有项目都将具有变量值-DEUREKA_SERVER=http://discovery:8761/eureka 。 这将告诉您在哪里可以找到发现服务器。 Docker Compose在服务之间创建一个虚拟网络,每个服务使用的DNS名称就是其名称:这就是为什么可以将discovery用作主机名的原因。
  • Config服务将具有用于配置文件的卷。 该卷将映射到docker容器内的/var/config-data 。 同样,属性spring.cloud.config.server.native.searchLocations将被覆盖为相同的值。 您必须将文件school-ui.properties存储在卷映射上指定的同一文件夹中(在上面的示例中, 相对文件夹./config-data )。
  • school-ui项目的属性将restart: on-failure 。 这将Docker Compose设置为在应用程序失败后立即重新启动。 与failFast属性一起使用可以使应用程序继续尝试启动,直到DiscoveryConfig项目完全准备好为止。

就是这样! 现在,构建图像:

cd config && ./mvnw clean install
cd ../discovery && ./mvnw clean install
cd .. && ./mvnw clean install

school-ui项目中,最后一个命令可能会失败,并显示以下错误:

java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: java.lang.IllegalStateException: No instances found of configserver (CONFIGSERVER)

要解决此问题,请创建一个school-ui/src/test/resources/test.properties文件并添加属性,以使Okta的配置通过,并且在测试时不使用发现或配置服务器。

okta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId=TEST
spring.cloud.discovery.enabled=false
spring.cloud.config.discovery.enabled = false
spring.cloud.config.enabled = false

然后修改UIWebApplicationTests.java以加载此文件以用于测试属性:

import org.springframework.test.context.TestPropertySource;...
@TestPropertySource(locations="classpath:test.properties")
public class UIWebApplicationTests {...
}

现在,您应该能够在school-ui项目中运行./mvnw clean install

完成后,运行Docker Compose以启动所有容器(在docker-compose.yml所在的目录中)。

docker-compose up -d
Starting okta-microservice-docker-post-final_discovery_1 ... done
Starting okta-microservice-docker-post-final_config_1    ... done
Starting okta-microservice-docker-post-final_school-ui_1      ... done
Starting okta-microservice-docker-post-final_school-service_1 ... done

现在,您应该能够像以前一样浏览该应用程序。

使用Spring配置文件来修改您的微服务的配置

现在,您已经到达了微服务之旅的最后阶段。 Spring Profiles是一个功能强大的工具。 使用配置文件,可以通过完全注入不同的依赖项或配置来修改程序行为。

假设您有一个结构良好的软件,其持久层与业务逻辑分离。 例如,您还提供对MySQL和PostgreSQL的支持。 每个数据库可能有不同的数据访问类,这些数据访问类仅由定义的概要文件加载。

另一个用例是配置:不同的配置文件可能具有不同的配置。 以身份验证为例。 您的测试环境会进行身份验证吗? 如果是这样,则不应使用与生产相同的用户目录。

将您的配置项目更改为在Okta中有两个应用程序:一个默认(用于开发),另一个用于生产。 在Okta网站上创建一个新的Web应用程序,并将其命名为“ okta-docker-production”。

现在,在您的config项目中,创建一个名为school-ui-production.properties的新文件。 您已经有了school-ui.properties ,每个School UI实例都将使用它。 在文件末尾添加环境时,Spring将合并两个文件,并优先于最特定的文件。 使用生产应用程序的客户端ID和密码保存文件,如下所示:

school-ui-production.properties

okta.oauth2.clientId={YOUR_PRODUCTION_CLIENT_ID}
okta.oauth2.clientSecret={YOUR_PRODUCTION_CLIENT_SECRET}

现在,使用Maven运行配置项目,然后运行以下两个curl命令:

./mvnw spring-boot:run> curl http://localhost:8888/school-ui.propertiesokta.oauth2.issuer: https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId: ==YOUR DEV CLIENT ID HERE==
okta.oauth2.clientSecret: ==YOUR DEV CLIENT SECRET HERE==> curl http://localhost:8888/school-ui-production.properties
okta.oauth2.issuer: https://{yourOktaDomain}/oauth2/default
okta.oauth2.clientId: ==YOUR PROD CLIENT ID HERE==
okta.oauth2.clientSecret: ==YOUR PROD CLIENT SECRET HERE==

如您所见,即使文件school-ui-production具有两个属性, config项目也会显示三个属性(因为配置已合并)。

现在,您可以在docker-compose.yml中将school-ui服务docker-compose.yml为使用production配置文件:

school-ui:image: developer.okta.com/microservice-docker-school-ui:0.0.1-SNAPSHOTenvironment:- JAVA_OPTS=-DEUREKA_SERVER=http://discovery:8761/eureka-Dspring.profiles.active=productionrestart: on-failuredepends_on:- discovery- configports:- 8080:8080

您还需要将school-ui-production.properties复制到您的config-data目录中。 然后关闭所有Docker容器并重新启动它们。

docker-compose down
docker-compose up -d

您应该在school-ui容器的日志中看到以下内容:

The following profiles are active: production

而已! 现在,您可以在生产配置文件中运行微服务架构。 头晕!

提示:如果要证明使用了okta-docker-production应用程序而不是okta-docker ,可以在Okta中停用okta-docker应用程序,并确认您仍然可以登录http://localhost:8080

了解有关微服务,Spring,Docker和现代应用程序安全性的更多信息

在这篇文章中,您了解了有关微服务以及如何部署它们的更多信息,以及:

  • 什么是微服务?
  • 服务应该如何发现其依赖关系而无需事先知道它们的位置。
  • 如何以信息的中心点维护分布式配置。 该配置可以管理一个或多个应用程序和环境。
  • 如何使用Spring Cloud Config配置OAuth 2.0。
  • 如何使用Docker和Docker Compose部署微服务
  • 如何使用Spring Profiles在生产环境中进行部署。

您可以在oktadeveloper / okta-spring-microservices-docker-example上的GitHub上找到本教程的完整源代码。

如果您有兴趣在Spring中学习有关微服务或现代应用程序开发的更多信息,建议您查看以下资源:

  • 使用Spring Boot 2.0和OAuth 2.0构建并保护微服务
  • 使用JHipster和OAuth 2.0开发微服务架构
  • 使用Spring Boot为Microbrews构建微服务架构
  • Spring Boot 2.1:出色的OIDC,OAuth 2.0和反应式API支持
  • 使用Spring Boot和MongoDB构建一个反应式应用程序

如果您对此帖子有任何疑问,请在下面发表评论。 您可以在Twitter上关注@oktadev以获取更多精彩内容!

Build Spring Microservices和Dockerize Them for Production''最初于2019年2月28日发布在Okta开发者博客上。

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕? 尝试使用Okta API进行托管身份验证,授权和多因素身份验证。

翻译自: https://www.javacodegeeks.com/2019/04/build-spring-microservices-dockerize-production.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/342312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RS-485通信接口编码器

CUI Devices的RS-485编码器采用快速的位置安全协议&#xff0c;该安全协议容许编码器在一个字节的时间内积极响应位置。参考RS485标准的OSI模型应用层所说&#xff0c;这样的格式支持64个唯一的编码器地址。编码器的地址是一个字节的高6位&#xff0c;低2位是命令。这种地址可通…

RS485接口OSI模型的应用层

OSI 模型不是一套规则&#xff0c;而更像是一个帮助工程师表征系统的模型。RS485接口很好地包含在OSI 模型的前三层中&#xff0c;总线的实际实现在应用层中进行了表征。这一层涵盖了设备使用的地址或命令集以及数据的解释。它还包括设计人员期望获得多少数据&#xff0c;以及对…

理解Spring中bean的作用域及其生命周期

作用域 singleton:Spring Ioc容器中只会存在一个共享的Bean实例&#xff0c;无论有多少个Bean引用它&#xff0c;始终指向同一个对象&#xff0c;作用域为Spring中的缺省&#xff08;同一package&#xff09;作用域 prototype:每次通过Spring容器获取prototype定义的bean时&am…

国内外知名工业交换机品牌有哪些?

近年来&#xff0c;随着工业级交换机的广泛应用&#xff0c;各项交换机技术的不断突破&#xff0c;国内外很多知名通信企业都涉及到工业级交换机这一块&#xff0c;在市场上工业交换机的品牌可以说是很多。那么&#xff0c;2021年国内外知名工业交换机品牌有哪些呢&#xff1f;…

无线通信中LoRa技术特点

相信对于很多朋友来说LORA通讯协议还是比较陌生的&#xff0c;因为LORA模块这种通讯技术是在2016年开始才正式传入中国的。现在阿里、Google、腾讯等互联网巨头都已经加入了LORA联盟&#xff0c;最有意思的是亚马逊&#xff0c;它在今年西雅图举行的硬件大会上&#xff0c;发布…

[渝粤教育] 九江学院 计量经济学 参考 资料

教育 -计量经济学-章节资料考试资料-九江学院【】 第1讲单元测试 1、【单选题】回归分析中关于解释变量X和被解释变量Y的说法正确的是&#xff1a; A、解释变量X和被解释变量Y都是随机变量 B、解释变量X和被解释变量Y都是非随机变量 C、解释变量X是非随机变量&#xff0c;被解释…

工业交换机是什么?矿用交换机采用的是工业级交换机吗?

随着工业交换机在各个行业领域的广泛应用&#xff0c;现在国内有了很多专业研发生产各种交换机的厂家&#xff0c;也有很多公司由于工作环境的需求需要采购工业级交换机&#xff0c;那么&#xff0c;工业交换机是什么&#xff1f;矿用交换机采用的是工业级交换机吗&#xff1f;…

react 消息队列_具有AkkaReact流的React队列

react 消息队列React性流是最近宣布的一项计划&#xff0c;旨在在JVM上为具有内置背压的异步流处理创建标准。 该工作组由Typesafe&#xff0c;Red Hat&#xff0c;Oracle&#xff0c;Netflix等公司组成。 早期的实验性实现之一是基于Akka的 。 预览版0.3包括演员生产者和消费…

lora和lorawan无线技术在物联网的应用

Lora联盟表示&#xff1a;“Lora设备和开放的LoRaWAN协议使智能物联网应用能够解决我们智慧城市建设面临的一些最大挑战&#xff1a;能源管理、自然资源减少、污染控制、基础设施效率、防灾等。” LoRaWAN的用途是什么&#xff1f; LoRaWAN在物联网和智慧城市部署中具有多重用途…

LoRa无线技术与LoRaWAN网关模块的区别

有不少人分不清lorawan无线模块与LoRa网关无线传输技术到底有什么区别&#xff0c;他们在物联网领域的应用到底是什么样的。 LoRaWAN指的是MAC层的组网协议&#xff0c;而LoRa是一个物理层的协议。虽然现有的LoRaWAN组网基本上都使用LoRa作为物理层&#xff0c;但是LoRaWAN的协…

工业交换机和普通交换机的区别

对于交换机领域这一块&#xff0c;想必很多做安防的朋友不会陌生吧&#xff0c;交换机又分为商用网络交换机跟工业以太网交换机&#xff0c;为满足灵活多变的工业环境(environment)和抗干扰的方面来看&#xff0c;工业交换机和商业(Business)的交换机有特别大的差距&#xff0c…

AWS Messaging Services:选择合适的服务

1. AWS Messaging Services AWS Messaging服务使云中相似和不同的软件系统能够异步通信和交换信息。 这些软件系统可能不兼容且使用不同的语言&#xff0c;也可能在不同的平台上。 AWS消息传递服务具有高度可用性&#xff0c;高度可扩展性和高度可靠性。 AWS支持针对不同类型用…

变频器的四大组成部分和工作原理

随着电子技的发展变频器已经有了很大的变化&#xff0c;但其基本原理并没有发生改变。变频器的主要部分有四个&#xff1a;整流器、中间电路、逆变器、控制电路。 1&#xff09;、整流器 通用变频器的整流电路是由三相桥式整流桥组成。它的功能是将工频电源进行整流&#xff0…

工业交换机与普通商用交换机的对比详解

工业交换机是专门为满足灵活多变的工业应用需求而设计的&#xff0c;从可靠性和抗干扰方面考虑&#xff0c;工业级别的交换机和商用的产品相差还是很大得&#xff0c;而且工业交换机有很多实用的功能&#xff0c;安装和电源的使用方面更符合工业现场的要求。工业场合选用该类型…

变频器的工作原理和功能应用

变频器&#xff08;Variable-frequency Drive&#xff0c;VFD&#xff09;是应用变频技术与微电子技术&#xff0c;通过改变电机工作电源频率方式来控制交流电动机的电力控制设备。 变频器主要由整流&#xff08;交流变直流&#xff09;、滤波、逆变&#xff08;直流变交流&am…

飞畅科技-工业以太网交换机组网方式介绍

工业以太网交换机专门为满足灵活多变的工业应用需求而设计&#xff0c;提供一种高性价比工业以太网通讯解决方案。工业交换机的应用十分广泛&#xff0c;在行业应用方面&#xff0c;主要应用于&#xff1a;煤矿安全、轨道交通、工厂自动化、水处理系统、城市安防等。接下来&…

轻松读懂三极管,原来它是这样工作的

一&#xff1a;三极管的介绍 三极管&#xff0c;也就是半导体三极管&#xff0c;是一种控制电流的半导体器件&#xff0c;其作用就是吧微弱信号放大成幅度值较大的电信号&#xff0c;也用作于无触点开关。通常&#xff0c;三极管具有电流放大的作用&#xff0c;它的结构是在一…

飞畅科技-工业以太网的应用现状及前景展望

由于以太网无可争议的优势&#xff0c;将以太网应用于工业自动化领域正成为人们关注的热点。那么&#xff0c;以太网用于工业领域需要要解决哪些问题&#xff0c;其发展前景怎么样呢&#xff1f;接下来我们就来详细的介绍下工业以太网的应用现状及发展前景。感兴趣的朋友就一起…

Spring Boot –如何跳过缓存thyemeleaf模板,js,css等以每次绕过重启服务器

Spring Boot自动配置为ThyemeLeaf注册的默认模板解析器是基于类路径的&#xff0c;这意味着它从编译的资源/ target / classes / **加载模板和其他静态资源。 要加载对资源&#xff08;HTML&#xff0c;js&#xff0c;CSS等&#xff09;的更改&#xff0c;我们可以 每次都重新…