vlambda博客
学习文章列表

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

第 8 章平台部署 – AWS

在本章中,我们将介绍 Amazon AWS 平台中可用的一些部署选项。 AWS 平台是最古老、最成熟的云服务提供商之一。它于 2002 年推出,从那时起一直是该领域的领导者。 AWS 也一直在不断创新,并推出了几项新服务,这些服务已在从单人初创企业到企业的各种客户中广泛采用。

在本章中,我们将介绍以下主题:

  • AWS 平台
  • AWS 平台部署选项

AWS平台


Amazon AWS 是云计算 的先驱,此后一直在扩展其云产品以保持其领先地位。下图给出了 AWS 平台为应用程序开发人员提供的服务的指示性列表:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

这只是一个指示性清单,绝不是详尽清单;有关完整列表,请参阅 Amazon AWS 门户。

类别如下:

  • 基础设施: 这可能是 AWS 平台的核心,使其能够提供大量其他服务。这些可以进一步分为:
    • 计算: EC2、Lambda、ECS、ELB等服务。我们将主要使用计算服务来演示示例应用程序的部署,但将它们与 AWS 提供的其他服务结合起来相对容易。
    • 存储: S3、EBS、CloudFront等服务。
    • 网络: VPC、Route53、DirectConnect等服务。
  • Application: 这些服务可以使用 作为构建和支持应用程序的组件。
  • 数据库: 这些服务target数据库,提供对不同关系数据库管理系统的访问(RDBMS ) 和 NoSQL 数据存储。
  • DevOps: 这些服务提供构建构建管道和实现持续交付的能力。其中包括源代码托管、持续集成工具以及云和软件供应工具。
  • 安全性: 这些服务提供基于角色的访问控制 (RBAC) 到各种服务由 AWS 提供,并提供一种机制来指定和执行配额、密钥管理和秘密存储。
  • 移动: 这些服务是旨在为移动应用程序和服务(例如通知)提供后端。
  • 分析: 这些服务包括MapReduce 等批处理系统和 Spark 等流处理系统可用于构建分析平台。

AWS 平台部署选项


在 AWS 平台提供的各种服务中,我们将在本章重点介绍一些专门针对此类的部署选项以我们已经为例的Web APIs。因此,我们将部署覆盖到:

  • AWS Elastic Beanstalk
  • AWS 弹性容器服务
  • AWS 拉姆达

由于我们将在云环境中运行我们的应用程序,我们不需要直接管理基础设施,也就是说,我们不会启动虚拟机并在其中安装应用程序,我们不需要服务发现,因为弹性负载均衡器将自动路由到所有启动的应用程序实例。因此,我们将使用不使用 Eureka 发现客户端的 product API 版本:

package com.mycompany.product;

 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;

 @SpringBootApplication
 public class ProductSpringApp {

    public static void main(String[] args) throws Exception {
       SpringApplication.run(ProductSpringApp.class, args);
    }

 }

将 Spring Boot API 部署到 Beanstalk

AWS Elastic Beanstalk (AEB) 是一项服务 由 AWS 提供,用于在 AWS 上托管 Web 应用程序,而无需直接预置或管理 IaaS 层。 AEB 支持 Java、.NET、Python、Ruby、Go 和 PHP 等流行语言。最近,它还提供了对运行 Docker 容器的支持。我们将采用我们迄今为止在旅程中构建的 product 服务的简化版本,并将其作为可运行的 JAR 和 Docker 容器部署在 AEB 中。

部署可运行的 JAR

登录AWS控制台,选择Compute下的Elastic Beanstalk服务 类别,然后点击 开始 按钮:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

在下一个屏幕中填写应用程序详细信息:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

target 文件夹上传 product.jar 并点击 Configure more选项按钮。您将看到不同的类别,您可以通过选择 Software, 并在其下的 < code>环境属性,添加一个名为SERVER_PORT的新环境变量,并将值设置为5000 .这是必要的,因为默认情况下,由 AEB 环境创建的 NGINX 服务器会将所有请求代理到该端口,并且通过设置变量,我们确保我们的 Spring Boot 应用程序将在端口 5000:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

现在,AWS 将预置一个运行我们的应用程序的新环境:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

配置环境后,AEB 将为应用程序生成 URL

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

我们可以使用这个 URL 来访问 API 端点:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

部署 Docker 容器

现在我们已经了解了如何将可运行的 JAR 部署到 Elastic Beanstalk 服务,让我们也看看相同 where 的变体我们将部署一个运行相同应用程序的 Docker 容器。使用 Docker 容器的优势在于,我们可以使用 AWS Elastic Beanstalk 服务尚未支持的语言和平台,并且仍然将它们部署在云中,从而获得服务提供的好处。

对于此部署,我们将使用 弹性容器服务 (ECS< /strong>) 提供存储我们从应用程序构建的 Docker 容器。当我们部署到 ECS 时,我们将介绍如何将本地 Docker 容器推送到 ECS 存储库。现在,假设我们要部署的 Docker 容器在名为 <aws-account-id>.dkr.ecr.us-west-2.amazonaws.com 的存储库中可用/product-api。由于我们需要访问这个存储库,我们需要将 AmazonEC2ContainerRegistryReadOnly 策略添加到默认的 Elastic Beanstalk 角色, aws-elasticbeanstalk-ec2-role

这可以从 Roles 部分下的 IAM 控制台完成:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

创建一个名为 Dockerfile.aws.json 的文件,其内容如下:

{ 
  "AWSEBDockerrunVersion": "1", 
  "Image": { 
    "Name": "<aws-account-id>.dkr.ecr.us-west-2.amazonaws.com/product-api", 
    "Update": "true" 
  }, 
  "Ports": [ 
    { 
      "ContainerPort": "8080" 
    } 
  ] 
} 

现在我们已经准备好部署我们的 Docker 容器了。在 Elastic Beanstalk 控制台中,我们将选择单个 DockerJava > 容器并创建一个新应用程序:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

选择并上传 Dockerfile.aws.json 以创建环境:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

我们可以测试我们的 API 端点来验证我们的 Docker 容器是否正常运行。我们还可以将容器配置为使用 Amazon CloudWatch 日志记录和监控来更好地监控我们的应用程序:

读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

将 Spring Boot 应用部署到弹性容器服务

AWS Elastic Container Service (ECS) 是一项 用户使用托管Docker 实例部署应用程序。在这里,AWS ECS 服务负责 用于预置虚拟机和 Docker 安装。我们可以通过执行以下操作来部署我们的应用程序:

  1. 启动ECS,点击Continue
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 创建名为 product-api 的 ECS 存储库,然后单击 Next step
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 按照屏幕上的说明构建 Docker 容器并将其推送到存储库:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. GUI 生成的 Docker 登录命令有一个额外的 http:// 应排除在以下情况下:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们现在可以构建 Docker 容器并将其推送到创建的存储库:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 在配置我们的任务定义时,我们将使用这个容器存储库:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 在高级选项中,我们可以在 STORAGE AND LOGGING 部分下配置 AWS CloudWatch 日志记录以从 Docker 容器中捕获日志:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们需要在 CloudWatch 控制台中创建一个相应的日志组来捕获从我们的应用程序创建的日志:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们可以创建一个服务映射到容器暴露的端口,即8080:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 可选地,我们可以描绘 EC2 实例类型 并配置 Key pair 以便我们能够登录到 ECS 将为我们的应用程序创建的 EC2 实例:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 一旦我们审查了配置并提交,ECS 将开始创建 EC2 实例并将我们的应用程序部署到其中:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们可以点击Auto Scaling group,找到启动的实例:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 查找实例:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 查找实例主机名:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 通过实例主机名访问应用程序:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

但是通过主机名单独访问应用程序是不可行的,因此,我们将创建一个弹性负载均衡器,它将请求路由到实例,从而允许我们在扩展或缩减时拥有稳定的端点:

  1. 我们将进入 EC2 控制台,选择Create 在Application Load Balancer
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 配置 Load Balancer Port
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 配置 Target groupHealth checks 端点:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 将目标实例注册到由我们的集群定义创建的实例:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 查找 Load balancer 的 DNS 记录:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 连接到负载均衡器端点并验证 application 是否正常工作:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

部署到 AWS Lambda

AWS Lambda 服务允许在事件触发器上调用简单的函数。这些事件触发器可以分为四种类型,即:

  • 数据存储(例如,AWS DyanmoDB)
  • 队列和流(例如,AWS Kinesis)
  • Blob 存储(例如,AWS S3)
  • API 日期:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

AWS Lamda 支持的事件源的完整列表可以在 https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#api-gateway-with-lambda。

与前面讨论的其他部署选项不同,AWS Lambda 提供了最透明的扩展选项,AWS 平台可以根据需求扩展所需的实例。我们从配置实例、负载均衡器等中解放出来,而可以专注于应用程序逻辑。

我们现在将构建一个简单的 AWS Lambda 函数并将其绑定到 API 端点以调用它。

我们将首先创建一个具有以下依赖项的新 Spring Boot 应用程序。我们还将使用 maven-shade-plugin 创建一个可运行的 JAR:

<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.mycompany</groupId>
   <artifactId>hello-lambda</artifactId>
   <version>0.0.1-SNAPSHOT</version>

   <dependencies>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
       <scope>test</scope>
     </dependency>
     <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-lambda-java-core</artifactId>
       <version>1.1.0</version>
     </dependency>
     <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-lambda-java-events</artifactId>
       <version>2.0.1</version>
     </dependency>
     <dependency>
       <groupId>com.amazonaws</groupId>
       <artifactId>aws-lambda-java-log4j2</artifactId>
       <version>1.0.0</version>
     </dependency>
   </dependencies>

   <build>
     <finalName>hello-lambda</finalName>
     <plugins>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-compiler-plugin</artifactId>
         <configuration>
           <source>1.8</source>
           <target>1.8</target>
         </configuration>
       </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
         <version>3.0.0</version>
         <configuration>
           <createDependencyReducedPom>false</createDependencyReducedPom>
         </configuration>
         <executions>
           <execution>
             <phase>package</phase>
             <goals>
               <goal>shade</goal>
             </goals>
           </execution>
         </executions>
       </plugin>
     </plugins>
   </build>

 </project>

现在创建 HelloHandler.java,内容如下:

package com.mycompany;

 import com.amazonaws.services.lambda.runtime.Context;
 import com.amazonaws.services.lambda.runtime.RequestHandler;
 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;

 import java.net.HttpURLConnection;

 public class HelloHandler implements RequestHandler<APIGatewayProxyRequestEvent, APIGatewayProxyResponseEvent> {

   @Override
   public APIGatewayProxyResponseEvent handleRequest(APIGatewayProxyRequestEvent request, Context context) {
     String who = "World";
     if ( request.getPathParameters() != null ) {
       String name  = request.getPathParameters().get("name");
       if ( name != null && !"".equals(name.trim()) ) {
         who = name;
       }
     }
     return new APIGatewayProxyResponseEvent().withStatusCode(HttpURLConnection.HTTP_OK).withBody(String.format("Hello %s!", who));
   }

 }

由于 lambda functions 是简单的函数,我们只需使用函数的输入和输出就可以很容易地测试它们。例如,一个示例测试用例可以是:

package com.mycompany;

 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
 import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.BlockJUnit4ClassRunner;

 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;

 import static org.junit.Assert.*;

 @RunWith(BlockJUnit4ClassRunner.class)
 public class HelloHandlerTest {

   HelloHandler handler;
   APIGatewayProxyRequestEvent input;
   @Before
   public void setUp() throws Exception {
     handler = new HelloHandler();
     Map<String, String> pathParams = new HashMap<>();
     pathParams.put("name", "Universe");
     input = new APIGatewayProxyRequestEvent().withPath("/hello").withPathParamters(pathParams);
   }

   @Test
   public void handleRequest() {
     APIGatewayProxyResponseEvent res = handler.handleRequest(input, null);
     assertNotNull(res);
     assertEquals("Hello Universe!", res.getBody());
   }
   @Test
   public void handleEmptyRequest() {
     input.withPathParamters(Collections.emptyMap());
     APIGatewayProxyResponseEvent res = handler.handleRequest(input, null);
     assertNotNull(res);
     assertEquals("Hello World!", res.getBody());
   }
 }

现在我们可以使用 Maven 构建 lambda 函数:

$ mvn clean package [INFO] Scanning for projects... [WARNING] [WARNING] Some problems were encountered while building the effective model for com.mycompany:hello-lambda:jar:0.0.1-SNAPSHOT [WARNING] 'build.plugins.plugin.version' for org.apache.maven.plugins:maven-compiler-plugin is missing. @ line 35, column 15 [WARNING] [WARNING] It is highly recommended to fix these problems because they threaten the stability of your build. [WARNING] [WARNING] For this reason, future Maven versions might no longer support building such malformed projects. [WARNING] [INFO] [INFO] ------------------------------------------------------------------------ [INFO] Building hello-lambda 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ [INFO] [INFO] --- maven-clean-plugin:2.5:clean (default-clean) @ hello-lambda --- [INFO] Deleting /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ hello-lambda --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/src/main/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ hello-lambda --- [INFO] Changes detected - recompiling the module! [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file to /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target/classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ hello-lambda --- [WARNING] Using platform encoding (UTF-8 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/src/test/resources [INFO] [INFO] --- maven-compiler-plugin:3.1:testCompile (default-testCompile) @ hello-lambda --- [INFO] Changes detected - recompiling the module! [WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build is platform dependent! [INFO] Compiling 1 source file to /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target/test-classes [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ hello-lambda --- [INFO] Surefire report directory: /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target/surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running com.mycompany.HelloHandlerTest Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.055 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] [INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ hello-lambda --- [INFO] Building jar: /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target/hello-lambda.jar [INFO] [INFO] --- maven-shade-plugin:3.0.0:shade (default) @ hello-lambda --- [INFO] Including com.amazonaws:aws-lambda-java-core:jar:1.1.0 in the shaded jar. [INFO] Including com.amazonaws:aws-lambda-java-events:jar:2.0.1 in the shaded jar. [INFO] Including joda-time:joda-time:jar:2.6 in the shaded jar. [INFO] Including com.amazonaws:aws-lambda-java-log4j2:jar:1.0.0 in the shaded jar. [INFO] Including org.apache.logging.log4j:log4j-core:jar:2.8.2 in the shaded jar. [INFO] Including org.apache.logging.log4j:log4j-api:jar:2.8.2 in the shaded jar. [INFO] Replacing original artifact with shaded artifact. [INFO] Replacing /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target/hello-lambda.jar with /Users/shyam/workspaces/msa-wsp/CloudNativeJava/chapter-09/hello-lambda/target/hello-lambda-0.0.1-SNAPSHOT-shaded.jar [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.549 s [INFO] Finished at: 2018-02-12T13:52:14+05:30 [INFO] Final Memory: 25M/300M [INFO] ------------------------------------------------------------------------

我们现在已经构建了 hello-lambda.jar,我们将把它上传到我们将从 AWS 控制台创建的 AWS Lambda 函数。

  1. 我们将首先转到 API Gateway 控制台,该控制台出现在 AWS 控制台的 Network and Content Delivery 类别中,并创建一个新的接口:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们将为路径 /hello: 添加一个名为 hello 的新资源
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们还将创建一个带有路径参数的子资源:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 现在,我们将附加 HTTP GET 方法:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 创建一个 lambda 函数,其详细信息如下所示:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 上传可运行的 JAR 并设置处理程序方法:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 现在将这个 lambda function 添加到 API 方法中:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 确保选择 Use Lambda Proxy Integration 这样我们就可以使用特定的RequestHandler接口,而不是使用通用的 RequestStreamHandler。这也将授予 API Gateway 对 lambda 函数的权限:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 使用 lambda 函数调用完成 API 定义:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 我们可以从控制台测试 API 端点:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 现在我们可以部署 API:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. API 的成功部署将产生 API 端点:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序
  1. 现在我们可以使用为此部署环境生成的 API 端点来访问应用程序:
读书笔记《cloud-native-applications-in-java》编写您的第一个本地云应用程序

概括


在本章中,我们介绍了 AWS 平台提供的一些选项,以及如何从用于 Web 应用程序的 Elastic Beanstalk 部署我们的应用程序。我们部署到 ECS,用于部署容器化工作负载,不限于 Web 应用工作负载。然后我们部署了一个 AWS Lambda 函数,无需配置底层硬件。我们将在下一章中使用 Azure 进行部署,以了解它为部署云原生应用程序提供的一些服务。