Skip to content

Latest commit

 

History

History
506 lines (353 loc) · 21.4 KB

File metadata and controls

506 lines (353 loc) · 21.4 KB

八、平台部署——AWS

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

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

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

自动气象站平台

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

这只是一份指示性清单,并非详尽无遗的清单;有关完整列表,请参阅 Amazon AWS 门户。

这些类别如下:

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

AWS 平台部署选项

在 AWS 平台提供的各种服务中,我们将在本章重点介绍一些专门针对我们作为示例使用的 web API 类型的部署选项。因此,我们将把部署包括到:

  • 弹性豆茎
  • AWS 弹性集装箱服务
  • AWS Lambda

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

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 引导 API 部署到 Beanstalk

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

部署可运行的 JAR

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

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

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

现在,AWS 将提供一个运行应用的新环境:

设置环境后,AEB 将为应用生成 URL:

我们可以使用此 URL 访问 API 端点:

部署 Docker 容器

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

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

这可以从 IAM 控制台的角色部分完成:

创建一个名为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 控制台中,我们将不选择 Java,而是选择单个 Docker 容器并创建一个新的应用:

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

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

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

AWS弹性容器服务ECS是一种允许用户使用托管 Docker 实例部署应用的服务。这里,AWS ECS 服务负责提供虚拟机和 Docker 安装。我们可以通过执行以下操作来部署应用:

  1. 启动 ECS,点击继续:

  1. 创建名为product-api的 ECS 存储库,点击下一步:

  1. 按照屏幕上给出的说明,构建 Docker 容器并将其推送到存储库:

  1. GUI 生成的 Docker login 命令有一个额外的http://,应排除该命令:

  1. 现在,我们可以构建 Docker 容器并将其推送到已创建的存储库中:

  1. 配置任务定义时,我们将使用此容器存储库:

  1. 在高级选项中,我们可以在存储和日志部分下配置 AWS CloudWatch 日志以从 Docker 容器捕获日志:

  1. 我们需要在 CloudWatch 控制台中创建相应的日志组,以捕获从应用创建的日志:

  1. 我们可以创建一个到容器中公开的端口的服务映射,即8080:

  1. 或者,我们可以描绘 EC2 实例类型并配置密钥对,以便能够登录 ECS 将为我们的应用创建的 EC2 实例:

  1. 一旦我们审查配置并提交,ECS 将开始创建 EC2 实例并在其中部署我们的应用:

  1. 我们可以单击自动缩放组并查找已启动的实例:

  1. 查找实例:

  1. 查找实例主机名:

  1. 通过实例主机名访问应用:

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

  1. 我们将转到 EC2 控制台,并在应用负载平衡器下选择创建:

  1. 配置负载平衡器端口:

  1. 配置目标组和运行状况检查终结点:

  1. 将目标实例注册到由群集定义创建的实例:

  1. 查找负载平衡器的 DNS 记录:

  1. 连接到负载平衡器端点并验证应用是否正常工作:

部署到 AWS Lambda

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

  • 数据存储(例如,AWS DyanmoDB)
  • 队列和流(例如,AWS 动画)
  • Blob 存储(例如,AWS S3)
  • API 日期方式:

AWS Lamda 支持的事件源的完整列表可在中找到 https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html#api-兰博达的网关。

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

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

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

<project  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 函数是简单的函数,我们可以通过使用函数的输入和输出非常容易地测试它们。例如,示例测试用例可以是:

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 Lambda 函数,该函数将从 AWS 控制台创建。

  1. 我们将首先转到 API 网关控制台,它出现在 AWS 控制台的网络和内容交付类别中,并创建一个新的 API:

  1. 我们将为路径/hello:添加一个名为hello的新资源

  1. 我们还将使用路径参数创建子资源:

  1. 现在,我们将附加 HTTPGET方法:

  1. 创建一个 lambda 函数,详细信息如下所示:

  1. 上载 runnable JAR 并设置处理程序方法:

  1. 现在将此 lambda 函数添加到 API 方法:

  1. 确保您选择使用 Lambda 代理集成,以便我们可以使用特定的RequestHandler接口,而不是使用通用的RequestStreamHandler。这还将为 lambda 函数授予 API 网关权限:

  1. 使用 lambda 函数调用完成 API 定义:

  1. 我们可以从控制台测试 API 端点:

  1. 现在我们可以部署 API:

  1. 成功部署 API 将导致 API 端点:

  1. 现在,我们可以使用为该部署环境生成的 API 端点来访问应用:

总结

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