首页 > 解决方案 > AWS CodePipeline 与 ECS 容器使用 CodeDeploy appspec.yml 文件

问题描述

我在 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-codepipelineaws-code-deployaws-code-deploy-appspec

解决方案


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

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

任何进一步的问题,只是问。


推荐阅读