Category: 大咖专栏


走近Fargate,动手配置属于自己的Fargate集群

Fargate简介

就在大约三年前,AWS宣布Amazon Elastic Container Service(Amazon ECS)在AWS上大规模运行和管理容器。 利用Amazon ECS,您可以高规模和高可用性地运行您的工作负载,而无需担心运行自己的集群管理和容器编排软件。

现在,AWS宣布推出AWS Fargate ,使您能够将容器作为基本的计算单元,而无需管理底层实例。 借助Fargate,您不需要在集群中配置,部署或扩展虚拟机来运行容器。 今天,Fargate可以与Amazon ECS一起使用,计划在未来支持Kubernetes(Amazon EKS)的亚马逊弹性容器服务。

Fargate具有灵活的配置选项,因此您可以紧密匹配您的应用程序需求和精确的每秒计费。

  • 无需管理任何EC2实例
  • 基于任务的源生API
  • 计费到CPU和Memory级别,按照Task使用的这些资源按秒计费

Fargate优势

无需管理群集

使用 AWS Fargate,您只需考虑容器,以便能够集中精力构建和运行应用程序。AWS Fargate 消除了管理 Amazon EC2 实例群集的需要。您不必再选择实例类型、管理群集调度和优化群集利用率。使用 Fargate,所有这些麻烦都将不复存在。

无缝扩展

借助 Fargate,您可以轻松扩展应用程序。您不必再担心是否为容器应用程序预置了足够多的计算资源的问题。在您指定应用程序要求 (例如 CPU、内存等) 后,AWS Fargate 会以高度可用的方式管理运行您的容器所需的所有扩展和基础设施。您不再需要决定何时扩展群集,也不需要将其打包即可实现最佳利用率。使用 Fargate,您可以在几秒钟内启动数以万计的容器,并轻松扩展以运行最重要的关键任务型应用程序。

与 AMAZON ECS 和 EKS 集成

AWS Fargate 与 Amazon ECS 无缝集成。您只需按照定义 Amazon ECS 的方式定义您的应用程序。您可以将应用程序打包到任务定义中,指定所需的 CPU 和内存,定义每个容器需要的联网和 IAM 策略,并将所有这些内容上传至 Amazon ECS。完成所有设置后,AWS Fargate 就会为您启动和管理容器。

ECS和Fargate集成的优势总结:

开始动手配置Fargate集群

在之前配置基于EC2的AWS ECS服务时,您需要首先创建包含几个EC2实例的ECS集群。在这个集群中,您需要根据所有运行的容器性能选择合适的EC2实例, 配置好自动扩展策略,以保证底层的EC2实例可以完全有效的承载上层容器。

但是在Fargate中,您可以直接跳过配置底层EC2实例的细节,直接根据您的需要配置您希望使用的容器任务参数。而将承载容器的底层EC2实例交给AWS来维护。

下面就让我们来亲手配置一个属于自己的Fargate集群,来体验Fargate给我们带来的容器配置和管理的便利性。

目前Fargate已经可以在AWS的控制台上正式使用了,不过现在仅支持us-east-1区域。我们登录后切换到us-east-1进行配置。Docker镜像采用的是Docker官方的Nginx镜像。

Fargate基本配置步骤

1. 在Amazon ECS服务下面的Task Definition创建界面可以选择Fargate。

如果希望创建原来使用的普通ECS集群,则是选择EC2类型

2. 设置Task和Container细节

目前Fargate的网络类型只能选择awsvpc类型。awsvpc网络类型会给每一个任务单独分配一个ENI网卡,可以使得在网络层面访问每个任务容器和访问VPC内部的EC2一个。详细关于awsvpc网络类型的介绍,可以参考下面两篇文档:

Under the Hood: Task Networking for Amazon ECSIntroducing Cloud Native Networking for Amazon ECS Containers

因为没有EC2的概念了,所以需要给Task设定内存和CPU。而且在配置的时候下面有推荐的CPU和内存数量

Container配置部分和普通的ECS一样

配置容器名称,容器镜像的URL,容器占用的内存大小以及容器内部希望暴露的端口等。

使用Fargate创建的任务,容器日志会自动启用。启用后容器的日志会在CloudWatch创建并直接打入CloudWatch日志流中。因为用户使用Fargate时不会创建EC2实例,所以推荐打开容器日志,可以更好的监控容器的状态。

3. 设置ECS Cluster

在Cluster配置中选择AWS Fargate使用的集群

在配置集群和网络中,如果要创建新的VPC,那么需要勾选该选项。如果希望利用现有的VPC,那么不勾选该选项即可。后面在创建任务时,会进入分配任务到某个VPC的步骤。

在创建好了集群之后,在总界面上可以看见,每个Cluster可以混搭Fargate以及普通基于EC2的ECS主机。而我们刚才通过Fargate创建的集群,从控制台可以看见,是无需我们维护底层的EC2实例信息的。也不会在这里出现。

集群创建完毕之后。开始创建Fargate的任务。在集群中启动Task,Launch类型选择Fargate

网络部分选择对应希望放入的VPC,如果希望Fargate创建的Task可以被公网访问的话,勾选auto-assign public IP。全部设置完毕之后,点击Run Task,启动任务

4. 查看Fargate相关Task细节

在Task处可以看见运行了Fargate的容器,

点入Task,可以看见该Task绑定的网卡细节,包括私网IP地址和MAC地址。但是如果要看公网地址,那么需要进一步点击ENI

访问该界面发现Nginx已经启动

5. 增加负载均衡器(ALB/NLB)

多个任务创建完毕之后。我们需要给新创建的任务添加负载均衡器。因为Fargate没有EC2实例会暴露出来。只会暴露ENI网卡的IP地址。所以在负载均衡器方面,仅支持可以添加IP地址的ALB和NLB。

添加Target Group,再次注意,因为Fargate没有Instance的概念,所以Target Type需要选择IP

针对这个Target Group添加目标

创建ALB,Target Group选择之前创建的组

创建ALB结束之后,已经可以通过ALB的域名可以正常访问Fargate创建的容器了

6. Fargate容器日志查看

前面说到可以通过CloudWatch Logs的方式记录任务容器的日志。打开CloudWatch服务,在Logs界面写,可以找到新创建的ECS Fargate任务的日志流。

在该日志流中记录了Fargate生成的2个容器的日志

点击进入其中一个容器的日志,可以看见刚才MAC笔记本通过firefox浏览器通过ALB访问容器的日志记录

至此,一个前端通过ALB做负载均衡,后端由Fargate生成的由Nginx组成的集群搭建完成。整个过程不需要您维护任何EC2的实例。完全由AWS来帮您维护EC2实例,您只需要关系您的容器即可。

 

作者介绍:

姚远

亚马逊AWS解决方案架构师,负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内的应用和推广。现致力于网络和DevOps相关领域的研究。在加入AWS之前,在思科中国担任系统工程师,负责方案咨询和架构设计,在企业私有云和基础网络方面有丰富经验。

 

 

如何使用AWS CodePipeline,AWS CodeBuild与AWS CloudFormation实现Amazon ECS上的持续集成持续部署解决方案

作者:郭威

1. 前述

通过本文章,您将了解如何通过AWS CodePipeline,AWS CodeBuild,AWS CloudFormation 来实现基于Amazon ECS的持续集成持续部署方案。

开发人员在GitHub中提交的新版本代码,会自动触发代码获取,打包镜像,上传镜像仓库,更新新版本容器服务,注册到负载均衡器等操作。

方案中会涉及使用如下组件:

GitHub:示例使用的源,一个提交到GitHub上的PHP示例网站。AWS CodePipeline支持GitHub, AWS CodeCommit服务,或者S3作为源。此次实例使用的Demo软件工程可以从以下链接Fork:

https://github.com/awslabs/ecs-demo-php-simple-app

Docker:作为发布服务使用的容器。演示方案的Build阶段会使用AWS CodeBuild托管的ubuntu/docker 1.12.1基础镜像。

Amazon EC2:作为ECS的容器宿主机集群。

Amazon VPC:服务所在的网络。

Amazon ECS:AWS托管的容器编排服务。文档链接 http://docs.aws.amazon.com/zh_cn/AmazonECS/latest/developerguide/Welcome.html

Amazon ECR:AWS 托管的容器镜像仓库。文档链接 http://docs.aws.amazon.com/zh_cn/AmazonECR/latest/userguide/what-is-ecr.html

AWS CodePipeline:AWS 托管的持续集成持续交付服务,可以快速可靠的更新应用程序和服务,集成支持GitHub,Jenkins等主流开源工具。文档链接 http://docs.aws.amazon.com/zh_cn/codepipeline/latest/userguide/welcome.html

AWS CodeBuild:AWS 托管的构建服务,用于打包代码生成可部署的软件包。文档链接 http://docs.aws.amazon.com/zh_cn/codebuild/latest/userguide/welcome.html

AWS CloudFormation:批量创建和管理AWS资源的自动化脚本。文档链接 http://docs.aws.amazon.com/zh_cn/AWSCloudFormation/latest/UserGuide/Welcome.html

2.方案架构

流程如下:

  1. 开发者将一个新版本的代码工程提交到GitHub
  2. Pipeline的Source阶段,检测到指定GitHub的repo有新版本的更新,从GitHub上拉取代码工程,开启已设定好的CICD Pipeline
  3. Pipeline的Build阶段,AWS CodeBuild将新版本的代码工程打包为Docker镜像
  4. AWS CodeBuild将打包好的镜像推送到Amazon ECR
  5. Pipeline的Deploy阶段,AWS CodePipeline触发AWS CloudFormation,其定义了Amazon ECS的Task definition和service
  6. AWS CloudFormation创建新版本的Task definition关联到新版本的Docker镜像,并更新Service
  7. Amazon ECS从Amazon ECR中取到新版本的Docker镜像,并运行来替换旧Task以完成服务的更新部署

3. 搭建

搭建部分分为以下几个步骤:基础设施,与CICD Pipeline的搭建。

3.1   基础设施部分的搭建

这里需要准备好网络,负载均衡器,S3以及运行ECS所需要的宿主机集群。

3.1.1 网络搭建

创建VPC,子网,Internet Gateway,路由表。将Internet Gateway Attach到VPC上,路由表配置0.0.0.0/0指向Internet Gateway,并关联子网。

之后的EC2宿主机集群,负载均衡器等都使用在这个网络里。

3.1.2 负载均衡器

创建ALB应用负载均衡器,监听80端口

选择对应的子网

新建安全组,端口80,并新建目标组

注册目标此时不选择,ECS创建服务时会注册集群和对应端口进来。

下一步审核后创建。

3.1.3 ECS宿主机集群

在ECS的界面下,创建集群

实例配置保持默认或根据情况自行选择,示例中保持默认。

联网配置,选择创建好的VPC,子网,创建Role允许宿主机上的ECS代理调用ECS服务的API。

创建后画面下面会显示集群信息

集群一览会显示

修改ECS宿主机集群的安全组,inbound源设置为建好的应用负载均衡器的安全组ID

3.1.4 ECR镜像仓库创建

创建一个用于Build阶段上传存放软件工程Docker镜像的镜像仓库

ECS界面下,创建存储库,创建好后如下

3.1.5 S3桶创建

创建一个S3桶用来存放Deploy阶段CloudFormation使用的脚本模版,创建桶时选择和以上服务同一Region,并且打开桶的版本控制。

将CloudFormation模版压缩zip后上传到桶中。

示例中将模版文件service.yaml放在templates文件夹后压缩为templates.zip。

service.yaml如下,注意缩进

Parameters:

  Tag:

    Type: String

    Default: latest


  DesiredCount:

    Type: Number

    Default: 0


  TargetGroup:

    Type: String


  Cluster:

    Type: String


  Repository:

    Type: String


Resources:

  ECSServiceRole:

    Type: AWS::IAM::Role

    Properties:

      Path: /

      AssumeRolePolicyDocument: |

        {

            "Statement": [{

                "Effect": "Allow",

                "Principal": { "Service": [ "ecs.amazonaws.com" ]},

                "Action": [ "sts:AssumeRole" ]

            }]

        }

      Policies:

        - PolicyName: root

          PolicyDocument:

            Version: 2012-10-17

            Statement:

              - Resource: "*"

                Effect: Allow

                Action:

                  - ec2:*

                  - elasticloadbalancing:*


  Service:

    Type: AWS::ECS::Service

    Properties:

      Cluster: !Ref Cluster

      Role: !Ref ECSServiceRole

      DesiredCount: !Ref DesiredCount

      TaskDefinition: !Ref TaskDefinition

      LoadBalancers:

        - ContainerName: simple-app

          ContainerPort: 80

          TargetGroupArn: !Ref TargetGroup


  TaskDefinition:

    Type: AWS::ECS::TaskDefinition

    Properties:

      Family: !Sub ${AWS::StackName}-simple-app

      ContainerDefinitions:

        - Name: simple-app

          Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/${Repository}:${Tag}

          EntryPoint:

            - /usr/sbin/apache2

            - -D

            - FOREGROUND

          Essential: true

          Memory: 128

          MountPoints:

            - SourceVolume: my-vol

              ContainerPath: /var/www/my-vol

          PortMappings:

            - ContainerPort: 80

          Environment:

            - Name: Tag

              Value: !Ref Tag

        - Name: busybox

          Image: busybox

          EntryPoint:

            - sh

            - -c

          Essential: false

          Memory: 128

          VolumesFrom:

            - SourceContainer: simple-app

          Command:

            - /bin/sh -c "while true; do /bin/date > /var/www/my-vol/date; sleep 1; done"

      Volumes:

        - Name: my-vol

到此基础设施部分的搭建工作结束。

3.2   Pipeline的搭建

分为Source,Build以及Deploy三阶段:

Source阶段设置GitHub上的软件工程位置,并设置Deploy阶段会使用的CloudFormation脚本模版来更新ECS服务,

Build阶段使用AWS CodeBuild来打包软件工程到Docker镜像并上传到ECR,

Deploy阶段使用Source阶段引入的CloudFormation脚本,找到对应的宿主机集群,负载均衡器,以及上传到ECR的Docker镜像等对象,更新服务。

AWS CodePipeline创建后的展示图是这样的,串起了整个CICD流程

在AWS CodePipeline界面点击创建管道Pipeline,可以看到画面左侧一个基本流程,从源,到生成Build,到部署Deploy,到角色等配置。实际应用中用户可以随实际需要,或随着CICD流程的由简入繁在创建后编辑加入新的阶段或操作。

点击下一步。

3.2.1 Source阶段配置

源提供商下拉菜单选择GitHub,

点击连接到GitHub来授权访问权,来允许AWS CodePipeline从GitHub上获取软件工程源内容,认证后选择GitHub上软件工程所在位置和分支

此次实例使用的Demo软件工程可以从以下链接Fork:

https://github.com/awslabs/ecs-demo-php-simple-app

点击下一步。

3.2.2 Build阶段配置

AWS CodePipeline在Build阶段支持包括AWS CodeBuild,Jenkins在内的引擎,此方案选用AWS 托管的CodeBuild服务

选择新建构建项目

选择AWS CodeBuild托管的镜像,支持Ubuntu系统,运行时支持包括Java,Python,Go语言,Node.js,Docker在内的众多选择,此次方案使用Docker。

构建规范这里选择使用buildspec.yml,里面预定了AWS CodeBuild在Build生命周期中要执行的动作,如login到ECR,打包Docker镜像,给Docker镜像打tag,上传Docker镜像到已login的ECR镜像仓库。

Buildspec.yml放在GitHub软件工程源代码目录中,如果复制粘贴的话注意yaml文件的缩进

version: 0.2

phases:

  pre_build:

    commands:

      - $(aws ecr get-login)

      - TAG="$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | head -c 8)"

  build:

    commands:

-    docker build --tag "替换创建好的ECR镜像仓库的URI:${TAG}" .

  post_build:

    commands:

      - docker push "替换创建好的ECR镜像仓库的URI:${TAG}"

      - printf '{"tag":"%s"}' $TAG > build.json

artifacts:

  files: build.json

选择Role

新建一个Role,这个Role允许AWS CodeBuild来调用相关的AWS服务,此方案中需要调用包括S3,ECR,CloudWatch在内的服务。

*默认创建的Role不具备对ECR的权限,需要在保存构建项目后,到IAM找到新创建的Role,编辑添加对ECR的权限否则后面Pipeline执行到Build时会报错。

保存构建项目。

点击下一步。

3.2.3 Deploy

AWS CodePipeline部署阶段支持包括AWS CloudFormation,AWS CodeDeploy,AWS Elastic Beanstalk在内的服务提供商,此方案选用AWS CloudFormation来部署ECS容器服务。

这里暂时选择无部署,等Pipeline创建好后,编辑引入Deploy的CloudFormation模版源,再进行配置。

点击下一步。

3.2.4 角色

配置AWS CodePipeline对AWS服务的调用权限,包括S3,AWS CodeBuild,AWS CloudFormation,IAM等。点击创建角色到IAM界面选择相对应的策略创建。

创建好后画面回到Pipeline,IAM创建好的Role已经显示在里面。

点击下一步。

3.2.5 审核后创建管道。

管道创建好后会自动运行,现有的从GitHub软件工程源代码抓取工程,打包Docker镜像并推送到ECR上。界面上显示如图

3.2.6 添加Deploy阶段CloudFormation需要的模版源以及配置Deploy阶段

点击编辑,点击Source阶段右上角的画笔图标

可以看到AWS CodePipeline的编辑界面在南北纵向和东西横向都可以添加

在GitHub这个Source右侧,点击添加操作,选择源,操作名称Template,选择S3,输入创建好的S3桶的地址

画面往下拉,注意在输出项目这里,输入Template。

Pipeline中各阶段的传递需要制定南北向的输入输出,即Source阶段S3源的输出Template,在Deploy阶段用输入Template来衔接。

点击更新。

点击Build阶段下面的添加阶段,画面右侧选择部署,选择AWS CloudFormation

操作模式选择创建或更新堆栈,输入创建的堆栈名称,模版这里输入Template::templates/service.yaml,也就是对应的输入是S3源桶中templates.zip里的service.yaml文件。功能选择CAPABILITY_NAMED_IAM。

同样需要创建一个Role,允许AWS CloudFormation调用包括IAM,ECS,ECR在内的AWS服务。

在IAM界面创建好后选择Role。

高级这里点开

在参数覆盖这里输入CloudFormation需要传入的参数,其中的固定参数也可以在S3的service.yaml中直接定义。

{

  "Tag" : { "Fn::GetParam" : [ "MyAppBuild", "build.json", "tag" ] },

  "DesiredCount": "2",

  "Cluster": "CICD-DEMO-CLUSTER-01",

  "TargetGroup": "arn:aws:elasticloadbalancing:us-east-2:305890642658:targetgroup/CICD-DEMO-TG-01/b7649674ee8ab97b",

  "Repository": "cicd-demo-ecr-01"

}

Tag是Build阶段传出的Docker镜像Tag使用的值,传入CloudFormation中用于建立Task Definition的Container时从ECR拉取对应版本的Docker镜像。

DesiredCount,即想要在ECS的Service中建立的Task的数量。

Cluster,即建立好的宿主机集群的名称。

TargetGroup,即建立好的宿主机集群的应用负载均衡器的ARN。

Repository,即建立好的ECR的镜像仓库名称。

 

输入项目这里输入Build阶段和S3模版源的输出。

点击添加操作。

保存管道更改。

4. 运行以及模拟版本更新

4.1   运行

访问负载均衡器的DNS地址来确认目前服务已经运行正常。

4.2   模拟版本更新

4.2.1 修改代码

在开发本地更新代码,示例中在src/index.php加入一行文字

<h1 style="color:FF7F00;">Amazon ECS Awesome!</h1>

4.2.2 提交新版本后查看AWS CodePipeline各阶段触发

4.2.3 刷新服务来确认新修改的部分已经发布

作者简介

郭威,目前在AWS负责推广针对初创企业的最佳云计算架构实践。具有8年软件设计研发,存储,容器与云计算方案经验。曾任某跨国外企高级研发经理与售前经理,在广电视频行业,金融行业等方面有丰富的云计算经验。后加入某容器创业公司任产品研发与容器云方案工作。现致力于容器服务与Devops等方向的学习与研究。

AWS之旅:混合云构建及常见的应用场景

作者:袁春华,AWS解决方案架构师

混合云融合了公有云和私有云,是企业上云的中间之旅,也是云计算发展过程中的一种模式和阶段。出于如信息保密、行业合规、市场细分等因素或考虑,有些企业更愿意将数据存放在本地数据中心或私有云中,但是同时又希望可以获得公有云的计算资源、存储资源。在这种情况下,混合云在企业上云之初被广泛的采用。

1     构建混合云的关键技术点

1.1   网络连接

第一点,公有云具备用户自定义网络的能力。这里自定义网络泛指云上的私有网络,Amazon VPC允许企业用户在AWS云上自定义逻辑隔离的专用网络的能力,包括允许用户可以自行划分子网、配置路由,设置公网、VPN网关等基础能力。

第二点,多样、稳定的网络接入能力。混合云最突出的特点就是连接,连接方式主要分为两种:公网接入和专线接入。

为保护用户的信息安全,公网接入在Internet上的流量通常需要经过IPsec VPN加密,AWS提供了托管VPN服务和用户自建VPN两种方式,如果采用用户自建方式,一般小型VPN网关(200Mbps以下)可以通过虚拟软件VPN网关实现,中或大型VPN网关(300Mbps及以上)可通过硬件VPN网关设备实现。VPN部署一般半天即可完成,可以快速满足企业用户需求。

AWS Direct Connect(简称DX专线)为企业上云提供了专线接入能力,能够全时段、全方位的保障用户数据传输隔离加密,并保证可用网络带宽,用户无需担心容量太大而无法承载的问题。

示图1:AWS DX专线与VPN混合组网示意图

1.2   安全与监控

混合云在网络层面上将私有云和公有云进行了连接,如果发生网络故障或者攻击,需要有能力保障网络间故障不会相互影响,将攻击或者故障限制在一定范围之内。云上的网络环境需要具备灵活、易配置的网络安全访问控制能力。Amazon VPC提供了有状态的EC2实例级别的网络防火墙功能(安全组),还则提供了无状态的子网级别的网络访问控制(网络ACL)。

监控方面,Amazon VPC 提供了流日志(flow log)功能,可以利用此项功能来捕获传入和传出的网络IP流量信息。流日志数据使用 Amazon CloudWatch Logs 存储,创建流日志后,用户可以在 Amazon CloudWatch Logs 中监控和查看其VPC网络的流量传入和传出状态,并可通过CloudWatch预设告警策略以及时反馈网络异常。VPC流日志还可以帮助用户排查网络故障等问题,比如流量为什么没有进入到EC2实例的原因。另外,针对VPC流日志的分析,可以帮助检查AWS云上的网络安全设置,消除网络安全漏洞。

1.3   统一服务,混合云管理平台

云管理平台的定义是Gartner提出来的,总结起来就是两块,第一是管理,管理公有云、私有云,形成混合云。第二是自服务,镜像划分,计量与计费,以及基于策略的负载优化。云管理平台最终的目标是应用在云平台上运行时取得最优化的效果。

企业用户构建混合云平台之后,首先需要解决的就是服务门户的统一,资源状态监控界面的统一,在一个平台上实现本地数据中心(或私有云)和公有云资源的统一申请、统一审批、统一监控、统一计费。这样能够大幅度降低用户跨平台切换带来的复杂运维工作量,让用户跨平台的资源使用与监控更加方便。

示图2:混合云管理平台(CMP)功能示例图

2     常见的混合云应用场景

2.1   利用AWS公有云应对业务的爆发式增长

大多数互联网业务呈指数型增长,很难预测基础设施的储备量。一般在业务成长初期很长一段时间内,小规模的物理机托管可以满足用户需求。但随着市场推广活动展开,业务规模爆发增长,原物理托管机房可部署机位有限,只能选择公有云作为弹性手段,快速部署业务,满足用户需求。

今天已经有越来越多的企业用户将他们的互联网业务系统,诸如电子商务系统、Web网站、移动端产品和信息订阅服务系统迁移到AWS云上。应对业务规模爆发增长的用户需求,AWS云服务采用多种方式确保应用系统的可靠性和稳定性,作为最基础的云服务,Amazon EC2提供了大小可调的计算容量,用户可以根据自己的实际需求,完全控制所需的计算资源。配合AWS的Auto Scaling功能,用户可以按照事先设定的条件自动扩展Amazon EC2容量,确保所使用的Amazon EC2数量在需求峰值期间实现无缝增长以保持性能,同时也可以在需求平淡期间自动减少Amazon EC2数量以降低成本。此外,Elastic Load Balancing可以在多个Amazon EC2实例之间自动分配应用程序的访问流量,也可以检测出系统中不健康的实例并自动更改路由,将应用的访问流量导向健康的实例。通过不同功能云服务之间的组合,AWS云平台可以确保应用系统的高可靠性和稳定性。

示图3:混合云架构下的电商平台三层系统架构图

与早期基于传统数据中心的系统相比,依托AWS云平台的新系统在稳定性、可靠性和响应速度等方面都有了极大的提升,同时大幅度降低了固定资产投资(CAPEX)和运营成本(OPEX)。在传统数据中心模式下,用一台服务器的寿命大约为5年来计算,购入一台与Amazon EC2 c3.4xlarge机型(16 vCPU,30 GB 内存)配置对应的物理服务器,加上网络和托管费用, 核算出5年的总费用;如果按这个需求迁移到AWS云平台后,企业用户在总费用上可以节省20%。

使用AWS云服务也大幅度降低了用户的运营成本。在传统数据中心模式,企业用户的运维团队工作任务繁重,需要做很多简单、重复的工作,比如服务器的上下架、系统重装、硬件检修等。采用AWS云服务之后,运维人员可以把主要精力放在研究和部署更好的技术架构和方案上,提升了工作效率,减少了人力投入,运维人员的人力成本可以节省一半左右。

2.2   利用AWS公有云实现多地容灾的高可用架构部署

与前一类用户不一样,很多企业用户已经具有很大规模,在本地数据中心或运营商机房中运行大量服务器,他们的核心问题不再是担心基础设施部署速度无法满足业务增长,更多的是从稳定性、可靠性等寻求从单中心向多中心化发展,通过消灭单点,解决单数据中心故障带来的业务风险。

根据业务可靠性要求不同,一般多地容灾常见的采用方式有同城容灾、异地双活、两地三中心等。按照传统建设模式,用户只能重新选址并租赁数据中心,发起服务器、网络设备采购,部署网络环境等,中间涉及到一系列的商务沟通、基础设施建设等操作,周期往往从6个月到1年不等。而在公有云蓬勃发展的今天,他们可以不必担心此类问题,直接注册个账号,购买服务器,拉个专线,基础架构就搞定了。

AWS的混合云容灾架构,就是在AWS的云环境中实现“两地三中心”,同时利用AWS云中资源的弹性大幅度降低资源成本和建设以及运维的复杂性。AWS云资源池通过软件定义的方式,能够打造与企业内部完全相同的复杂IT环境,实现企业级应用的完整镜像。

成立于2005年的IGG,是全球领先的手机游戏开发商及运营商,为全球游戏玩家提供游戏和相关服务。为了给玩家提供最佳的用户体验,IGG需要在全球多个区域部署服务器。在使用AWS云服务之前,IGG拥有自建的机房,同时也采用多家公司的主机租用服务。随着业务的快速发展,原有的服务部署模式开始面临多方面的挑战,主要体现在三个方面:其一是服务器资源管理不方便、扩展性和灵活性受限制,当访问峰值来临时,部署新服务器的周期较长,而当某款游戏的访问量暂时降低时,又会造成已有服务器资源的浪费;其二是开发和运营新游戏时难以控制成本,因为对游戏运营公司来说,一款游戏是否成功受很多因素的影响,但在运营每一款新游戏时都需要投入大量的IT资源,万一不成功,则会浪费已购置的服务器资源;其三是如何建立双活的灾备系统以确保在任何情况下都能为全球玩家提供持续可靠的服务。

为了应对这些挑战,IGG开始考虑采用公有云服务作为自有数据中心(下面简称:自有IDC)的补充。经过考察、分析和对比,IGG决定采用AWS云服务,并从几年前开始尝试使用AWS在新加坡、法兰克福、日本、美国等可用区的云服务部署海外游戏节点。2016年8月,为了提升整个游戏运营系统的稳定性和可靠性,IGG决定为其位于北加州的核心业务系统建立双活灾备系统。使用AWS云服务给IGG带来的最大好处是用较低的成本建立起远程的双活灾备中心,实现了核心业务异地实时同步备份,在主机房出现故障时能快速切换到云端,保证服务的连续性。整个灾备系统完全满足IGG的业务需求:RTO不超过30分钟、RPO为0,增强了IGG游戏运营系统的稳定性和可靠性。除此之外,使用AWS云服务也节省了灾备系统的成本,因为利用AWS云服务建立双活灾备系统时并不需要1:1的资源配置,这点相比传统的本地灾备具有极大的性价比优势。

示图4: 基于AWS云服务的IGG双活灾备中心

AWS混合云容灾架构的用户价值: 从容灾系统的TCO上看,AWS混合云容灾解决方案更是具备明显优势。无需前期对硬件、软件的采购和安装,省去了大量前提投入成本。更重要的是,容灾方案中AWS云中资源可以选择不开机或只开启少量小机型资源,对于不开机的资源将完全不收取EC2虚拟机资源的费用,又能保持EC2虚拟机的状态和后台数据的增量更新。经过我们的测算,一个典型的容灾系统项目,以5年为周期进行计算,TCO只需花费原有私有云容灾环境的1/3,而第一年的投入资金更是传统项目的1/10。

另外,随着应用容灾系统迁移至AWS云中,可以将企业现有的容灾中心转变成生产中心,从而扩大客户自建数据中心的承载能力,或大幅降低IT资源的运营成本,享受更多AWS云带来的好处。下图从AWS混合云容灾开始,到容灾切换,到生产系统平滑上云的进阶演进图:

 示图5:AWS混合云容灾,到容灾切换,到生产系统平滑上云的进阶演进图

2.3   利用AWS公有云实现混合云备份

将数据备份到云端的好处显而易见:管理和操作更加便捷,无需搭建IT备份系统架构、将一次性大额支付变为每月或每年的按需付费,进而减少企业对IT资源的投入等等。现在越来越多的企业客户更倾向于通过其现有的备份软件或云网关,将其现有的备份体系架构扩展到云上。这样做的好处可以用云备份替换离线磁带,大大降低本地存储的资源开销,利用云端完成备份工作,更具性价比。

AWS Storage Gateway (又称“AWS存储网关”,架构图见示图6)是AWS提供的一种混合云存储服务,企业本地数据中心的内部应用程序可以借助它来无缝地使用 AWS 云上的存储资源(如S3,Glacier等),AWS存储网关可以帮助企业用户实现本地数据中心向AWS云端进行备份、存档、灾难恢复、云突增、分级存储分层和迁移。并具有无缝集成能力,企业应用程序或备份工具可以使用 NFS、iSCSI 等标准存储协议通过本地存储网关镜像设备连接到AWS云端的该存储网关,同时存储网关连接 Amazon S3、Amazon Glacier、Amazon EBS 等 AWS 存储服务,可实现文件、卷和虚拟磁带的存储或备份。

示图6:AWS存储网关服务架构示意图(Gateway-Cached Volume)

AWS存储网关具有高度优化的数据传输机制,能够进行带宽管理、自动实现网络弹性、高效传输数据,并为活动数据的低延迟本地访问提供本地缓存。

当然从企业用户的混合云备份需求角度,通常需要考虑:

  • 如何实现对于虚拟机和物理机进行统一管理?
  • 如何缩短备份时间?
  • 如何快速恢复? 能否恢复到任何环境(如物理机,虚拟机,云中的虚拟机) ?
  • 能否确保备份与恢复数据的完整性及安全性?
  • 如何实现冷热数据的分级存储,降低云上的存储成本?
  • 如何避免重复备份数据?

目前已经有很多国内外备份服务厂商基于AWS存储网关构建了一体化AWS混合云备份方案,利用AWS云存储资源便宜的特点帮助企业用户实现AWS云端备份,而且方案能很好的满足前述提到用户对混合云备份的要求。下图为国内某备份服务厂商基于AWS存储网关的混合云备份解决方案。

示图7:国内某备份服务厂商基于AWS存储网关的混合云备份解决方案

该备份服务厂商的AWS混合云备份解决方案具有如下优势和技术特点:

  • 操作简便:提供功能丰富,操作简便的用户界面,支持手工备份、时间计划、多任务多计划、备份状态监控、版本管理、恢复操作、并提供一键式故障转移切换功能。
  • 块级别全局重复删除数据:基于源端的重复数据删除技术,相同数据块仅发送和存储一次,可以减少备份时间80%以上,这不仅带来效率的提高、带宽、存储空间的节约;还保证窄带宽条件下大数据异地灾备。
  • 瞬间恢复:通过Snapshot技术,保证备份期间不需要关闭应用,定时对发生变更的数据块进行标记并实施迁移 (最小支持一分钟),在下一个备份时间点只检索被标记的数据块,这不仅节省磁盘IO,并且能够瞬间恢复数据。
  • 冷热数据分级存储功能:充份利用Amazon S3提供的生命周期管理策略的功能,用户通过简单的策略制定就可以自动化实现冷热数据的分级存储,冷数据自动归档存储到更便宜的Amazon Glacier上,既实现了用户长期归档的需求,还可以大幅降低云上的存储成本。
  • 支持Windows/Linux/Unix等多种环境的网络集中备份,同时支持多种应用服务类型,如各种常见的应用服务器、数据库(如MySQL、SQL Server、Oracle、Sybase、DB2)、 Domino、AD、Exchange、File server等等。
  • 三重数据安全:
  1. 硬盘锁:数据与设备进行绑定,数据只能恢复到原有硬件
  2. 无规则存放:数据以无规则的块存储,避免坚守自盗
  3. 通信与数据加密:私有通信协议,可选AES数据加密
  • 异地灾备与恢复: 备份到异地云计算中心,支持恢复到本地物理机、虚拟机、AWS云上虚拟机等多种不同平台,当本地数据中心出现物理故障时,这种异地恢复机制既可以满足企业用户异地灾备恢复的需求,还可以实现数据异地使用的业务价值。
  • 传输优化:经优化的灾难恢复和复制,几分钟内返回到联机状态。

作者介绍:

袁春华,AWS解决方案架构师,负责AWS合作伙伴生态系统的云计算方案架构咨询和设计,同时致力于AWS云服务在国内的应用和推广。在加入AWS之前,在IBM中国担任解决方案架构师,负责金融行业的方案咨询和架构设计,在企业软件架构、数据库、中间件软件等方面有丰富经验。

Amazon IoT 安卓SDK 中国区实践

作者:任庆杰,AWS解决方案架构师。

简介

物联网(IOT)是现在非常火热的一个概念。那么怎么样利用AWS提供的IoT服务,快速在自己的安卓智能设备上搭建起IoT的消息推送和订阅呢?

本文将分两部分为你解读:

1)如何动态生成和绑定设备证书

2)如何利用MQTT协议接入Amazon IoT 平台

服务简介

AWS IoT平台

AWS IoT 是一种托管的云平台,使互联设备可以轻松安全地与云应用程序及其他设备交互。AWS IoT 可以支持数十亿台设备和数万亿条消息,并且可以对这些消息进行处理并将其安全可靠地路由至 AWS 终端节点和其他设备。借助 AWS IoT,您的应用程序可以随时跟踪您的所有设备并与其通信,即使这些设备未处于连接状态也不例外。

Amazon Cognito

Cognito Identity 可让您轻松实现联合身份,您可以让用户通过Amazon, OpenID, SAML或者自定义的身份认证系统进行认证。匿名或认证登录后,IoT设备被赋予临时性受限权限凭证,可以利用此凭证访问 AWS 资源。

过程介绍

1.  获取连接IoT的权限证书

1)通过安装在安卓设备上的SDK连接Amazon Cognito,认证用户(本文示例将采用匿名模式),并且获取临时权限的Access Key和Secret Key。

2)使用临时权限向AWS IoT发起创建证书请求,激活证书并且下载。

3)将证书加密存储为Keystore在设备上,用以认证之后的IoT请求。Android Keystore保护你存储在设备上的证书,让它不容易被它人破解和提取。

2.  通过MQTT协议连接AWS IoT,并订阅和推送消息。

1)连接设备与AWS IoT,使用Keystore来通过认证

2)与AWS IoT的设备网关交互来订阅和推送消息

3)通过规则引擎还可以过滤消息,并调度AWS各种服务

操作指南

1.  配置环境

1)下载并安装Android Studio

2)下载样例代码https://github.com/awslabs/aws-sdk-android-samples/

3)在Android Studio中,创建导入项目,源指定为样例代码中的AndroidPubSub文件夹

4)修改build.gradle文件,引入支持中国区服务的Android SDK版本(大于等于2.6.7)。

替换

dependencies {

    compile 'com.amazonaws:aws-android-sdk-iot:2.2.+'

}


为

dependencies {

    compile 'com.amazonaws:aws-android-sdk-iot:2.6.7'

}

2.  配置Amazon Cognito

1)  在console上打开Cognito服务,创建新的身份池,启用未经验证的身份的访问权限,点击创建池。在下一页点击允许。

2)  记录下身份池ID

3)  console中点击进入IAM,在角色中找到Cognito_xxxxUnauth_Role (本例中为Cognito_android_iot_exampleUnauth_Role),加入一条新的内联策略。

{

  "Version": "2012-10-17",

  "Statement": [

    {

      "Effect": "Allow",

      "Action": [

        "iot:AttachPrincipalPolicy",

        "iot:CreateKeysAndCertificate"

      ],

      "Resource": [

        "*"

      ]

    }

  ]

}

3.  配置AWS IoT

1)  在console中进入AWS IoT服务,创建一个新的事物。点击新创建的事物,左边栏点击交互,记录Rest API的终端节点

2)  在左侧边栏,点击安全=》策略,创建一个新的策略

输入并记录下策略名称android_iot_example_policy

点击高级模式输入:

{

"Version": "2012-10-17",

"Statement": [

{

"Effect": "Allow",

"Action": [

"iot:Publish",

"iot:Subscribe",

"iot:Connect",

"iot:Receive"

],

"Resource": "*"

}

]

}

更多策略请见这里

4.  找到文件PubSubActivity,更新代码配置

// rest API 终端节点
private static final String CUSTOMER_SPECIFIC_ENDPOINT = "xxxxxxx.iot.cn-north-1.amazonaws.com.cn";
// Cognito身份池ID
private static final String COGNITO_POOL_ID = "cn-north-1:xxxxxxxx";
// 策略名称

private static final String AWS_IOT_POLICY_NAME = "android_iot_example_policy";
// Region填写CN_NORTH_1
private static final Regions MY_REGION = Regions.CN_NORTH_1;
// keystore存储名字
private static final String KEYSTORE_NAME = "yourkeystorename";
// keystore密码
private static final String KEYSTORE_PASSWORD = "yourpassword";
// 证书别名
private static final String CERTIFICATE_ID = "default";

5.  测试订阅和推送

1)  在android studio中运行项目AndroidPubSub项目。点击界面上的Connect按钮,等待Status变为“Connected”。在topic name处输入话题名称“to_android_device”,并点击SUBSCRIBE按钮。

2)  打开AWS控制台,进入IoT服务。点击左侧栏“测试”,点击生成客户端ID,并点击连接。

在订阅主题中输入“to_console”,并点击订阅主题按钮

3)测试从安卓设备发送消息到控制台客户端

在Topic中输入“to_console”,Message中输入“hello, console”,点击PUBLISH按钮

在控制台确认收到消息

4)从控制台客户端发送消息至安卓设备

在控制台点击“发布消息至一个主题”,在主题中输入“to_android_device”,内容输入“Hello,Android device”,点击发布到主题按钮

在安卓设备确认收到的消息

更多信息

在本文中,我们使用了Cognito的匿名模式来做身份认证。在生产中我们还可以使用Cognito的联合认证进行身份认证。

AWS IoT服务的规则功能使您的设备能够与 AWS 服务交互。基于 MQTT 主题流分析规则并执行操作。

作者简介:

任庆杰,AWS解决方案架构师。负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内和全球的应用和推广。在加入AWS前,拥有超过7年的软件开发和IT项目管理经验。

Amazon Cognito集成Login with Amazon详解

作者:薛峰,亚马逊AWS解决方案架构师

背景介绍

Amazon Cognito可以为我们移动开发中的终端用户维护唯一标识符,跨不设备和平台维护用户登录的一致。Cognito还可以为我们的应用提供限制权限的临时凭据来访问 AWS的资源。

使用Amazon Cognito我们的应用可以支持未验证用户,以及使用公开的身份提供方来验证用户,目前支持的身份提供方包括Facebook, Google 和Login with Amazon。

未验证的用户绑定到设备,即通过Cognito客户端SDK在用户使用相同设备时为他们维护唯一标识符。而已验证的用户则可以跨设备维护唯一标识符,即使他们使用iOS和Android这样不同的操作系统。

今天我们通过一个Android开发实例,详细讲解Amazon Cognito 与 Login with Amazon 集成,以针对移动应用程序和 Web 应用程序用户提供联合身份验证。

Login with Amazon
使用Amazon账号登录,可以省去注册账号的繁琐,使用用户已经熟练使用的账号直接登录。借助Amazon.com相同的验证机制,可以轻松享受其健壮的安全性和可扩展性。开发者可以不必自己再构建用户管理系统,而集中精力于自己的产品。Login with Amazon使用业界主流的OAuth 2.0标准,方便更快速地接入开发,也基于此Amazon Cognito也可以方便地接入。

我们使用当前主流的 Android Studio,为了方便调试先使用模拟器进行开发和演示。这些基础工作请大家自行准备好。

注册Login with Amazon
首先需要注册成Amazon开发者,然后到以下网址注册一个应用。

http://login.amazon.com/manageApps

左上角Applications 模块下点击“Register New Application”按钮。Name 和 Description按自己需求填写。

Privacy Notice URL这是在登录时显示给用户的隐私协议页面,生产环境中需要是你的网站的一个页面。这里我们可以使用演示页面的URL https://www.example.com/privacy.html

Logo Image 这里是显示给用户的我们的应用图标,该图片会被自动缩小到50 x150像素,所以选择一个适当尺寸的图片上传。点击 save保存即可。

创建成功后,我们到该应用详情页,标题名称右下角有一行形如下面的应用ID:

App ID: amzn1.application.188a56d827a7d6555a8b67a5d

这个我们记下来,后面关联Amazon Cognito时会使用。

点击 Android Settings 展开,添加Android 设置。

Is this application distributed through the Amazon Appstore? 我们的应用只用于Cognitor验证,不需要上架Amazon Appstore,所以选 No.

Label 填写和前述Name相同值即可。

Package Name 填写后面我们创建Android App 时工程的包名,比如这里我们使用“com.example.cognito”。

Signature 填写App打包时的SHA-256签名值。详情可参考 https://developer.android.com/tools/publishing/app-signing.html

保存之后,这里会多出一项 API Key Value,我们点击“Get API Key Value”按钮,在弹出层显示一个长字符串,这个后续我们开发也会用到。

使用Login with Amazon开发

我们使用Android Studio 创建一个项目,先把Login with Amazon集成进来。

新建Android项目

启动Android Studio,创建一个新项目。

Application name 输入 Cognito。Company name 输入 example.com。这样下面显示的 Package name 刚好和我们前面创建 Login with Amazon 应用时填写的包名一致——“com.example.cognito”。

在目标设备选项页,Minimum SDK 选择 API 11 或以上,我这里选择的是 API 23。

Activity页选Empty Activity。最后Customize the Activity页保持默认,点击Finish按钮完成创建项目。

添加Login with Amazon SDK

从以下地址下载Login with Amazon SDK:

https://images-na.ssl-images-amazon.com/images/G/01/lwa/sdk/LoginWithAmazonSDKForAndroid._TTH_.zip

解压到本地磁盘,找到 login-with-amazon-sdk.jar 文件备用。

我们在Project面板切换成Project 模式。把刚才解压出的login-with-amazon-sdk.jar 文件复制到 /app/libs 目录下,在这个文件上右键菜单点选Add As Library…,弹出 Create Library 对话框,直接点击 OK 按钮即可。

设置AndroidManifest.xml配置

我们这个项目需要访问互联网,在/app/src/main/AndroidManifest.xml 中根节点manifest下添加

<uses-permission android:name="android.permission.INTERNET"/>

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

即可。

默认情况下屏幕转向或改变键盘状态会导致活动重启以重新配置界面,这样的重启会把我们的登录界面消失。我们把这个活动配置成手动变更就可以避免这个问题了。

在处理Login with Amazon的活动中,这里我们是默认的MainActivity,添加一个属性

android:configChanges="keyboard|keyboardHidden|orientation|screenSize"

用户点击按钮要登录时,API 会调起一个浏览器来展示登录页面,我们需要在项目中添加一个工作流WorkflowActivity。

和MainActivity所在的活动并列,添加以下代码

<activity android:name="com.amazon.identity.auth.device.workflow.WorkflowActivity"
            android:theme="@android:style/Theme.NoDisplay"
            android:allowTaskReparenting="true"
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <!-- android:host must use the full package name found in Manifest General Attributes -->
                <data android:host="${applicationId}" android:scheme="amzn"/>
            </intent-filter>
        </activity>

添加Login with Amazon的API Key
我们在/app/src/main 下创建一个目录,名为assets,然后在这个目录里新建一个文本文件,名为api_key.txt。这是Login with Amazon的API默认读取API Key的方式。

回到我们的Login with Amazon 应用管理

https://sellercentral.amazon.com/gp/homepage.html

点开我们刚才创建的应用,在 Android Settings 下面点击Get API Key Value 按钮

在弹出层中点击Select All 按钮把  API Key 选中,然后复制一下。

打开api_key.txt文件,把刚才复制的API Key粘贴进去。

添加Login with Amazon Button按钮
我们编辑activity_main.xml,添加一个标准ImageButton。给这个图片按钮设置一个id,比如:

android:id="@+id/login_with_amazon"

为这个按钮添加图片。

https://login.amazon.com/button-guide 可以下载默认的英文按钮或者简体中文的按钮

解压出相应的图片文件,放到 /app/src/main/res/ 下相应图片目录中。给这个图片按钮设置

android:src="@drawable/btnlwa_gold_loginwithamazon.png"

构建和运行一下App,确认这个按钮已经正常显示出来了。

调用Login with Amazon SDK中的 API
我们首先需要一个RequestContext对象,建议做法是声明成一个private的属性,然后在活动的onCreate 方法中实例化。

private RequestContext requestContext;


@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    requestContext = RequestContext.create(this);

}

然后创建一个AuthorizeListener,它会监听并处理authorize 的调用,包含3个回调方法,如下创建出来然后注册到requestContext对象上。

requestContext.registerListener(new AuthorizeListener() {

    /* Authorization was completed successfully. */

    @Override

    public void onSuccess(AuthorizeResult result) {

        /* Your app is now authorized for the requested scopes */

    }


    /* There was an error during the attempt to authorize the

       application. */

    @Override

    public void onError(AuthError ae) {

        /* Inform the user of the error */

    }


    /* Authorization was cancelled before it could be completed. */

    @Override

    public void onCancel(AuthCancellation cancellation) {

        /* Reset the UI to a ready-to-login state */

    }

});

}

验证的流程是跳转到浏览器显示网页,登录成功后会回调onSuccess方法,但是也可能用户没有登录而是取消或者浏览到别处去了,所以这个接口有3个方法onSuccess, onError 和 onCancel。

为了适应Android 的应用生命周期管理,我们还需要在当前活动的onResume方法里加一段,以便用户没完成登录流程前操作系统把App关闭时,用户重新打开App时可以把验证界面恢复起来。

@Override

protected void onResume() {

    super.onResume();

    requestContext.onResume();

}

我们在刚才添加的按钮上注册 onClick 事件处理器调用AuthorizationManager的authorize方法来提示用户登录并授权我们的应用。

// 给登录按钮注册点击事件
mLoginButton = findViewById(R.id.login_with_amazon);
mLoginButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        AuthorizationManager.authorize(
                new AuthorizeRequest.Builder(requestContext)
                        .addScopes(ProfileScope.profile(), ProfileScope.postalCode())
                        .build()
        );
    }
});

这个方法在用户登录时会自动选择以下几种方式:

  1. 切换到系统浏览器,显示网页供用户输入或操作。
  2. 如果设备上安装了Amazon Shopping 电商App,会切换到安全上下文的WebView。如果用户在Amazon Shopping是已登录状态,会直接把我们的应用也登录授权,无需用户再输入或点击授权,这样就能实现单点登录了。

当我们的应用得到授权后,可以有权获取Amazon用户信息,不同的数据集称作scope。目前Login with Amazon支持以下scope:

  1. profile: 包含用户姓名,电子邮件和账号ID。
  2. profile:user_id:  仅账号ID。
  3. postal_code: 用户邮政编码。

我们上述的代码中使用addScopes(ProfileScope.profile(), ProfileScope.postalCode())声明获取了我们可以通过Login with Amazon得到的全部用户数据。

到这里,我们编译运行一下,注意因为Login with Amazon使用APK签名的APK Key来验证我们 App 的有效性,所以需要使用生成签名APK方式来构建,然后安装到模拟器中才能正常运行。还可以在build.gradle 里配置上 signingConfigs,以实现调试编译时也签名。

顺利的话,点击我们的“通过Amazon登录”按钮,模拟器中通常不会安装 Amazon Shopping,所以会跳转到浏览器,显示Amazon 登录页面。首次登录需要输入用户名和密码,之后是一个提示页,询问用户是否允许我们的应用 Cognito 访问用户数据。

获取用户信息
我们在实现AuthorizeListener的onSuccess() 方法中加上获取用户信息的方法 fetchUserProfile()。这个方法的概要定义如下:

private void fetchUserProfile() {

    User.fetch(this, new Listener<User, AuthError>() {
        /* fetch completed successfully. */
        @Override
        public void onSuccess(User user) {
            final String name = user.getUserName();
            final String email = user.getUserEmail();
            final String accountId = user.getUserId();
            final String zipCode = user.getUserPostalCode();

            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    StringBuilder profileBuilder = new StringBuilder("Profile Response: ");
                    profileBuilder.append(String.format("Welcome, %s!\n", name));
                    profileBuilder.append(String.format("Your Account Id is %s\n", accountId));
                    profileBuilder.append(String.format("Your email is %s\n", email));
                    profileBuilder.append(String.format("Your zipCode is %s\n", zipCode));
                    String profile = profileBuilder.toString();
                    Toast.makeText(MainActivity.this, profile, Toast.LENGTH_LONG).show();
                }
            });
        }
        /* There was an error during the attempt to get the profile. */
        @Override
        public void onError(AuthError ae) {
     /* Retry or inform the user of the error */
        }
    });
}

User.fetch() 方法使用一个回调监听器,可以理解获取用户信息这里又是发起一次HTTPS请求,在成功响应时onSuccess(User user)这个回调方法会传入一个User对象,从这个User 对象里可以得到前面我们提到的scope 下相应的用户数据。

在应用启动时检测用户登录
用户登录进我们的应用后,关闭应用再启动应该仍可保持登录状态,仍然有权限获取Amazon用户信息。我们在当前活动的onStart()方法使用AuthorizationManager.getToken()方法来检测用户是否仍是授权状态。

@Override
protected void onStart(){
    super.onStart();
    Scope[] scopes = { ProfileScope.profile(), ProfileScope.postalCode() };
    AuthorizationManager.getToken(this, scopes, new Listener<AuthorizeResult, AuthError>() {

        @Override
        public void onSuccess(AuthorizeResult result) {
            if (result.getAccessToken() != null) {
        /* The user is signed in */
            } else {
        /* The user is not signed in */
            }
        }

        @Override
        public void onError(AuthError ae) {
    /* The user is not signed in */
        }
    });
}

由于刚才我们已经授权过了。这时关闭我们的应用再打开时,直接就弹出获取到的用户信息了。

清理授权信息注销登录
最后我们要在用户主动退出我们的应用时也把Login with Amazon的授权也注销掉。最简单的办法是放一个退出按钮,给它的onClick事件注册监听器,执行AuthorizationManager.signOut() 方法。当然还有一些额外的处理,加上界面切换登录退出状态。下面我们只列主体代码片段。

    /**
     * Sets the state of the application to reflect that the user is currently authorized.
     */
    private void setLoggedInState() {
        mLoginButton.setVisibility(Button.GONE);
        mLogoutButton.setVisibility(Button.VISIBLE);
    }

    /**
     * Sets the state of the application to reflect that the user is not currently authorized.
     */
    private void setLoggedOutState() {
        mLoginButton.setVisibility(Button.VISIBLE);
        mLogoutButton.setVisibility(Button.GONE);
    }

然后在 fetchUserProfile()方法里调用setLoggedInState(),在onCreate()方法里加一个退出按钮。

// 退出按钮,及注册点击事件
mLogoutButton = (Button) findViewById(R.id.logout);
mLogoutButton.setOnClickListener(new View.OnClickListener() {

    @Override
    public void onClick(View v) {
        // 退出 Amazon 登录
        AuthorizationManager.signOut(getApplicationContext(), new Listener<Void, AuthError>() {
            @Override
            public void onSuccess(Void response) {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        setLoggedOutState();
                    }
                });
            }

            @Override
            public void onError(AuthError authError) {
                Log.e(TAG, "Error clearing authorization state.", authError);
            }
        });
    }
});

配置Amazon Cognito

下面我们来配置Amazon Cognito并关联前面我们创建的Login with Amazon的Application。

创建联合身份池
我们打开Cognito控制台

https://console.amazonaws.cn/cognito/home

点击管理联合身份大按钮,来到联合身份页。

点击创建新的身份池按钮创建一个新的用户池,来到 创建新的身份池向导页。

身份池名称输入LoginWithAmazon。

选中启用未经验证的身份的访问权限前面的复选框。这样我们可以不登录也能访问。

在身份验证提供商下面点选Amazon,在Amazon 应用程序 ID 输入前面我们在Login With Amazon 创建的应用的App ID,比如 amzn1.application.188a56d827a7d6555a8b67a5d。

然后点击创建池按钮创建,会跳转到 Your Cognito identities require access to your resources 页。这里其实是 Cognito调用 IAM 去创建2个角色。可以点开查看详细信息查看一下详情,我们只需要点击右下角允许按钮即可。然后就来到示例代码页,表示已经创建成功。点击展开获取 AWS 凭证一段,这里显示如下的示例代码 :

// 初始化 Amazon Cognito 凭证提供程序

CognitoCachingCredentialsProvider credentialsProvider = new CognitoCachingCredentialsProvider(

    getApplicationContext(),

    "cn-north-1:12345678-abcd-1234-efgh-12345678abcd", // 身份池 ID

    Regions.CN_NORTH_1 // 区域

);

配置Cognito相关的IAM角色
为了演示登录和未登录用户的不同权限,我们给授权用户赋予可以查看S3桶列表的权限。后面,我们会进一步完善我们的 App,使得授权的用户可以显示出当前S3的桶列表信息。

打开 IAM 控制台

https://console.amazonaws.cn/iam/home

点击左侧导航链接中的角色,可以看到刚才我们创建Cognito身份池时创建出来的2个角色:Cognito_LoginWithAmazonAuth_Role和Cognito_LoginWithAmazonUnauth_Role。顾名思义,前者是授权用户的角色,而后者是未授权用户的角色。

点击Cognito_LoginWithAmazonAuth_Role链接,编辑这个角色。在权限选项卡下点击附加策略按钮,搜索并添加AmazonS3ReadOnlyAccess这个策略。这样这个授权用户的角色就可以有S3读取相关的权限了。

把Cognito认证加入App
我们继续打开Android Studio 中 Cognito 这个项目。先把 AWS SDK 集成进项目中。使用 Gradle 管理库时,集成非常方便。我们只需要在 /app/build.gradle 中dependencies 段加上以下几行即可:

compile 'com.amazonaws:aws-android-sdk-core:2.2.22'

compile 'com.amazonaws:aws-android-sdk-cognito:2.2.22'

compile 'com.amazonaws:aws-android-sdk-cognitoidentityprovider:2.2.22'

我们把前面Cognito控制台的的示例代码分离出常量和类变量,再添加到在 onCreate() 方法中。我们再添加一个getIdentity()方法,先只获取 身份ID ,验证 Cognito 已正常启用。注意获取身份ID是调用  AWS 的API,需要发起HTTP请求,按照Android开发的习惯,需要在主界面开新线程来做请求。示例代码如下:

private static final Regions MY_REGION = Regions.CN_NORTH_1;
private String identityPoolId;
private CognitoCachingCredentialsProvider credentialsProvider;

…

   private void getIdentity() {
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {

    // 先只获取身份ID ,验证 Cognito 已正常启用。
                    String identityId = credentialsProvider.getIdentityId();
                    Log.d(TAG, "my ID is " + identityId);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            }
        }.start();
    }


@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        identityPoolId = "cn-north-1:12345678-abcd-1234-efgh-12345678abcd";
        // 初始化 Amazon Cognito 凭证提供程序
        credentialsProvider = new CognitoCachingCredentialsProvider(
            getApplicationContext(),
            identityPoolId, // 身份池 ID
            MY_REGION // 区域
        );

    getIdentity();

…

编译调试运行一下,顺利的话,我们可以在日志输出看到

11-06 16:05:11.151 25362-25381/com.example.cognito D/CognitoLwA: my ID is cn-north-1: 12345678-abcd-1234-efgh-12345678abcd

表示Cognito已经接入成功,并认证为一个未授权用户。

这时回到Cognito的控制台,可以看到LoginWithAmazon 这个项目下的身份信息已经从以前的0变成了1。

集成Cognito 和 Login with Amazon
我们把通过Login with Amazon 登录的用户联合认证为AWS的授权用户,由Cognito赋予前述的Cognito_LoginWithAmazonAuth_Role角色。

用户首次通过Login with Amazon登录,是在onCreate() 方法里AuthorizeListener的onSuccess() 回调方法里获取的用户信息,已登录的用户关闭再打开App时是在onStart() 方法里的AuthorizationManager.getToken 的onSuccess() 回调方法里获取的用户信息。我们需要在这2处都加上集成 Cognito联合身份认证的功能。

由于用户在Login with Amazon时会跳转到浏览器,所以登录成功后再回到我们的MainActivity时都会触发onStart()方法把界面重绘。前面我们已经在onStart()方法里编写了实现用户已登录状态的代码,已经得到了Login with Amazon的验证token。我们把如下联合认证的代码示例添加到onStart()方法里,就是把Login with Amazon的验证token传递给CognitoCachingCredentialsProvider 的 setLogins()方法。

@Override

            public void onSuccess(AuthorizeResult result) {

                String token = result.getAccessToken();

                if (null != token) {

                    /* 用户已登录,联合登录Cognito*/

                    Map<String, String> logins = new HashMap<String, String>();

                    logins.put("www.amazon.com", token);

                    credentialsProvider.setLogins(logins);

                    getIdentity();

                    fetchUserProfile();

                } else {

                    /* The user is not signed in */

                }

      }

使用S3读取桶列表演示已登录和未登录用户的不同权限
我们给Cognito相关的2个IAM角色中已授权的角色赋予读取S3的权限,另一个保持不变,然后在我们的 App 里尝试读取 S3 桶列表,以分别演示已登录和未登录用户的不同权限效果。

在 IAM 控制台找到Cognito_LoginWithAmazonAuth_Role  这个角色,在其权限选项页点击附加策略按钮。找到并添加AmazonS3ReadOnlyAccess这个策略,就可以让这个角色有S3的只读权限了。

然后我们回到Android Studio 。之前我们已经有了一个 getIdentity() 方法,当时只是为了验证Cognito已经成功获取AWS账号权限。现在我们在这个方法里再加上读取S3的部分代码,把它扩展成下面这样。

private void getIdentity() {
    new Thread(){
        @Override
        public void run() {
            super.run();
            // 先只获取身份ID ,验证 Cognito 已正常启用。
            String identityId = credentialsProvider.getIdentityId();
            Log.d(TAG, "my ID is " + identityId);
            try {
                AmazonS3 s3 = new AmazonS3Client(credentialsProvider);
                s3.setRegion(Region.getRegion(MY_REGION));
                List<Bucket> bucketList = s3.listBuckets();
                final StringBuilder bucketNameList = new StringBuilder("My S3 buckets are:\n");
                for (Bucket bucket : bucketList) {
                    bucketNameList.append(bucket.getName()).append("\n");
                }
                Log.d(TAG, "s3 bucket" + bucketNameList);
                runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                      Toast.makeText(MainActivity.this, bucketNameList, Toast.LENGTH_LONG).show();
                   }
                 });
               }
               catch (Exception e) {
                   e.printStackTrace();
                   runOnUiThread(new Runnable() {
                       @Override
                       public void run() {
                        Toast.makeText(MainActivity.this, "This is OK as not authenticated to list S3 bucket.", Toast.LENGTH_LONG).show();
                       }
                   });
            }
        }
    }.start();
}

熟悉 S3 SDK 的朋友们应该不会感到陌生,这里核心就是用 AmazonS3 s3 = new AmazonS3Client(credentialsProvider); 初始化一个 s3 客户端对象实例。而传递给这个构造函数的参数,就是我们之前通过 Cognito 联合认证得到的 CognitoCachingCredentialsProvider 的实例。这其实也是 AWS 对移动应用建议的验证方式。

为退出按钮增加退出 Cognito 验证
前面我们已经做了一个退出按钮,点击时会退出Login With Amazon 的登录,我们再把退出 Cognito 验证的功能也加上,整个项目的功能就完成了。我们找到那个退出按钮注册的点击事件 mLogoutButton.setOnClickListener(),只要加一句

credentialsProvider.clearCredentials();
就行了。

小结

今天我们结合实例,为大家介绍了使用Amazon Cognito 集成 Login with Amazon 的配置及使用方法。演示了 Cognito在移动端用户认证管理方面的功能,以及开发的便捷。 AWS SDK 支持的主流移动端开发都可以接入Cognito,并且可以实现不同设备间用户状态的统一管理。希望此例可以帮助大家更多体验Cognito,便利大家移动端的开发工作。

相关资源链接

本例代码已在 github 分享:
https://github.com/xfsnow/android/tree/master/Cognito

Login with Amazon:

http://login.amazon.com

Amazon Cognito:

https://aws.amazon.com/cognito

 

作者介绍:

薛峰,亚马逊AWS解决方案架构师,AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内和全球的应用和推广,在大规模并发应用架构、移动应用以及无服务器架构等方面有丰富的实践经验。在加入AWS之前曾长期从事互联网应用开发,先后在新浪、唯品会等公司担任架构师、技术总监等职位。对跨平台多终端的互联网应用架构和方案有深入的研究。

 

如何使用Amazon EC2 Systems Manager自动创建数据一致的EBS快照(Part 2)

作者:王宇

上一期我们讨论了如何在不关机的前提下实现AWS上实例的数据一致性快照问题,传送门:《如何使用Amazon EC2 Systems Manager自动创建数据一致的EBS快照(Part 1)

在本文的介绍中,我们将共同探索如何利用Amazon EC2 Systems Manager(SSM)和Microsoft VSS (Volume Shadow Copy Service)来创建数据一致的EBS快照。

SSM + VSS数据一致快照的原理

VSS (Volume Shadow Copy Service)是一个Windows操作系统内置的服务,用来协调与VSS兼容的应用程序(如SQL Server、Exchange Server等)的备份工作,如冻结或释放这些应用程序的I/O操作等。

VSS服务能够启动和监督副本拷贝的创建。“副本拷贝”是指一个逻辑卷在某一个时间点上的数据一致快照。比如:“C:”是一个逻辑卷,它与EBS快照不同。创建副本拷贝的步骤包括:

  • 请求方向VSS发出创建副本拷贝的请求。
  • VSS provider创建并维护副本拷贝。
  • VSS写入器(writer)保证数据的一致性。写入器负责在VSS provider创建副本拷贝之前固化临时数据并冻结I/O操作,并在VSS provider完成创建后释放I/O操作。通常情况下,会为每一个与VSS兼容的应用程序分配一个独立的写入器(writer)。

我们可以使用Run Command在windows实例中运行一个PowerShell脚本:

$EbsSnapshotPsFileName = "C:/tmp/ebsSnapshot.ps1"

$EbsSnapshotPs = New-Item -Type File $EbsSnapshotPsFileName -Force

Add-Content $EbsSnapshotPs '$InstanceID = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/instance-id'

Add-Content $EbsSnapshotPs '$AZ = Invoke-RestMethod -Uri http://169.254.169.254/latest/meta-data/placement/availability-zone'

Add-Content $EbsSnapshotPs '$Region = $AZ.Substring(0, $AZ.Length-1)'

Add-Content $EbsSnapshotPs '$Volumes = ((Get-EC2InstanceAttribute -Region $Region -Instance "$InstanceId" -Attribute blockDeviceMapping).BlockDeviceMappings.Ebs |? {$_.Status -eq "attached"}).VolumeId'

Add-Content $EbsSnapshotPs '$Volumes | New-EC2Snapshot -Region $Region -Description " Consistent snapshot of a Windows instance with VSS" -Force'

Add-Content $EbsSnapshotPs 'Exit $LastExitCode'

首先创建了一个名为“ebsSnapshot.ps1”的PowerShell脚本文件,脚本中为实例的每一个EBS卷创建一个快照。

$EbsSnapshotCmdFileName = "C:/tmp/ebsSnapshot.cmd"

$EbsSnapshotCmd = New-Item -Type File $EbsSnapshotCmdFileName -Force

Add-Content $EbsSnapshotCmd 'powershell.exe -ExecutionPolicy Bypass -file $EbsSnapshotPsFileName'

Add-Content $EbsSnapshotCmd 'exit $?'

再创建第二个名为“ebsSnapshot.cmd”脚本文件,用来执行之前创建的PowerShell脚本。

$VssScriptFileName = "C:/tmp/scriptVss.txt"

$VssScript = New-Item -Type File $VssScriptFileName -Force

Add-Content $VssScript 'reset'

Add-Content $VssScript 'set context persistent'

Add-Content $VssScript 'set option differential'

Add-Content $VssScript 'begin backup'

$Drives = Get-WmiObject -Class Win32_LogicalDisk |? {$_.VolumeName -notmatch "Temporary" -and $_.DriveType -eq "3"} | Select-Object DeviceID

$Drives | ForEach-Object { Add-Content $VssScript $('add volume ' + $_.DeviceID + ' alias Volume' + $_.DeviceID.Substring(0, 1)) }

Add-Content $VssScript 'create'

Add-Content $VssScript "exec $EbsSnapshotCmdFileName"

Add-Content $VssScript 'end backup'

$Drives | ForEach-Object { Add-Content $VssScript $('delete shadows id %Volume' + $_.DeviceID.Substring(0, 1) + '%') }

Add-Content $VssScript 'exit'

第三个名为“scriptVss.txt”的文件包含了DiskShadow命令。DiskShadow是一个包含在Windows Server 2008及以上版本中的VSS工具。这个脚本在EBS上为每一个逻辑卷创建一个副本拷贝,再为这个EBS创建一个快照,最后删除副本拷贝来释放磁盘空间。

diskshadow.exe /s $VssScriptFileName
Exit $LastExitCode

最终,在脚本模式中来运行DiskShadow命令。

这个脚本将保存在一个新的SSM Document中并关联到一个维护窗口中,在每天的午夜时间在每一台标签“consistentsnapshot”等于“windowsvss”的实例上运行。

在AWS console中快速实践

1.  使用AWS CloudFormation快速创建一组资源,包括:

a)   VPC和互联网网关

b)   VPC中创建一个子网和一个新的路由表,来实现互联网连接和AWS APIs

c)   创建一个IAM角色来赋予EC2实例相应的权限

d)   创建一个安全组,来允许来自internet的RDP访问,稍后将要通过远程登录到这个EC2实例中

e)   在子网中使用IAM角色创建和启动一个Windows实例,并分配好安全组

f)   创建一个包含上面例子中脚本的SSM document文件,来创建数据一致EBS快照

g)   创建另一个SSM document文件,其中的脚本来恢复逻辑卷中的数据,这些脚本将在下面的章节中详细说明

h)   创建一个能够生成Maintenance Windows的IAM role

2.  创建一个Maintenance Window

a)   在EC2 Console中选择:Systems Manager Shared Resources -> Maintenance Windows -> Create a Maintenance Window

b)   Name:ConsistentSnapshots

c)   Specify with:CRON/Rate expression

d)   CRON/Rate expression:cron(0 0 * * ? *)

e)   Duration:2 hours

f)   Stop initiating tasks:0 hour

g)   选择Create maintenance window

3.  为Maintenance Window关联一组目标:

a)   在Maintenance Window列表中选择刚刚创建的维护窗口

b)   在Actions中选择Register targets

c)   Owner information:WindowsVSS

d)   Select targets by:Specifying tags

e)   Tag Name:ConsistentSnapshot

f)   Tag Value:WindowsVSS

g)   选择Register targets

4.  给Maintenance Window分配一个任务

a)   在Maintenance Window列表中选择刚刚创建的维护窗口

b)   在Actions中选择Register targets

c)   在Document中选择此前创建的用于创建EBS快照的SSM document文件名

d)   在Target by中选择刚刚创建的目标

e)   在Role中,选择在CloudFormation中创建的IAM Role

f)   在Execute on中,Targets:1,Stop after:1 errors

g)   选择Register task

运维窗口和一致性快照操作已经创建完毕,你可以在Maintenance Windows窗口中的History页面查看每一次任务的执行情况。

如何将逻辑卷恢复到数据一致状态

在本文上面的例子中,我们会注意到,在给EBS进行快照的脚本中,我们实际上是先使用DiskShadow进行了副本拷贝操作,这个操作中已经包含了对I/O操作的冻结和释放,在此之后再进行了EBS的快照操作。而在这两个操作之间的瞬间如果数据发生改变,那么EBS快照中的数据就可能不能保持数据一致状态。

解决这个问题有两个方向,其一是自己创建一个VSS provider来创建EBS快照,而不是使用DiskShadow采用的windows内置的VSS provider来执行,在自己创建的VSS provider中做到先创建EBS快照再释放I/O操作,来保持数据一致性。

本文会介绍另一个解决方向,就是检查在EBS快照中的每一个逻辑卷数据,如果发现数据不一致的情况,就将其恢复到此前的副本拷贝(副本拷贝中的数据被VSS writer保持了数据一致性)。

步骤如下:

1.  在EC2 console中,选择Instances

2.  搜索获得EBS进行了快照的实例,注意这个实例所在的AZ

3.  选择Snapshots

4.  选择最新的快照,再选择Actions,Create Volume

5.  选择与此前相同的AZ,然后选择Create, Volumes

6.  选择刚刚创建的Volume,然后选择Actions, Attach Volume

7.  在Instances中选择进行了EBS快照的实例,然后选择Attach

8.  选择Run Command, Run a command

9.  在Command document中选择恢复EBS的脚本Document,在Target中选择这个Windows实例,然后选择Run

恢复EBS的脚本如下:

$OfflineDisks = (Get-Disk |? {$_.OperationalStatus -eq "Offline"})

foreach ($OfflineDisk in $OfflineDisks) {

  Set-Disk -Number $OfflineDisk.Number -IsOffline $False

  Set-Disk -Number $OfflineDisk.Number -IsReadonly $False

  Write-Host "Disk " $OfflineDisk.Signature " is now online"

}

$ShadowCopyIds = (Get-CimInstance Win32_ShadowCopy).Id

Write-Host "Number of shadow copies found: " $ShadowCopyIds.Count

foreach ($ShadowCopyId in $ShadowCopyIds) {

  "revert " + $ShadowCopyId | diskshadow

}

foreach ($OfflineDisk in $OfflineDisks) {

  $CurrentSignature = (Get-Disk -Number $OfflineDisk.Number).Signature

  if ($OfflineDisk.Signature -eq $CurrentSignature) {

    Set-Disk -Number $OfflineDisk.Number -IsReadonly $True

    Set-Disk -Number $OfflineDisk.Number -IsOffline $True

    Write-Host "Disk " $OfflineDisk.Number " is now offline"

  }

  else {

    Set-Disk -Number $OfflineDisk.Number -Signature $OfflineDisk.Signature

    Write-Host "Reverting to the initial disk signature: " $OfflineDisk.Signature

  }

}

通过比较,将数据不一致的逻辑卷恢复到了数据一致状态,这个EBS就回到了数据一致的状态。

本次的介绍就到这里。如对AWS混合云架构解决方案感兴趣,请联系我们:yuwangcn@amazon.com

 

作者介绍:

王宇,AWS企业容灾解决方案业务拓展经理,目前负责AWS中国区的混合云、容灾和DevOps产品和解决方案。曾服务于VMware等传统私有云厂商,熟悉传统IT架构和私有云、混合云、公有云的解决方案融合。

Amazon S3 深度实践系列之一:S3 CLI深度解析及性能测试

背景

作者在实际的工作当中遇到越来越多关于S3的实践问题,其中被问的最多的也是使用最广泛的工具就是AWS S3 CLI命令行;AWS S3 CLI命令行工具对大文件提供了默认的分段上传下载的能力,同时支持并发上传下载多个文件;AWS S3 CLI 命令行不仅仅提供了高级抽象的cp、sync等操作同时还提供了相对底层的s3api相关的操作以帮助客户应对各种高度定制化的应用场景。

本文通过实验帮助大家更好地理解AWS S3 CLI常用命令的底层原理及在AWS EC2上使用该命令行工具与AMAZON S3交互场景下,影响性能的几个关键要素及几个常见EC2实例类型上的上传下载测试性能情况。

本文是AMAZON S3深度实践系列之一,接着作者会带着大家从AWS CLI探索到Python boto3 S3多线程应用开发实践,再到如何利用前面学到的知识,基于AWS平台利用托管服务构建一个实用的跨区域迁移S3数据的解决方案架构探讨及实践。

基本概念

并发上传vs 分段上传

刚刚使用AWS S3命令行工具的时候,总是混淆分段上传和并发上传的含义;分段上传是指将单个大文件切分成多个小数据块进行上传,而并发上传特指有多个待上传文件或小数据块的情况下,利用多线程同时并发上传多个文件或小数据块。

如下图所示,分段上传首先将文件切分成固定数量的小数据块,再利用多线程并发上传这些小数据块,等 S3收到该文件所有的数据块之后再将它们合并成原始的文件存放在存储桶里。分段上传功能默认会帮你实现并发上传;这样做的好处,显而易见,既可以充分利用网络带宽进行文件处理,同时还可以在网络带宽有限的情况下,减小因网络抖动导致的整个大文件需要重传的问题,即当分段上传中的某个数据块因各种异常没能上传成功时,只需要重新尝试上传该数据块即可。

分段上传vs 断点续传

AWS CLI S3命令行工具默认并没有帮大家实现断点续传的功能,也就是说哪怕我们用cp或sync命令上传一个文件的时候,默认后台会做文件分片进行分段并发上传,但如果由于网络抖动导致其中某些数据块传输失败,那么整个文件又得需要重头开始上传。

但同时我们可以利用 AWS CLI s3api底层接口非常容易地实现断点续传,后面章节我们再展开探讨。

AWS CLI S3 cp命令是如何工作的?

AWS S3 cp 命令是最常用的单文件和多文件复制方法,很多人使用过该命令,但大家知道该命令是如何帮我们执行文件复制的吗?

  • 该命令有帮我们自动做分段上传和下载吗?
  • 分段上传切分文件的依据是什么?每个数据块大小是多大?
  • 这么多数据块,有多线程并发上传吗?我们可以指定线程数量吗?
  • 多个文件的上传下载是如何工作的?

下面我们通过实验来观察和研究下这些问题,整个测试环境基于如下Amazon Linux上如下版本的AWS CLI命令行:

aws-cli/1.11.132 Python/2.7.12 Linux/4.9.51-10.52.amzn1.x86_64 botocore/1.5.95

AWS EC2机型是R4.2xlarge,挂载了一个500GB的gp2测试盘

本地单文件上传场景

第一步,我们首先生成一个48MB的本地测试文件,并通过cp命令上传到S3存储桶,并通过debug开关把详细的日志记录到本地的upload.log文件中,详细命令如下:

$ dd if=/dev/zero of=48Mfile bs=1M count=48
$ aws --debug s3 cp 48Mfile s3://bjslabs/s3/ > upload.log 2>&1
$ ls -lh
总用量 49M
-rw-rw-r-- 1 ec2-user ec2-user 48M 10月 18 00:26 48Mfile
-rw-rw-r-- 1 ec2-user ec2-user 13K 10月 18 00:31 upload.log

第二步,我们通过底层的s3api命令来了解存储到S3上的对象的基本情况,如下所示,通过head-object命令我们可以了解该文件的最后修改时间,对象大小以及一个用于完整性校验的ETag值,那我们如何知道该文件是否是分段上传的呢?

$ aws s3api head-object --bucket bjslabs --key s3/48Mfile
{
    "AcceptRanges": "bytes",
    "ContentType": "binary/octet-stream",
    "LastModified": "Wed, 18 Oct 2017 00:32:56 GMT",
    "ContentLength": 50331648,
    "ETag": "\"64db0c827ecffa128fa9440d3b04ff18-6\"",
    "Metadata": {}
}

要知道该文件是否是利用了分段上传,我们只需要在以上命令中加一个参数就可以判断,如下所示,如果该文件是利用分段上传的功能,通过head-object查询第一个数据块的基本信息就可以得到该文件一共包含多少个数据块,测试文件48Mfile一共包含6个数据块(PartsCount)。

$ aws s3api head-object --part-number 1 --bucket bjslabs --key s3/48Mfile
{
    "AcceptRanges": "bytes",
    "ContentType": "binary/octet-stream",
    "LastModified": "Wed, 18 Oct 2017 00:32:56 GMT",
    "ContentLength": 8388608,
    "ETag": "\"64db0c827ecffa128fa9440d3b04ff18-6\"",
    "PartsCount": 6,
    "Metadata": {}
}

这里我们可以得知,默认情况下cp命令就会采用分段上传功能。而且从以上命令返回信息我们可以看出来,该文件第一个数据块分片大小是8388608 bytes(ContentLength)即8MB,48MB大小的文件一共被分了6个数据块,所以,可以大胆猜测默认的AWS CLI S3命令行工具默认的文件分片大小就是8MB。

第三步,我们已经判断出cp命令的默认是分段上传,接下来我们通过第一步保存的详细日志(该日志文件比较大,可以点击下载)来分析下,cp命令基本的工作流及它采用的多线程并发上传的具体情况:

通过该实验的日志,我们基本了解了cp命令的基本内部流程,因此,这里面有些参数肯定是可以通过配置来进行修改的,接下来我们来根据官方文档:http://docs.aws.amazon.com/cli/latest/topic/s3-config.html 的说明来试验下,修改相应配置,是否如我们所理解的那样运行,这几个参数是:

接下来我们修改参数并对比下,实验内容为同样大小的本地文件在不同参数条件下,通过cp上传到s3,两次运行的结果对比:

在AWS Configure配置文件中,指定S3的配置参数:

$ cat ~/.aws/config
[default]
region = cn-north-1
[profile dev]
region = cn-north-1
s3 =
  max_concurrent_requests = 5
  multipart_threshold = 10MB
  multipart_chunksize = 6MB

执行profile 为dev的上传测试命令:

$ aws --debug s3 cp 48Mfile s3://bjslabs/s3/ --profile dev > 2>&1

对比upload.log和upload2.log,我们可以看出,multipart_threshold和multipart_chunksize参数是对分段上传的影响是很好理解的,但 max_concurrent_requests参数与单个文件的分段上传的并发线程总数的关系,从日志中只能看出单文件的分段上传的并发线程总数受max_concurrent_requests参数影响,但并发线程的总数上限值还取决于文件分片后进入队列被消费者线程消耗的速度。

感兴趣的读者可以在以上实验的基础上,保持其他参数不变,只修改max_concurrent_requests参数,观察并发线程数的情况,作者在max_concurrent_requests参数值分别为8、15、20、30的情况下观察cp同样的文件的线程数情况如下:

$ aws --debug s3 cp 48Mfile s3://bjslabs/s3/ --profile dev > upload2.log 2>&1

对于单文件上传,我们经过前面的实验有了基本的认识,那我们接下来再看看cp命令在分段上传中途发生网络故障,能否实现类似断点续传的功能效果呢?

整个实验思路如下:

  •  利用dd命令产生一个26GB大小的文件
  • 在cp传送中途强行断开
  • 检查此时在S3桶里面的分片情况
  • 尝试再次上传并观察结果
$ dd if=/dev/zero of=26Gfile bs=1G count=26
$ aws --debug s3 cp 26Gfile s3://bjslabs/s3/ > upload.log 2>&1
$ Ctrl-Z(强行中止)

AWS CLI s3api提供了list-parts和list-multipart-uploads两个命令分别可以查看已经完成的分片上传及正在进行的分片上传:

$ aws s3api list-multipart-uploads --bucket bjslabs

list-multipart-uploads 命令会列出指定的bucket桶里面所有的正在进行上的分片信息,如上所示,对我们有帮助的是其中的UploadId的值,在执行list-parts命令时需要传入:

$ aws s3api list-parts --bucket bjslabs --key s3/26Gfile --upload-id OwKKv3NOfXiOq7WwdBt0vYpKGVIXxzrGkxnSwSFGv8Lpwa94xzwj4IDgPvpw9Bp1FBjqUeRf2tEtL.SMCgLPhp23nw4Ilagv7UJDhPWQ0AalwwAC0ar4jBzfJ08ee4DKLd8LroSm0R7U_6Lc8y3HgA-- > parts.info 2>&1

打开parts.info文件可以看到所有的已经上传好的分片信息,包含每个分片的最后修改时间,大小,ETag以及PartNumber:

接下来,我们看看如果再次运行同样的cp命令,会帮我们进行断点续传吗?判断逻辑有两点(1)两次的UploadId是否一致(2)同一个PartNumber的数据块的最后修改时间是否一致。先记录好目前的这两个值:

UploadId:

OwKKv3NOfXiOq7WwdBt0vYpKGVIXxzrGkxnSwSFGv8Lpwa94xzwj4IDgPvpw9Bp1FBjqUeRf2tEtL.SMCgLPhp23nw4Ilagv7UJDhPWQ0AalwwAC0ar4jBzfJ08ee4DKLd8LroSm0R7U_6Lc8y3HgA—

选取PartNumber=1的数据块,该数据块最后修改时间是:

"2017-10-18T14:43:54.000Z"

重新执行一边cp命令并记录结果:

$ aws --debug s3 cp 26Gfile s3://bjslabs/s3/ > upload2.log 2>&1
$ Ctrl-Z(强行中止)
$ aws s3api list-multipart-uploads --bucket bjslabs > processing.info 2>&1

从结果我们发现,有两个正在上传的数据分片,一个是我们前一次命令产生的,一个是最近一次cp命令被中断产生的。

$ aws s3api list-parts --bucket bjslabs --key s3/26Gfile --upload-id OwKKv3NOfXiOq7WwdBt0vYpKGVIXxzrGkxnSwSFGv8Lpwa94xzwj4IDgPvpw9Bp1FBjqUeRf2tEtL.SMCgLPhp23nw4Ilagv7UJDhPWQ0AalwwAC0ar4jBzfJ08ee4DKLd8LroSm0R7U_6Lc8y3HgA-- > parts2.info 2>&1
$ aws s3api list-parts --bucket bjslabs --key s3/26Gfile --upload-id 7P10pMiJ.Tj.xsogV7JeG99G4Ev6kV_5SqsdcEBKXzVi9Kg1SgvcWkTmay0wpB2WYsdnXtsFyofRIjOMfu9hZnh6DXmggVzSpyiKbAgw0qSyZDHVt5OdkcqpfX52uHpM5tc9BQUkIVD3dWu29xUeyg-- > parts3.info 2>&1

我们会发现执行两次cp命令对同一个文件,会帮我们保留两个UploadId及其对应的已经上传的分段数据,根据complete-multipart-upload的文档说明,我们知道,分段上传在合并分段数据的时候,是根据UploadId进行合并的,两个不同的UploadId说明AWS CLI cp命令不会智能帮我们判断已经上传的分段之后开始续传我们的文件分片即没有直接支持断点续传。

一个文件如果经过分段上传了一部分数据但没有传完的情况下,已经传完的数据块和正在进行上传的数据块占用的存储是需要收费的,因此,我们需要清除掉这些无用的数据块,一种办法是通过命令,另外一种方式可以利用AMAZON S3的生命周期管理定期自动删除掉这样的数据块。

$ aws s3api abort-multipart-upload --bucket bjslabs --key s3/26Gfile --upload-id OwKKv3NOfXiOq7WwdBt0vYpKGVIXxzrGkxnSwSFGv8Lpwa94xzwj4IDgPvpw9Bp1FBjqUeRf2tEtL.SMCgLPhp23nw4Ilagv7UJDhPWQ0AalwwAC0ar4jBzfJ08ee4DKLd8LroSm0R7U_6Lc8y3HgA--
$ aws s3api abort-multipart-upload --bucket bjslabs --key s3/26Gfile --upload-id 7P10pMiJ.Tj.xsogV7JeG99G4Ev6kV_5SqsdcEBKXzVi9Kg1SgvcWkTmay0wpB2WYsdnXtsFyofRIjOMfu9hZnh6DXmggVzSpyiKbAgw0qSyZDHVt5OdkcqpfX52uHpM5tc9BQUkIVD3dWu29xUeyg--

S3下载到本地单文件场景

该场景下,我们关注的点主要是,对于在S3桶里面的对象,cp命令都会自动帮我分段分片下载吗?

首先尝试通cp直接下载上一个章节通过cp命令上传到S3桶里面的对象:

$ aws --debug s3 cp s3://bjslabs/s3/ . > download.log 2>&1

从日志文件里面可以看出,cp命令确实是默认采用了分段下载,调用GetObject接口,在Header里面设置range值并通过多线程并发下载;似乎非常完美,但等等,我们还忘了一个场景,假如文件上传到S3桶的时候没有使用分段上传呢?我们试验一下:

  • 还是利用本地dd出来的48Mfile的文件
  • 修改AWS CLI S3的参数,将multipart_threshold改到50MB,这样对于小于50MB的文件上传就不会采用分段上传
  • cp上传该文件并确认该文件没有被分段上传
  • cp 下载,看看是否是分段下载

第一步,修改aws configure配置文件:

第二步,通过cp上传该文件,并通过head-object命令发现该文件没PartsCount 信息即没有被分片分段上传(因为前面我们设置了自动分片的最小文件大小是50MB,该文件48MB小于50MB)

$ aws s3 cp ./48Mfile s3://bjslabs/s3/48Mfile_nonparts --profile dev
upload: ./48Mfile to s3://bjslabs/s3/48Mfile_nonparts
$ aws s3api head-object --part-number 1 --bucket bjslabs --key s3/48Mfile_nonparts
{
"AcceptRanges": "bytes",
"ContentType": "binary/octet-stream",
"LastModified": "Wed, 18 Oct 2017 15:52:34 GMT",
"ContentLength": 50331648,
"ETag": "\"f6a7b2f72130b8e4033094cb3b4ab80c\"",
"Metadata": {}
}

第三步,通过cp下载该文件,并分析日志文件

$ aws --debug s3 cp s3://bjslabs/s3/48Mfile_nonparts . > download2.log 2&>1

$ aws s3api head-object --part-number 1 --bucket bjslabs --key s3/48Mfile_nonparts
{
"AcceptRanges": "bytes",
"ContentType": "binary/octet-stream",
"LastModified": "Wed, 18 Oct 2017 15:52:34 GMT",
"ContentLength": 50331648,
"ETag": "\"f6a7b2f72130b8e4033094cb3b4ab80c\"",
"Metadata": {}
}

透过日志,我们可以看到,虽然我们上传该文件是没有使用多文件上传,但利用cp命令在默认的S3参数的情况下也会自动分片分段下载我们的对象。

本地目录与S3批量上传下载场景

前面两个小节,我们通过实验深度探索了,单文件通cp操作的一些细节;本小节我们一起来看看,在本地批量上传文件到S3及从S3批量下载文件的场景。对于性能这块我们放到后面章节,本小节主要探讨:

1.    max_concurrent_requests参数对于文件并发上传下载的影响

2.    cp命令中的一些高级特性

第一步,随机生成20个100MB的测试数据文件,并准备aws configure 配置文件,修改最大并发数的参数值,保持其它参数默认,并通过不同的profile指定不同的max_concurrent_requests的参数值:

$ seq 20 | xargs -i dd if=/dev/zero of={}.data bs=1M count=100
$ vi ~/.aws/config

第二步,跑测试命令,记录详细日志:

顺序分析dev1.log到dev30.log日志文件,可以观察到Thread数量变化,最大编号分别为Thread-7,Thread-9,Thread-14,Thread-18,Thread-26,Thread-36; 为了观察最大的线程数量,作者增加测试了max_concurrent_requests分别为100,1000的结果:

由上表可见,当我们逐渐增大max_concurrent_requests参数值的时候,实际的并发线程数是随着线性增长的,直到一个合理的线程数量,此案例里面256个8MB的数据分片,AWS CLI cp命令使用到309个线程就足够速度消费添加到队列里面的上传任务。

同理,我们从S3批量下载的情况,执行如下命令:

$ aws --debug s3 cp s3://bjslabs/s3/data1000/ ./data1000 --recursive --profile dev1000 > dev1000.log 2>&1
$ aws --debug s3 cp s3://bjslabs/s3/data100/ ./data100 --recursive --profile dev100 > dev100.log 2>&1

从日志文件中分析出,max_concurrent_requests为100或1000时,cp命令并发下载的线程数最大编号为107和275。


对于批量操作,不可避免有时会有选择的上传和下载某些特征的文件,cp 命令可以通过include和exclude参数进行文件模式匹配,看以下几个例子:

通过以下命令仅仅下载所有 ”.data” 结尾的文件:

$ aws s3 cp s3://bjslabs/s3/data1000/ ./data1000 --recursive --profile dev1000 --exclude “*” --include “*.data”

通过以下命令上传当前目录中,除子目录 “data100” 之外的所有文件到存储桶:

$ aws s3 cp ./ s3://bjslabs/s3/data1000/ --recursive --profile dev1000 --exclude “data100/*”

S3到S3的复制场景

首先,我们先来看一个同区域的文件复制案例,执行如下命令并记录详细日志:

$ aws --debug s3 cp --source-region cn-north-1 s3://bjslabs/s3/data100/1.data s3://bjslabs/s3_2/ --profile dev100 > sameregion100.log 2>&1

查看日志文件,可以了解两点(1)默认也是支持分段并发的(2)每个分段是调用的upload-part-copy 接口执行复制操作的:

而如果不是S3到S3的复制的话,比如前面两个场景,源是本地目标地址是S3的话则cp命令最终会调用upload-part 方法进行上传,这两个命令有什么区别吗?

参见upload-part-copyupload-part在线文档,仔细对比如下两个命令最终的REST请求,可以看到,UploadPartCopy请求只需要将源数据指向源的对象(x-amz-copy-source)以及相应的数据范围(x-amz-copy-source-range);但UploadPart请求中必须要讲该分段的数据包含进去;也就是可以推断,S3到S3的复制不需要经过我们执行该命令的机器进行数据中转;

样例请求(UploadPartCopy):

样例请求(UploadPart):

本章小结

本章节,我们深度解析了cp命令在不同场景下的数据传输行为,基本的一些结论见下表;其中,S3到S3的复制,从底层API可以分析出不需要经过运行命令的机器进行中转,这样节约进出执行cp命令的机器流量;同时我们s3api提供了很多底层S3底层接口,基于这些接口,可以方便地在分段上传的基础上实现断点续传。

AWS S3 CLI上传下载性能测试

本章继续和大家一起来探讨下影响AWS S3 CLI进行数据传输的性能的基本因素以及实际场景下基于AWS EC2的一些数据传输性能测试情况。

同区域不同S3桶数据的复制性能测试

测试环境:

  • BJS区域
  • R4.2xlarge 跑cp命令
  • AWS CLI S3参数max_concurrent_requests为1000
  • 测试方法,脚本跑10次取平均时间

测试结果如下,时间单位是秒:

总体平均时间:(29+29+28+6+29+5+6+6+6+29)/10=17.3秒,root.img的大小为8.0GB,AWS北京区域不同桶之间的数据平均传输速度为473.52MB/s,最高速度可以达到1.6GB/s,最低282.48MB/s。

为了验证该场景下对网络的影响,我们截取了这阶段的网络方面的监控,我们观察到这段时间,该测试机的网络输出和网络输入有几个波峰,但峰值最高没超过12MB,从侧面验证了我们的前面的判断,即cp在S3到S3复制的场景下,数据不会经过命令行运行的机器转发,而是直接由S3服务直接完成:

S3桶数据到AWS EC2下载性能测试

测试环境(针对下面表格的场景一):

  • BJS区域
  • R4.2xlarge 跑cp命令,
  • EC2挂500GB的gp2 ssd磁盘
  • AWS CLI S3参数max_concurrent_requests为1000
  • 测试方法,脚本跑10次取平均时间

测试结果如下,时间单位是秒:

总体平均时间:(67+64+66+65+65+65+66+65+64+65)/10=65.2秒,root.img的大小为8.0GB,该测试场景的数据平均传输速度为125.64MB/s,下载速率比较平稳。

在继续试验之前,我们总结下,影响EC2虚机下载S3数据的速度的几个因素:

  • 磁盘的吞吐率(SSD还是实例存储还是HDD)
  • EBS带宽,是否EBS优化(EBS本身也是通过网络访问,是否有EBS优化为EBS I/O提供额外的专用吞吐带宽,否则和EC2网络共享带宽性能)
  • S3服务的带宽(通常我们认为S3服务不是性能瓶颈)

我们已经测试了R4.2xlarge的下载性能,接下来我们选择几个典型的虚机类型分别进行测试,由于测试时间跨度比较大,测试场景中的EC2操作系统层没有任何调优,测试结果仅供参考。所有场景测试都针对一块盘,进行同一个8GB的文件下载,下载10次,根据时间算平均下载速率。

通过简单的测试我们发现,

1.    虽然随着机型增大比如从R4.xlarge 到R4.4xlarge,同样是单块SSD磁盘的情况,磁盘就成为整个下载速度的瓶颈,因为R4.4xlarge EBS优化的专用吞吐量理论值可以达到437MB/s,而简单测试下来的下载速度最高到130MB/s左右;

2.    SSD磁盘在整个测试场景中,是最优的选择,而且SSD盘的大小无需到3TB就可以有很好的性能表现

3.    实例存储在没有预热(dd整个盘)的情况下表现很一般

4.    st1盘6TB左右的测试盘,下载测试性能不如500GB的SSD盘

5.    3334GB的SSD盘的表现跟比预期要差很多

6.    测试场景中EC2下载S3的平均速度没有超过150MB/s

以上表格中的虽然我们测试了不同系列的不同规格的机型,但是我们可以通过修改实例类型非常方便地切换,因此监控上我们可以看出测试时间跨度中,我们的机器资源使用情况。

整个测试过程中R4系列机器的CPU的利用率总体还算正常,在15%到60%之间浮动:

整个测试过程中500GB的gp2磁盘的写入带宽变化如下图所示:

整个测试过程中3334GB的gp2磁盘的写入带宽变化如下图所示:

整个测试过程中6134GB的st1磁盘的写入带宽变化如下图所示:

AWS S3 CLI的一些限制

S3 CLI提供的分段上传算法有些限制在我们实际使用的时候,特别要关注比如分段总大小不能超过10000,每个分段的数据块最小为5MB,分段上传最大的对象为5TB等等。详细情况请参考AWS官方文档

下一篇将要探讨和解决的问题

在了解AWS S3 CLI命令底层原理和影响基本性能的基本因素之后,我们接下来会继续来探讨,如何利用S3的底层API实现S3中的数据的跨区域可靠、低成本、高效传输。

总结

本文和大家一起深度研究了AWS S3 cp命令在各种场景中的底层实现原理,同时利用实验,总结了关于AWS EC2下载S3数据的基本性能测试结果,期待更多读者可以实践出更多的性能优化方法。随着大数据分析的蓬勃发展,存放在S3上的数据越来越多,本系列主要会和大家一起深度探讨S3数据复制迁移的最佳实践。

作者介绍:

薛军

AWS 解决方案架构师,获得AWS解决方案架构师专业级认证和DevOps工程师专业级认证。负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内的应用和推广,在互联网金融、保险、企业混合IT、微服务等方面有着丰富的实践经验。在加入AWS之前已有接近10年的软件开发管理、企业IT咨询和实施工作经验。

如何使用Amazon Macie 进行安全数据自动分类和用户行为监控

概述

当我们在Amazon S3中存储大量内容时,识别和分类其中潜在敏感数据可能会有点像在一个非常大的干草堆中找绣花针针,整个的过程是非常低效。那么能否有一种工具可以在数据加入到S3后就自动的识别敏感信息并自动进行分类?

同时在我们日常的工作中,访问数据的时间间隔及物理位置相对的固定。如果发生异常的事件,例如原来某个用户一天访问一到两个文件,但如果突然在某天访问超过一百个文件,能否由系统发出告警事件提醒相关人员进行相关检查,确保安全?

本文从如下几部分介绍Amazon Macie服务及主要功能,同时还列出常见使用场景,以及如何配置的简要步骤。希望能给大家带来帮助。

什么是Macie

Amazon Macie 是一种支持人工智能技术的安全服务,可以帮助您通过自动发现、分类和保护存储在 AWS 中的敏感数据来防止数据丢失。Amazon Macie 使用机器学习来识别敏感数据 (例如,个人身份信息 [PII] 或知识产权),分配业务价值,提供此数据的存储位置信息及其在组织中的使用方式信息。

Amazon Macie 可持续监控数据访问活动异常,并在检测到未经授权的访问或意外数据泄漏风险时发出警报。

为什么要使用Macie

对现存的海量文件进行自动的分类并且根据不同的分类制定不同的监控策略,一旦发生异常的访问需要得到及时的告警,是每个组织面临的挑战。

Amazon Macie提供了简单高效并且安全的方案,Macie通过使用机器学习来了解存储的内容和用户行为,同时提供可视化界面,展示安全分类,从而使客户能够专注于保护敏感信息,而不是浪费时间手工的处理。

Amazon Macie内置检测个人身份信息(PII)或敏感个人信息(SP)的常见敏感信息的引擎,可以自动识别出S3存储文件是否包含例如个人身份信息(PII)或其他公司财报等敏感信息,在没有Macie之前,这样的工作都需要手工的处理或者使用第三方平台。而现在可使用Macie很容易解决上述问题。

Amazon Macie持续监控数据和账户凭证 。 在发现可疑行为或对实体或第三方应用程序进行未经授权的数据访问时撤销访问或触发密码重置策略,来防范安全威胁。当Amazon Macie发出警报时,您可以使用预先在Amazon CloudWatch设置的规则发送告警,以便迅速采取行动,保护数据。

Macie主要功能

Amazon Macie首先功能分为两部分,一方面是使用自然语言处理(NLP)来理解数据,Macie可以自动对您的S3桶中的数据进行分类。另外一个是使用机器学习理解用户访问数据的行为,同时利用动态分析数据访问模式的预测分析算法,并使用日常访问的用户行为数据不断的训练并优化模型。

Macie 主要功能

  • 自动化处理数据:分析,分类和自动处理数据,从现有的数据和访问日志分析出有规律的历史模式,用户认证数据,用户位置信息及时间信息。
  • 数据安全和监控: 主动监控 日志数据,检测到的异常情况,转发告警信息到CloudWatch 事件或和Lambda以进行后续处理,例如自动修复安全漏洞或者发送邮件通知。
  • 主动预防的数据的丢失,提供即时保护,无需手动干预。
  • 可视化分析:提供存储数据的可视化详细信息。
  • 数据研究与报告:允许用户管理配置报告。

Macie如何运作

在数据分类过程中,Amazon Macie 识别 S3 存储桶中的对象,并将对象内容流式传输到内存中进行分析。

当需要对复杂文件格式进行更深入的分析时,Amazon Macie 将下载对象的完整副本,并在短时间内保存,直到完成对象的全面分析。Amazon Macie 对数据分类的文件内容分析完毕后,它将立即删除存储内容,仅保留未来分析所需的元数据。

Amazon Macie持续监控分析当前的用户行为习惯并以基础形成基线,例如敏感数据在哪个IP地址以及何时以什么样的频率被进行访问。随后Macie持续监控CloudTrail日志并训练机器学习所使用的模型,,一旦发现可疑的访问活动,Macie可以及时发出告警。

示意图如下:

信息分类

Amazon Macie已经定义好信息分类标准:

  • 配置合规性 – 与合规性内容策略,配置设置,数据日志记录以及补丁级别相关​。
  • 数据合规性 – 与合规性或安全控制内容的发现相关,例如存在个人身份信息或访问密钥。
  • 文件托管 – 与恶意软件,不安全的软件或攻击者的命令和控制基础架构相关的托管主机或存储服务。
  • 服务中断 – 可能导致无法访问资源的配置更改。
  • 恶意软件或活动- 潜在的恶意软件或活动。
  • 可疑访问 – 从风险异常的IP地址,用户或系统访问您的资源 。
  • 身份枚举 – 一系列API调用或访问,枚举系统的访问级别,可能指示攻击的早期阶段或受到破坏的凭据。
  • 特权升级 – 成功或不成功的尝试,以获得对通常受应用程序或用户保护的资源的高级访问,或尝试长时间访问系统或网络。
  • 匿名访问 – 尝试从IP地址,用户或服务访问资源,以隐藏用户的真实身份。 例如包括使用代理服务器,虚拟专用网络和其他匿名服务,如Tor。
  • 开放许可 – 识别受潜在过度允许访问控制机制保护的敏感资源。
  • 位置异常 – 访问尝试敏感数据的异常和危险的位置。
  • 信息丢失 – 敏感数据的异常和冒险访问。
  • 凭证丢失 – 可能损害您的凭据。

告警严重级别

Macie 内置了5种告警信息级别

  • 严重(Critical) – 该级别可能导致信息机密性,完整性和可用性受到损害的安全问题。建议将此安全问题视为紧急情况,并实施立即的补救或加固措施。
  • 高 –该级别可能导致信息机密性,完整性和可用性受到损害的安全问题。建议将此安全问题视为紧急情况,并实施立即的补救或加固措施。
  • 中等(Medium) – 该级别可能导致信息机密性,完整性和可用性受到中等性损害。建议在下次更新服务期间修复此问题。
  • 低(Low) -该级别可能导致信息机密性,完整性和可用性受到低等性损害。建议您将此问题作为未来服务更新的一部分。
  • 信息(Informational) – 该级别警告只描述基础架构的特定安全配置详细信息。根据相关业务和组织目标,可以简单地记录这些信息或使用它来提高系统和资源的安全性。

注:严重(Critical) 和高(High)之间的主要区别:

产生严重(Critical)警报的事件可能导致大量资源或系统受到损害。

产生高(High)警报的事件可能导致一个或多个资源或系统受到损害。

开始使用Macie

需要配置的IAM 角色

创建身份访问管理(IAM)角色,为Macie提供对您的AWS帐户的访问权限。这些角色只需要创建一次,以便在所有地区使用。

我们以Oregon区域作为例子 ,可以通过启动下面列出的URL中找到的AWS CloudFormation堆栈模板来创建这些角色和所需的策略。

https://s3-us-west-2.amazonaws.com/us-west-2.macie-redirection/cfntemplates/MacieServiceRolesMaster.template

配置Cloud Trail服务

首先需要建立相应IAM Role, 确保Amazon Macie能够访问S3及 CloudTrail,另外是要启动CloudTrail服务以对相关的AWS API操作进行分析。

开始使用Macie服务

添加需要管理的S3 存储桶

需要在Macie中添加需要访问S3存储桶并对其中的文件进行统计分类。

步骤如下:

1.     进入Macie Console 页面,点击 Integrations 页面后,选择SERVIES 标签页面,点击 Select an account 下拉框

2.     选择 Amazon S3 Detail

3.     选择Selected S3 buckets and prefixes

4.     选择Macie分析的S3 bucket

文件分类

在Macie中添加需要分析的S3 存储桶后,Macie会自动开始分析其中的文件,稍等一段时间后,进入Macie的Dashboard 页面。

在Dashboard中共提供10种视图,为了演示本文章仅仅展示其中的两个。

S3文件分类统计结果

S3文件分类统计详细结果

用户行为分析

根据CloudTrail的日志,Macie自动分析所有IAM user的相关操作。根据Macie预先定义的规则,把IAM用户分为如下四类:

  • 白金:这类IAM用户或角色曾有管理员或root用户的高风险API调用的历史,例如创建用户,授权安全组入口或更新策略。 应对这类帐户进行重点监控。
  • 黄金:这类IAM用户或角色曾有创建与基础设施相关的API调用的历史,这些API调用标志其具有高级权限,例如创建实例或拥有向Amazon S3写入数据的权限。 这些帐户应密切监控。
  • 白银:这类IAM用户或角色曾大量中等风险的API调用的历史,例如Describe和 List操作或对Amazon S3的只读访问请求。
  • 青铜:这些IAM用户或角色通常在AWS环境中执行较少量的Describe 和List  API调用。

Macie使用如下的界面展示用户行为分析。

告警管理

Macie内置了40种告警规则,我们可以根据实际情况选择启用或者禁用该规则。

下面我们选择级别为 严重(Critical) 的告警规则进行分析。

从上图可知该告警的规则是 “如果 IAM Policy 设置 S3存储桶可以被任何人可读,那么触发告警”, 告警样例如下:

添加自定义告警

Macie不仅仅支持内置的告警规则设置,同时也支持自定义规则的定义。

场景:

根据 Macie 定义的规则,如果文档中包含 “merger, acquisition, divestiture, reorganization, consolidation, demerger, restructuring, rebranding, takeover, subsidiary” 关键字,那么该文档会被分类为 “Corporate Growth Keywords”的类别。

现在假设用户往S3上传了具有上述关键字文档,可能存在信息泄漏的风险,需要发送告警严重级别为 “Medium”的告警。

下面演示如何添加自定义告警

1.     进入Macie Console

2.     点击 Setting-> Protect data –> Basic alerts

3.     点击 Add New 按钮

4.     按照如下样例填写

Alert Title: Test Customer Alert

Description: Test Customer Alert

Category: 选择 Information Loss

Query: themes:” Corporate Growth Keywords ”

Index: 选择S3 objects

Min number of matches:1

Severity:Medium

Whitelisted Users: 不选

5.     点击 Save 按钮.

6.     往S3上传如下内容的word文件

7.     上传成功(存储桶名字暴露了)

8.     回到DASHBOARD界面,收到告警

注:在Alert中定义最关键的部分是Query, Macie会把Query中的内容使用JavaCC转换成Lucene Query。有关查询语法的更多信息,请参阅Apache Lucene – Query Parser Syntax

如下是常见搜索的示例:

  • 搜索任何不是源自Amazon IP地址的控制台登录:

eventNameIsp.compound:/ConsoleLogin:~(Amazon.*)/

  • 公共S3存储中的PII(Personally Identifiable Information)

filesystem_metadata.bucket:”my-public-bucket” AND (pii_impact:”moderate” OR pii_impact:”high”)

同AWS其他服务集成

Amazon Macie 可以同其他AWS服务集成。如下示例如何使用CloudWatch和SNS集成。具体演示如下:

1.     创建SNS并使用相应的邮箱确认

2.     在CloudWatch 创建Rule

3.     收到告警邮件

更改S3元数据(Metadata)的保存周期

缺省条件下,Macie保存S3 元数据的周期是一个月,但可以系统支持该周期最长为12个月。更改步骤如下:

  1. 进入Macie Console
  2. 点击 Integrations -> Choose retention duration for S3 metadata –> Select a range

总结

Macie使用机器学习算法自然语言处理S3中存储的数据,并且使用上下文信息对信息进行分类,并给数据分配相应的业务价值标签。自动发现分类和保护存储在AWS中的敏感数据,同时持续检查S3桶中PUT请求的CloudTrail事件,并几乎实时自动对新对象进行分类。同时监控CloudTrail的API日志,识别出日常的用户行为模式,一旦Macie发现异常的行为,可以及时的发出告警。

通过Macie,可以方便的实现数据安全自动化,及数据可视化和数据丢失防护。

 

作者介绍
刘春华,AWS 解决方案架构师,AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内和全球的应用和推广,在大规模并发应用架构、无服务器架构,人工智能与安全等方面有丰富的实践经验。 曾任IBM云架构师,对企业应用迁移到云及应用系统改造有深入的研究。

 

 

使用AWS Lambda和AWS Step Functions轻松构建Serverless应用

作者: Vivian Zhang(张芸)

Serverless(无服务器)应用可以说是当前的行业热点,用户无需预配置或管理服务器,只需要部署功能代码,AWS Lambda会在需要的时候执行代码并自动缩放, 从每天几个请求到每秒数千个请求,轻松地实现FaaS (Function as a Service)。无服务器应用的使用场景非常广阔,从微服务架构,到批处理、流处理、运维自动化和移动计算。

实现Serverless应用,除了AWS Lambda还需要什么?

我们来看一个典型的基于Lambda的无服务器应用。

当我们将作为计算和存储实体的Lambda函数、消息队列、DB去掉,可以看到下面这张图。

这张图上的箭头,就是上一张图里Lambda函数之间的流程,或者可以称为Lambda函数之间的“胶水”,它们起到了编排协调各个Lambda函数的作用。通常在应用中,我们会需要有这样的一些流程:

  • 我想要顺序地执行方法。
  • 我想要并行地运行这些方法。
  • 我想要基于数据选择执行方法。
  • 我想要重试某些方法。
  • 我想要try/catch/finally。
  • 我想要代码运行一定时间或者等待一段时间……

通常我们可以通过方法调用、函数链、DB和消息队列来协调这些函数,实现流程。但是对于所采用的协调机制,我们都希望它具有以下功能:

  • 可以自动伸缩;
  • 不会丢失状态;
  • 可以处理错误和超时;
  • 可以简单的搭建和运维;
  • 可以审计。

这里我们介绍一种方式,采用AWS Step Functions协调Lambda函数之间的流程。

AWS Step Functions

AWS Step Functions是一个可视工作流服务,可用来轻松协调分布式应用程序和微服务的各个组件。用户从单个组件构建应用程序,每个组件都执行一个特定的功能,也就是Task(可以采用Lambda函数实现)。Step Functions提供了一种可靠的方法来协调这些组件并逐步完成应用程序中的这些功能,并且 提供了一个图形控制台,将应用程序的组件可视化为一系列步骤,它可以自动触发并跟踪每一个步骤,并在出现错误时重试,这样应用程序就可以每一次都按照预先设定的顺序执行。Step Functions会记录每一步的状态,因此当事情出错时,用户可以快速地诊断和调试问题。

要使用Step Functions构建应用,首先我们需要在Step Functions里创建State Machine(状态机),也就是对应每一个应用的工作流程。可以采用以下8种蓝图,包括7种预定义好的状态机和1种自定义的。创建好的状态机用JSON描述。

在每一个状态机里,我们需要定义一系列的State(状态),用来完成不同的功能:

  • Task:在状态机中完成特定的功能,可以采用Lambda函数实现。
  • Choice:在各种执行分支中进行选择。
  • Fail和Success:停止一个执行,并设为Fail或者Success。
  • Pass:简单地将输入传给输出,或者注入一些数据。
  • Wait:提供一定时间的延迟,或者等待到特定的时间/数据。
  • Parallel:并行地执行分支。

可以看出,上一节中我们所需要的协调和流程在这些状态中都得到了支持。其中的Task状态是用来真正实现应用的功能,而其他状态用来处理功能之间的流程。比如说,下面是一个名为HelloWorld,执行Lambda函数的状态。

下图是一个拥有所有状态的状态机:

在Console里看到一个创建好的状态机是这样:

我们点击New Execution并且传入input数据,就可以启动该状态机的一次执行,并且可以从界面上查看执行的情况。

此外也可以通过AWS SDKs,Step Functions API和AWS CLI来启动状态机的执行并且查看进程。

采用AWS Lambda和AWS Step Functions构建Serverless应用的例子

这里介绍一个镜像识别和后端处理的例子,展示如何使用 AWS Step Functions 编排一个集成 AWS Lambda、Amazon S3、Amazon DynamoDB 和 Amazon Rekognition 的无服务器处理工作流。此工作流处理上传至 Amazon S3 的照片,并从镜像中提取元数据,如地理位置、大小/格式、时间等。然后,它使用镜像识别功能标记照片中的对象,同时还生成照片的缩略图。该例子的源代码在github上:https://github.com/awslabs/lambda-refarch-imagerecognition

整个架构的流程如下:

  1. 一张图片上传到名为PhotoRepo的S3 bucket里,位于“Incoming/”前缀下。
  2. S3 upload event产生,触发了名为ImageProcStartExecution的Lambda函数,该函数启动了AWS Step Functions中ImageProc状态机的执行,并将S3 bucket和object key作为参数传入状态机。
  3. ImageProc状态机执行以下步骤:
  4. 从S3中读取文件并抽取出图片的元数据(格式、EXIF数据、大小等等);
  5. 基于上一步骤的输出,验证上传的文件格式是否支持(png或者jpg);如果不支持,抛出NotSupportedImageType错误并且结束执行。
  6. 将抽取出的元数据保存在ImageMetadata DynamoDB中。
  7. 并行地同时启动两个进程:
  • 1)    调用Amazon Rekognition探测图像文件的对象,如果探测到,将tag保存到ImageMetadata DynamoDB中;
  • 2)    生成缩略图并且保存在名为PhotoRepo的S3 bucket的“Thumbnails/”前缀下面。

可以通过源代码中的test web app上传图片来测试该图像识别和处理工作流的结果。

可以通过源代码中的CloudFormation template来创建该后端处理应用程序。

结论

用AWS Lambda函数定义应用程序需要执行的每一个特定功能,而用AWS Step Functions定义在各个功能中流转的流程,这样采用AWS Lambda和AWS Step Functions联合使用的方式,可以轻松地构建出Serverless应用。

此外,AWS 还提供一系列完全托管的服务,可以用来构建和运行无服务器应用程序。

参考

可以在我们的网站上下载到相关例子的sample code:https://github.com/awslabs/lambda-refarch-imagerecognition

关于AWS Step Functions的 更多内容请参考网站:https://aws.amazon.com/cn/step-functions/

关于AWS Lambda的更多内容请参考网站:https://aws.amazon.com/lambda/

关于AWS服务器平台请参考网站:https://aws.amazon.com/cn/serverless/