使用VPC Endpoint之Gateway Endpoint和Interface Endpoint实现从VPC或IDC内网访问S3

2023年5月更新

一、背景

本文讲述了从内网访问S3的两种Endpoint配置方式,并对Interface Endpoint进行了访问压力测试。

1、从内网访问S3的两个场景

通常情况下应用程序和客户端对S3的访问是通过互联网进行,被访问的地址是S3在本区域的Endpoint,例如北京区域是 https://s3.cn-north-1.amazonaws.com.cn。来自广域网直接访问S3的不需要在解释;当应用程序在VPC内要访问S3时,应用所在VPC子网必须具有能通达到互联网的路由条目,例如具备公网IP、弹性IP从互联网抵达,或者借助NAT网关做地址转换才能连接到S3位于互联网上的终端节点。

在2021年2月之前,如果客户对访问S3合规要求必须从内部网络而不是互联网访问,一般会考虑采用如下方案:

  • 来自IDC的内网流量希望访问S3:使用Direct Connect的Public VIF功能。从私有IDC连接过来的专线在Direct Connect服务上创建Public VIF,并通过公网抵达S3服务;
  • 来自云上VPC内的流量:使用VPC Endpoint Gateway Endpoint。这一功能在2015年发布。在VPC内配置一个网关类型的终端节点并自动在路由表中增加一条路由,内部子网可通过本路由条目访问到S3的终端节点,并且还可保持本子网没有其他外网访问策略。

2、现有方案的挑战

使用Gateway Endpoint的这种方式的局限性是本方式只能对VPC内的服务有效,如果要访问S3的客户端是位于另外一个AWS Region的VPC内或者位于私有IDC,那么无法借助Gateway Endpoint的VPC路由表访问到S3。此时的方案是可以在当前VPC内先创建好Gateway Endpoint,然后部署一个EC2作为访问S3的代理,所有流量都从内网发送给VPC内的EC2,再从这个EC2经过Gateway Endpoint访问到S3。这样的架构设计带来了复杂性,且流量大的情况下难于管理。

3、2021年新发布的VPC Endpoint Interface Endpoint

2021年2月,AWS海外区域和中国区域同时推出了S3 VPC Endpoint Interface Endpoint的支持,在此模式下可将S3的访问节点映射为一个VPC内的ENI入口,用户可在其他AWS区域的VPC内或者IDC内直接调用本地址即可访问到S3。此方式既不需要Public VIF又无须在VPC内部署S3代理,大大简化了架构设计。

4、本文实验环境

本文实验环境如下:

系统架构

实验架构如上所示,接下来将分别展示 Gateway Endpoint 模式和 Interface Endpoint 模式,并使用测试脚本模拟访问。此外,Interface Endpoint模式还允许从其他区域的VPC和私有IDC中通过Direct Connect专线等方式访问,在上图中也有标注对应架构,由于其访问方式和原理与从本VPC内的访问方式相同,本文不再描写这部分内容。

二、搭建实验环境(首先在内部子网通过NAT网关访问S3环境)

在不开启VPC Endpoint的时候,在VPC内访问S3或者是要求EC2具有EIP,或者是通过NAT Gateway访问到S3。下面先搭建一个具有私有子网的VPC,并在EC2上测试经过NAT Gateway访问到S3。

1、创建VPC、外部子网、内部子网和NAT Gateway

构建如下测试环境:

  • 使用AWS本区域的默认VPC;分别构建外部子网和内部子网共2种类型的子网,可用区不限,两个子网使用独立的路由表;
  • 外部子网允许EC2自动获得Public IP,路由表的默认网关0.0.0.0/0指向IGW,一跳即可到达互联网;外部子网中创建一个NAT网关,分配一个EIP作为出口IP;
  • 内部子网不自动分配Public IP,路由表默认允许VPC内路由,0.0.0.0/0指向NAT网关,通过NAT网关可对外访问;
  • 两个子网重分别创建一个EC2,选择Amazon Linux 2操作系统,其余参数默认;
  • 使用外部子网的EC2作为跳板机,然后ssh登录到内部子网的EC2。

在实验开始之前,确认内部子网的路由表中包含 0.0.0.0/0 的路由条目,这将允许内部子网中的EC2向外访问安装软件和补丁升级。在此时默认路由应指向NAT网关。如下截图:

2、在VPC内测试AWS CLI访问(流量通过NAT Gateway)

首先配置好CLI所需要的 Access Key,或者使用IAM Role更加安全。然后在本VPC内,执行 aws s3 ls s3://mybucket/ 可以正常列出本存储桶内的文件。由于内部子网配置了默认网关出口是NAT Gateway,因此这个访问可以直接访问成功。返回信息如下:

[ec2-user@ip-172-31-200-161 ~]$ aws s3 ls s3://mybucket/
                           PRE demo1/
                           PRE demo2/
                           PRE demo3-zips/
                           PRE spectrum/
                           PRE spectrum2/
2021-02-10 03:44:46          0 demo3-zips_$folder$
[ec2-user@ip-172-31-200-161 ~]$

3、测试Python程序API调用(流量通过NAT Gateway)

为了测试应用客户端访问,我们使用Python构建一个简单的程序,通过boto3库调用S3的API。执行如下命令安装Python3和Boto3库:

yum install python3 -y
pip3 install -i https://opentuna.cn/pypi/web/simple boto3

安装完成后,新建 ListBucket.py 文件,内容如下:

import boto3

# Retrieve the list of existing buckets
s3 = boto3.client('s3')
response = s3.list_buckets()

# Output the bucket names
print('Existing buckets:')
for bucket in response['Buckets']:
    print(f'  {bucket["Name"]}')

保存后,运行 python3 ListBucket.py 这个测试程序,将返回所有的桶名称。由此表示S3访问正常。

三、使用Gateway Endpoint实现从VPC内访问S3

1、断开内部子网的对外访问路由(关闭NAT Gateway)

找到上文配置的路由表,将其中 0.0.0.0/0 这一条指向NAT Gateway的默认路由条目删除。在本子网内的EC2上执行ping、cli等操作验证本子网不能访问S3。

2、在VPC内配置Gateway Endpoint

进入VPC控制台,在左侧找到Endpoints终端节点,点击创建。如下截图:

在创建终端节点界面,选择服务是AWS服务,在搜索框中输入关键字 s3,找到类型是Gateway,并在下方选择VPC。然后向下滚动页面。如下截图:

在页面下方找到路由表配置,选中内部子网所对应的路由表。在策略位置,选择Full Access给予完全访问权限。接下来继续向下滚动屏幕。如下截图:

在最后一步点击创建。如下截图:

看到绿色的Available表示Gateway模式的Endpoint创建完成。如下截图:

为了验证创建成功,返回VPC所在的内部子网对应的路由表。点击查看路由表,点击第二个标签页路由条目,可看到VPC Endpoint Gateway Endpoint会在其中加入了路由条目。它包含本区域的S3终端节点的IP地址,并且下一跳为VPC Endpoint。这表示配置成功。如下截图:

接下来验证访问。

3、测试AWS CLI访问

在内部子网所在的EC2上,执行如前文测试过的 aws s3 ls s3://mybucket/ ,可看到访问正常。

4、测试Python程序API调用

运行 python3 ListBucket.py 测试程序,将返回所有的桶名称。

由此表示S3访问正常。

5、删除Gateway Endpoint

测试成功,从VPC界面删除Gateway Endpoint。在VPC的Endpoint界面删除掉终端服务后,路由表中的条目也会自动消失,不需要手工删除。

四、使用Interface Endpoint实现从VPC内、其他VPC和私有IDC访问S3

1、断开内部子网的对外访问路由(关闭NAT Gateway)

找到上文配置的路由表,将其中 0.0.0.0/0 这一条指向NAT Gateway的默认路由条目删除。在本子网内的EC2上执行ping、cli等操作验证本子网不能访问S3。

2、配置Interface Endpoint所需要的安全规则组

进入VPC控制台,创建一个新的安全规则组,规则如下:

  • 入栈:只允许443端口来源是本VPC的流量,例如 172.31.0.0/16 。如果是生产环境则可以进一步提升安全设置为仅允许EC2的IP地址访问;
  • 出栈:允许去往 0.0.0.0/0 的所有出栈流量。

保存安全规则组名称叫做 VPCEndpoint,后文配置中将要使用。

3、配置Interface Endpoint

进入VPC控制台,在左侧找到Endpoints终端节点,点击创建。在创建Endpoint界面,选择服务是 AWS Service,在搜索框中输入s3过滤显示结果,然后选择S3的Interface类型。如下截图:

选择Endpoint所在的子网。本实验使用的是可用区 northwest-1c 的子网,因此仅选中本可用区,其他可用区不选中。如果是生产环境,则可以将应用所使用的多个子网都选中。如下截图:

在选择安全组界面,首先点击默认选中的安全组边上的叉子符号,取消其选中。然后从下拉框中,选择上一步创建好的安全规则组 VPCEndpoint 。如下截图:

最后一步选择访问授权是完全访问。如下截图:

点击创建完成Interface Endpoint的创建。

进入VPC Endpoint界面,在其中找到刚才创建的类型是Interface的节点,且要等待其 Status 状态变为绿色即可开始使用。在右下角的DNS名称中,格式为 *.vpce-0f56080ae4146381b-qeitm5wo.s3.cn-northwest-1.vpce.amazonaws.com.cn 的这一段请复制下来,后续代码中需要调用。如下截图:

4、测试AWS CLI访问(显式声明Endpoint)

请注意,需要AWSCLI版本在2.1以上才可以通过VPC Endpoint访问到S3。

执行以下命令,在AWSCLI命令后额外指定Endpoint。注意bucket是保留名称不要替换,从vpce起更换为上一步创建Endpoint后获得的域名。命令如下:

aws s3 ls s3://mybucket/ --endpoint-url https://bucket.vpce-0f56080ae4146381b-qeitm5wo.s3.cn-northwest-1.vpce.amazonaws.com.cn

命令执行后可以看到访问正常。

请注意,如果AWSCLI是1.x版本,那么在使用Endpoint访问时候可能提示如下错误:

An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied

解决这个问题需要升级到2.x版本才可以支持。执行 aws --version 命令可查询AWSCLI的版本。例如以下是的例子是1.18版本需要到升级2.x版本才可以正常访问。

aws-cli/1.18.147 Python/2.7.18 Linux/4.14.214-160.339.amzn2.x86_64 botocore/1.18.6

升级方法请参考相关文档。

5、测试Python程序API调用(显式声明Endpoint)

构建一个 ListBucket_endpoint.py 文件,内容如下。请替换其中的endpoint_url为上一步在CLI测试中使用的url。注意以bucket开头。代码如下:

import boto3

# Retrieve the list of existing buckets
s3 = boto3.client(
        service_name='s3',
        endpoint_url='https://bucket.vpce-0f56080ae4146381b-qeitm5wo.s3.cn-northwest-1.vpce.amazonaws.com.cn'
)
response = s3.list_buckets()

# Output the bucket names
print('Existing buckets:')
for bucket in response['Buckets']:
    print(f'  {bucket["Name"]}')

保存后,运行 python3 ListBucket_endpoint.py 测试程序,将返回所有的桶名称。表示通过Endpoint访问S3正常。

6、关于显式声明Endpoint

以上的AWSCLI命令和Python使用Boto3 SDK访问S3的过程中,都显式的生命了S3 Endpoint是新创建的Interface Endpoint。这一个步骤可通过Route53 Private Zone解析功能来解决。

五、为 Interface Endpoint 配置 Route 53 Private Zone 解析实现免显式声明 Endpoint 入口访问方式S3

1、显示声明S3 Endpoint带来的原S3访问代码的改动

本节在前一环节实验可观察到,我们使用AWSCLI或者SDK访问S3 API时候,都需要显式的声明要访问的Endpoint是新创建的Interface Endpoint。这样不免对现有应用代码产生了改动。

这是因为直接通过AWS SDK访问各Region的S3时候,SDK内已经有了S3在互联网上默认的Endpoint地址,所以是不需要在代码里显式生命S3地址的。而改用Interface Endpoint后,不管是AWSCLI还是通过SDK编写代码,都必须显式的传入在本VPC创建的S3 Endpoint的地址。这一步是对现有应用造成了额外改动。

对于无法修改的现有存量应用,可以通过配置Route 53 Private Zone实现本VPC私有解析的方式,来实现无须声明Endpoint,即可让流量通过Interface Endpoint发送。

继续探讨如何免修改代码实现 Interface Endpoint 的调用,并将对 Interface Endpoint 在VPC内生成的访问入口进行吞吐量测试。

2、使用 Route 53 创建 Private Zone

现在通过使用 Route 53 Private Zone 设置专门的 Zone解析文件,将原有的S3访问地址以别名方式解析到 Interface Endpoint 新生成的访问地址,即可解决这一问题。

首先进入Route53服务。点击创建新的Hosted Zone。如下截图:

以宁夏区域为例,在域名位置输入 s3.cn-northwest-1.amazonaws.com.cn ,也就是要访问的S3桶所在的区域对应的Endpoint。

此处请注意,在对应的Region的VPC配置Route 53 Private Zone时候,要输入本Region的S3对应的Endpoint。具体Endpoint地址可以从这里的文档查询。

在描述位置输入 PrivateLink for S3 ,在类型位置选择 Private hosted zone 。如下截图:

在要关联的VPC位置,选择EC2所在的区域和VPC,并点击右下角按钮创建。如下截图:

创建Private Zone完成。

3、设置Private Zone的根记录

创建Zone成功后,点击右侧Create record按钮创建新的解析记录。如下截图:

在创建新的解析记录界面,记录名称的位置留空,此时页面会自动显示一个灰色的blog,不用输入信息则表示是留空没有填写的状态。在记录类型的位置选择 A 记录,在 Route traffic to 选项右侧有一个 Alias 别名的开关,打开这个开关。最后从下拉框中找到最后一项 Alias to VPC endpoint 。如下截图:

此时页面将自动加载显示VPC Endpoint的区域,从下拉框中选择所在的区域,再从页面中选择VPC Endpoint的第一条记录。最后点击创建记录按钮创建解析。如下截图:

解析创建成功过后,可以看到有如下一条记录,其中Value部分值的位置,显示的是 \052 开头的一条记录。052在ASCII字符中表示通配符。至此表示解析创建成功。如下截图:

4、设置Private Zone的子域名通配符记录

上一条zone的根记录是提供ListBucket的终端节点访问。对单个桶的文件访问还需要在这个终端节点下进一步添加通配符为子域名提供解析。添加如下一条解析记录,记录名称位置输入星号,解析目标的填写方法同上一步。如下截图:

配置解析完成。

5、验证访问确认Private Zone解析有效(免声明S3 Endpoint)

现在回到前文实验中的EC2上,执行 aws s3 lspython ListBucket.py,不再需要为命令显式声明Interface Endpoint,也就不再需要修改原S3代码,即可看到通过内网访问S3正常。其原理就是通过Route 53 Private Zone将正常的对S3 Endpoint的访问通过别名方式解析到了Interface Endpoint上,即实现了不用声明Endpoint地址。

6、使用Interface Endpoint时原S3 Presign-URL访问的测试

前文两个测试,AWSCLI和Python编写的Boto3 SDK的代码,都是针对AWS的API发起访问的。S3有一个很重要的场景就是通过HTTPS的方式直接访问,此形式常见于网页的图片托管(又叫图床)、以及对外数据文件下载等场景。

在S3提供HTTPS访问时候,又包括Public模式的匿名访问(来自互联网访问),以及带有Presign-URL的签名授权访问。经过测试,通过Interface Endpoint可继续使用原先的Presign-URL签名的地址,通过HTTPS访问S3。(注:2023年5月测试通过)

也就是说,Interface Endpoint是改变了S3访问流量的入口,但是不影响Presign-URL功能。

7、来自其他Peering-VPC以及来自Direct Connect另一侧IDC的访问

如果访问S3的客户端不在本VPC而是在远端VPC,例如本Peering到另一个Region的VPC,或者是通过专线到企业私有IDC,那么在远程网络另一个VPC或者IDC内也配置如上类似的解析服务即可实现不修改代码访问S3。

例如当私有IDC内的应用需要通过终端节点访问时,可以在IDC所使用的企业自有DNS服务器(如微软DNS或Bind)上创建如上域名解析,将普通S3访问的Endpoint以别名方式指向到Interface Endpoint,即可完成平滑割接。

六、对 Interface Endpoint 进行压力测试确保能达到高吞吐量

1、性能测试背景

在Interface Endpoint功能尚未推出时,VPC之外的环境要通过内网访问S3,需要在VPC内架设一个EC2作为代理。而此台EC2的网络出口性能是制约S3吞吐的一个关键,且EC2还存在单点故障问题。使用Interface Endpoint后,Interface实际由VPC内的ENI虚拟网卡提供服务,可以获得更高性能和可靠性。

2、搭建环境

吞吐量测试方法是在同一个VPC内配置数个EC2作为客户端并发访问S3的Interface Endpoint。EC2配置如下:

  • 规格 c5.xlarge,4vCPU,8GB,Up to 10Gbps的网络性能;
  • 磁盘 200GB gp3,3000 IOPS,吞吐250MB(在gp3原125MB吞吐基础上配置额外的预置吞吐);
  • 要传输的文件尺寸在3.4GB左右,格式为ISO,位于S3存储桶根目录。

3、测试数据传输

测试过程使用CLI从单个节点发起访问,记录传输时间和吞吐速率;再从4个节点和8个节点并行方式同时发起访问,观察多节点访问速度是否会存在瓶颈。

单节点访问传输时间约16秒传输完成。如下截图:

4节点并发传输发起操作时,可看到瞬时速度如下截图:

4节点并发传输完毕后,传输时间如下截图:

随着访问节点的数量增多,Interface Endpoint 的吞吐依然能够保证每个节点都达到和单节点一样的速度。4个节点同时传输时候速度在210MB/s左右,折算下来总吞吐在6.72Gbps以上。

接下来我们将节点增加到8个,同时发起操作,可看到8个节点都在以210MB/s以上的速度传输,速度不会因为节点的增多而衰减。

8个节点以210MB/s的速度传输折算为 210MB/s * 8 * 8 = 13.44Gbps ,可看到 Interface Gateway 的ENI网卡可满足10Gbps以上的传输需求。换句话说,当通过 Direct Connect 架设了10Gbps专线, Interface Gateway 也可完全满足对S3的访问需求。针对大于10Gbps的吞吐场景本文并未进一步发起测试,当前结果仅供参考。

4、性能测试结论

通过以上测试,可以看到使用Interface Endpoint可以获得比使用EC2作为S3 Proxy更好的性能。

七、小结

Gatewa Endpoint和Interface Endpoint两种方式提供S3访问。二者可同时使用不冲突。同时使用二者时候网络流量的区分是:

  • CLI和程序如果不指定Endpoint时候,流量从默认路由条目自动寻找Gateway Endpoint,然后流向S3入口;
  • CLI和程序指定Interface Endpoint的时候,流量会送往本VPC的一个ENI网卡对应的内网IP,然后流量去往S3;
  • CLI和程序不指定Endpoint时候,如果通过 Route 53 Private Zone 配置了私有解析将S3访问的域名做别名指向到Interface Endpoint上,那么CLI和程序将通过Interface Endpoint访问S3。

二者差异:

  • Gateway Endpoint方式可以无需修改代码;Interface Endpoint方式需要在程序中显式声明接口地址,或者通过Route53 Private Zone增加别名解析;
  • Gateway Endpoint方式只能在本VPC内访问,如果从VPC以外的地方发起访问,则需要搭建代理机制;Interface Endpoint 方式则体现为本VPC的ENI网卡具有内网地址,可以从三层网络能通达的位置(包括其他Region的VPC Peering以及IDC等地点)发起调用;
  • Gateway Endpoint方式不限制网络性能,每个EC2访问S3的吞吐由本EC2的网络吞吐能力决定;Interface Endpoint 方式是在VPC内模拟了ENI网卡作为S3流量入口,测试结果显示ENI可承担超过10Gbps的流量。此外还可以通过在每个AZ分别部署 Interface Endpoint 并且一个子网内可以部署多个Interface Endpoint来提升吞吐能力。

综上所述,Interface Endpoint方式赋予整个架构更大的灵活性,更加简单方便的实现私密性的合规接入,建议与Gateway Endpoint搭配使用可获得最佳效果。

八、参考资料

英文博客新功能发布:

https://aws.amazon.com/blogs/aws/aws-privatelink-for-amazon-s3-now-available/

官网文档:

https://docs.aws.amazon.com/AmazonS3/latest/userguide/privatelink-interface-endpoints.html#accessing-bucket-and-aps-from-interface-endpoints

Python Boto3 SDK参考文档:

https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-example-creating-buckets.html