【问题标题】:AWS CodePipeline with ECS containers using CodeDeploy appspec.yml fileAWS CodePipeline 与 ECS 容器使用 CodeDeploy appspec.yml 文件
【发布时间】:2021-08-24 19:08:50
【问题描述】:

我在 CodePipeline 中使用 ECS 容器和 CodeBuild 和 CodeDeploy(用于蓝/绿部署)阶段。我在应用程序代码的根目录中有一个 appspec.yml 文件,其中包含任务定义 arn 和容器名称。所有这些在单一环境场景中运行良好。在我的情况下,当我有一个单独的用于开发、测试和生产的 AWS 账户时,我需要 CodeDeploy 来根据环境上下文交换任务定义 arn。有没有办法像我们为 buildspec.yml 文件和自定义环境变量一样传递参数和修改 appspec.yml 文件?如果没有,使用 appspec.yml 文件进行跨账户部署的最佳解决方案是什么?

更新

感谢 Ronan Cunningham 的 Python 脚本 - 请参见下面的代码示例 - 它允许生成 appspec.json 文件作为 CodeBuild 阶段的工件并将其作为输入传递给 CodeDeploy 阶段。您可以从 buildspec.yml 文件调用脚本并将自定义环境变量作为脚本参数传递,这将根据 AWS 环境上下文定义您的 appspec.json。该脚本应与 buildspec.yml 和 Dockerfile 一起放置在应用程序的根目录中。

create_appspec_json.py 脚本:

#!/usr/bin/python
import json
from sys import argv


def print_obj_to_disk(obj, file_type):
    if file_type == 'app_spec':
        file_name = 'appspec.json'
    if file_type == 'task_def':
        file_name = 'taskdef.json'

    print('Writing {}:'.format(file_name))
    print(obj)
    print(json.dumps(obj, indent=2))
    with open(file_name, 'w') as outfile:
        json.dump(obj, outfile, indent=2)


def return_appspec(container_name, container_port, task_definition_arn):
    appsec_obj = {
        "version": 0.0,
        "Resources": [
            {
                "TargetService": {
                    "Type": "AWS::ECS::Service",
                    "Properties": {
                        "TaskDefinition": task_definition_arn,
                        "LoadBalancerInfo": {
                            "ContainerName": container_name,
                            "ContainerPort": container_port
                        },
                    }
                }
            }
        ]
    }
    return appsec_obj


appspec_obj = return_appspec(argv[1], argv[2], argv[3])

print_obj_to_disk(appspec_obj, 'app_spec')

从 buildspec.yml 调用脚本并将环境变量作为参数传递。

artifacts: 
  files: 
    - appspec.json
phases:
  install:
    commands:
      - pip install boto3
  build: 
    commands: 
      - python create_appspec_json.py $CONTAINER_NAME $CONTAINER_PORT $TASK_DEFINITION_ARN
      ...
  post_build: 
    commands: 
      ...
  pre_build: 
    commands:
      ...
version: 0.2

【问题讨论】:

    标签: aws-codepipeline aws-code-deploy aws-code-deploy-appspec


    【解决方案1】:

    我的方法(听起来像是相同的管道设置)是在具有 CodeDeployToECS 操作的阶段之前有一个具有 CodeBuild 操作的阶段。前者的工作是以编程方式生成应用规范和任务定义,后者的 OutputArtifact 和 InputArtifact。
    任何必需的参数都通过管道操作 Configuration 传递给 CodeBuild 项目。

    更新:

    基本方法如下:

    在 codedeploy 之前的 codebuild 项目运行 python 脚本来生成 appspec 和任务定义,根据需要查找值并将它们作为 json 文件写入磁盘。这不是完整的脚本,只是 app_spec 部分作为示例。

    def print_obj_to_disk(obj,type):
    
        if type=='app_spec':
            file_name='appspec.json'
        if type=='task_def':
            file_name='taskdef.json'
    
        print('Writing {}:'.format(file_name))
        print(obj)
        print(json.dumps(obj, indent=2))
        with open(file_name, 'w') as outfile:
            json.dump(obj, outfile,indent=2)
    
    
    def return_appspec(container_name,container_port,AfterAllowTestTrafficHookFunctionName):
     
        appsec_obj= {
                    "version": 0.0,
                    "Resources": [
                        {
                        "TargetService": {
                            "Type": "AWS::ECS::Service",
                            "Properties": {
                            "TaskDefinition": "<TASK_DEFINITION>",
                            "LoadBalancerInfo": {
                                "ContainerName": container_name,
                                "ContainerPort": container_port
                            },
                            
                            }
                        }
                        }
                    ],
                    "Hooks": [
    
                            {
                                "AfterAllowTestTraffic": AfterAllowTestTrafficHookFunctionName
                            }
    
                    ]
        }
        return appsec_obj
    
    
    appspec_obj=return_appspec('my_container',8080,'FunctionName')
    
    print_obj_to_disk(appspec_obj,'app_spec')
    
    

    此项目处于下面管道定义示例部分的“PrepareCodeDeploy”阶段。 json 文件存储为管道工件,然后成为下一阶段的输入。

    
                - Name: PrepareCodeDeploy
                  ActionTypeId:
                    Category: Build
                    Owner: AWS
                    Provider: CodeBuild
                    Version: "1"
                  Configuration:
                    ProjectName: !ImportValue CodeDeployPrepareProjectName
                    EnvironmentVariables: !Sub "[{\"name\":\"Environment\",\"value\":\"Testing\",\"type\":\"PLAINTEXT\"},{\"name\":\"Account_Id\",\"value\":\"${TestingAccountId}\",\"type\":\"PLAINTEXT\"},{\"name\":\"Region\",\"value\":\"${DeployRegion1}\",\"type\":\"PLAINTEXT\"},{\"name\":\"BucketKmsKey\",\"value\":\"${TestingAccountKmsKeyId}\",\"type\":\"PLAINTEXT\"}]"
                  InputArtifacts:
                    - Name: !Ref SourceArtifactName
                  OutputArtifacts:
                    - Name: PrepareCodeDeployOutputTesting
                  RunOrder: 3
    
                - Name: BlueGreenDeploy
                  InputArtifacts:
                  - Name: BuildDockerOutput
                  - Name: PrepareCodeDeployOutputTesting
                  Region: !Ref DeployRegion1
                  ActionTypeId:
                    Category: Deploy
                    Owner: AWS
                    Version: '1'
                    Provider: CodeDeployToECS
                  RoleArn: !Sub arn:aws:iam::${TestingAccountId}:role/whatever/CrossAccountsDeploymentRole
                  Configuration:
    
                    AppSpecTemplateArtifact: PrepareCodeDeployOutputTesting
                    AppSpecTemplatePath: appspec.json
    
                    ApplicationName: !Ref ApplicationName
                    DeploymentGroupName: !Ref ApplicationName
                    
                    TaskDefinitionTemplateArtifact: PrepareCodeDeployOutputTesting
                    TaskDefinitionTemplatePath: taskdef.json
    
                    Image1ArtifactName: BuildDockerOutput
                    Image1ContainerName: "IMAGE1_NAME"
                  RunOrder: 4
    
    

    希望这会给你一些想法,让你知道如何让它以适合你的方式工作。我刚刚决定写一篇关于我的方法的博客文章,虽然并不完美,但确实有效,并遵循了一条以线性方式创建管道和所有必需组件的路径。官方文档似乎有很多不同的方向,我看到很多人都在为此苦苦挣扎。

    还有什么问题,尽管问。

    【讨论】:

    • 我的也部署在跨帐户设置中 - 您没有说明该特定方面如何或是否会导致问题。
    • 我也在考虑在 CodeBuild 阶段生成 appspec.yml。像- printf '[{"name":"imageName","imageUri":"%s"}]' $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG &gt; imagedefinitions.json 这样的东西只有这个例子适用于 JSON 格式和不同的内容类型。然后将其转换为 .yml 文件。不过,也许有更好的方法。有没有一个例子说明你是如何做到的?
    • 刚刚看到这个——当然——我会把它挖出来给你发一个链接。如果我记得我最初将 python 与 taskdef 和 appspec 一起用作字典。我用相关值更新它们,转换为 json,然后写入磁盘。无需转换为 yaml。
    • 罗南,非常感谢这个例子!很高兴阅读您关于此主题的博客文章。再次感谢。
    猜你喜欢
    • 1970-01-01
    • 2016-08-25
    • 1970-01-01
    • 2016-03-15
    • 2021-10-12
    • 1970-01-01
    • 2021-07-21
    • 2018-03-16
    • 2018-05-16
    相关资源
    最近更新 更多