python-3.x 使用AWS CDK和Chalice cdk构建体在合成的sam模板中接收重复的λ

k4emjkb1  于 2023-03-20  发布在  Python
关注(0)|答案(1)|浏览(116)

在项目中,我们有一个Chalice项目用于整个API +纯lambda(云观察事件、触发器等)并且随着项目的增长,项目生成的lambda越来越大。我们决定把它分成几个圣杯项目,这些项目将合并成一个自定义域名下的API。为了便于管理和部署,我们决定把它们放在CDK项目中。因此,我创建了下一个堆栈,其中包含所有圣杯构造的声明(component_with_stack_declaration.py):

class RestApi(Stack):

    def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
        super().__init__(scope, construct_id, **kwargs)
        ...
        code_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "src")

        app1 = Chalice(self, "App1", source_dir=os.path.join(code_dir, "app1"))
        app2= Chalice(self, "App2", source_dir=os.path.join(code_dir, "app2"))
        ...
When I ran cdk synth I got next traceback:

Creating deployment package.
Creating deployment package.
jsii.errors.JavaScriptError: 
  @jsii/kernel.RuntimeError: Error: section 'Resources' already contains 'Lambda1LogicalID'
      at Kernel._ensureSync (C:\....\AppData\Local\Temp\tmpgg97w2wj\lib\program.js:10364:27)
      at Kernel.invoke (C:\....\AppData\Local\Temp\tmpgg97w2wj\lib\program.js:9764:34)
      at KernelHost.processRequest (C:\....\AppData\Local\Temp\tmpgg97w2wj\lib\program.js:11539:36)
      at KernelHost.run (C:\Users\....\Local\Temp\tmpgg97w2wj\lib\program.js:11499:22)
      at Immediate._onImmediate (C:\....\AppData\Local\Temp\tmpgg97w2wj\lib\program.js:11500:46)
      at processImmediate (node:internal/timers:466:21)

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "B:\...\cdk_app\app.py", line 15, in <module>
    app.synth()
  File "B:\....\cdk_app\venv\lib\site-packages\aws_cdk\__init__.py", line 20043, in synth
    return typing.cast(_CloudAssembly_c693643e, jsii.invoke(self, "synth", [options]))
  File "B:\....\cdk_app\venv\lib\site-packages\jsii\_kernel\__init__.py", line 149, in wrapped
    return _recursize_dereference(kernel, fn(kernel, *args, **kwargs))
  File "B:\....\cdk_app\venv\lib\site-packages\jsii\_kernel\__init__.py", line 399, in invoke
    response = self.provider.invoke(
  File "B:\....\cdk_app\venv\lib\site-packages\jsii\_kernel\providers\process.py", line 377, in invoke
    return self._process.send(request, InvokeResponse)
  File "B:\....\cdk_app\venv\lib\site-packages\jsii\_kernel\providers\process.py", line 339, in send
    raise RuntimeError(resp.error) from JavaScriptError(resp.stack)
RuntimeError: Error: section 'Resources' already contains 'Lambda1LogicalID'

当我查看cdk.out和chalice.out模板中生成的文件时,我发现这个lambda实际上声明了两次。示例来自生成的chalice.out文件夹中的. sam_with_assets.json文件:

Chalice app1
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Outputs": {},
  "Resources": {
    "Lambda1LogicalID": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Runtime": "python3.9",
        "Handler": "chalicelib.app1.handler.lambda1",
        "CodeUri": {
          "Bucket": "cdk-hnb659fds-assets-XXXXXXXXXXXX-us-east-1",
          "Key": "963f64d496d61b07c0f89b0c476f7ff9a0ebdc17e4f8b80484a4f957eecca97e.zip"
        },
        "Tags": {
          "aws-chalice": "version=1.27.3:stage=dev/RestApi:app=app1"
        },
        ...

Chalice app2
{
  "AWSTemplateFormatVersion": "2010-09-09",
  "Transform": "AWS::Serverless-2016-10-31",
  "Outputs": {},
  "Resources": {
    "Lambda1LogicalID": {
      "Type": "AWS::Serverless::Function",
      "Properties": {
        "Runtime": "python3.9",
        "Handler": "chalicelib.app1.handler.lambda1",
        "CodeUri": {
          "Bucket": "cdk-hnb659fds-assets-XXXXXXXXXXXX-us-east-1",
          "Key": "972986e72385367de3787cc5004baa8efe91c8c98335a070ee636b9f7519afd7.zip"
        },
        "Tags": {
          "aws-chalice": "version=1.27.3:stage=dev/RestApi:app=app2"
        },
        ...

正如你所看到的,除了aws-chalice标签和bucket中的键,它们是相同的。我在生成的.zip资产中检查了几次,它们包含正确的2个不同的chalice项目代码,但由于某种原因第二个圣杯项目的模板(app 2)包含与第一个项目中相同的所有lambda(app 1),并且不包含在为其创建的.zip存档的代码中声明的任何lambda。代码按以下顺序存储:

cdk_app
|  - app.py
|  - restapi_stack
      |  - component_with_stack_declaration.py
      |  - src
           | - app1
           |      | - .chalice
           |           chalicelib
           |           app.py
           | - app2
                  | - .chalice
                       chalicelib
                       app.py

也许有人有类似的问题?我很乐意收到您的反馈
我尝试将Chalice构造放在RestApi堆栈的单独内部堆栈中,但得到了相同的结果-代码中的不同资产,但具有相同lambdas资源(分开标记)的相同模板。

更新日期:2023年3月19日

我发现实际的问题是我想为所有的圣杯结构保留相同的圣杯阶段名称,所以我为它们设置了相同的堆栈范围。看起来圣杯结构要求每个堆栈都是单一的。
我试着这样做,因为现在在圣杯构造你不能设置什么圣杯阶段名称从配置文件使用。这将是非常好的有这样的可能性,因为现在你必须设置阶段名称的基础上堆栈全名,什么是不是很友好,如果你迁移现有的应用程序到cdk。
例如,在我的例子中,我有下一个路径dev/RestApi/App 1作为圣杯阶段名称。我将把它包括在项目存储库的问题中
此外,我运行了一些测试,发现了strage行为,即使我成功地部署了不同的lambdas,看起来CDK创建了不同的API,但是使用了与第一个Chalice构造中相同的路径和方法结构,当我一次部署所有堆栈时会发生这种情况(--all flag)。奇怪的是它Map这些相同的APIGateway(不同的id,但相同的路径和方法定义)到不同的lambdas。似乎所有圣杯项目中的API端点(对于APIGateway)定义总是用于第一个圣杯项目。

1cosmwyk

1cosmwyk1#

我通过chalice的代码Map了一点,最后这一次我终于找到了问题所在。Chalice基于导入模块中的代码构建模板,但是在python中导入工作的逻辑是,如果你已经导入了一些模块,你应该重新导入它,仅仅导入不适用于更新。所以实际上发生的是,chalice第一次正确导入项目模块时,它加载了app.py模块然后当它 checkout 到具有相同结构的不同目录时(chalice需要与app.py和chalicelib相同的结构),它试图再次导入app.py,但由于我们已经导入了它,它不会更新它。
发生问题的代码块位于chalice/cli/factory.py中:

class CLIFactory(object):
   ...
      def load_chalice_app(self, environment_variables=None,
                         validate_feature_flags=True):
        ...
        try:
            app = importlib.import_module('app')

            chalice_app = getattr(app, 'app')
            
            ## BEGIN Added code
            module_names = list(sys.modules.keys()).copy()
            for module_name in module_names:
                if "app" == module_name or "chalicelib" in module_name:
                    del sys.modules[module_name]
            ## END

        except SyntaxError as e:
            ...
        return chalice_app
   ...

这实际上可以很容易地解决删除应用程序和chalicelib从导入的sys.modules,但这在我的情况下工作得很好,因为我没有模块/包与同名的2个圣杯项目(appart应用程序和chalicelib).所以我找到了修复它的方法,在更通用的4行调用后调用Chalice cdk构造声明:

module_names = list(sys.modules.keys()).copy()
for module_name in module_names:
  if "app" == module_name or "chalicelib" in module_name:
      del sys.modules[module_name]

你可以直接把它们放在模块中,或者像我在Chalice构造使用之后调用as方法一样。
如果能在Chalice中看到这样的更新就太好了。也许在调用importlib.import_module('app')期间跟踪所有导入的包会有更复杂的变化,包括第三方库,在构建应用程序包后会从系统导入的模块中删除它们,这样我们就不会在不同的应用程序之间发生冲突,但目前我发现的唯一修复是指定的更高

相关问题