亚马逊AWS官方博客

Amazon S3新版管理控制台的正确打开方式

Amazon Simple Storage Service(简称S3)是AWS在2006年发布的第一款云服务产品,S3作为对象存储具有海量存储、接口灵活、11个9持久性、价格便宜等特点,特别适合存放静态文件如图片、视频、日志以及备份文件等等,同时S3是AWS大数据解决方案中重要组成部分,以EMRFS的形式与EMR(AWS托管的Hadoop平台)结合提供计算与存储分离的灵活架构。

熟悉S3控制台的小伙伴一定发现,自从5月份开始,控制台的界面焕然一新,甚至有点无处下手,但熟悉起来后又有些爱不释手,下面我们将介绍下新版控制台带来了哪些新的功能,以及如何给你的工作带来极大效率提升。

新版控制台的操作说明,图文并茂,详见:

http://docs.amazonaws.cn/AmazonS3/latest/user-guide/what-is-s3.html

一、创建存储桶

创建存储桶的时候除了配置桶名、存储桶区域之外,还可以配置版本控制、日志、标签以及访问权限,现在用户可以在新版控制台使用“从现有存储桶复制设置”的功能,选择相同配置的存储桶即可,避免重复设置。

二、上传对象

在控制台下除了正常的通过点击上传按钮选择上传文件完成上传外,新版控制台支持在存储桶界面下,直接将待上传的对象拖放到页面上。通过新版控制台上传单个文件支持最大78GB。

对存储桶中对象的上传、删除、重命名等操作,页面底部可以看到该操作的进度及其他操作的历史记录。

三、ACL

我们可以通过配置存储桶及对象的ACL来实现存储桶和对象的访问控制,老版控制台的部分名称容易让人引起歧义,以存储桶ACL为例,如下图所示,其中查看权限是指查看该存储桶权限的权限,即查看该存储桶ACL的权限,而不是指查看存储桶的权限,编辑权限同样是指编辑权限的权限,不是编辑存储桶的权限。

在新版控制台中很好的避免了这点误区,分对象访问和权限访问,这里以存储桶的ACL举例,见下图:

其中一个新功能是我们可以在管理用户处添加其他帐号的规范ID(https://docs.aws.amazon.com/zh_cn/general/latest/gr/acct-identifiers.html )或帐号的Email向该帐号中的IAM user/role授权访问,以实现跨帐号访问,需要注意的是对方帐号的IAM user/role拥有对该存储桶的操作权限取决于此处我们设置的ACL以及对方帐号中IAM user/role本身policy设定的权限。

四、标签Tag

S3标签是随S3新版控制台一起发布的一个服务特性。标签可以帮助你对存储桶以及对象进行分类或标记,类似我们给EC2等资源添加标签一样,每个S3标签也是一个键值对,每个对象最多可添加10个标签键值对,键值对大小写敏感。通过使用标签,我们可以创建基于标签的IAM policy以实现细粒度的权限控制,比如标签为PHI的值为true时,仅供只读。同时,在使用S3数据生命周期管理、分析、指标等功能的时候,可以创建基于标签的过滤器,实现细粒度的资源管理。

S3 标签作为新服务特性,相应的API也同步发布,比如PUT Object tagging, GET Object tagging, DELETE Object tagging以及其他支持标签的API如PUT Object, GET Object, POST Object, PUT Object-Copy,详细可参考:

http://docs.aws.amazon.com/zh_cn/AmazonS3/latest/dev/object-tagging.html 需要注意的是,标签遵循最终一致性模型。

五、生命周期管理

数据通常从创建后会经历频繁访问、低频访问、极少访问的数据热度周期,相对于热数据,冷数据适合以成本更低的方式存储,比如热数据存放在S3 standard,冷数据存放在S3-IA,归档数据存放在Glacier等,以达到成本最优的目标。我们可以使用S3数据生命周期管理通过配置相应的规则来实现数据的生命周期管理,随着S3标签的发布,现在我们可以创建基于前缀和/或基于标签的规则,以实现更细粒度的资源管理。详细操作步骤见:

http://docs.amazonaws.cn/AmazonS3/latest/user-guide/create-lifecycle.html

六、存储类分析

存储类分析是新发布的功能,通过该工具,我们可以更加直观的了解到存储桶中的数据活跃情况,帮助我们决策何时将不常访问的数据从S3 Standard转换为S3-IA,即为配置数据生命周期管理提供数据支持。

同时,可以创建筛选条件,选择对整个桶中对象或者具有某些前缀或标签的对象进行分析,即对对象进行分类分析,需要注意的是,分析是从启用该功能后一段时间后才能看到结果(通常是24~48小时),并不是可以立刻可以看到分析结果。

通过存储类分析,我们可以可视化的了解到存储桶数据在过去30天的检索量,占比,以及多个时间范围段内数据存储与检索的情况,该数据每天更新,并且可以以csv的格式导出到S3存储桶以供下载,可使用Quicksight等BI工具进行展现。

csv中字段说明见:

http://docs.amazonaws.cn/en_us/AmazonS3/latest/dev/analytics-storage-class.html#analytics-storage-class-export-to-file

配置存储类分析详细操作步骤见:

http://docs.amazonaws.cn/AmazonS3/latest/user-guide/configure-analytics-storage-class.html

七、存储指标

CloudWatch可以监控S3存储桶的使用情况,过去只有两个指标:存储桶大小和对象数量,随着新版控制台的发布,又有两类指标发布,即请求指标和数据传输指标。

请求指标(收费功能)中包含GetRequest, PutRequest, ListRequest, AllRequest, PostRequest, DeleteRequest, HeadRequest, 4xxErrors, 5xxErrors。数据传输指标(收费功能)包含TotalRequestLatency,FirstByteLatency,BytesDownloaded,BytesUploaded。这些指标均为1分钟报告1次,我们可以通过这些指标快速了解和定位S3使用过程中的一些问题,比如当前S3存储桶是否遇到性能瓶颈,是否需要提case提升限制等等。同样可以通过配置基于前缀/标签的过滤器实现细粒度的管理。

S3请求速率及性能注意事项参见:

http://docs.amazonaws.cn/AmazonS3/latest/dev/request-rate-perf-considerations.html

指标详细解释可以见:

http://docs.amazonaws.cn/en_us/AmazonS3/latest/dev/cloudwatch-monitoring.html#s3-request-cloudwatch-metrics

配置请求指标操作步骤见:

http://docs.amazonaws.cn/AmazonS3/latest/user-guide/configure-metrics.html

八、存储清单

S3存储清单是S3 提供的一项存储管理工具,S3存储清单可以每天或每周输出指定S3存储桶或存储桶中指定前缀的对象及其相关元数据信息的列表,并以CSV文件的格式存储在指定的S3存储桶中。存储清单遵循最终一致性模型,即列表中可能没有最近添加或删除的对象信息,如果需要确认某一个对象的状态,我们可以使用HEAD Object REST API(或命令行,SDK)来获取该对象的元数据。

对于存储桶中有海量文件的用户而言,存储清单可以方便的帮助用户了解当前存储桶中的文件列表而不是像过去那样需要频繁调用GET Bucket API(每次返回最多1000个对象),从而加速一些业务工作流及大数据作业等等。

配置存储清单时,我们可以指定清单筛选条件、生成频率、存储位置、清单中包含的字段等等,一个存储桶可以配置多个清单。

配置存储清单操作步骤见:

http://docs.amazonaws.cn/AmazonS3/latest/user-guide/configure-inventory.html

看完上面S3新版控制台的介绍,是不是对这个新工具又有了一些新的认识,不妨将这些新功能用起来,优化成本,提升工作效率,在AWS上面诞生更多的创新应用。

作者介绍

王世帅,AWS解决方案架构师,负责基于AWS的云计算方案架构的咨询和设计,同时致力于AWS云服务在国内教育、医疗行业的应用和推广。在加入AWS之前曾在国航担任系统工程师,负责存储方案的架构设计,在企业私有云方面有丰富经验。

利用Kong及AWS Lambda构建无服务器的后端逻辑

Kong是一个开源的API GW及微服务管理层,基于Nginx, Cassandra或者PostgreSQL构建,最初由Mashape开发,用于为其API Marketplace管理超过15,000个API和微服务,并于2015年开源。Kong具有如下优点:

  1. 可扩展性:通过简单地添加更多的机器,Kong可以轻松地水平缩放,这意味着您的平台可以处理几乎任何负载,同时保持低延迟。
  2. 模块化:可以通过添加新的插件来扩展,并通过RESTful Admin API轻松配置所有插件。
  3. 平台无关性:Kong可以运行在任何地方,包括本地数据中心及公有云,支持物理机,虚机及容器部署。

目前Kong的最新版本为0.10.2,支持的插件如下:

  • 认证:Basic Authentication, Key Authentication, OAuth2.0 Authentication, OAuth 2.0 Introspection, HMAC Authentication, JWT, LDAP Authentication
  • 安全:ACL, CORS, Dynamic SSL, IP Restriction, Bot Detection
  • 流控:Rate Limiting, Response Rate Limiting, Request Size Limiting
  • 无服务器架构:AWS Lambda, OpenWhisk
  • 分析监控:Galileo, Datadog, Runscope
  • 内容转换:Request Transformer, Response Transformer, Correlation ID
  • 日志:TCP/UDP/HTTP Logging, File Logging, Syslog, StatsD, Loggly

通过AWS Lambda插件,Kong可以作为一个统一的API前端,接收用户的请求,并调用不同的lambda函数做相关的处理,最后将结果返回给客户端,目前Lambda插件支持如下region:

us-east-1, us-east-2, ap-northeast-1, ap-northeast-2, ap-southeast-1, ap-southeast-2, eu-central-1, eu-west-1

Kong的安装

Kong支持多种安装方式,对于在AWS上运行Kong的情况,主要有三种:

  1. 通过marketplace安装,此安装方式会在一台EC2上同时安装Kong及其存储数据库Cassandra,适合单机部署或者测试环境
  2. 通过CloudFormation安装,CloudFormation安装方式目前支持如下region,可以通过配置选择存储数据库的类型Cassandra还是PostgreSQL,或者不由CloudFormation创建存储数据库
  3. 通过yum在EC2上安装

本文主要讲解第三种方式的安装过程,安装Kong 0.10.1版本

第一步 配置Kong的数据存储

Kong支持两种datastore:Cassandra和PgSQL,方便起见,这里利用AWS RDS创建PgSQL作为数据存储

注意事项:

  1. RDS的安全组需要放行5432端口
  2. 记录RDS创建过程中设置的用户名,密码及数据库名

比如:username/password:ivan/ivan

DB:kong

第二步 安装配置Kong (Amazon Linux)

yum update –y

wget https://github.com/Mashape/kong/releases/download/0.10.1/kong-0.10.1.aws.rpm

yum install kong-0.10.1.aws.rpm –nogpgcheck

cp /etc/kong/kong.conf.default /etc/kong/kong.conf

vim /etc/kong/kong.conf,

修改配置如下

database = postgres

pg_host = postgre-kong.XXXXXXXX.rds.cn-north-1.amazonaws.com.cn

pg_port = 5432

pg_user = ivan

pg_password = ivan

pg_database = kong

简单测试

第三步 创建AMI,利用ELB,Autoscaling Group构建高可用架构

注意,如果EC2上已经启动过kong,那么会将生成的id也打包进入AMI,导致基于AMI生成的多台机器的id相同,从而无法建立集群,创建AMI前需要删除id

rm –f /usr/local/kong/serf/serf.id

参考:

https://github.com/Mashape/kong/issues/1751

检查集群状态

第四步 创建Lambda函数并且测试

第五步 配置Kong API GW并调用Lambda

5.1.配置API

测试

5.2.配置Lambda插件

测试

第六步 配置启用额外的插件

6.1为API开启key认证

配置用户并绑定key

测试

6.2为API限速

6.3基于IP地址的过滤

作者介绍

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

利用Amazon ElastiCache寻找附近的X

基于地理信息的应用已经越来越深入到日常生活中,人们经常会在应用中寻找附近的朋友,车,餐厅或其它资源。而与此同时,随着物理网技术及设备的普及,应用需要更加实时和精确的处理来自各种数据源(包括用户手机,各种传感器设备及其他系统)的大量数据,以完成相关的搜索和计算距离等操作。

架构

对于开发者来说,Redis因为其性能上的优势往往会被采用作为位置数据的缓存,只是在3.2版本之前需要代码中把位置数据进行Geohash后才能有效的排序和分析。不过3.2版本后,Redis已经能够原生支持基于位置信息的存储,计算及搜索了。Amazon ElastiCache是AWS提供的托管型的数据缓存服务,借助该服务,用户能够在云中轻松部署、运行和扩展分布式内存数据存储或缓存。 Amazon ElastiCache 的Redis引擎是一项与 Redis 兼容的内存服务,兼具 Redis 的易用性和强大功能,同时还可为要求最苛刻的应用程序提供适用的可用性、可靠性和性能,提供单节点和多达 15 个分片的群集,从而可将内存数据扩展到高达 3.55TiB。这里,我们可以基于Elasticache并结合AWS其他服务构建出以下的示例架构:

1)终端设备获取GPS位置信息,定时或基于事件将数据上传到云端。在AWS上可以选择使用IoT或Kinesis等托管型服务作为数据收集的接收端,也可以使用部署在EC2/Lambda上的自定义服务。

2)所有位置信息写入可以自动扩展的DynamoDB,基本Schema包含设备Id/Timestamp/Geo location, 方便历史查询或轨迹查询。

3)打开DynamoDB流,用KCL或Lambda监听DynamoDB的数据改变,并将当前变化的位置数据更新到Elasticache中建立基于Geospatial的索引缓存。

4)手机应用搜索附近资源时,部署在EC2/Lambda的查询服务利用Elasticache geospatial直接获取结果。

实现

如前文所述,步骤1和2可选择的方案很多,比如采用AWS IoT服务甚至可以无需任何代码仅通过配置即可完成云端的功能讲数据实时写入相应的DynamoDB表中。因此,本文将着重介绍如何实现前文架构中的3和4步:

a) 打开DynamoDB 流,获取流的ARN用于读取,如下图:

读取DynamoDB流数据有三种方式:利用Kinesis adapter,利用低级别API以及利用Lambda函数来进行读取。从易用性的角度来说,当然是Lambda函数最简单,不需要考虑shard,吞吐和checkpoint等问题而专注于业务逻辑。但是Lambda函数并不是在所有的AWS区域都支持,因此本文采用第一种方式利用Kinesis adapter完成读取。具体参考文档:http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/Streams.KCLAdapter.html

b) 在读取流的同时,我们需要将最新的地理位置信息利用GEOADD更新到Elasticache中。前文提到Redis在3.2版本后,Geospatial Indexing已经被原生支持,而它实际上是Sorted List数据结构的一种扩展,即排序 key扩展成了经纬度,如下图所示的数据结构,并且可以方便的使用基于地理信息的API,例如GEOADD——添加地理位置 。

通过Elasticache可以快速构建出一个Redis环境,包括支持shard的集群模式,如下图所示。

构建完成后,通过Elasticache提供的终端节点就可以访问cache了。

需要注意的是如果选择的Redis是集群模式,那么就得同步升级支持Redis集群模式的客户端SDK用以开发。因为Redis的集群提供的是分片功能,它会把不同的slots分布在不同的节点上,需要由客户端通过CRC16(Key)取模从而计算出数据在哪个节点上。目前可以支持redis集群模式的客户端有很多,比如本文用到的java的jedis以及nodejs的ioredis。

综合a,b两步的示例代码的StreamCacheProcessor.java如下(其余代码参考http://docs.amazonaws.cn/amazondynamodb/latest/developerguide/Streams.KCLAdapter.Walkthrough.CompleteProgram.html ):


import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import redis.clients.jedis.GeoCoordinate
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;

import com.amazonaws.services.dynamodbv2.streamsadapter.model.RecordAdapter;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessor;
import com.amazonaws.services.kinesis.clientlibrary.interfaces.IRecordProcessorCheckpointer;
import com.amazonaws.services.kinesis.clientlibrary.lib.worker.ShutdownReason;
import com.amazonaws.services.kinesis.model.Record;

public class StreamsCacheProcessor implements IRecordProcessor {

        private static Log LOG = LogFactory.getLog(StreamsCacheProcessor.class);

  private Integer checkpointCounter;

  private String clusterHost;
  private int port;
  private JedisCluster jedis;

public StreamsCacheProcessor(String clusterHost,int port) {
      this.clusterHost=clusterHost;
      this.port=port;
}

@Override
public void initialize(String shardId) {
      Set jedisClusterNode=new HashSet();
      jedisClusterNode.add(new HostAndPort(clusterHost,port));
      jedis=new JedisCluster(jedisClusterNode);
 checkp ointCounter = 0;
}
@Override
public void processRecords(List records, IRecordProcessorCheckpointer checkpointer) {
 for (Record record : records) {
  String data = new String(record.getData().array(), Charset.forName("UTF-8"));
  LOG.debug("Received the data as:"+data);
  if(record instanceof RecordAdapter)
     com.amazonaws.services.dynamodbv2.model.Record streamRecord = ((RecordAdapter) record).getInternalObject();
    //新增GPS数据更新到Elasticache中
    if(streamRecord.getEventName().equals("INSERT")){
    Map coordinateMap = new HashMap();
    double longitude = Double.parseDouble(streamRecord.getDynamodb().getNewImage().get("longitude").getN());
    double latitude = Double.parseDouble(streamRecord.getDynamodb().getNewImage().get("latitude").getN());
    String deviceId = streamRecord.getDynamodb().getNewImage().get("deviceId").getS();
    coordinateMap.put(deviceId, new GeoCoordinate(longitude, latitude));

    jedis.geoadd("bikes", coordinateMap);
    LOG.info("Updated "+deviceId+" GPS information as:"+longitude+","+latitude);
    }
  }
checkpointCounter += 1;
if(checkpointCounter % 10 == 0){ //checkpoint大小需根据实际需求调整
  try {
    checkpointer.checkpoint();
   }
  catch (Exception e) {
    e.printStackTrace();
   }
  }
 }
 
}

@Override
public void shutdown(IRecordProcessorCheckpointer checkpointer, ShutdownReason reason) 
{
  if(reason == ShutdownReason.TERMINATE) {
    try {
      checkpointer.checkpoint();
    }
    catch (Exception e) {
     e.printStackTrace();
    }
   }
  }
}

c)在完成地理信息的实时更新后, 可以基于Elasticache的数据利用GEORADIUS搜索周边的资源。使用nodejs示例代码如下:


var express = require('express');
var app = express();
var Redis = require('ioredis');

var cluster = new Redis.Cluster([{
    port:6379,
    host:'lab-cluster.4bh9j8.clustercfg.cnn1.cache.amazonaws.com.cn'
}]);

app.get('/bikes', function(req,res){
if(req.query['longitude'] && req.query['latitude']){
console.log ('longitude = %s,latitude = %s',req.query['longitude'],req.query['latitude']);
cluster.send_command('GEORADIUS',
[ 'bikes',req.query['longitude'],req.query['latitude'],2000,
'm',
'WITHDIST',
'WITHCOORD',
'COUNT',
10], (error, reply) =>{

if (error) {
res.status(500).send("无法获取附近车辆信息");
return;
}

var stations = reply.map( (r) =>{
return {
name: r[0],
distance: `${r[1]} m`,
coordinates: {
latitude:  Number(r[2][1]),
longitude: Number(r[2][0])
} }
});

res.status(200).json(stations);
});
}
});

var server = app.listen(8080,function(){
var host = server.address().address
var port = server.address().port
console.log("应用实例,访问地址为 http://%s:%s", host, port)
});

基于以上代码,服务端就可以返回最近的10个资源以及每个资源离当前位置的距离。例如:
请求
http://hostname or elb address/bikes?longitude=116&latitude=39.4
返回


[
 {
  "name": "48093ba0-f8f1-49f0-b312-285800341b08",
  "distance": "1117.8519 m",
   "coordinates": {
    "latitude": 39.40640623614937,
    "longitude": 116.01002186536789
 }
},
{
  "name": "950fb5df-c0ff-4a95-90ea-2f5f574c5796",
  "distance":"1305.5083 m",
  "coordinates": {
   "latitude": 39.40184880750488,
   "longitude": 116.01500004529953
  }
 },
……
]

d)通过封装http请求构建手机应用。

总结

Redis Geospatial功能可以让开发者更高效的搜索和计算位置信息的记录。同时,Amazon ElastiCache提供的托管Redis服务大大简化了对于Redis集群的维护工作,包括搭建,备份和迁移等工作。最后,自动扩展的Amazon DynamoDB则负责位置信息数据的持久化和检索,而它的流功能也使得数据能够快速实时的流转起来。

 

作者介绍

赵霏,AWS解决方案架构师。负责基于AWS的云计算方案架构咨询和设计,同时致力于AWS云服务在国内的应用和推广。他拥有超过13年IT行业从业经验,长期专注于企业IT云转型、物联网、移动互联网、Devops等领域,在大规模后台架构、分布式计算和自动化运维等方面有着广泛的设计和实践经验。

基于Amazon EC2 Container Service的持续集成/持续交付解决方案

基本概念

持续集成/持续交付

互联网软件的开发和发布,已经形成了一套标准流程,最重要的组成部分就是持续集成(Continuous integration,简称CI)和持续交付(Continuous delivery,简称CD)。

通过CI/CD构建自动化的代码交付管道,可以实现:

(1) 快速交付。通过CI/CD自动化软件发布的过程,可以更快速地发布新的功能,快速迭代反馈并让新功能更快提供给客户。

(2) 提高质量。自动化构建、测试和发布过程致使可以轻松测试每次代码更改并捕捉易于修复的小型漏洞,通过标准化发布过程运行每一项更改,从而保证应用程序或基础设施代码的质量。

(3) 可配置工作流。通过图形用户界面模拟软件发布过程的各个阶段。

容器

本文涉及到的另外一个概念是容器,相信大家都已经不再陌生,并且很多朋友已经在自己的环境中有实际的运行、部署基于容器的应用,这边简单的回顾下容器的几个重要优势:

一是因为容器可以跨平台,从而让程序猿可以享受到研发生产环境一致性的便利,也就是DevOps。在没有容器之前,常常一个应用做好了在笔记本上可以运转起来,在数据中心就运转不起来,因为操作系统版本不同、库版本不对;或者有的时候生产环境里出现了问题,在笔记本的开发环境中复制不出来。有了容器之后,这些问题就大大减少了。

其二,容器在虚拟机里面可以大幅度提升资源利用率。因为一旦把应用容器化,虚拟机资源就可以通过部署多个容器而得到充分利用,而不是每一个应用去申请一个虚拟机,造成资源的浪费。

Amazon ECS/ECR

Amazon EC2 Container Service (ECS)是一个托管的容器集群管理和调度服务, 可使您在 Amazon EC2 实例集群中轻松运行和管理支持 Docker 的应用程序。

使用 Amazon ECS 后,您不再需要安装、操作、扩展您自己的集群管理基础设施,可以根据资源需求和可用性要求在集群中安排支持 Docker 的应用程序。借助 Amazon ECS,您可以从一个容器扩展到覆盖数百个实例的数千个容器,而运行应用程序的方式并不会因此而变得复杂。您可以运行包括应用程序、批量作业和微服务在内的任何东西。Amazon ECS 将基础设施的一切复杂因素全部消除,让您能够集中精力设计、开发、运行容器化应用程序。

Amazon EC2 Container Registry (ECR) 是完全托管的 Docker 镜像仓库,可以让开发人员轻松存储、管理和部署 Docker 容器映像。Amazon ECR 集成在 Amazon EC2 Container Service (ECS) 中,从而简化产品工作流的开发。

本文主要介绍如何在AWS云上,使用Jenkins快速构建针对容器应用的持续集成/持续交付管道。

下图是整个CI/CD的流程图:

  1. 开发者commit/push新版本的软件工程到GitHub仓库
  2. GitHub的webhook触发Jenkins预先定义好的构建Pipeline
  3. Jenkins下载GitHub,并基于下载的DockerFile构建新的docker镜像并上传ECR仓库
  4. Jenkins调用aws cli更新ECS的task definition引用新的docker镜像并更新相关的service
  5. ECS基于新的service配置更新集群中的container

可以看到,整个代码的发布过程,开发人员只需要在本地开发新版本的软件并提交到GitHub,之后的一系列代码构建、部署等过程可以完全实现自动化,无需人为干预,大大提高了软件迭代的速度,为敏捷开发、微服务化提供支持,同时,可以根据需要添加测试步骤、代码审查、人工审批等步骤,构建一条更为强大灵活的代码交付流程。

 

  1. 准备工作启动一台EC2用于安装jenkins,本例使用Amazon Linux AMI,建议分配EIP(52.34.X.X),并且赋予合适role使其能够操作ECR和ECS
  2. 安装java jdk 1.8
  3. 安装并运行docker daemon,

    yum install docker –y

    chkconfig docker on

    service docker start

  4. 安装并运行jenkins

    wget -O /etc/yum.repos.d/jenkins.repo http://jenkins-ci.org/redhat/jenkins.reporpm –import http://pkg.jenkins-ci.org/redhat/jenkins-ci.org.keyyum install jenkins

    chkconfig jenkins on

    service jenkins start

  5. 将jenkins用户加入docker用户组,使其拥有docker cli执行权限
    usermod -a -G docker jenkins
  6. 安装git,jq
    yum install git jq –y

第一步 配置GitHub与Jenkins联动

生成GitHub token,用于jenkins访问GitHub

记录下生成的token字符串

为需要做CI/CD的GitHub创建hook,实现代码更新自动通知Jenkins,Payload URL设置Jenkins Server的地址,默认Jenkins监听8080端口

第二步 配置Jenkins的构建步骤

在浏览器中输入jenkins server的地址(52.34.X.X:8080),开始配置jenkins

初始化登入

安装推荐的插件

安装CloudBees Docker Build and Publish plugin,用于构建docker镜像并上传到ECR仓库

配置github插件,使得jenkins能够连接到github

点击Add GitHub Server增加GitHub服务器,并添加Credentials

在Secret中输入之前记录的GitHub Personal Access Token

点击Test connection测试连通性

创建jenkins项目

设置GitHub项目路径

源代码管理选择Git

勾选GitHub hook trigger for GITScm polling,使得github项目的更改自动触发构建行为

添加三个构建步骤

第一个步骤为通过aws cli登入ECR仓库

第二个步骤为通过CloudBees Docker Build and Publish plugin构建docker镜像并上传到ECR中

第三个步骤为通过脚本注册新的ECS task definition并更新service,实现服务部署,详细代码可以从如下链接获取

https://s3.cn-north-1.amazonaws.com.cn/junyublog/buildstep3.sh

第三步 测试

https://github.com/iwasnobody/ecs-cicd-jenkins

本例使用的GitHub工程对外提供一个apache PHP的静态页面

在本地PC上clone下工程兵修改工程下的src/index.php文件

commit并push到GitHub上

Jenkins界面可以看到自动触发了一次新的构建

查看新的镜像已经被上传到ECR

查看jenkins创建了新版本的task definition

查看service已经引用了新版本的task definition

访问应用,确认已经更新到最新版

 

作者介绍

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

 

 

 

新增:Amazon Kinesis Streams 服务器端加密

在这个智能家居、大数据、物联网设备、手机、社交网络、聊天机器人和游戏机的时代,流媒体数据场景无处不在。利用 Amazon Kinesis Streams,您可以构建自定义应用程序,以便从数千个流媒体数据源捕获、处理、分析和存储每小时数 TB 的数据。由于 Amazon Kinesis Streams 允许应用程序从同一个 Kinesis 流并发处理数据,因此您可以构建并行处理系统。例如,您可以将处理后的数据发送到 Amazon S3,使用 Amazon Redshift 执行复杂分析,甚至使用 AWS Lambda 构建强大的无服务器流媒体解决方案。

Kinesis Streams 为消费者提供了多种流媒体使用案例,现在我们通过为 Kinesis Streams 添加服务器端加密 (SSE) 支持,让服务能够更有效地保护您的传输中数据。借助 Kinesis Streams 的这一新功能,现在您可以提高数据安全性和/或满足任何法规和合规性要求,以符合组织的任何数据流式处理需求。
事实上,Kinesis Streams 现在是支付卡行业数据安全标准 (PCI DSS) 合规性计划涵盖的 AWS 服务之一。PCI DSS 是由主要金融机构成立的 PCI 安全标准委员会管理的专有信息安全标准。PCI DSS 合规性适用于存储、处理或传输持卡人数据和/或敏感的身份验证数据的所有实体,其中包括服务提供商。您可以使用 AWS Artifact 索要 PCI DSS 合规性证明和责任摘要。但是,关于 Kinesis Streams 合规性的好消息还不止这些。Kinesis Streams 现在还符合 AWS GovCloud 中的 FedRAMP 标准。FedRAMP 代表“联邦风险与授权管理项目”(Federal Risk and Authorization Management Program),是美国政府实施的一个项目,为云产品和云服务的安全评估、授权和持续监控提供了一种标准方法。您可以在这里了解有关 AWS 服务的 FedRAMP 合规性的更多信息。

现在您准备好了解关键点了吗?那就记住关键点,而不是纠结于细节。好吧,是有点陈词滥调,但这是我能想到的最好类比了。回到 Kinesis Streams 的 SSE 讨论中,让我来解释 Kinesis 的服务器端加密流程。使用 PutRecord 或 PutRecords API 放入 Kinesis Stream 中的每个数据记录和分区键均使用 AWS Key Management Service (KMS) 主密钥进行加密。通过 AWS Key Management Service (KMS) 主密钥,Kinesis Streams 使用 256 位高级加密标准 (AES-256 GCM 算法) 对传入数据进行加密。

为了对新的或现有流启用 Kinesis Streams 服务器端加密,您可以使用 Kinesis 管理控制台或利用某个可用的 AWS 软件开发工具包。此外,您还可以使用 AWS CloudTrail 服务审核流加密历史记录,验证 Kinesis Streams 控制台中特定流的加密状态,或者检查 PutRecord 或 GetRecord 事务是否已加密。

演练:Kinesis Streams 服务器端加密

我们来快速演练一下 Kinesis Streams 的服务器端加密。首先,我将访问 Amazon Kinesis 控制台并选择 Streams 控制台选项。

进入 Kinesis Streams 控制台后,我可以向我现有的某个 Kinesis 流添加服务器端加密,或者选择创建一个新的 Kinesis 流。对于此演练,我将选择快速创建一个新的 Kinesis 流,因此,我选择 Create Kinesis stream 按钮。

我将该流命名为 KinesisSSE-stream,然后为它分配一个分区。请记住,您的流的数据容量是根据为流指定的分区数量计算的。您可以使用控制台中的 Estimate the number of shards you’ll need 下拉菜单,或者通过在此处阅读更多有关计算方式的内容来估计流中的分区数。为了完成流的创建,现在我单击 Create Kinesis stream 按钮。

创建 KinesisSSE-stream 后,我将在控制面板中选择它,然后选择 Actions 下拉菜单并选择 Details 选项。


KinesisSSE-stream 的“Details”页面上,现在有一个 Server-side encryption 部分。在该部分中,我将选择 Edit 按钮。

现在,我可以通过选择 Enabled 单选按钮,使用 AWS KMS 主密钥为我的流启用服务器端加密。选择该选项后,我就可以选择将哪个 AWS KMS 主密钥用于 KinesisSSE-stream 中的数据加密。我可以选择 Kinesis 服务生成的 KMS 主密钥 (Default) aws/kinesis,也可以选择之前我自己生成的一个 KMS 主密钥。我将选择默认主密钥,然后只需单击 Save 按钮即可。


就是这样!从下面的屏幕截图中可以看出,只有大约 20 秒,服务器端加密即添加到我的 Kinesis 流中,现在传入到我的流中的任何数据都将被加密。需要注意的是,服务器端加密仅对在启用加密后传入的数据进行加密。启用服务器端加密之前 Kinesis 流中预先存在的数据将保持未加密状态。

总结

使用 AWS KMS 密钥的 Kinesis Streams 服务器端加密让您可以轻松地自动加密传入流中的流媒体数据。您可以使用 AWS 管理控制台或 AWS 软件开发工具包启动、停止或更新任何 Kinesis 流的服务器端加密。要了解有关 Kinesis 服务器端加密、AWS Key Management Service 或 Kinesis Streams 的更多信息,请查看 Amazon Kinesis 入门指南AWS Key Management Service 开发人员指南Amazon Kinesis 产品页面

祝流式处理顺利。

Tara

Lambda@Edge – 在边缘智能地处理 HTTP 请求

去年年底,我宣布推出预览版 Lambda@Edge,并谈论了如何使用它在靠近客户的位置 (低延迟) 智能地处理 HTTP 请求。申请并获得预览版访问权的开发人员一直在很好地使用它,并为我们提供了大量非常有用的反馈意见。在预览期间,我们添加了生成 HTTP 响应和支持 CloudWatch Logs 的功能,并根据反馈意见更新了路线图。

现已正式发布
今天我很高兴地宣布 Lambda@Edge 现已正式发布!您可以使用它来:

  • 检查 Cookie 并重写 URL 以执行 A/B 测试。
  • 根据 User-Agent 标头将特定对象发送给用户。
  • 通过在将请求传递到源之前查找特定标头来实现访问控制。
  • 添加、删除或修改标头以将用户指引到不同的缓存对象。
  • 生成新的 HTTP 响应。
  • 干净利落地支持旧 URL。
  • 修改或缩减标头或 URL 以提高缓存利用率。
  • 向其他 Internet 资源发出 HTTP 请求,并使用结果自定义响应。

Lambda@Edge 允许您创建基于 Web 的丰富且个性化的用户体验。由于它正迅速成为当今世界的常态,因此您不需要配置或管理任何服务器。您可以直接上传代码 (在Node.js 中编写的 Lambda 函数),并选择您为分发版创建的其中一个 CloudFront 行为以及所需的 CloudFront 事件:

在本例中,我的函数 (假设名为 EdgeFunc1) 将会运行,以响应对指定分发版中的 image/* 的源请求。如您所见,您可以运行代码以响应四种不同的 CloudFront 事件:

查看器请求 – 当事件从查看器 (HTTP 客户端,通常是 Web 浏览器或移动应用程序) 到达并且可以访问传入 HTTP 请求时,触发此事件。众所周知,每个 CloudFront 边缘站点都维护一个大型对象缓存,以便它可以高效地响应重复的请求。无论请求的对象是否已被缓存,都会触发此特定事件。

源请求 – 由于所请求的对象未缓存在边缘站点而导致边缘站点反过来要向源发出请求时,触发此事件。它可以访问将向源 (通常是 S3 存储桶或在 EC2 实例上运行的代码) 发出的请求。

源响应 – 在源返回对请求的响应后触发此事件。它能够访问来自源的响应。

查看器响应 – 在边缘站点返回对查看器的响应之前触发此事件。它可以访问此响应。

函数在全局范围内复制,并且请求将自动路由到最佳位置进行执行。您可以编写一次代码,然后无需您执行任何明确的操作,即可以较低延迟向世界各地的用户提供代码。

您的代码可以完全访问请求和响应,包括标头、Cookie、HTTP 方法 (GET、HEAD 等) 和 URI。它可以修改现有标头并插入新标头,但存在一些限制。

操作中的 Lambda@Edge
我们来创建一个为了响应查看器请求事件而运行的简单函数。我打开 Lambda 控制台并创建一个新函数。我选择 Node.js 6.10 运行时并搜索 cloudfront 蓝图:

我选择 cloudfront-response-generation 并配置一个触发器来调用该函数:

Lambda 控制台为我提供了有关我函数的运行环境的一些信息:

我照常输入函数的名称和描述:

蓝图包括一个可正常运行的函数。它生成一个“200”HTTP 响应和一个非常简单的主体:

我将该主体用作我自己代码的起点,以便从请求中提取一些有趣的值,并将它们显示在表中:

'use strict';
exports.handler = (event, context, callback) => {

    /* Set table row style */
    const rs = '"border-bottom:1px solid black;vertical-align:top;"';
    /* Get request */
    const request = event.Records[0].cf.request;
   
    /* Get values from request */ 
    const httpVersion = request.httpVersion;
    const clientIp    = request.clientIp;
    const method      = request.method;
    const uri         = request.uri;
    const headers     = request.headers;
    const host        = headers['host'][0].value;
    const agent       = headers['user-agent'][0].value;
    
    var sreq = JSON.stringify(event.Records[0].cf.request, null, ' ');
    sreq = sreq.replace(/\n/g, '<br/>');

    /* Generate body for response */
    const body = 
     '<html>\n'
     + '<head><title>Hello From Lambda@Edge</title></head>\n'
     + '<body>\n'
     + '<table style="border:1px solid black;background-color:#e0e0e0;border-collapse:collapse;" cellpadding=4 cellspacing=4>\n'
     + '<tr style=' + rs + '><td>Host</td><td>'        + host     + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Agent</td><td>'       + agent    + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Client IP</td><td>'   + clientIp + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Method</td><td>'      + method   + '</td></tr>\n'
     + '<tr style=' + rs + '><td>URI</td><td>'         + uri      + '</td></tr>\n'
     + '<tr style=' + rs + '><td>Raw Request</td><td>' + sreq     + '</td></tr>\n'
     + '</table>\n'
     + '</body>\n'
     + '</html>'

    /* Generate HTTP response */
    const response = {
        status: '200',
        statusDescription: 'HTTP OK',
        httpVersion: httpVersion,
        body: body,
        headers: {
            'vary':          [{key: 'Vary',          value: '*'}],
            'last-modified': [{key: 'Last-Modified', value:'2017-01-13'}]
        },
    };

    callback(null, response);
};

我配置我的处理程序,并请求创建一个具有 Basic Edge Lambda 权限的新 IAM 角色:

在下一页上,我确认我的设置 (就像我对常规 Lambda 函数所做的那样),然后单击 Create function

这将创建函数,将触发器附加到分发版,并且还将启动该函数的全局复制。在复制期间 (通常为 5 至 8 分钟),我的分发版状态将更改为 In Progress

复制完成后,状态立即改回 Deployed

然后,我访问分发版的根 (https://dogy9dy9kvj6w.cloudfront.net/),函数将运行,下面是我看到的样子:

尽管单击图片 (它链接到我的分发版的根) 以运行我的代码!

与往常一样,这是一个非常简单的例子,我相信您可以做得更好。以下是帮助您开始使用的几点提示:

站点管理 – 在维护或灾难恢复操作期间,您可以使用 Lambda@Edge 函数使整个动态网站脱机,并替换关键页面。

大量内容 – 您可以创建记分卡、天气预报或公共安全页面,并快速、经济高效地在边缘提供它们。

创建一些很酷的东西,并在评论或博客文章中分享,我会关注的。

需知信息
当您开始思考如何将 Lambda@Edge 用于您的应用程序时,请记住以下几点:

超时 – 处理源请求和源响应事件的函数必须在 3 秒钟内完成。处理查看器请求和查看器响应事件的函数必须在 1 秒钟内完成。

版本控制 – 在 Lambda 控制台中更新代码后,必须发布新版本并为其设置一组新的触发器,然后等待复制完成。您必须始终使用版本号来引用您的代码;$LATEST 和别名不适用。

标头 – 就像您在我的代码中看到的那样,HTTP 请求标头可作为数组访问。标头分为四类:

  • 可访问 – 可以读取、写入、删除或修改。
  • 受限 – 必须传递给源。
  • 只读 – 可以读取,但不能以任何方式修改。
  • 列入黑名单 – 代码看不到它,并且无法添加。

运行时环境 – 运行时环境为每个函数提供 128 MB 内存,但没有内置库,也无法访问 /tmp

Web 服务访问 – 处理源请求和源响应事件的函数必须在 3 秒钟内完成,并且可以通过 HTTP 访问 AWS API 并获取内容。这些请求总是与对原始请求或响应的请求同步进行。

函数复制 – 如前所述,您的函数将在全局范围内复制。复制副本在 Lambda 控制台的“其他”区域中可见:

CloudFront – 您已经了解的有关 CloudFront 和 CloudFront 行为的一切都与 Lambda@Edge 密切相关。您可以使用每个行为类别中的多个行为 (每个行为最多四个 Lambda@Edge 函数),自定义标头和 Cookie 转发等等。还可以在编辑行为时在事件和函数之间建立关联 (通过包含函数版本的 ARN):

现已推出
Lambda@Edge 现已推出,您可以立即开始使用。定价取决于您的函数被调用的次数及其运行时间的长短 (有关详细信息,请参阅 Lambda@Edge 定价页面)。

Jeff

使用 Amazon Redshift 中的查询监控规则管理查询工作负载

本文主要介绍了如何利用Amazon Redshift的WLM(工作负载管理)功能,监控数据仓库的查询性能,从而优化队列优先级并保障关键任务的执行。本文还列出了三个常见场景,给出了简单的配置过程。

众所周知,数据仓库的工作负载由于周期性、潜在高开销的数据探索查询以及SQL开发人员不同的技能水平等会出现比较大的性能变化。

为了在面临高度变化的工作负载下仍然能使Redshift集群获得较高的性能,Amazon Redshift工作负载管理(WLM)使您能够灵活地管理任务优先级和资源使用情况。通过配置WLM,短时间,快速运行的查询不会停留在需要较长时间运行的查询之后的队列中。 但尽管如此,某些查询有时可能会陷入不相称的资源分配,并影响系统中的其他查询。 这种查询通常被称为流氓查询或失控查询。

虽然WLM提供了一种限制内存使用并将超时查询移动到其他队列的方法,但多重精细控制依然很需要。您现在可以使用query monitoring rules查询监视规则为查询创建资源使用规则,监视查询的资源使用情况,然后在查询违反规则时执行操作。

工作负载管理并发和查询监控规则

在Amazon Redshift环境中,单个集群最多可以同时连接500个连接。 吞吐量(Throughput)通常表示为每小时的查询量以最大化性能,但像MySQL这样的行数据库使用并发连接数进行衡量。 在Amazon Redshift中,工作负载管理(WLM)可以最大限度地提高吞吐量,而不太考虑并发性。 WLM有两个主要部分:队列和并发。 队列允许您在用户组或查询组级别分配内存。 并发或内存是如何进一步细分和分配内存到一个查询。

例如,假设您有一个并发度为10的队列(100%内存分配)。这意味着每个查询最多可以获得10%的内存。 如果大部分查询需要20%的内存,那么这些查询将交换到磁盘,导致较低的吞吐量。 但是,如果将并发度降低到5,则每个查询分配20%的内存,并且最终结果是更高的吞吐量和更快的SQL客户端响应时间。 当从行数据库切换到基于列的数据库的时候,常见的错误认知是认为更高的并发性将产生更好的性能。

现在你了解了并发性,这里有更多关于查询监控规则的细节。 您可以基于资源使用情况定义规则,如果查询违反了该规则,则会执行相应的操作。 可以使用十二种不同的资源使用指标,例如查询使用CPU,查询执行时间,扫描行数,返回行数,嵌套循环连接等。

每个规则包括最多三个条件,或谓词,和一个动作。谓词由一个指标,比较条件(=、<、>),和一个值组成。如果所有的谓词满足任何规则,该规则的行动被触发。可能的规则操作包括日志记录、跳过任务和中止任务。

这样就可以在导致严重问题前捕获流氓或失控查询。该规则触发一个动作来释放队列,从而提高吞吐量和响应速度。

例如,对于专用于短时运行查询的队列,您可能会创建一个规则来中止超过60秒的查询。 要跟踪设计不当的查询,您可能会有另一个规则记录包含嵌套循环的查询。 在Amazon Redshift控制台中有预定义的规则模板让您使用。

使用场景

使用查询监控规则来执行查询级别的操作,从简单地记录查询到中止查询,以下所有采取的操作都记录在STL_WLM_RULE_ACTION表中:

  • 日志记录(log):记录信息并继续监视查询。
  • 跳出(hog):终止查询,并重新启动下一个匹配队列。 如果没有其他匹配队列,查询将被取消。
  • 中止(abort):中止违反规则的查询。

以下三个示例场景显示如何使用查询监视规则。

场景1:如何管理您临时查询队列中的未优化查询?

连接两个大表的失控查询可能返回十亿行或更多行。 您可以通过创建规则来中止返回超过十亿行的任何查询来保护您的临时队列。 在逻辑上如下所示:

IF return_row_count > 1B rows then ABORT.

在以下截图中,任何返回BI_USER组中超过十亿行的查询都将中止。

场景2:如何管理和控制未调优的CPU密集型查询?

偶尔引起CPU飙升的查询不一定有问题。 然而,持续的高CPU使用率可能会导致其他并发运行查询的延迟时间增加。 例如,在较长时间内使用高百分比CPU的未调优查询可能是由于不正确的嵌套连接引起的。

您可以通过创建规则来中止超过10分钟使用80%或更多CPU的任何查询来提高群集吞吐量和响应能力。 在逻辑上如下所示:

IF cpu_usage > 80% AND query_exec_time > 10m then ABORT

以下屏幕截图显示,任何使用超过80%CPU超过10分钟的查询都将中止。

您可以通过使用80%CPU记录查询超过5分钟进一步扩展此规则,并终止使用了80%CPU超过10分钟的查询。 在逻辑上如下所示:

IF cpu_usage > 80% AND query_exec_time > 5m then LOG and  IF cpu_usage > 80% AND query_exec_time > 10m then ABORT

以下屏幕截图显示,系统将记录使用了80%CPU并运行5分钟以上的查询,并且中止使用了80%CPU并运行超过10分钟的查询。

场景3:如何监视和记录没有任何进展的查询?

例如,在混合工作负载环境中,ETL作业可能会将S3中的大量数据从大量的数据传输到Amazon Redshift中。 在数据摄取过程中,您可能会发现一个COPY命令被卡在队列中而没有进行任何进展。 这样的查询可能会增加数据吞吐延迟并影响业务SLA。

您可以通过创建跟踪和记录查询的规则来查找此类查询。 创建一个规则来查找具有低CPU利用率和过长执行时间的查询,例如,使用1%CPU记录查询超过10分钟的规则。 在逻辑上如下所示:

IF cpu_usage < 1% AND query_exec_time > 10m then LOG

以下屏幕截图显示,系统将记录使用1%CPU并运行10分钟以上的查询。

总结

Amazon Redshift是一个功能强大,全托管的数据仓库,可以在云计算框架中显著提升性能并降低成本。 但是,查询集群资源(流氓查询)可能会影响您的体验。

在这篇文章中,我们讨论了如何使用查询监视规则帮助过滤和中止不符合要求的任务。 这反过来也可以帮助您在支持混合工作负载时顺利地进行业务操作,以最大限度地提高集群性能和吞吐量。

如果您有任何问题或建议,请在下面留言。


关于作者

Gaurav Saxena是Amazon Redshift查询处理团队的软件工程师。 他负责Amazon Redshift工作负载管理和性能改进的几个方面。 在业余时间,他喜欢在他的PlayStation上玩游戏。

Suresh Akena是AWS专业服务的高级大数据/ IT转型架构师。 他与企业客户合作,为大型数据战略提供领导,包括迁移到AWS平台,大数据和分析项目,并帮助他们在使用AWS时优化和改进数据驱动应用的上市时间。 在业余时间,他喜欢和他8岁和3岁的女儿一起玩,看电影。

译者:

屈铭,AWS中国专业服务团队大数据咨询顾问

曾供职于亚马逊电商和澳大利亚智能交通研究机构,拥有多年电商平台和智慧供应链的数据分析经验。现任职于AWS中国专业服务团队,主要为客户提供云上大数据平台设计,数据仓库解决方案和优化,数据建模等咨询服务。

如何在Amazon EC2 Container Service上实现服务高可用的自动伸缩

1. 简介

Amazon EC2 Container Service (ECS)是Amazon提供的一项Docker容器管理服务,可以让您轻松构建、运行、管理您的Docker容器服务。ECS Service是ECS的重要组件,它可以在集群中运行指定数量的任务,当某个任务不可用时,它会重新启动新的任务,维持住任务的指定数量。这个特性从一定程度上保证了服务的可用性,但当面对突发流量,ECS本身并不能动态地进行任务数量的扩展,当流量较少时,ECS也无法动态地进行任务数量的缩减。为解决此问题,可以使用Auto Scaling和Amazon CloudWatch等实现服务的自动伸缩,保证服务高可用。本文将介绍一个运用Auto Scaling在ECS中实现服务高可用的方案,并通过对方案构建过程的剖析,让您对高可用、自动伸缩的服务架构有更进一步的了解。

2. 方案构建

2.1 整体结构图

本方案使用AWS CloudFormation模板声明整个资源堆栈所需资源和相关配置,并实现自动化构建。关于AWS CloudFormation相关知识,请通过以下链接了解:CloudFormation入门

从上面这个结构图可以看出以下主要组成部分:

  1. 初始由两个跨AZ的Container Instance组成的ESC Cluster。
  2. 初始ESC Service中有两个task。
  3. Service Auto Scaling与CloudWatch结合,当Service维度的 CPUUtilization与Threshold满足设定的触发条件时,触发CloudWatch Alarm,CloudWatch Alarm根据设定的Scaling Policy进行Task数量伸缩。
  4. Auto Scaling Group与CloudWatch结合,当Cluster维度的CPUReservation与Threshold满足设定的触发条件时,触发CloudWatch Alarm,CloudWatch Alarm根据设定的Scaling Policy进行实例数量的伸缩。

2.2 准备工作

(1)准备好CloudFormation模板脚本

请从这里(点我)下载用于构建整个资源堆栈的模板脚本文件。

或者通过点击下面按钮来运行堆栈:

(2)准备好Lambda Function

本方案在Auto Scaling Group中的实例关闭时,会调用一个实现自动切换Draining状态的Lambda Function。那么在构建整个堆栈之前,需要先准备好这个Lambda Function。这里我们使用Python编写了这个脚本auto_drain.py来实现此功能。您可以了载包含了这个脚本的压缩包(点我)

下载完成后,请将它上传到您账户上同个Region的S3 Bucket中。记住这个S3 Bucket Name,在构建堆栈时,将它传给Lambda Function S3 Bucket 这个参数。

2.3 构建过程

(1)构建堆栈

打开您的CloudFormation控制面板,使用模板脚本创建堆栈,创建成功后,打开堆栈“输出”栏,可以看到ALBDNS输出值如下所示:

将这个URL复制到浏览器访问,看到It Works字样,表示堆栈构建成功。

(2)测试service auto scaling

我们在模板中定义了基于ECS Service维度的CPU使用率(CPU Utilization)指标进行自动伸缩service的task个数。初始状态下,我们设置Service的Desired Count(参见模板中的ServiceDesiredCount参数)为2,查看ECS Service Management Console,选中左边栏“集群”,再选中as-demo-cluster,点击“ECS Service实例”。可以看到现在有两个容器实例,每个容器实例运行一个任务,总共有两个任务。

接下来我们利用Apache ab压测工具进行测试,向我们的ALB发送30000条请求,并发为每秒1000。相应的ab命令如下:

$ ab -n 300000 -c 1000 http://as-in-publi-xxxxxxxxxxxxxx/

等待ab请求运行完成后,查看此时ECS Service中实例的状态,如下图所示,可以发现在某台实例上新增加了一个task,总的task数量变成了3个,也就是成功触发了Service Scale out。

等待几分钟,再次查看ECS Service中实例的状态,可以看到task总数量又变成了2个,即当Service CPUUtilization小于我们设的的Threshold一定时间(默认设置的是5分钟)后,自动触发了Service Scale in。

(3)测试cluster auto scaling

同样使用Apache ab进行压测,不过为了达到效果,需要加大请求数量和并发数量。

使用以下ab命令(当然您可以根据自己实际情况进行调整)

$ ab -n 1000000 -c 2000 http://as-in-publi-xxxxxxxxxxxxxx/

如果遇到 socket:Too many open files异常。可以使用以下命令修改linux系统最大打开文件数

$ ulimit -n 2048

等待压测结束后,查看ECS中实例的状态(因为实例的启动与容器的启动不同,耗费时间比较长,可能需要等待几分钟才能看到效果),如下图所示,Cluster自动增加了一台实例。

等待一段时间后,再次查看ECS Service中实例的状态,如下图所示,Cluster自动减少了一台实例。

(4)测试自动切换实例Draining状态

我们使用Lambda Function实现在Scale in时自动切换实例到Draining状态(具体讲解见后面章节)。

当前集群中总的实例数量为2,我们通过修改Auto Scaling Group的desired count(在EC2管理窗中界面选中我们模板创建的Auto Scaling Group,修改desired count),将它设为1,这样Auto Scaling会自动关闭一台实例,看看运行的任务数量有什么变化。

可以看到被停掉的实例状态自动切换成了DRAINING,任务也被移到了另一台实例上。

3. 基于两个维度的自动伸缩

CloudWatch监听ECS中的四个指标,分别是CPUUtilization、MemoryUtilization、CPUReservation和MemoryReservation。这四个指标可以分为两大类,前两者是指资源使用率,后两者是指资源占有率。本方案通过集群和服务这两个维度,分别对占用率和使用率进行监控,定制不同的伸缩策略,从而实现自动伸缩。

3.1 什么是资源使用率和资源占有率

资源使用率(CPUUtilization和MemoryUtilization)是指ECS集群(或服务)中所有运行着的任务实际使用资源的总和除以EC2 Container Service集群(或服务)总的资源得到的数值,表现的是集群或服务中资源的使用情况

资源占有率(CPUReservation和MemoryReservation)是指ECS集群(或服务)中所有运行着的任务申请占有的资源的总和除以EC2 Container Service 集群(或服务)总的资源得到的数值,表现的是集群或服务中资源的剩余情况

3.2 基于服务中CPU使用率实现服务自动伸缩

为了简单起见,本方案只监控CPU的使用率和占有率。

当服务面对越来越大的流量时,服务中CPU使用率快速上升,这时可以根据这个CPU使用率来动态地增加任务数量,以达到分担负载的作用。而不是根据CPU占有率来进行扩展,因为无论流量如何变大,CPU占用率是与服务中任务数量相关的,任务数量不同,CPU占用率也不会改变。

在本方案中,具体的构建过如下:

(1)首先,我们声明了对服务中task desired count作为Auto Scaling调整的对象ScalableTarget,ResourceId指向我们创建的ECS Service代码如下:

ServiceScalingTarget:

    Type: AWS::ApplicationAutoScaling::ScalableTarget

    DependsOn: DemoService

    Properties:

      MaxCapacity: !Ref ServiceMaxSize

      MinCapacity: !Ref ServiceMinSize

      ResourceId: !Join [”, [service/, !Ref ‘ECSCluster’, /, !GetAtt [DemoService, Name]]]

      RoleARN: !GetAtt [ServiceAutoscalingRole, Arn]

      ScalableDimension: ecs:service:DesiredCount

      ServiceNamespace: ecs

(2)我们声明了两个Scaling Policy,分别针对Scale Out和Scale In两种情况,当Scale Out时修改ServiceScalingTarget中的Service Desired Count,增加1个task;当Scale In时修改ServiceScalingTarget中的Service Desired Count,减少1个task。主要代码如下:

ServiceScaleOutPolicy:

    Type: AWS::ApplicationAutoScaling::ScalingPolicy

    Properties:

      PolicyType: StepScaling

      PolicyName: StepOutPolicy

      ScalingTargetId: !Ref ‘ServiceScalingTarget’

      StepScalingPolicyConfiguration:

        AdjustmentType: ChangeInCapacity

        MetricAggregationType: Average

        StepAdjustments:

        – MetricIntervalLowerBound: “0”

          ScalingAdjustment: “1”

 

  ServiceScaleInPolicy:

    Type: AWS::ApplicationAutoScaling::ScalingPolicy

    Properties:

      PolicyType: StepScaling

      PolicyName: StepInPolicy

      ScalingTargetId: !Ref ‘ServiceScalingTarget’

      StepScalingPolicyConfiguration:

        AdjustmentType: ChangeInCapacity

        MetricAggregationType: Average

(3)最后,我们声明两个CloudWatch Alarm,分别在Service的CPUUtilization满足Threshold关系时,触发Scale Out和Scale In的Alarm。

ServiceCPUUtilizationScaleOutAlarm:

    Type: AWS::CloudWatch::Alarm

    Properties:

      EvaluationPeriods: !Ref ServiceCPUUtilizationScaleOutMinutes

      Statistic: Average

      Threshold: !Ref ServiceCPUUtilizationScaleOutThreshold

      AlarmDescription: Alarm if Service CPUUtilization greater then threshold.

      Period: ’60’

      AlarmActions: [!Ref ‘ServiceScaleOutPolicy’]

      Namespace: AWS/ECS

      Dimensions:

      – Name: ClusterName

        Value: !Ref ECSCluster

      – Name: ServiceName

        Value: !GetAtt [DemoService, Name]

      ComparisonOperator: GreaterThanThreshold

      MetricName: CPUUtilization

 

  ServiceCPUUtilizationScaleInAlarm:

    Type: AWS::CloudWatch::Alarm

    Properties:

      EvaluationPeriods: !Ref ServiceCPUUtilizationScaleInMinutes

      Statistic: Average

      Threshold: !Ref ServiceCPUUtilizationScaleInThreshold

      AlarmDescription: Alarm if Service CPUUtilization less then threshold.

      Period: ’60’

      AlarmActions: [!Ref ‘ServiceScaleInPolicy’]

      Namespace: AWS/ECS

      Dimensions:

      – Name: ClusterName

        Value: !Ref ECSCluster

      – Name: ServiceName

        Value: !GetAtt [DemoService, Name]

      ComparisonOperator: LessThanThreshold

      MetricName: CPUUtilization

3.3 基于集群中CPU占有率实现集群自动伸缩

当服务中任务的数量随着流量增大也不断增加时,集群中CPU的占有率也会上升,当CPU占有率不断上升,表示可用的资源已不多了,此时需要扩展新的实例,增加整个集群总的资源。

在本方案中,具体的构建过如下:

(1)首先声明两个Scaling Policy,当集群Scale Out时修改集群中的实例数量,增加1个实例;当Scale In时修改集群中的实例数量,减少1个task。主要代码如下:

ClusterScaleOutPolicy:

    Type: “AWS::AutoScaling::ScalingPolicy”

    Properties:

      AdjustmentType: “ChangeInCapacity”

      AutoScalingGroupName:

        Ref: “ECSAutoScalingGroup”

      PolicyType: “StepScaling”

      MetricAggregationType: “Average”

      StepAdjustments:

        –

          MetricIntervalLowerBound: “0”

          ScalingAdjustment: “1”

 

  ClusterScaleInPolicy:

    Type: “AWS::AutoScaling::ScalingPolicy”

    Properties:

      AdjustmentType: “ChangeInCapacity”

      AutoScalingGroupName:

        Ref: “ECSAutoScalingGroup”

      PolicyType: “StepScaling”

      MetricAggregationType: “Average”

      StepAdjustments:

        –

          MetricIntervalUpperBound: “0”

          ScalingAdjustment: “-1”

(2)我们声明两个CloudWatch Alarm,分别监听的指标是集群的CPUReservation,当CPUReservation满足Threshold设定的关系时,触发Scale Out和Scale In的Alarm。主要代码如下:

ClusterCPUReservationScaleOutAlarm:

    Type: AWS::CloudWatch::Alarm

    Properties:

      EvaluationPeriods: !Ref ClusterCPUReservationScaleOutMinutes

      Statistic: Average

      Threshold: !Ref ClusterCPUReservationScaleOutThreshold

      AlarmDescription: Alarm if Service CPUUtilization greater then threshold.

      Period: ’60’

      AlarmActions: [!Ref ‘ClusterScaleOutPolicy’]

      Namespace: AWS/ECS

      Dimensions:

      – Name: ClusterName

        Value: !Ref ECSCluster

      ComparisonOperator: GreaterThanThreshold

      MetricName: CPUReservation

 

  ClusterCPUReservationScaleInAlarm:

    Type: AWS::CloudWatch::Alarm

    Properties:

      EvaluationPeriods: !Ref ClusterCPUReservationScaleInMinutes

      Statistic: Average

      Threshold: !Ref ClusterCPUReservationScaleInThreshold

      AlarmDescription: Alarm if Service CPUUtilization less then threshold.

      Period: ’60’

      AlarmActions: [!Ref ‘ClusterScaleInPolicy’]

      Namespace: AWS/ECS

      Dimensions:

      – Name: ClusterName

        Value: !Ref ECSCluster

      ComparisonOperator: LessThanThreshold

      MetricName: CPUReservation

4. Cluster scale in时切换实例到Draining保证服务容量

我们的ECS集群并不是一创建好就一成不变的,特别是当集群空闲的时候,触发了EC2的Auto Scaling Group的缩减实例之后. 有些时候我们需要关闭其中某个实例,无论是由于系统升级、安装软件、还是为了节省运行成本。那么在关闭实例时,实例上正在运行的task怎么办呢?把它们全杀掉?那可不行,那样就等于减小了整个ECS服务的容量和负载能力,对性能有很大影响。另外就是当直接关闭EC2机器的时候杀死其中的task, 可能造成运行在上面的task处理的请求异常终止,而影响到我们所提供服务的SLA, 这种情况下,将实例切换到Draining状态就是一种解决方案。

当您要关闭集群中某个实例时,运行在这个实例上的task会被停止,然后ECS会在集群中另一个(或多个)实例上运行这些被停掉的task,从而保证整个ECS Service 的task数量保持不变,保证了整个服务容量和负载能力不受影响。同时,当实例被设置成为Draining状态之后,ECS将会自动平滑关闭EC2宿主机中的task资源,利用定义task模版时候所定义的放置规则,将其移动到其他拥有剩余资源的集群中的宿主机上. 关于实例Draining的更多信息,请参见文档.

在本方案中,我们使用AWS SNS 和Lambda实现了实例在集群的Auto Scaling Group Scale in时关联Auto Scaling的LifecycleHook,实现被关闭前调用Lambda Function自动切换Draining状态,将task转移到集群中其它实例上。

我们定义了一个Lambda Function运行Python脚本auto_drain.py,这个脚本主要完成两个工作:

(1)判断传到Lambda Function中的event是否包含autoscaling:EC2_INSTANCE_TERMINATING,是的话就将这个实例状态切换成Draining,这样ECS就会自动停止此实例上所有运行中的task,并在集群中别的实例启动同样数量的task。

切换实例到Draining状态的代码:

containerStatus = containerInstances[‘status’]

            if containerStatus == ‘DRAINING’:

                tmpMsgAppend = {“containerInstanceId”: containerInstanceId}

            else:

                # Make ECS API call to set the container status to DRAINING

                ecsResponse = ecsClient.update_container_instances_state(cluster=clusterName,

containerInstances=[containerInstanceId],status=’DRAINING’)

(2)判断实例上还有没有task正在运行中。如果仍然有运行中的task,则过发送一条SNS通知重新触如这个Lambda Function。如果实例上所有task都已经被停止,则解开LifecyleHook,使这个实例被停掉。

根据task运行情况做不同处理的代码:

# If tasks are still running…

            if tasksRunning == 1:

                response = snsClient.list_subscriptions()

                for key in response[‘Subscriptions’]:

                    if TopicArn == key[‘TopicArn’] and key[‘Protocol’] == ‘lambda’:

                        snsClient.publish(

                            TopicArn= key[‘TopicArn’],

                            Message=json.dumps(message),

                            Subject=’Publishing SNS message to invoke lambda again..’

                        )

            # If tasks are NOT running…

            elif tasksRunning == 0:

                completeHook = 1

                try:

                    response = asgClient.complete_lifecycle_action(

                        LifecycleHookName=lifecycleHookName,

                        AutoScalingGroupName=asgGroupName,

                        LifecycleActionResult=’CONTINUE’,

                        InstanceId=Ec2InstanceId)

                    logger.info(“Response received from complete_lifecycle_action %s”,response)

                    logger.info(“Completedlifecycle hook action”)

                except Exception, e:

整个Python脚本代码,可以解压您在准备工作中下载的auto_drain.zip,查看auto_drain.py脚本文件。

5. 总结

本文主要介绍了如何在ECS 中使用 Auto Scaling、Amazon CloudWatch和其他常用AWS服务实现高可用,可伸缩的服务架构,并结合Lambda Function和AWS SNS 做到当集群中由于Auto Scaling缩减关闭某个实例时,自动切换实例状态到Draining,将task转移到其他实例上,保证服务容量以及回收实例资源时候的task的平滑过渡。通过本套方案,可以让ECS服务在可用性和伸缩性上得到保证,从而为您解决实际业务场景中相类的问题提供一定的帮助和思路。您也可以在本方案基础上,结合实际业务需求,定制自己的Scaling Policy、监控指标和监控维度,实现更符合您需求的解决方案。

作者介绍

李磊,AWS解决方案架构师,负责基于AWS的云计算方案的架构设计,同时致力于AWS云服务在国内和全球的应用和推广。在大规模并发后台架构,电商系统,社交网络平台、互联网领域应用,DevOps以及Serverless无服务器架构等领域有着广泛的设计与实践经验。在加入AWS之前超过十年的开发和架构设计经验, 带领团队攻克各种技术挑战,总是希望站在技术的最前沿。

使用Amazon EC2 Systems Manager取代堡垒机

通常情况下,堡垒机(也称为“跳转机”)是在系统中访问私有主机的一个最佳实践。例如,您的系统可能包含一个不希望被公开访问的应用服务器,当需要在这台服务器上进行产品的更新或系统补丁程序的管理时,您通常会登录到堡垒机,然后从那里访问(即“跳转到”)应用服务器。

本文将向您介绍使用Amazon EC2 Systems Manager替换您的堡垒机,实现在服务器上运行命令的同时缩小系统攻击平面并获取更好的可见性。

堡垒机方案

堡垒机最好仅向特定的IP地址范围开放,这个地址范围通常可设定为您单位的企业网络。使用堡垒机的好处是,任何对内部服务器的访问都被限定到一种方式:通过单个或一组堡垒机。为了获取进一步的隔离,堡垒机通常被安放在单独的VPC中。

这种设计方案如下图所示:

应用服务器运行在与管理VPC对等相连的一个VPC的私有子网中。 应用服务器设定了一个安全组规则,其仅允许来自管理VPC中堡垒机所在安全组的22端口访问(本文的示例仅针对端口22和SSH访问。Windows用户可将其相应的替换为端口3389和RDP访问)。同样,堡垒机也设定了一个安全组规则,其仅允许来自公司网络IP地址空间的22端口访问。

由于应用服务器运行在私有子网中,所以它只能通过VPC公共子网中的NAT网关来建立出站公网连接。

假设您希望查看应用程序服务器的网络接口,您需要执行以下步骤:

  1. 将应用服务器的私钥安装在堡垒机上。
  2. 从可信网络(如公司网络)发起,在堡垒机上建立SSH会话。
  3. 从堡垒机发起,建立SSH会话到应用服务器。
  4. 运行“ifconfig”命令。
  5. 如需保存命令的结果,您可以复制和粘贴命令的输出,或者将输出重定向到文件。

这个方案中的安全措施限制了对应用服务器和堡垒机的访问,但堡垒机模式存在一些缺点:

  • 像任何基础设施服务器一样,堡垒机必须进行管理和维护。
  • 运行时会产生成本。
  • 允许堡垒机访问的每个安全组都需要设置一个安全组入口规则,即SSH(用于Linux)的22端口或RDP的3389端口(用于Windows服务器)。
  • 堡垒机和应用服务器的RSA密钥需要进行管理、保护和轮换。
  • SSH操作无缺省日志记录。

替代方案

Systems Manager允许您在被管理的服务器上远程执行命令,而不使用堡垒机(此功能称为EC2 Run Command)。安装于服务器上的代理程序会自动轮询Systems Manager以确定是否有命令在等待执行。

此方案具备以下优点:

  • 使用AWS托管服务,这意味着Systems Manager组件具备高可用性。
  • Systems Manager通过IAM策略设定是否允许用户或角色远程执行命令。
  • Systems Manager代理程序需要IAM角色和策略才能允许它们调用Systems Manager服务。
  • Systems Manager不可更改的记录每个执行的命令,从而提供了可审计的命令历史,包括:

 o   执行命令的内容

 o   执行命令的主体

 o   执行命令的时间

 o   命令的输出摘要

  • 当AWS CloudTrail在当前区域中启用时,CloudTrail会记录每个事件,并写入Amazon CloudWatch Logs。
  • 使用CloudTrail和CloudWatch规则,您可以将Systems Manager事件用作自动响应的触发器,例如Amazon SNS通知或AWS Lambda函数调用。
  • Systems Manager可以选择将命令历史记录和每个命令的全部输出存储在Amazon S3中。
  • Systems Manager可以选择发布消息到SNS主题,从而在命令开始执行时和完成时通知订阅者。
  • Systems Manager是基于代理程序的,这意味着它不限于管理Amazon EC2实例。它还可以管理运行在自有数据中心或者另一个云服务提供商的非AWS服务器上。
  • 无需管理SSH密钥。

Systems Manager本身没有成本,但是您需要支付Systems Manager所管理的资源(如EC2实例、SNS消息和S3存储等)的成本。

Systems Manager代理程序

Systems Manager代理是运行在被管理的服务器上的开源可执行程序,支持Linux和Windows操作系统(请参见支持的详细操作系统版本列表)。

Systems Manager代理通过IAM角色与Systems Manager进行通信。这个角色必须具有足够权限与Systems Manager及其辅助服务进行交互。IAM已经提供了一个名为AmazonEC2RoleforSSM的托管策略,为您预先定义了这些权限:允许代理程序获取并回复Systems Manager消息、发布CloudWatch指标、并将日志文件写入S3。

Systems Manager代理通过HTTPS协议与Systems Manager通信,这意味着服务器和Systems Manager服务之间的通信是被加密的,而且安全组也不需要特殊的出口规则。

理想情况下,您可以在实例引导时安装代理程序。您可以将其安装在已经运行的EC2实例或非AWS的服务器上。例如,您可以通过EC2实例的user data在系统引导时使用yum安装Systems Manager代理:

#!/bin/bash
cd /tmp
curl https://amazon-ssm-region.s3.amazonaws.com/latest/linux_amd64/amazon-ssm-agent.rpm -o amazon-ssm-agent.rpm
yum install -y amazon-ssm-agent.rpm

关于安装Systems Manager代理的更多信息,请参考以下链接:Installing SSM Agent.

优化后的系统架构

现在您已了解了Systems Manager的众多优点,下面我们看一下如何据此优化您的系统架构。

如下图所示,Systems Manager消除了系统对堡垒机的需求,从而简化了系统架构。 用户不再直接与应用服务器进行交互,Systems Manager成为了执行命令的代理。

在此设计中,Systems Manager代理程序驻留在应用服务器上,使其成为 “受管实例”——这意味着它可以从Systems Manager接收命令。 要执行一个命令,只需在Systems Manager中创建一个命令请求,并派发到该实例或一组实例上运行。

创建命令请求时,您可以选择一个用于存储命令执行结果的S3存储桶,和用于发送执行通知的SNS主题。 您还可以创建通过Systems Manager事件触发的CloudWatch事件。

操作演示

您可以使用下面的【Launch Stack】链接来启动一个AWS CloudFormation栈,后者将创建上述架构,然后运行命令并在Systems Manager控制台中查看结果。最后,您可以销毁整个CloudFormation栈。

注:该链接会在北弗吉尼亚州区域启动相关资源,并产生相应的成本。SSM 只在如下区域可应用。

1.选择Launch Stack打开CloudFormation控制台(请确保您已经使用您的AWS账户登录)并启动CloudFormation模板。选择Next。

2.对于Stack name,请使用缺省的ssm-demo或输入自定义名称。 对于通知电子邮件,请输入您的电子邮件地址,以在Systems Manager执行操作时通知您。CloudFormation模板启动后,您将收到一个订阅确认邮件,您必须确认才能收到后续的通知。选择Next 。

3.在Review 屏幕上,确认允许CloudFormation创建IAM角色。选择Create

4.要查看栈的创建进度,请选择Refresh,然后选择ssm-demo栈以查看启动过程。

5.当栈成功启动时,状态会从CREATE_IN_PROGRESS更改为CREATE_COMPLETE 。 要查看本文后面需要使用的输出值,请选择Outputs.

这个CloudFormation模板创建的资源将在本文的后面中使用。您应该看到下表中列出的值。

Output名称 示例值 用途
S3Bucket S3Bucket ssm-output-history -account-id – 区域 S3桶用于存储命令的输出
ApplicationHostInstance id-instance-id 用于执行命令的EC2实例
SnsTopicArn arn:aws:sns: region : account-id :SsmNotificationTopic 用于发送命令执行通知的SNS主题
RoleArn arn:aws:iam :: account-id :role / SsmNotificationRole- region 用于向SNS发送通知的角色

Systems Manager演示

在本节中,您将使用Systems Manager发出与上文堡垒机方案中相同的“ifconfig”命令来查看应用服务器的网络接口配置。

首先,您需要指定要在其上执行命令的实例。在Systems Manager执行命令之后,它会报告执行状态,显示输出,并将输出的历史存储在S3桶中,并向您发送一封电子邮件通知。

Systems Manager代理以root权限运行,管理员在授予用户执行Systems Manager命令的权限时应注意这一点。

要使用Systems Manager,请按照下列步骤操作:

1.登录到您的AWS帐户并跳转到EC2控制台

2.在左侧导航窗格的SYSTEMS MANAGER 下 ,选择Managed Instances

注意:如果您看到“欢迎使用EC2系统管理器 ”的界面,而非受管实例列表,请确保您的系统已满足以下条件:

  • 您正在查看的EC2控制台与您启动CloudFormation模板的区域相同
  • 至少有一个实例正在运行Systems Manager代理
  • 运行Systems Manager代理的实例具有关联的实例角色,并具有允许Systems Manager操作的策略
  • 实例已完成初始化(通常只有几分钟)

3.选择本文前面CloudFormation模板创建的实例,然后选择 Run a command.4.在Run a command屏幕上,向下滚动命令文档列表并选择AWS-RunShellScript,平台类型是Linux。如果您使用Windows服务器,请选择AWS-RunPowerShellScript。本文不会涉及命令文档列表中的其他命令,但您可以在工作中根据实际用途选择使用其他命令文档。
5.确保选择了前文CloudFormation模板创建的EC2实例。
6.在Commands输入框中输入以下命令:

ifconfig

这会发出一个命令来查看主机的网络接口配置。 您可以在这里输入任何有效的bash命令(对于Linux),例如:安装补丁程序,执行应用程序,或更新配置文件等。

7.输入以下值,然后选择Run

  • 对于S3存储桶,请输入上文CloudFormation模板创建的存储桶名称(将S3前缀值留空)。
  • 对于角色ARN,输入从CloudFormation模板创建的ARN。
  • 对于SNS主题ARN,输入从CloudFormation模板创建的ARN。
  • 对于Notify me on,选择全部。
  • 对于Notify me for,选择命令。
  • 所有其他字段采用默认值。

8.在你启动这个要求之后, 选择 View Managed Instances.

9.如需查看命令历史,在 SYSTEMS MANAGER SERVICES,选择 Run Command.

10.要在Systems Manager控制台上查看命令的简要输出,请选择 Output, 然后再选择View Output.

对于较长的输出,您可以在先前指定的S3存储桶中查看完整的命令输出。此外,您应该在几分钟内收到一封电子邮件,通知您Systems Manager的命令已执行完成。

注意:如果您没有收到电子邮件通知,请确保您在启动CloudFormation栈时收到的SNS主题确认邮件中,点击链接确认接收SNS通知。

截至目前,您已经在应用服务器上成功执行了一个命令,而没有使用堡垒机或直接访问服务器。您可以审核命令的执行,并使用现有的IAM框架来控制对服务器的访问,而无需管理专用的RSA密钥。有关使用IAM框架控制Systems Manager的权限的详细信息,请参阅Configuring Access to Systems Manager

要清理CloudFormation模板创建的资源,请确保删除CloudFormation栈。如果您使用Systems Manager时将执行日志存储在存储桶中,您在删除栈之前必须手动删除存储桶及其内容。

下一步

大多数AWS服务都包含强大的API让您实现服务交互的自动化。使用控制台是学习和理解远程命令执行过程的好方法,但自动化这一能力提供了一种可靠和规范的方式来管理用户对此功能的使用。

例如,许多组织都依靠人员的轮值来支持生产环境。由于Systems Manager支持IAM集成,您可以根据人员上岗的特定时间段来自动授予或撤消Systems Manager调用权限。

在上文的示例中,您在控制台中执行了一个随意的命令。实际工作中,您将执行存储在具备访问控制的源代码库中的脚本,并可将此功能集成到现有的运维工作流程中,实现请求的跟踪、审批或拒绝。另外,使用AWS CLI、AWS API和AWS SDK,您还可以实现Systems Manager的全程自动化。

更多Systems Manager的特性

远程命令执行只是Systems Manager的一个功能。 其他功能包括:自动补丁管理,服务器软件清单,AMI自动化和参数库等。更多详细信息,请参阅Amazon EC2 Systems Manager产品详细信息页面。

总结

本文介绍了如何在EC2实例上远程执行命令,同时减少系统的攻击平面并简化系统的架构。您还可以使用AWS的IAM、日志和警报等服务帮助您获取服务器上所执行命令的详细信息。

您可能会遇到此解决方案无法满足的SSH或RDP的用例,例如SSH隧道或依赖于SSH的专有软件。希望这篇文章为您提供了关于如何访问和管理服务器的新思路。

 

译者介绍

寇欣,亚马逊AWS中国区专业服务部咨询顾问,加入AWS之前,在IT和云计算行业积累了丰富的云服务架构和应用软件架构的经验。历任Oracle电信事业部专业服务部门资深顾问、中国移动研究院云计算研究员、Lucent Tech研发中心系统架构师。

基于Amazon EC2 Container Service构建安全高可用的Docker私有库

1. 背景

Docker hub作为docker官方镜像仓库,提供了大量Docker镜像的托管服务。但使用它来托管企业私有Docker镜像存在一些问题,比如:

(1)Docker hub托管私有镜像的服务目前只面对收费账户;

(2)使用Docker hub托管私有镜像并不适合一些特殊场景,比如工作环境网络是内网,不允许访问外网,那就更不可能到Docker hub去下载镜像。

在这种情况下,如果能构建一个安全可靠的Docker私有库,将会是一个更好的选择。本文将介绍在Amazon EC2 Container Service基础上结合AWS Elastic LoadBalancer、AWS Autoscaling Group、AWS S3及Docker官方提供的registry镜像构建安全、高可用的Docker私有库的方案,帮助您轻构实现这一需求。

2. 方案详解

我们会使用AWS CloudFormation服务,使用自定义的模板脚本声明所需的资源,实现自动化构建。接下来结合我们的模板脚本对本方案进行详细介绍。

注意:以下内容与代码相关部分只贴出主要代码,部分代码用…表示省略;红字部分请替换成您自己账号相关的信息。

完整模板代码地址:https://s3-us-west-2.amazonaws.com/blog.leonli.org/registry.yml

         或者点击按钮直接在控制台中运行:

2.1 架构图

根据以上架构图,基本数据传输过程为:

(1)Docker客户端向镜像仓库发送的pull/push等命令事实上都是通过docker daemon转换成restful请求的形式再发送给镜像仓库的。在本架构中,我们利用AWS Elastic LoadBalancer(简称ELB)接收客户端发来的请求,作为整个架构的接入层。由于我们要求数据是通过TLS加密传输的,所以我们需要使用AWS IAM中的server certificate(由AWS IAM账户上传的TLS证书)与ELB关联,实现对客户端发来的请求进行加密。

(2)ELB会将请求反向代理给后端分布在不同可用区的两台Container Instance(安装了Docker运行环境的EC2实例),Container Instance中运行了Docker registry服务。当请求到达registry时,我们需要首先使用内置在registry中的用户认证文件(比如本架构中使用apache htpasswd创建的基本用户名密码保护文件),进行用户认证,认证不通过,则驳回请求,认证通过,才可以读写数据。

(3)我们将数据统一存储在一个只供创建者使用的S3 Bucket中。

2.2 基于AWS ECS运行Docker Registry服务

Amazon EC2 Container Service (ECS) 是一项高度可扩展的高性能容器管理服务,它让您能够在托管的 Amazon EC2 实例群集上轻松运行Docker应用程序。 Amazon ECS主要有以下几个组件:ECS Cluster、 Container Instance、Task , ECS Service。这里我们基于ECS运行了Docker registry服务,架构如下:

(1)首先我们在模板中定义了一个ECS Cluster,用来管理相关的Container Instance。ECS提供了ECS-Optimize AMI来创建EC2实例作为Container Instance,ECS-Optimize AMI已经内置Docker运行环境和Container Agent代理,可以为我们节省安装这些环境所需的时间。Container Instance在启动时可以由Container Agent根据配置文件/etc/ecs/ecs.config中的ClusterName属性的值知道需要将实例注册到哪个ECS Cluster上。

因为我们要使用Auto Scaling服务实现对EC2实例的伸缩控制。所以我们使用Auto Scaling的Launch Config组件声明我们的Container Instance。并通过UserData传入Shell脚本,此脚本主要完成以下三件事:

– 调用 echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config ,将Container Instance注册到我们的ECS Cluster中。

– 创建/docker-registry/certs目录和/docker-registry/auth目录,并调用aws s3 copy命令从指定的S3 Bucket中复制TLS证书文件和htpasswd用户认证文件。这些文件将在运行Docker Registry时使用到。

– 调用cfn-signal命令,通知AutoScaling Group资源EC2实例已经启动完毕。

Container Instance相关的主要模板代码如下:

(2)然后我们定义了一个TaskDefinition来声明我们要运行的容器及其相关配置。这里我们运行的是Docker registry镜像的容器,它是官方提供的用于构建镜像仓库服务的镜像文件。

ContainerInstances:

    Type: AWS::AutoScaling::LaunchConfiguration

Properties:

      …

      UserData:

Fn::Base64: !Sub |

          #!/bin/bash -xe

          echo ECS_CLUSTER=${ECSCluster} >> /etc/ecs/ecs.config

  yum install -y aws-cfn-bootstrap

          yum install -y aws-cli

          sudo rm -rf /docker-registry

          sudo mkdir -p /docker-registry/certs

          sudo chmod 777 /docker-registry/certs

          sudo mkdir -p /docker-registry/auth

          sudo chmod 777 /docker-registry/auth

          aws s3 cp s3://${SSLCertificateFileBucket}/${SSLCertificateFileName} /docker-registry/certs/domain.crt

          aws s3 cp s3://${SSLCertificateFileBucket}/${SSLKeyFileName} /docker-registry/certs/domain.key

          aws s3 cp s3://${SSLCertificateFileBucket}/${HtpasswdFileName} /docker-registry/auth/htpasswd

          /opt/aws/bin/cfn-signal -e $? –stack ${AWS::StackId} –resource ECSAutoScalingGroup –region ${AWS::Region}

– 配置环境变量REGISTRY_AUTH、REGISTRY_AUTH_HTPASSWD_REALM、REGISTRY_AUTH_HTPASSWD_PATH指定宿主机目录/auth/htpasswd文件作为Basic Authentication基础用户认证文件,从而实现用户授权认证。

    – 配置环境变量REGISTRY_HTTP_TLS_CERTIFICATE、REGISTRY_HTTP_TLS_KEY指定宿主机目录/certs/中存放的domain.crt作为TLS证书文件,domain.key作为TLS秘钥,从而实现TLS传输加密。

    – 通过配置环境变量REGISTRY_STORAGE指定registry的存储驱动为AWS S3,配置REGISTRY_STORAGE_S3_REGION为存储的S3所在Region,配置REGISTRY_STORAGE_S3_BUCKET为存储的Bucket。从而实现将镜像文件存储到AWS S3指定的Bucket中。

关于构建私有库的更多细节和更多配置可以通过Docker官方文档进行了解:Deploy a registry server

TaskDefinition相关的主要模板代码如下:

RegistryTaskDefinition:

    Type: AWS::ECS::TaskDefinition

Properties:

      NetworkMode: bridge

      ContainerDefinitions:

       – Name: RegistryContainer

          Image: registry:2

          …

PortMappings:

            – ContainerPort: 443

              HostPort: 443

          Environment:

            – Name: REGISTRY_AUTH

              Value: htpasswd

            – Name: REGISTRY_AUTH_HTPASSWD_REALM

              Value: “Registry Realm”

           – Name: REGISTRY_AUTH_HTPASSWD_PATH

             Value: /auth/htpasswd

            – Name: REGISTRY_HTTP_TLS_CERTIFICATE

              Value: /certs/domain.crt

            – Name: REGISTRY_HTTP_TLS_KEY

              Value: /certs/domain.key

            – Name: REGISTRY_HTTP_ADDR

              Value: 0.0.0.0:443

             – Name: REGISTRY_STORAGE

              Value: s3

            – Name: REGISTRY_STORAGE_S3_REGION

              Value: !Ref AWS::Region

            – Name: REGISTRY_STORAGE_S3_BUCKET

              Value: !GetAtt StorageBucket.DomainName

          …

(3)最后我们定义了一个ECS Service,指定TaskDefinition、Cluster和所需运行的任务个数DesiredCount。这样,我们就构建了一个运行着docker registry镜像的ECS服务了。

2.2 如何实现高可用性

(1) 跨可用区部署服务

– 首先,我们在模板中声明了一个VPC和两个私有子网PrivateSubnet1和PrivateSubnet2,这两个子网是分别部署在不同可用区的。

– 其次,我们的ECS服务是通过Elastic Load Balancing(ELB)来平衡多个容器的负载,ELB是高可用且自动伸缩的。我们在模板中定义了一个命名为RegistryELB的ELB组件,指定它是internet-facing模式可供外网访问、并且是跨可用区的。ELB接收外网的请求,并且将请求代理给Container Instance中的容器。

– 最后,我们在模板中声明了一个Auto Scaling Group,指定VPCZoneIdentifier为跨可用区的两个子网PublicSubnet1和PublicSubnet2,由RegistryELB代理请求,从而实现跨可用区部署服务。

(2) 利用Auto Scaling保障可用实例数量

当服务遇到一些突发或者预期的高流量时,或者您的服务出现某些异常时,可以利用Auto Scaling服务保障可用实例数量。比如某台Container Instance宕机了,那么可以利用Auto Scaling自动启动相同配置的另一台Container Instance代理宕机的实例继续提供服务。

大部分情况下,企业Docker私有库承受的流量负载不会太大,所以本方案不介绍Auto Scaling的扩展策略,当然您也可以根据自己的业务需要修改模板代码,实现此功能。本方案使用Auto Scaling主要是为了保障可用实例数量一直维持在DesiredCapacity,这个参数是我们通过模板的参数传入的。

Auto Scaling Group相关的主要模板代码如下:

ECSAutoScalingGroup:

    Type: AWS::AutoScaling::AutoScalingGroup

    Properties:

    …

LaunchConfigurationName: !Ref ‘ContainerInstances’

      MinSize: !Ref MinSize

      MaxSize: !Ref MaxSize

     DesiredCapacity: !Ref DesiredCapacity

    …

(3) 利用s3确保数据完整性

Amazon Simple Storage Service (Amazon S3) 是AWS提供的对象存储服务,它可用于在 Web 上的任何位置存储和检索任意数量的数据,能够提供 99.999999999% 的持久性,使用S3来存储Docker私有库的镜像文件,可以确保数据完整。Docker registry镜像支持使用S3作为存储驱动,只需传入存储所在Bucket和所在Region即可。我们在模板中声明了一个Bucket用来存储镜像数据。并且这个Bucket只能由创建者进行控制。

2.3 如何实现安全性

(1) 利用TLS加密传输

Docker官方建议与私有库通信的所有数据传输皆使用TLS加密,我们通过上传IAM server certificate证书并将其与ELB关联,ELB的Listener使用SSL安全监听443端口,并将请求代理给实例的443端口,实例上也需要安装TLS证书。从而实现全链路的安全传输。

(2)利用apache htpasswd实现基本用户认证

如果我们创建的私有库没有用户认证机制,那么无论是谁只要知道私有库链接,就可以肆无忌惮地访问和操作我们托管在私有库的文件,这显然不是我们想要看到的。所以需要实现用户认证,只有通过认证的用户,才有权进行相关操作。

这里,我们使用apache htpasswd实现基本用户认证。这也是Docker registry镜像默认支持的认证方式。

3. 构建过程

3.1 准备工作

(1) 域名

您必须拥有一个用来指向registry的域名,这样您才可以通过域名访问您的私有库。

(2)TLS证书

yourcertificate.crt intermediate-certificates.pem > yourcertificate.crt

Docker官方建议私有库数据传输基于TLS,这样您还需要为您的域名申请CA认证,得到TLS证书和秘钥文件。有以下两种方式:

向CA供应商购买。

利用Let’s Encrypt生成免费的证书,具体操作参考letsencrypt官网。

另外,如果您的证书供应商还提供给你intermedia证书,那么您需要将它与你的证书进行合并,运行如下命令可以进行合并

aws iam upload-server-certificate —server-certificate-name YourCertName —certificate-body file:///path/to/certificate.pem —certificate-chain file:///path/to/chained.pem —private-key file:///path/to/private_key.pem

(3)上传IAM Server Certificate

{

    “ServerCertificateMetadata”: {

        “Path”: “/”,

        “ServerCertificateName”: “MyRegistryCert”,

        “ServerCertificateId”: “ASCAJHAWE3QRHEHH3L6KM”,

         “Arn”: “arn:aws:iam::xxxxxxxxx:server-certificate/YourCertName”,

“UploadDate”: “2017-07-01T16:16:45.125Z”,

        “Expiration”: “2017-09-26T12:15:00Z”

     }

}

 

openssl x509 -inform DER -in Certificate.der -outform PEM -out Certificate.pem

当Docker客户端向我们的ELB发送请求时,出于安全考虑,需要对传输加密,这时可以利用IAM Server Certificate,将我们的TLS证书、秘钥以及证书链文件上传,IAM会帮我们自动生成一个Server Certificate,再将它与ELB绑定即可。

可以使用AWS CLI按照以下命令上传:

openssl rsa -in PrivateKey.pem -out PrivateKey.pem

上传完成后,您会得到类似以下响应信息,将红字部分ARN记录下来,后面构建时需要它作为参数传入模板中。

注意:

– TLS证书、秘钥及证书链必须都是PEM格式,如果不是,可以使用以下命令进行转换:

– AWS IAM 要求证书秘钥是不加密的,所以当您的秘钥是加密时,需要使用以下命令转换

– 如果您在上传Server Certificate过程中遇到问题,可参考AWS官网指南:

Working with Server Certificate

(4)htpasswd用户认证文件

使用apache htpasswd来做基本认证,首先需要安装httpd,安装完后运行以下命令可以生成一个包含 一条用户名和密码记录的htpasswd文件

htpasswd -c username password > htpasswdfile

后面如果需要加入更多条记录,只需将 -c 参数去除,运行上面相同的命令即可。

(5) 将证书、秘钥和认证文件上传到S3

我们需要将前面几步生成好的TLS证书、秘钥、htpasswd文件上传到S3中,Container Instance启动后会从S3中将这些文件拷贝下来,通过映射到容器中供registry容器使用。

创建一个S3 Bucket, 将这几个相关文件上传。

3.2 自动化构建

接下来,我们在AWS Management Console中使用CloudFormation指定我们的模板文件创建一个Stack。如何使用CloudFormation创建Stack,请参考AWS CloudFormation用户指南:CloudFormation入门

Stack构建成功后,在输出栏会有输出值 RegistryELBDns,它是我们创建的ELB的DNS域名。我们需要将我们之前TLS证书签发的域名DNS解析(CName解析)转发到这个ELB的DNS域名。这样就可以使用我们自己的域名访问我们的私有库了。

3.3 开始使用

构建完Docker私有库后,现在可以开始使用了。

首先,在Docker客户端,我们可以从Docker Hub上拉取一个ubuntu镜像。

docker pull ubuntu:16.04

然后tag这个镜像到我们自己的域名下

docker tag ubuntu:16.04 myregistrydomain.com/my-ubuntu

登录我们的私有库

docker login myregistrydomain.com

输入用户名,密码后,调用push命令将镜像上传

docker push myregistrydomain.com/my-ubuntu

上传完后,可以调用pull命令拉取镜像

docker pull myregistrydomain.com/my-ubuntu

4. 总结

本文介绍了如何利用AWS ECS及其他AWS服务构建一个高可用、安全可靠的docker私有库,通过本方案的详细介绍和构建实践,相信您对于docker registry以及AWS Elastic LoadBalancer、AWS Autoscaling Group、AWS S3及AWS CloudFormation有了更进一步的认识。接下来,您可以利用AWS ECS容器管理服务及Docker容器技术,更加轻松地构建和管理您的应用程序,发挥更大的效益。

作者介绍

李磊,AWS解决方案架构师,负责基于AWS的云计算方案的架构设计,同时致力于AWS云服务在国内和全球的应用和推广。在大规模并发后台架构,电商系统,社交网络平台、互联网领域应用,DevOps以及Serverless无服务器架构等领域有着广泛的设计与实践经验。在加入AWS之前超过十年的开发和架构设计经验, 带领团队攻克各种技术挑战,总是希望站在技术的最前沿。