通过亚马逊云科技实现基于 Restful API 的 CloudFront Distribution 复制/克隆功能

亚马逊云科技
Amazon CloudFront
0
0
{"value":"#### **背景**\n\n**[Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail)** 是一个全球性的内容分发网络 (CDN),您可以借助 CloudFront 以低延迟和高可用性向查看者或者最终用户分发内容。通常来讲,[Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail) 的客户都拥有**多个 CloudFront Distribution**,每个 Distribution 都包含了一组待加速的内容以及相关的配置信息,例如源站、加速域名、缓存策略、证书、日志、访问控制等。\n\n以下主题说明了一些有关 **CloudFront Distribution 的基础知识**,并提供了有关可选择用来配置 Distribution 以满足业务需求的设置的信息。在 CloudFront 自动化操作的过程中,一个常见的任务是**创建多个 Distribution**,这些 Distribution 的配置在同一类型作业下配置参数完全相同,例如动态加速场景下缓存 TTL 均设置为0。在该场景下,客户往往需要一个克隆已存在的 Distribution 的配置项,并希望通过标准 Restful API 接口调用以实现借助程序快速创建多个域名 Distribution 的目的。而目前亚马逊云科技支持的新建 Distribution 的 API :CreateDistribution,需要提供完整的配置参数信息,暂时不支持复制或者克隆 Distribution 的功能。\n\n本文介绍的方案是利用 **[Amazon API Gateway](https://aws.amazon.com/cn/api-gateway/?trk=cndc-detail) 和 Amazon Lambda**,后端基于**Amazon SDK for Python (Boto3)**,实现基于一个参考 Distribution的配置信息,仅修改域名与源站等变更项,来复制一个新 Distribution 的 Restful API。\n\n### **解决方案简介**\n**CloudFront Distribution** 复制/克隆功能的解决方案基本实现思路如下:\n\n1)复制已存在有一个参考 Distribution 的配置信息;\n2)改造可变项,例如新 Distribution 的加速域名 CNAME 和源站的域名,组成新的完整配置信息;\n3)执行新建 Distribution 的操作。\n\n在此过程中,会调用 **Amazon Certificate Manager** 的相关 API 查找到新 CNAME 所对应的 ACM 证书的 ARN,从而完成加速域名的证书关联操作。其中,查询证书 ARN 的过程对用户是透明的。\n\n#### **解决方案的详细实现流程如下:**\n\n1)**获取参考域名的配置信息做为基准配置(**函数:get_reference_config)\n调用**Amazon Boto3 API get_distribution_config**,输入 distribution id,以 Json 形式获取 distribution 的配置信息 DistributionConfig;\n\n2)**获取账号下 ACM 证书与域名的对应列表**(函数get_certificate_mapping)\n调用Amazon Boto3 API list_certificates,输入 CertificateStatuses=’ISSUED’,查询账号下 ACM 中的已签发的证书与域名的对应列表;\n\n3)**获取该 CNAME 对应的 ACM 证书**(函数get_certificate_arn)\n从步骤3中查出的列表里取得源站 origin 所对应的 ACM 证书的 ARN(Amazon Resource Names,资源名的唯一标识符),用于下一步创建 Distribution 时的证书参数;\n\n4)**构造新Distribution的配置信息**(函数set_config_based_on_ref)\n根据步骤一获取的基准 DistributionConfig,修改域名和源站,添加证书 ARN,创建新的 DistributionConfig\n\n5)**创建新Distribution**(函数create_distribution)\n调用 Amazon Boto3 API create_distribution, 输入步骤4构造的 DistributionConfig,创建所需Distribution。\n\n### **解决方案架构**\n方案用户接口通过 **API Gateway** 和 **Lambda 函数 cf_distribution_clone** 生成一个 Restful API clone_distribution, 函数 cf_distribution_clone 会依据触发 event 中的 queryStringParameters 创建克隆的 Distribution。为了进一步加强安全管理限制 API 的访问,此例中 API Gateway 中将**开启 Cognito 授权**,访问接口的用户需携带 Cognito 令牌才能正常请求 API。方案的架构图如下所示。\n![image.png](https://dev-media.amazoncloud.cn/ec1668ff54b445d6b46e0fed4186d480_image.png)\n\n### **解决方案部署**\n\n#### **1. 部署 Lambda 函数**\n\n创建一个IAM 角色 **cf-clone-distribution-role **供 Lambda 执行时 Assume,为该角色创建如下 IAM 策略,注意需要将与分别替换成 CloudFront 日志所在的 S3 Bucket 的桶名与账号 ID。该策略具有对已有 Distribution 的配置查询权限,新建 Distribution 权限,ACM 证书的列出权限,以及对 CloudFront 日志所在 S3 存储桶的 Bucket ACL 的查询与修改权限。示例以美东区域 us-east-1 作为参考,可以根据实际情况进行替换。\n```\\n{ \\n \\"Version\\": \\"2012-10-17\\", \\n \\"Statement\\": [ \\n { \\n \\"Sid\\": \\"VisualEditor0\\",\\n \\"Effect\\": \\"Allow\\", \\n \\"Action\\": [ \\n \\"acm:ListCertificates\\", \\"cloudfront:CreateDistribution\\", \\"cloudfront:GetDistributionConfig\\" \\n ], \\n \\"Resource\\": \\"*\\" \\n }, \\n {\\n \\"Sid\\": \\"VisualEditor1\\", \\n \\"Effect\\": \\"Allow\\", \\n \\"Action\\": [ \\n \\"s3:PutBucketAcl\\",\\n \\"s3:GetBucketAcl\\", \\n \\"logs:CreateLogGroup\\"\\n ], \\n \\"Resource\\": [\\n \\"arn:aws:s3:::\\", \\n \\"arn:aws:logs:us-east-1::*\\" \\n ] \\n }, \\n {\\n \\"Sid\\": \\"VisualEditor2\\", \\n \\"Effect\\": \\"Allow\\",\\n \\"Action\\": [ \\n \\"logs:CreateLogStream\\", \\n \\"logs:PutLogEvents\\"\\n ], \\n \\"Resource\\": \\"arn:aws:logs:us-east-1: ::log-group:/aws/lambda/cf_distribution_clone:*\\"\\n } \\n ] \\n }\\n```\n创建 Lambda函数 cf_distribution_clone,设置 Lambda 执行角色为上文创建的 **cf-clone-distribution-role**。解决方案使用的运行时环境为 Python 3.9,其对应的完整 Lambda 代码如下所示。\n```\\nimport boto3 \\nfrom botocore.config import Config \\nimport botocore.exceptions \\nfrom datetime import datetime, timezone\\nimport logging \\nimport json \\n logging.basicConfig(level=logging.INFO)\\n logger = logging.getLogger() \\n cf_client = boto3.client('cloudfront') \\n my_config = Config( \\nregion_name = 'us-east-1'\\n ) \\nacm_client = boto3.client('acm', config=my_config) \\n def lambda_handler(event, context): \\nconf_domain = event['queryStringParameters']['domain'] \\nconf_origin = event['queryStringParameters']['origin'] \\nconf_ref_dist = event['queryStringParameters']['ref_dist']\\n# 1) 获取参考域名的配置信息做为基准配置 \\nref_config = get_reference_config(conf_ref_dist) \\n# 2) 获取账号下ACM证书与域名的对应列表 \\ncerts = get_certificate_mapping() \\n# 3) 获取该CNAME对应的ACM证书 \\ncertArn = get_certificate_arn(certs, conf_domain) \\n# 4) 构造新Distribution的配置信息 \\nnew_config = set_config_based_on_ref(ref_config, conf_domain, conf_origin, certArn) \\n# 5) 创建新Distribution \\ndistribution = create_distribution(new_config) response_body = {}\\nresponse_body['requestId'] = context.aws_request_id \\nresponse_body['distributionId'] = distribution['Distribution']['Id'] \\nresponseObject = {} \\nresponseObject['statusCode'] = 200 \\nresponseObject['body'] = json.dumps(response_body) \\nreturn(responseObject) \\n def get_reference_config(ref_dist): \\ntry: \\nreturn cf_client.get_distribution_config(Id=ref_dist) \\nexcept botocore.exceptions.ClientError as error: \\nlogger.exception(f\\"{format(error)}\\")\\nraise error\\ndef get_certificate_mapping(): \\ntry: \\nresponse = acm_client.list_certificates( \\n\\nCertificateStatuses=[ \\n\\n'ISSUED' \\n\\n], \\n\\nMaxItems=1000 \\n\\n) \\n\\ncerts = response['CertificateSummaryList'] \\n\\nwhile \\"NextToken\\" in response: \\n\\nresponse = acm_client.list_certificates( \\n\\nCertificateStatuses=[ \\n\\n'ISSUED' \\n\\n ], \\n\\nMaxItems=1000, \\n\\nNextToken= response['NextToken'] \\n\\n ) \\n\\ncerts.extend(response[\\"CertificateSummaryList\\"]) \\n\\ncert_dict = {} \\n\\nfor cert in certs: \\n\\ncert_dict[cert['DomainName']] = cert['CertificateArn'] \\n\\nreturn(cert_dict) \\n\\nexcept botocore.exceptions.ClientError as error: \\n\\nlogger.exception(f\\"{format(error)}\\") \\n\\nraise error\\n\\n\\n\\ndef get_certificate_arn(certs, domain): \\n\\nif domain in certs: \\n\\ncert = certs[domain] \\n\\nelse: \\n\\ncert_domain = '*.' + domain.split(\\".\\", 1)[-1] \\n\\nif cert_domain in certs: \\n\\ncert = certs[cert_domain] \\n\\nelse: \\n\\nlogger.info(f\\"No certificate for domain - {format(domain)} in ACM. Please create or import one.\\") exit(1) logger.info(f\\"Use ACM certificate for domain \\\\'{format(domain)}\\\\': {format(cert)}.\\") return cert \\n\\n\\n\\n def set_config_based_on_ref(ref_config, conf_domain, conf_origin, certArn): \\n\\nref_config['DistributionConfig']['Aliases'] = { \\n\\n'Quantity': 1, \\n\\n'Items': [ \\n\\nconf_domain \\n\\n] \\n\\n} \\n\\nnew_config = ref_config['DistributionConfig'] \\n\\nnew_config['CallerReference'] = str(datetime.now(tz=None).timestamp()) \\n\\nnew_config['Origins']['Items'][0]['Id'] = conf_origin \\n\\nnew_config['Origins']['Items'][0]['DomainName'] = conf_origin \\n\\nnew_config['DefaultCacheBehavior']['TargetOriginId'] = conf_origin \\n\\nnew_config['Comment'] = conf_domain \\n\\nnew_config['ViewerCertificate']['ACMCertificateArn'] = certArn \\n\\nreturn new_config \\n\\n\\n\\n def create_distribution(config): \\n\\ntry: \\n\\ndistribution = cf_client.create_distribution(DistributionConfig=config) \\n\\nlogger.info(f\\"Done! Created distribution {format(distribution['Distribution']['Id'])}.\\") except botocore.exceptions.ClientError as error: \\n\\nlogger.exception(f\\"{format(error)}\\") \\n\\n raise error \\n\\n return(distribution)\\n```\nLambda 函数部分的操作可以参考下图中的示例。\n\n![image.png](https://dev-media.amazoncloud.cn/580aaaa3a749477891a9e34823f76c98_image.png)\n![image.png](https://dev-media.amazoncloud.cn/1281b5e0c62346ad963f0ba4e308f1f1_image.png)\n#### **2. 创建 API Gateway**\n**创建 API Gateway 执行方法**,添加 URL 查询字符串参数(*注意:以下参数均为小写)。\n\n- domain:创建的 Distribution 所关联的 CNAME,即加速域名;\n- origin:新建的 Distribution 指向的源站域名;\n- ref_dist:参考 Distribution,新建 Distribution 参数参考 Ref_dist 的参数配置,仅修改 CNAME 和 Origin 域名。\n\nAPI Gateway 部分的操作可以参考下图中的示例。\n![image.png](https://dev-media.amazoncloud.cn/2cc7cf217547484989c0dbf0bb3e45fd_image.png)\n\n#### **3. 部署身份认证服务 [Amazon Cognito](https://aws.amazon.com/cn/cognito/?trk=cndc-detail)**\n默认 API Gateway 创建的 API 是公开的,所有人都可以访问,缺少身份认证部分。本方案会创建一个 Cognito 用户池、域名、资源服务器和应用程序客户端用来实现鉴权,即只有鉴权通过后才能访问 API。\n\nCognito 部分的操作可以参考下图中的示例。\n![image.png](https://dev-media.amazoncloud.cn/552d8917724e4f47a9fe5c9b60388813_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/3872096ee9e7482995922719da3e52fa_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/ff24c7f2fc9446da8e8444e6329f2166_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/80b6dad15cc54eb1b3be6ffa1f117c1d_image.png)\n\n然后返回到 API Gateway 页面,在 API Gateway 中创建一个 Cognito 授权方,将其配置到相应 API 资源中, Cognito 的令牌需要配置在 Authorization 标头中,如下图所示。\n\n![image.png](https://dev-media.amazoncloud.cn/d38d36c7928b492ebdd04a321ee02f5f_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/94ad615ebbc94897a0a318c2252b2aad_image.png)\n\n#### **4 测试验证**\ncurl -X POST -u <应用程序客户端ID>:<应用程序客户端密钥>\\n\\n```\\n'https://clone-distribution.auth.us-east-1.amazoncognito.com/oauth2/token?grant_type=client_credentials' -H 'Content-Type: application/x-www-form-urlencoded'\\n\\n```\\n执行完命令后会得到访问令牌,如下图所示:\\n\\n![image.png](https://dev-media.amazoncloud.cn/1818e7cf73e749c9a583430ee0fd53ef_image.png)\\n\\n这里,API 测试工具选择 Postman,打开工具后添加 header(key 为 Authorization,value 为上图中的 access_token),输入 API 链接,并加上查询字符串后发送请求。\\n\\n链接示例:\\n\\nhttps://5xx44xx6x0.execute-api.us-east-1.amazonaws.com/prod/?domain=service.yuhong.com&origin=ec2-ip.compute-1.amazonaws.com&ref_dist=E2Z4DXXXXXXXXX\\n![image.png](https://dev-media.amazoncloud.cn/1ad671b0ece641c7a9e2a139e154e52a_image.png)\\n如上图所示,CloudFront Distribution 复制成功,返回新创建的 CloudFront Distribution ID。\\n\\n#### **总结**\\n本文介绍了一种通过 Serverless 服务 Amazon API Gateway 和 Amazon Lambda 以及Amazon CloudFront SDK 来复制/克隆 CloudFront Distribution 的 Restful API 实现,并通过 Cognito 在 API Gateway 访问请求中提供了安全的访问接口。该方案适用于需要将相同配置项应用于多个 Distribution 域名的情形,能实际有效地简化客户的配置管理工作量,减少人工操作出错概率,并且可以达到快速一键部署的目的。\\n\\n##### **本篇作者**\\n\\n**马宇红**\\n亚马逊云科技技术客户经理,负责企业级大客户的运维与架构优化、成本管理、项目交付、技术咨询等。\\n加入亚马逊云科技前曾供职于 IBM 中国软件开发中心,拥有分布式软件开发经验。目前致力于 Edge、DevOps、Serverless 等方向的研究和实践。\\n\\n**史天**\\n亚马逊云科技资深解决方案架构师。拥有丰富的云计算、数据分析和机器学习经验。\\n目前致力于数据科学、机器学习、无服务器等领域的研究和实践。译有《机器学习即服务》《基于 Kubernetes的DevOps 实践》《Kubernetes 微服务实战》《Prometheus 监控实战》《云原生时代的 CoreDNS 学习指南》等。\\n\\n[阅读原文](https://github.com/yuhuiaws/ML-study/tree/main/)","render":"<h4><a id=\\"_0\\"></a><strong>背景</strong></h4>\\n<p><strong>Amazon CloudFront</strong> 是一个全球性的内容分发网络 (CDN),您可以借助 CloudFront 以低延迟和高可用性向查看者或者最终用户分发内容。通常来讲,[Amazon CloudFront](https://aws.amazon.com/cn/cloudfront/?trk=cndc-detail) 的客户都拥有<strong>多个 CloudFront Distribution</strong>,每个 Distribution 都包含了一组待加速的内容以及相关的配置信息,例如源站、加速域名、缓存策略、证书、日志、访问控制等。</p>\\n<p>以下主题说明了一些有关 <strong>CloudFront Distribution 的基础知识</strong>,并提供了有关可选择用来配置 Distribution 以满足业务需求的设置的信息。在 CloudFront 自动化操作的过程中,一个常见的任务是<strong>创建多个 Distribution</strong>,这些 Distribution 的配置在同一类型作业下配置参数完全相同,例如动态加速场景下缓存 TTL 均设置为0。在该场景下,客户往往需要一个克隆已存在的 Distribution 的配置项,并希望通过标准 Restful API 接口调用以实现借助程序快速创建多个域名 Distribution 的目的。而目前亚马逊云科技支持的新建 Distribution 的 API :CreateDistribution,需要提供完整的配置参数信息,暂时不支持复制或者克隆 Distribution 的功能。</p>\\n<p>本文介绍的方案是利用 <strong>Amazon API Gateway 和 Amazon Lambda</strong>,后端基于<strong>Amazon SDK for Python (Boto3)</strong>,实现基于一个参考 Distribution的配置信息,仅修改域名与源站等变更项,来复制一个新 Distribution 的 Restful API。</p>\\n<h3><a id=\\"_8\\"></a><strong>解决方案简介</strong></h3>\\n<p><strong>CloudFront Distribution</strong> 复制/克隆功能的解决方案基本实现思路如下:</p>\\n<p>1)复制已存在有一个参考 Distribution 的配置信息;<br />\\n2)改造可变项,例如新 Distribution 的加速域名 CNAME 和源站的域名,组成新的完整配置信息;<br />\\n3)执行新建 Distribution 的操作。</p>\n<p>在此过程中,会调用 <strong>Amazon Certificate Manager</strong> 的相关 API 查找到新 CNAME 所对应的 ACM 证书的 ARN,从而完成加速域名的证书关联操作。其中,查询证书 ARN 的过程对用户是透明的。</p>\\n<h4><a id=\\"_17\\"></a><strong>解决方案的详细实现流程如下:</strong></h4>\\n<p>1)<strong>获取参考域名的配置信息做为基准配置(<strong>函数:get_reference_config)<br />\\n调用</strong>Amazon Boto3 API get_distribution_config</strong>,输入 distribution id,以 Json 形式获取 distribution 的配置信息 DistributionConfig;</p>\n<p>2)<strong>获取账号下 ACM 证书与域名的对应列表</strong>(函数get_certificate_mapping)<br />\\n调用Amazon Boto3 API list_certificates,输入 CertificateStatuses=’ISSUED’,查询账号下 ACM 中的已签发的证书与域名的对应列表;</p>\n<p>3)<strong>获取该 CNAME 对应的 ACM 证书</strong>(函数get_certificate_arn)<br />\\n从步骤3中查出的列表里取得源站 origin 所对应的 ACM 证书的 ARN(Amazon Resource Names,资源名的唯一标识符),用于下一步创建 Distribution 时的证书参数;</p>\n<p>4)<strong>构造新Distribution的配置信息</strong>(函数set_config_based_on_ref)<br />\\n根据步骤一获取的基准 DistributionConfig,修改域名和源站,添加证书 ARN,创建新的 DistributionConfig</p>\n<p>5)<strong>创建新Distribution</strong>(函数create_distribution)<br />\\n调用 Amazon Boto3 API create_distribution, 输入步骤4构造的 DistributionConfig,创建所需Distribution。</p>\n<h3><a id=\\"_34\\"></a><strong>解决方案架构</strong></h3>\\n<p>方案用户接口通过 <strong>API Gateway</strong> 和 <strong>Lambda 函数 cf_distribution_clone</strong> 生成一个 Restful API clone_distribution, 函数 cf_distribution_clone 会依据触发 event 中的 queryStringParameters 创建克隆的 Distribution。为了进一步加强安全管理限制 API 的访问,此例中 API Gateway 中将<strong>开启 Cognito 授权</strong>,访问接口的用户需携带 Cognito 令牌才能正常请求 API。方案的架构图如下所示。<br />\\n<img src=\\"https://dev-media.amazoncloud.cn/ec1668ff54b445d6b46e0fed4186d480_image.png\\" alt=\\"image.png\\" /></p>\n<h3><a id=\\"_38\\"></a><strong>解决方案部署</strong></h3>\\n<h4><a id=\\"1__Lambda__40\\"></a><strong>1. 部署 Lambda 函数</strong></h4>\\n<p>创建一个IAM 角色 **cf-clone-distribution-role **供 Lambda 执行时 Assume,为该角色创建如下 IAM 策略,注意需要将与分别替换成 CloudFront 日志所在的 S3 Bucket 的桶名与账号 ID。该策略具有对已有 Distribution 的配置查询权限,新建 Distribution 权限,ACM 证书的列出权限,以及对 CloudFront 日志所在 S3 存储桶的 Bucket ACL 的查询与修改权限。示例以美东区域 us-east-1 作为参考,可以根据实际情况进行替换。</p>\n<pre><code class=\\"lang-\\">{ \\n &quot;Version&quot;: &quot;2012-10-17&quot;, \\n &quot;Statement&quot;: [ \\n { \\n &quot;Sid&quot;: &quot;VisualEditor0&quot;,\\n &quot;Effect&quot;: &quot;Allow&quot;, \\n &quot;Action&quot;: [ \\n &quot;acm:ListCertificates&quot;, &quot;cloudfront:CreateDistribution&quot;, &quot;cloudfront:GetDistributionConfig&quot; \\n ], \\n &quot;Resource&quot;: &quot;*&quot; \\n }, \\n {\\n &quot;Sid&quot;: &quot;VisualEditor1&quot;, \\n &quot;Effect&quot;: &quot;Allow&quot;, \\n &quot;Action&quot;: [ \\n &quot;s3:PutBucketAcl&quot;,\\n &quot;s3:GetBucketAcl&quot;, \\n &quot;logs:CreateLogGroup&quot;\\n ], \\n &quot;Resource&quot;: [\\n &quot;arn:aws:s3:::&quot;, \\n &quot;arn:aws:logs:us-east-1::*&quot; \\n ] \\n }, \\n {\\n &quot;Sid&quot;: &quot;VisualEditor2&quot;, \\n &quot;Effect&quot;: &quot;Allow&quot;,\\n &quot;Action&quot;: [ \\n &quot;logs:CreateLogStream&quot;, \\n &quot;logs:PutLogEvents&quot;\\n ], \\n &quot;Resource&quot;: &quot;arn:aws:logs:us-east-1: ::log-group:/aws/lambda/cf_distribution_clone:*&quot;\\n } \\n ] \\n }\\n</code></pre>\\n<p>创建 Lambda函数 cf_distribution_clone,设置 Lambda 执行角色为上文创建的 <strong>cf-clone-distribution-role</strong>。解决方案使用的运行时环境为 Python 3.9,其对应的完整 Lambda 代码如下所示。</p>\\n<pre><code class=\\"lang-\\">import boto3 \\nfrom botocore.config import Config \\nimport botocore.exceptions \\nfrom datetime import datetime, timezone\\nimport logging \\nimport json \\n logging.basicConfig(level=logging.INFO)\\n logger = logging.getLogger() \\n cf_client = boto3.client('cloudfront') \\n my_config = Config( \\nregion_name = 'us-east-1'\\n ) \\nacm_client = boto3.client('acm', config=my_config) \\n def lambda_handler(event, context): \\nconf_domain = event['queryStringParameters']['domain'] \\nconf_origin = event['queryStringParameters']['origin'] \\nconf_ref_dist = event['queryStringParameters']['ref_dist']\\n# 1) 获取参考域名的配置信息做为基准配置 \\nref_config = get_reference_config(conf_ref_dist) \\n# 2) 获取账号下ACM证书与域名的对应列表 \\ncerts = get_certificate_mapping() \\n# 3) 获取该CNAME对应的ACM证书 \\ncertArn = get_certificate_arn(certs, conf_domain) \\n# 4) 构造新Distribution的配置信息 \\nnew_config = set_config_based_on_ref(ref_config, conf_domain, conf_origin, certArn) \\n# 5) 创建新Distribution \\ndistribution = create_distribution(new_config) response_body = {}\\nresponse_body['requestId'] = context.aws_request_id \\nresponse_body['distributionId'] = distribution['Distribution']['Id'] \\nresponseObject = {} \\nresponseObject['statusCode'] = 200 \\nresponseObject['body'] = json.dumps(response_body) \\nreturn(responseObject) \\n def get_reference_config(ref_dist): \\ntry: \\nreturn cf_client.get_distribution_config(Id=ref_dist) \\nexcept botocore.exceptions.ClientError as error: \\nlogger.exception(f&quot;{format(error)}&quot;)\\nraise error\\ndef get_certificate_mapping(): \\ntry: \\nresponse = acm_client.list_certificates( \\n\\nCertificateStatuses=[ \\n\\n'ISSUED' \\n\\n], \\n\\nMaxItems=1000 \\n\\n) \\n\\ncerts = response['CertificateSummaryList'] \\n\\nwhile &quot;NextToken&quot; in response: \\n\\nresponse = acm_client.list_certificates( \\n\\nCertificateStatuses=[ \\n\\n'ISSUED' \\n\\n ], \\n\\nMaxItems=1000, \\n\\nNextToken= response['NextToken'] \\n\\n ) \\n\\ncerts.extend(response[&quot;CertificateSummaryList&quot;]) \\n\\ncert_dict = {} \\n\\nfor cert in certs: \\n\\ncert_dict[cert['DomainName']] = cert['CertificateArn'] \\n\\nreturn(cert_dict) \\n\\nexcept botocore.exceptions.ClientError as error: \\n\\nlogger.exception(f&quot;{format(error)}&quot;) \\n\\nraise error\\n\\n\\n\\ndef get_certificate_arn(certs, domain): \\n\\nif domain in certs: \\n\\ncert = certs[domain] \\n\\nelse: \\n\\ncert_domain = '*.' + domain.split(&quot;.&quot;, 1)[-1] \\n\\nif cert_domain in certs: \\n\\ncert = certs[cert_domain] \\n\\nelse: \\n\\nlogger.info(f&quot;No certificate for domain - {format(domain)} in ACM. Please create or import one.&quot;) exit(1) logger.info(f&quot;Use ACM certificate for domain \\\\'{format(domain)}\\\\': {format(cert)}.&quot;) return cert \\n\\n\\n\\n def set_config_based_on_ref(ref_config, conf_domain, conf_origin, certArn): \\n\\nref_config['DistributionConfig']['Aliases'] = { \\n\\n'Quantity': 1, \\n\\n'Items': [ \\n\\nconf_domain \\n\\n] \\n\\n} \\n\\nnew_config = ref_config['DistributionConfig'] \\n\\nnew_config['CallerReference'] = str(datetime.now(tz=None).timestamp()) \\n\\nnew_config['Origins']['Items'][0]['Id'] = conf_origin \\n\\nnew_config['Origins']['Items'][0]['DomainName'] = conf_origin \\n\\nnew_config['DefaultCacheBehavior']['TargetOriginId'] = conf_origin \\n\\nnew_config['Comment'] = conf_domain \\n\\nnew_config['ViewerCertificate']['ACMCertificateArn'] = certArn \\n\\nreturn new_config \\n\\n\\n\\n def create_distribution(config): \\n\\ntry: \\n\\ndistribution = cf_client.create_distribution(DistributionConfig=config) \\n\\nlogger.info(f&quot;Done! Created distribution {format(distribution['Distribution']['Id'])}.&quot;) except botocore.exceptions.ClientError as error: \\n\\nlogger.exception(f&quot;{format(error)}&quot;) \\n\\n raise error \\n\\n return(distribution)\\n</code></pre>\\n<p>Lambda 函数部分的操作可以参考下图中的示例。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/580aaaa3a749477891a9e34823f76c98_image.png\\" alt=\\"image.png\\" /><br />\\n<img src=\\"https://dev-media.amazoncloud.cn/1281b5e0c62346ad963f0ba4e308f1f1_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"2__API_Gateway_241\\"></a><strong>2. 创建 API Gateway</strong></h4>\\n<p><strong>创建 API Gateway 执行方法</strong>,添加 URL 查询字符串参数(*注意:以下参数均为小写)。</p>\\n<ul>\\n<li>domain:创建的 Distribution 所关联的 CNAME,即加速域名;</li>\n<li>origin:新建的 Distribution 指向的源站域名;</li>\n<li>ref_dist:参考 Distribution,新建 Distribution 参数参考 Ref_dist 的参数配置,仅修改 CNAME 和 Origin 域名。</li>\n</ul>\\n<p>API Gateway 部分的操作可以参考下图中的示例。<br />\\n<img src=\\"https://dev-media.amazoncloud.cn/2cc7cf217547484989c0dbf0bb3e45fd_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"3__Amazon_Cognito_251\\"></a><strong>3. 部署身份认证服务 Amazon Cognito</strong></h4>\\n<p>默认 API Gateway 创建的 API 是公开的,所有人都可以访问,缺少身份认证部分。本方案会创建一个 Cognito 用户池、域名、资源服务器和应用程序客户端用来实现鉴权,即只有鉴权通过后才能访问 API。</p>\n<p>Cognito 部分的操作可以参考下图中的示例。<br />\\n<img src=\\"https://dev-media.amazoncloud.cn/552d8917724e4f47a9fe5c9b60388813_image.png\\" alt=\\"image.png\\" /></p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/3872096ee9e7482995922719da3e52fa_image.png\\" alt=\\"image.png\\" /></p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/ff24c7f2fc9446da8e8444e6329f2166_image.png\\" alt=\\"image.png\\" /></p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/80b6dad15cc54eb1b3be6ffa1f117c1d_image.png\\" alt=\\"image.png\\" /></p>\n<p>然后返回到 API Gateway 页面,在 API Gateway 中创建一个 Cognito 授权方,将其配置到相应 API 资源中, Cognito 的令牌需要配置在 Authorization 标头中,如下图所示。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/d38d36c7928b492ebdd04a321ee02f5f_image.png\\" alt=\\"image.png\\" /></p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/94ad615ebbc94897a0a318c2252b2aad_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"4__269\\"></a><strong>4 测试验证</strong></h4>\\n<p>curl -X POST -u &lt;应用程序客户端ID&gt;:&lt;应用程序客户端密钥&gt;</p>\n<pre><code class=\\"lang-\\">'https://clone-distribution.auth.us-east-1.amazoncognito.com/oauth2/token?grant_type=client_credentials' -H 'Content-Type: application/x-www-form-urlencoded'\\n\\n</code></pre>\\n<p>执行完命令后会得到访问令牌,如下图所示:</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/1818e7cf73e749c9a583430ee0fd53ef_image.png\\" alt=\\"image.png\\" /></p>\n<p>这里,API 测试工具选择 Postman,打开工具后添加 header(key 为 Authorization,value 为上图中的 access_token),输入 API 链接,并加上查询字符串后发送请求。</p>\n<p>链接示例:</p>\n<p>https://5xx44xx6x0.execute-api.us-east-1.amazonaws.com/prod/?domain=service.yuhong.com&amp;origin=ec2-ip.compute-1.amazonaws.com&amp;ref_dist=E2Z4DXXXXXXXXX<br />\\n<img src=\\"https://dev-media.amazoncloud.cn/1ad671b0ece641c7a9e2a139e154e52a_image.png\\" alt=\\"image.png\\" /><br />\\n如上图所示,CloudFront Distribution 复制成功,返回新创建的 CloudFront Distribution ID。</p>\n<h4><a id=\\"_288\\"></a><strong>总结</strong></h4>\\n<p>本文介绍了一种通过 Serverless 服务 Amazon API Gateway 和 Amazon Lambda 以及Amazon CloudFront SDK 来复制/克隆 CloudFront Distribution 的 Restful API 实现,并通过 Cognito 在 API Gateway 访问请求中提供了安全的访问接口。该方案适用于需要将相同配置项应用于多个 Distribution 域名的情形,能实际有效地简化客户的配置管理工作量,减少人工操作出错概率,并且可以达到快速一键部署的目的。</p>\n<h5><a id=\\"_291\\"></a><strong>本篇作者</strong></h5>\\n<p><strong>马宇红</strong><br />\\n亚马逊云科技技术客户经理,负责企业级大客户的运维与架构优化、成本管理、项目交付、技术咨询等。<br />\\n加入亚马逊云科技前曾供职于 IBM 中国软件开发中心,拥有分布式软件开发经验。目前致力于 Edge、DevOps、Serverless 等方向的研究和实践。</p>\n<p><strong>史天</strong><br />\\n亚马逊云科技资深解决方案架构师。拥有丰富的云计算、数据分析和机器学习经验。<br />\\n目前致力于数据科学、机器学习、无服务器等领域的研究和实践。译有《机器学习即服务》《基于 Kubernetes的DevOps 实践》《Kubernetes 微服务实战》《Prometheus 监控实战》《云原生时代的 CoreDNS 学习指南》等。</p>\n<p><a href=\\"https://github.com/yuhuiaws/ML-study/tree/main/\\" target=\\"_blank\\">阅读原文</a></p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭