如何使用 Cloudformation 强制重新部署我的 API 网关

Posted

技术标签:

【中文标题】如何使用 Cloudformation 强制重新部署我的 API 网关【英文标题】:How do I force redeployment of my API Gateway using Cloudformation 【发布时间】:2016-11-16 18:24:40 【问题描述】:

我正在使用 AWS CloudFormation 创建 API 网关。我有一个AWS::ApiGateway::Deployment 资源,在我创建堆栈时效果很好。但是,如果我更新我的堆栈(例如,更改 AWS::ApiGateway::Method),API 不会再次部署。我必须在 API Gateway 中手动部署 API。

有人知道我如何在堆栈更新时自动部署网关吗?

【问题讨论】:

CloudFormation 团队的人可能会给出更好的解释,但据我了解,每次更新堆栈时,您都需要在模板中创建一个新的部署资源。 另请参阅此问题以供参考***.com/q/41423439/227821 【参考方案1】:

我在描述中添加了日期/时间戳,以便在每次更新模板时强制重新部署。这对我来说很容易,因为我使用对流层并且似乎可以解决问题。或者,您可以传入日期/时间戳作为参数。

【讨论】:

这不再起作用了。更改 AWS::ApiGateway::Deployment 资源的描述只会更新部署的描述字段,而不是创建新的。唯一的方法是使用 Lambda 支持的自定义资源创建部署。 @ÇağatayGürtürk 是正确的。有一个相关的 SO 帖子 here 提供更多信息。 不过,请注意已接受答案中的 cmets。 AWS 似乎提出了不同的解决方案。仍在等待 OP 关于那篇文章的建议。【参考方案2】:

您可以拥有自己的 Stage 和 Deployment 资源,其中 Stage 资源中的 DeploymentId 指的是部署资源,然后您可以使用附加时间戳等方法为部署资源定义动态名称。

这会强制每次重新部署。

【讨论】:

【参考方案3】:

我可能迟到了,但是如果 API 资源发生变化,您可以重新部署这些选项,这可能对仍在寻找选项的人有所帮助 -

    尝试将 AutoDeploy 设置为 true。如果您使用的是 V2 版本的部署。 请注意,您需要通过 V2 创建 APIGW。 V1 和 V2 是 彼此不兼容。 https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-apigatewayv2-stage.html#cfn-apigatewayv2-stage-autodeploy

    Lambda 支持的自定义资源,Lambda 反过来调用 createDeployment API - https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html

    具有调用 Lambda 函数的操作的 CodePipeline 与自定义资源非常相似 - https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html

    SAM(无服务器应用程序模型)遵循与 CloudFormation 类似的语法,它将资源创建简化为抽象,并使用这些抽象来构建和部署普通的 CloudFormation 模板。 https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html

    如果您使用任何抽象层来实现像 Sceptre 这样的 cloudformation,您可以在对资源进行任何更新后调用 createDeployment https://sceptre.cloudreach.com/2.3.0/docs/hooks.html

我选择了第三个选项,因为我一直使用 Scepter 进行 Cloudformation 部署。在 sceptre 中实现钩子也很容易。

【讨论】:

【参考方案4】:

在我们的例子中,使用上述建议的答案将当前时间戳添加到名称/描述以强制进行新部署并不是一个好的解决方案,因为这样每次部署的 Api 调用 URL 都会不同。

当我们所有的 Api 客户都连接到并使用之前的调用 Url 时,这是一个问题,我们不想更新我们的应用程序/强制我们的客户在每次部署时都更新这个 Url..

这是我们的解决方案:

1) 为已部署的 rest-api-id 添加一个输出,如下所示:

Outputs:
  OutputApiId:
    Value: 
      Ref: NameOfMyApiHere
    Description: Api generated Id
    Export: 
      Name: OutputApiId

注意:在我们的例子中,NameOfMyApiHere 是类型“AWS::ApiGateway::RestApi”。

2) 执行 cloudformation 部署/更改(像往常一样)

3) 在 AWS CLI 命令中使用云形成的“OutputApiId”值输出,如下所示:

aws apigateway create-deployment --rest-api-id  OutputApiId  --region my-region

我们使用 ansible 作为我们的部署工具来完成 cloudformation 步骤的工作并捕获返回的输出 - 进而调用 AWS CLI 命令,但我确信还有其他选项。

请参阅 AWS CLI 文档以在此处部署 Rest Api https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-deployments.html

【讨论】:

【参考方案5】:

*** 这是链接问题的转贴。 ***

如果您需要在资源名称中替换 $TIMESTAMP$,我可能会使用它,因为它更简洁,而且您无需进行任何手动 API 网关管理。

我发现这里发布的其他解决方案大部分在完成这项工作时有一个主要警告 - 您不能在 CloudFormation 中分别管理您的 StageDeployment,因为无论何时部署您的 API网关,在部署 API 和辅助流程(自定义资源/lambda、代码管道,你有什么)创建新部署之间,你有某种停机时间。此停机时间是因为 CloudFormation 仅将初始部署绑定到阶段。因此,当您对 Stage 进行更改并进行部署时,它会恢复为初始部署,直到您的辅助流程创建新部署为止。

*** 请注意,如果您在 Deployment 资源上指定 StageName,而不是显式管理 Stage 资源,则其他解决方案将起作用。

在我的情况下,我没有$TIMESTAMP$ 替换件,我需要单独管理我的Stage,以便我可以执行诸如启用缓存之类的操作,因此我必须找到另一种方法。所以工作流程和相关的CF片段如下

在触发 CF 更新之前,请查看您要更新的堆栈是否已经存在。设置stack_exists: true|false

stack_exists 变量传递到您的 CF 模板,一直到创建 DeploymentStage 的堆栈

以下条件:

Conditions:
  StackExists: !Equals [!Ref StackAlreadyExists, "True"]
以下DeploymentStage
  # Only used for initial creation, secondary process re-creates this
  Deployment:
    DeletionPolicy: Retain
    Type: AWS::ApiGateway::Deployment
    Properties:
      Description: "Initial deployment"
      RestApiId: ...

  Stage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !If
        - StackExists
        - !Ref AWS::NoValue
        - !Ref Deployment
      RestApiId: ...
      StageName: ...
执行以下操作的辅助过程:
# looks up `apiId` and `stageName` and sets variables

CURRENT_DEPLOYMENT_ID=$(aws apigateway get-stage --rest-api-id <apiId> --stage-name <stageName> --query 'deploymentId' --output text)
aws apigateway create-deployment --rest-api-id <apiId> --stage-name <stageName>
aws apigateway delete-deployment --rest-api-id <apiId> --deployment-id $CURRENT_DEPLOYMENT_ID

【讨论】:

【参考方案6】:

为了将部署传递到嵌套父级后面的多个模板中,我所做的是使用多阶段 (2) 并强制在每个部署中使用 diff 阶段:

脚本中的aws cli...

apideployment=$(aws cloudformation describe-stacks \
  --stack-name my-api-stack-name \
  --region $awsregion \
  --query "Stacks[0].Parameters[?ParameterKey=='APIDeploymentFlip'].ParameterValue" \
  --output text 2> /dev/null)
# revert the stages in AGW
if [ "$apideployment" = "A" ]
then
  # if A alternate with B
  deployment='B'
else
  # if B alternate with A otherwise defaults to A
  deployment='A'
fi


aws cloudformation deploy \
    --template-file $tempdir/template.yaml \
    --stack-name my-api-stack-name \
    --no-fail-on-empty-changeset \
    --parameter-overrides \
        APIDeploymentFlip=$deployment

在一个或多个模板内...

Parameters:
  APIDeploymentFlip:
    Type: String
    AllowedValues:
      - A
      - B
Conditions:
  IsDeploymentA:
    Fn::Equals: [Ref: APIDeploymentFlip, "A"]
  IsDeploymentB:
    Fn::Equals: [Ref: APIDeploymentFlip, "B"]
Resources:
  AGWDeploymentA:
    Type: AWS::ApiGateway::Deployment
    Condition: IsDeploymentA
    Properties:
      RestApiId: !Ref AGWRestApi
  AGWDeploymentB:
    Type: AWS::ApiGateway::Deployment
    Condition: IsDeploymentB
    Properties:
      RestApiId: !Ref AGWRestApi

  AGWStageA:
    Type: AWS::ApiGateway::Stage
    Condition: IsDeploymentA
    Properties:
      StageName: !Sub '$APIDeploymentFlip-stage1'
      DeploymentId: !Ref AGWDeploymentA
      RestApiId: !Ref AGWRestApi

  AGWStageB:
    Type: AWS::ApiGateway::Stage
    Condition: IsDeploymentB
    Properties:
      StageName: !Sub '$APIDeploymentFlip-stage2'
      DeploymentId: !Ref AGWDeploymentB
      RestApiId: !Ref AGWRestApi

其他资源中使用 If: 的示例阶段引用

  AGWUsagePlan:
    Type: AWS::ApiGateway::UsagePlan
    Properties:
      ApiStages:
        - ApiId: !Ref AGWRestApi
          Stage: !If [IsDeploymentA, !Ref AGWStageA, !Ref AGWStageB]

希望它对其他人有所帮助,因为 apigatewayv1(休息)的这一部分令人失望。

【讨论】:

【参考方案7】:

这就是我在几乎纯 sls 中的做法。您将需要安装 aws cli 工具和 json 解析器 jq。它只适用于 UNIX/Linux。

首先使用这些插件:

plugins:
  - serverless-plugin-scripts
  - '@anttiviljami/serverless-stack-output' 

配置如下:

  output:
    file: ./config.json
  scripts:
    hooks:
      'after:aws:deploy:finalize:cleanup': jq < config.json .myRestAPI | xargs -I%% aws --profile $self:custom.stage apigateway create-deployment --rest-api-id %% --stage-name $self:custom.stage --description 'Deployment $sls:instanceId'

一定要像这样在同一个堆栈中输出API rest id(值不需要导出):

  myRestAPI:
    Value:
      Ref: myApiGatewayRestAPI

请注意,我的阶段名称与我的 AWS 配置文件名称匹配,您可能需要更新“--profile $self:custom.stage”以适应。

【讨论】:

以上是关于如何使用 Cloudformation 强制重新部署我的 API 网关的主要内容,如果未能解决你的问题,请参考以下文章

如何在更新参数时强制 CloudFormation 堆栈更新?

AWS Amplify:如何重新创建手动删除的 CloudFormation 堆栈

我可以强制CloudFormation删除非空的S3 Bucket吗?

我可以强制删除正在进行回滚的 AWS CloudFormation 堆栈吗

如何使用 CloudFormation 将安全组添加到现有 EC2 实例

更改某些属性后如何强制重绘小部件?