deploy
命令时,第一次运行它会创建并部署到阶段。之后每次运行它都会更新资源,但不会部署到阶段。这个行为是否是预期的?如果是,那么如何在每次更新时进行部署到阶段?
(Terraform提到了类似的问题:https://github.com/hashicorp/terraform/issues/6613)
deploy
命令时,第一次运行它会创建并部署到阶段。之后每次运行它都会更新资源,但不会部署到阶段。似乎没有简单地创建新部署的方法,每当您的CloudFormation资源之一更改时。
解决这个问题的一个方法是使用基于Lambda的自定义资源(请参见http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources.html)。
Lambda应该只在您的某个资源已更新时创建新部署。要确定是否已更新其中一个资源,
您可能需要围绕此API调用实现自定义逻辑:http://docs.aws.amazon.com/AWSCloudFormation/latest/APIReference/API_DescribeStackEvents.html
为了触发Custom Resource上的更新,建议您提供一个CloudFormation参数,用于强制更新您的Custom Resource(例如,当前时间或版本号)。
请注意,您必须向Custom Resource添加一个DependsOn
子句,其中包括与您的API相关的所有资源。否则,可能会在更新所有API资源之前创建您的部署。
希望这可以帮助。
当您的模板指定了一个部署(deployment)时,CloudFormation只有在该部署不存在时才会创建它。 当您尝试再次运行它时,它会观察到该部署仍然存在,因此不会重新创建它,从而没有新的部署。 您需要为部署获取一个新的资源ID,以便它将创建一个新的部署。 阅读此处了解更多信息。
Amazon 的 CloudFormation 是:
AWS CloudFormation 可以帮助您预配和配置这些资源 http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/Welcome.html
重新部署 API 不是一个预配任务...... 它是软件发布流程中的一部分阶段性工作,即促销活动。
AWS CodePipeline 是一个持续交付服务,您可以使用它来建模、可视化和自动化发布软件所需的步骤。 http://docs.aws.amazon.com/codepipeline/latest/userguide/welcome.html
CodePipeline 还支持在管道中的操作执行 Lambda 函数。因此,如前所述,请创建一个 Lambda 函数来部署您的 API,但从 CodePipeline 调用它,而不是从 CloudFormation。
请参阅此页面获取详细信息: http://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html
我之前使用了上述方法,但是它对于仅仅部署API网关来说似乎太过复杂了。如果我们需要更改资源名称,那么删除并重新创建这些资源需要花费时间,这会增加应用程序的停机时间。
现在我正在采用以下方法,使用AWS CLI部署API网关,并且不会影响Cloudformation堆栈的部署。
我的做法是,在API Gateway部署完成后运行以下AWS CLI命令。它将使用最新的更新内容更新现有的阶段。
aws apigateway create-deployment --rest-api-id tztstixfwj --stage-name stg --description 'Deployed from CLI'
aws apigateway update-deployment...
实际上(目前)不起作用。 - mud--rest-api-id
值:aws apigateway get-rest-apis --no-paginate --query "items[?name == 'my-rest-api-name'].id | [0]
- Paolo Rovelli Stage:
Type: AWS::ApiGatewayV2::Stage
Properties:
StageName: v1
Description: 'API Version 1'
ApiId: !Ref: myApi
AutoDeploy: true
从TheClassic发布的博客文章(目前为止最佳答案!)中链接的内容来看,您必须记住,如果您没有使用可以在$TIMESTAMP$的位置插入有效时间戳的工具来生成模板,则必须手动使用时间戳或其他唯一标识更新该值。这是我的功能性示例,它成功删除了现有部署并创建了一个新的部署,但在我想创建另一个更改集时,我将不得不手动更新这些唯一值:
rDeployment05012019355:
Type: AWS::ApiGateway::Deployment
DependsOn: rApiGetMethod
Properties:
RestApiId:
Fn::ImportValue:
!Sub '${pApiCoreStackName}-RestApi'
StageName: !Ref pStageName
rCustomDomainPath:
Type: AWS::ApiGateway::BasePathMapping
DependsOn: [rDeployment05012019355]
Properties:
BasePath: !Ref pPathPart
Stage: !Ref pStageName
DomainName:
Fn::ImportValue:
!Sub '${pApiCoreStackName}-CustomDomainName'
RestApiId:
Fn::ImportValue:
!Sub '${pApiCoreStackName}-RestApi'
将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
使用CodePipeline并添加一个调用Lambda函数的action,就像自定义资源一样 - https://docs.aws.amazon.com/codepipeline/latest/userguide/actions-invoke-lambda-function.html
SAM(Serverless Application Model)遵循类似于CloudFormation的语法,将资源创建简化为抽象层,并使用这些抽象层来构建和部署常规CloudFormation模板。 https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/what-is-sam.html
如果您使用像Sceptre这样的云形态抽象层,则可以在任何更新资源后调用createDeployment钩子 https://sceptre.cloudreach.com/2.3.0/docs/hooks.html
API
├─ RestAPI (incl. Resource, Methods etc)
├─ Deployment
├─ Stage - v1 https://6s...com/v1
├─ Stage - v2 https://6s...com/v2
关系阶段和部署:
要通过CloudFormation(Cfn)部署AWS API网关,您需要一个RestApi-Cfn-Resource和一个Deployment-Cfn-Resource。如果您为Deployment-Resource提供了一个阶段名称,则部署会自动创建在“正常”创建之上的部署。如果您不加这个,API将被创建而没有任何阶段。无论哪种方式,如果您有一个部署,您可以通过链接两个来向部署添加n个阶段,但一个阶段及其API始终只有一个部署。
更新简单API:
现在,如果您想更新仅由RestAPI和部署组成的这个“简单API”,则面临的问题是,如果部署具有阶段名称 - 它无法更新,因为它已经“存在”。要首先检测到必须更新部署,必须在CloudFormation中将时间戳或哈希添加到部署资源名称中,否则甚至不会触发更新。
解决部署更新问题:
为了使部署可更新,您需要将部署和阶段拆分为单独的 Cfn-Resources。也就是说,您需要从 Deployment-Cfn-Resource 中删除阶段名称,并创建一个引用部署资源的新 Stage-Cfn-Resource。这样,您就可以更新部署。但是,阶段 - 您通过 URL 引用的部分 - 不会自动更新。
将更新从部署传播到阶段:
现在我们可以更新部署 - 即 API 的蓝图 - 我们可以将更改传播到其相应的阶段。据我所知,CloudFormation 目前不支持此步骤。因此,要触发更新,您需要添加一个 "custom resource" 或手动执行。其他 "none" CloudFormation 方法在 @Athi's 回答 above 中总结,但对我来说并非解决方案,因为我想限制使用的工具。
如果有人有Lambda更新的示例,请随时联系我-我会在这里添加它。到目前为止,我找到的链接仅涉及普通模板。
希望这有助于其他人更好地理解上下文。
来源:
如果你需要做 $TIMESTAMP$
的替换,我建议使用这种方式,因为它更简洁,而且你不需要手动管理 API Gateway。
我发现这里发布的其他解决方案在大多数情况下都能完成任务,但有一个主要问题 - 在 CloudFormation 中无法分别管理你的 Stage
和 Deployment
,因为每当你部署 API Gateway 时,在创建新部署的次要过程(自定义资源/lambda、代码管道等)之间会有某种形式的停机时间。这是因为 CloudFormation 只将初始部署与 Stage 相关联。所以当你对 Stage 进行更改并进行部署时,它会恢复到初始部署状态,直到你的次要过程创建新的部署。
*** 请注意,如果你在 Deployment
资源上指定了 StageName
,而没有明确管理 Stage
资源,则其他解决方案将可行。
$TIMESTAMP$
替换部分,我需要单独管理我的Stage
,以便我可以做一些像启用缓存这样的事情,所以我不得不找到另一种方法。因此,工作流程和相关的CF部件如下:
在触发CF更新之前,请查看您即将更新的堆栈是否已存在。设置stack_exists: true|false
将stack_exists
变量传递到您的CF模板中,一直到创建Deployment
和Stage
的堆栈
以下条件:
Conditions:
StackExists: !Equals [!Ref StackAlreadyExists, "True"]
部署
和阶段
的内容: # 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}
cfn.yml
APIGatewayStage:
Type: 'AWS::ApiGateway::Stage'
Properties:
StageName: !Ref Environment
DeploymentId: !Ref APIGatewayDeployment$TIMESTAMP$
RestApiId: !Ref APIGatewayRestAPI
Variables:
lambdaAlias: !Ref Environment
MethodSettings:
- ResourcePath: '/*'
DataTraceEnabled: true
HttpMethod: "*"
LoggingLevel: INFO
MetricsEnabled: true
DependsOn:
- liveLocationsAPIGatewayMethod
- testJTAPIGatewayMethod
APIGatewayDeployment$TIMESTAMP$:
Type: 'AWS::ApiGateway::Deployment'
Properties:
RestApiId: !Ref APIGatewayRestAPI
DependsOn:
- liveLocationsAPIGatewayMethod
- testJTAPIGatewayMethod
bitbucket-pipelines.yml
脚本: - python3 deploy_api.py
deploy_api.py
import time
file_name = 'infra/cfn.yml'
ts = str(time.time()).split(".")[0]
print(ts)
with open(file_name, 'r') as file :
filedata = file.read()
filedata = filedata.replace('$TIMESTAMP$', ts)
with open(file_name, 'w') as file:
file.write(filedata)
========================================================================
阅读此处以获取更多信息:https://currentlyunnamed-theclassic.blogspot.com/2018/12/mastering-cloudformation-for-api.html