配置CloudFront及Lambda@Edge为Bedrock加速

本文基于Github上作者jief123的方案编写。Github官方文档方案采用CDK形式部署,而本文是描述如何手工部署。

一、背景

Bedrock服务提供的多种大模型使用中需要遵循各模型原厂的合规要求。为了实现网络加速,可在全球统一配置一个Bedrock流量入口,为全球多地区的应用加速。

整个架构的主要步骤如下:

  • Bedrock的模型调用选择us-west-2区域
  • 配置CloudFront加速加速点,仅使用北美和欧洲的POP节点
  • 在CloudFront特定的us-east-1区域的Lambda控制台配置Lambda@Edge,然后关联到CloudFront
  • 修改代码重定向流量到CloudFront发布点

二、确认Bedrock上模型权限已经打开

通过AWS官方文档查询Bedrock的互联网Endpoint调用地址,例如在us-west-2,地址是bedrock.us-west-2.amazonaws.com。其他区域的Endpoint,请查询本文末尾的参考文档。 

三、配置CloudFront

在控制台左上角,输入CloudFront,然后点击服务打开控制台。如下截图。

点击新建发布点按钮,创建新的发布点。如下截图。

在发布点创建向导界面,在Origin源位置,输入上一步确认的Bedrock的互联网节点,例如本文使用us-west-2,地址是bedrock.us-west-2.amazonaws.com。在访问协议中,选择HTTPS only。本页其他选项无须改动,继续向下滚动页面。如下截图。

Enable Origin Shield位置,选择Yes,然后把Origin Shield的区域选择为us-west-2,与Bedrock所在Region一致。本页其他选项无须改动,继续向下滚动页面。如下截图。

Default cache behavior位置,选择允许的HTTP方法是GET, HEAD, OPTIONS, PUT, POST, PATCH, DELETE。这样配置的原因是Bedrock使用的是POST方法。本页其他选项无须改动,继续向下滚动页面。如下截图。

在缓存回源策略Cache key and origin requests位置,选择默认的Cache policy and origin request policy (recommended),然后在Cache policy位置选择CachingDisabled,在Origin request policy - optional位置选择AllViewer。本页其他选项无须改动,继续向下滚动页面。如下截图。

Web Application Firewall (WAF)的位置,选择不启用Web Application Firewall (WAF)。如果后续需要WAF的话,可以在后期额外配置。在Settings下的Price class位置,选择Use only North America and Europe。注意这个选项很重要,这样可以避免不合规的地区发起的调用。本页其他选项无须改动,继续向下滚动页面。如下截图。

本页其他选项无须改动,继续向下滚动页面。点击创建按钮,完成创建。如下截图。

创建后,在发布点页面,将可以看到CloudFront为新创建的分配点自动创建了一个Endpoint,即Distribution domain name,其域名是xxxxx.cloudfront.net的域名。将这个域名复制下来,最后代码中将会使用这个Endpoint。如下截图。

至此CloudFront发布点创建完成。

四、配置Lambda@Edge

1、创建Lambda@Edge

进去控制台,切换到us-east-1区域。就需要注意,无论您的Bedrock使用哪一个区域,Lambda@Edge必须部署在us-east-1区域。只有us-east-1区域的Lambda才可以与CloudFront进行集成。因此请切换当前区域。

查找Lambda服务,点击并进入。如下截图。

在Lambda控制台上,点击Create function按钮创建新的函数。如下截图。

在创建向导中,选择类型是Use a blueprint,在Blueprint里边,从下拉框选择Modify HTTP response header选项,这里选这个是为了使用Lambda自己的权限模版,这样比从空白开始创建配置更简单。在名称位置,输入自定义名称,例如bedrock-proxy。如下截图。

在执行者权限Excution role位置,选择Create a new role from AWS policy templates,然后输入角色名称例如bedrock-proxy。其他选项无须修改。继续向下滚动页面。如下截图。

将页面滚动到最下方,在代码部分,无须修改,稍后创建后再修改。点击右下角的Create按钮创建函数。如下截图。

由此创建函数完毕。

2、上传代码并发布新版本

函数创建完毕,页面提示是否要将Lambda@Edge和CloudFront发布点关联。这里选择取消,不进行关联。如下截图。

创建Lambda函数完毕。现在修改代码,粘贴如下代码到编辑窗口:

export const handler = async (event) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    
    request.origin = {
      custom: {
          domainName: `bedrock-runtime.us-west-2.amazonaws.com`,
          port: 443,
          protocol: 'https',
          sslProtocols: ['TLSv1.2'],
          readTimeout: 60,
          keepaliveTimeout: 5,
          customHeaders: {}
      }
    };
    
    return request;
  };

粘贴完毕后,点击左侧的Deploy部署按钮。如下截图。

部署完毕后,还要发布新版本。在Lambda控制台上切换到最后一个标签页Versions,然后点击右上角的Publish new version,发布新版本。如下截图。

在发布向导中,输入一个友好备注信息,这里不需要填写版本号码,因为Lambda会自动生成版本好。如下截图。

创建新版本号完成。点击版本号进入详情页面。如下截图。

从右上角可看到本Lambda的ARN,将其复制下来,下一步会使用。如下截图。

发布新代码完成。

3、关联到CloudFront发布点

进入CloudFront发布点界面,选择前文创建的CloudFront发布点。点击进入详情。如下截图。

在发布点里边,找到行为Behaviors标签页,选择默认的行为,点击编辑按钮。如下截图。

将页面滚动到最下方,找到Function associations位置,找到Origin request的下拉框,在下拉框中选择Lambda@Edge,然后在ARN位置输入上一步复制出来的ARN,然后点击保存修改按钮。如下截图。

以上步骤如果无法通过,很可能是之前创建Lambda@Edge时候么有选择正确的区域,请参考本文前序章节创建Lambda@Edge的部分。

返回CloudFront发布点,可以看到刚修改的发布点,显示为部署中Deploying,需要等到显示部署完成后,才可以发起测试。这一步将把Lambda函数部署到CloudFront的全球几百个加速点,可能需要5分钟时间才能完成。

至此CloudFront和Lambda绑定完成。等待几分钟即可访问。

五、在调用Bedrock的代码中指定Endpoint

本文以Python语言使用的Boto3 SDK为例,在调用Boto3过程中,可以在Session中加入参数指定Endpoint入口。

例如代码如下:

import boto3
import json

region_name = 'us-west-2'
#model_id = 'anthropic.claude-3-5-sonnet-20241022-v2:0'
model_id = 'anthropic.claude-3-5-sonnet-20240620-v1:0'

message_text = "写一段废话文学,不超过500个汉字,要求全都是废话,说了又仿佛没说。"
system_prompt = [
        { "text": "你是写文章助手" }
    ]

#session = boto3.Session(region_name='us-west-2')
session = boto3.Session(
    aws_access_key_id='XXXXXXXXXXXXXX',
    aws_secret_access_key='XXXXXXXXXXXXXXXXXXXXXXXXXXXX',
    region_name='us-west-2'
    )
bedrock = session.client(
    service_name="bedrock-runtime",
    endpoint_url='https://dptp3eos7v8rh.cloudfront.net'
    )

message_list = []
initial_message = {
    "role": "user",
    "content": [
        { "text": message_text } 
    ],
}
message_list.append(initial_message)

response = bedrock.converse(
    modelId = model_id,
    system = system_prompt ,
    messages = message_list ,
    inferenceConfig={
        "maxTokens": 2000,
        "temperature": 0
    },
)

# debug
# print(response)

# Only get content from response
response_message = response['output']['message']['content'][0]['text']
print(response_message)

# Print token usage
print("\n\n--- Token usage ---")
print("Usage:", json.dumps(response['usage'], indent=4))

执行完毕,可看到正常返回结果。

人生如梦,梦如人生。我们每个人都在这个世界上走着自己的路,追寻着内心的声音。有人说生活是一杯白开水,平淡无奇;也有人说生活是一杯咖啡,苦涩中带着醇香。其实生活就像一面镜
子,你对它微笑,它就对你微笑;你对它皱眉,它就对你皱眉。所以说,态度决定一切。

我们常常在追求完美的路上迷失了自我,殊不知真正的完美是接纳不完美。就像蝴蝶,如果不经历破茧的痛苦,怎能展翅高飞?所以说,没有伤痛就没有成长。生活中难免会遇到挫折和困难,但
这恰恰是上天给我们的礼物,让我们变得更加坚强。

时间就像海绵里的水,挤一挤总是有的。我们要学会珍惜时间,因为时间是我们最宝贵的财富。古人云:一寸光阴一寸金,寸金难买寸光阴。这句话诠释了时间的珍贵。所以,让我们携手并进,
共同努力,创造美好的未来!

--- Token usage ---
Usage: {
    "inputTokens": 50,
    "outputTokens": 391,
    "totalTokens": 441
}

至此确认CloudFront、Lambda@Edge和Bedrock都工作正常。

六、参考文档

Bedrock CloudFront 加速方案

https://github.com/jief123/bedrockproxy

Bedrock Endpoint

https://docs.aws.amazon.com/general/latest/gr/bedrock.html