几天前我在我们项目的API Gateway Cloudformation中添加了一些新的资源配置,然后使用这个Cloudformation更新了Cloudformation Stack,由于我只是新增了一些资源并且不会影响其它任何资源,所以运行完我就悠哉的回家了。
由于第二天早上有事,所以中午才到公司,刚到公司有同事告诉我,我昨天API Gateway的更新使得我们有一些API Path无法访问,我一脸茫然,我只不过是添加了一些新的资源怎么会导致API Gateway的API Path无法访问,我并没有对它做任何修改。
这时,我的同事告诉我他们已经把这个问题修改好了。在我还茫然于到底发生了什么事情的时候他们竟然已经把问题解决了,我心中不禁有一丝羞愧。我问同事到底发生了什么,你们又是如何解决的,同事告诉我,修改API Gateway Cloudformation并更新后API Gateway Stage上的一些Resource奇怪的就不见了,但如果手动在AWS Web Console上进行一次Deployment后,所有的Resource就会正常发布出来。
这么神奇?此时,这里有这么几个问题:
- 我们不是在Cloudformation里定义了
AWS::ApiGateway::Deployment
吗?为什么还需要手动在AWS Web Console上进行Deployment? - 我没有修改Cloudformation里定义的
AWS::ApiGateway::Deployment
和相关配置,为什么更新Cloudformation的Stack后,Stage上的一些Resource奇怪的就不见了?
经过分析之后,我们发现通过API Gateway Cloudformation更新Cloudformation stack后,Stage里的Resource会变成你第一次运行这个Cloudformation所创建出来的Deployment里的Resource配置。说到这里你可能已经明白了使用Cloudformation所创建出来的同一个Deployment一经创建,便再也无法更新。
找到这个问题的原因后,再来反推一下这个事情为什么会发生:
- 程序员A创建这个API Gateway Cloudformation然后运行,得到正确的API Gateway配置。
- 程序员B修改这个API Gateway Cloudformation然后运行,发现API Gateway Stage并没有被部署,于是程序B手动在AWS Web Console上进行Deployment发现这样可以部署更新。
- 程序员C follow了程序员B的做法。所以每当修改API Gateway Cloudformation并更新stack后,都需要手动进行一次Deployment。
- 到了我修改的时候,我并不知道这个上下文,所以我并没有手动进行Deployment。
我要这样继续follow下去吗?当然不,这样不make sense的事情,惨剧肯定还会发生。这样做还有一个坏处就是它带来了API Gateway 更新时API Path无法访问,当然这个时间取决于你手动进行Deployment的速度。
我真正想要的是通过Cloudformation自动化完成Stage Deployment。怎么做呢?
让我们先看看我们当时使用的API Gateway Cloudformation Template:
AWSTemplateFormatVersion: 2010-09-09
Description: Cloudformation template to create API gateway related resource
Parameters:
DeployStage:
Type: String
Description: deployment stage
CustomDomainName:
Type: String
Description: custom domain name
CertificateArn:
Type: String
Description: ACM Certificate arn
Resources:
MyRestAPI:
Type: AWS::ApiGateway::RestApi
Properties:
Name: my-rest-api
Description: Rest API
FailOnWarnings: true
MyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt MyRestAPI.RootResourceId
RestApiId:
Ref: MyRestAPI
PathPart: test
Deployment:
DependsOn:
- MyResourceGet
- Account
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId:
Ref: MyRestAPI
StageName:
Ref: DeployStage
StageDescription:
CacheClusterEnabled: true
CacheClusterSize: 1.6
DataTraceEnabled: true
LoggingLevel: ERROR
MethodSettings:
-
CachingEnabled: false
HttpMethod: GET
LoggingLevel: ERROR
ResourcePath: /test
LiveDomainName:
DependsOn: Deployment
Type: AWS::ApiGateway::DomainName
Properties:
RegionalCertificateArn:
Ref: CertificateArn
DomainName:
Ref: CustomDomainName
EndpointConfiguration:
Types:
- REGIONAL
LivePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
BasePath: "v1"
DomainName:
Ref: LiveDomainName
RestApiId:
Ref: MyRestAPI
Stage:
Ref: DeployStage
既然,同一个AWS::ApiGateway::Deployment
无法更新,那我我每次更改的时候使用一个新的AWS::ApiGateway::Deployment
可不可以呢?
于是我修改了Deployment
这个Resource名称到Deployment201905092310
,运行更新后我得到了这样的Error: StageDescription cannot be specified when stage referenced by StageName already exists
。
好吧,这种方式会将Stage和Deployment绑定在一起,我只想创建一个新Deployment但我并不想也创建一个新的Stage。看来,我寻找一种方式将Stage配置分离出来,正好这里就有一个API Gateway Resouce Type就是AWS::ApiGateway::Stage
。
经过修改,我们的API Gateway Cloudformation template变成了这样子:
AWSTemplateFormatVersion: 2010-09-09
Description: cloudformation template to create API gateway related resource
Parameters:
DeployStage:
Type: String
Description: deployment stage
CustomDomainName:
Type: String
Description: custom domain name
CertificateArn:
Type: String
Description: ACM Certificate arn
Resources:
MyRestAPI:
Type: AWS::ApiGateway::RestApi
Properties:
Name: my-rest-api
Description: Rest API
FailOnWarnings: true
MyResource:
Type: AWS::ApiGateway::Resource
Properties:
ParentId: !GetAtt MyRestAPI.RootResourceId
RestApiId:
Ref: MyRestAPI
PathPart: test
Deployment__VERSION__:
DependsOn:
- MyResourceGet
- Account
Type: AWS::ApiGateway::Deployment
Properties:
RestApiId:
Ref: MyRestAPI
ApiStage:
Type: AWS::ApiGateway::Stage
DependsOn:
- Deployment__VERSION__
Properties:
RestApiId:
Ref: SingleStackRestAPI
DeploymentId:
Ref: Deployment__VERSION__
StageName:
Ref: DeployStage
CacheClusterEnabled: !Ref CachingEnabled
CacheClusterSize: 1.6
-
CachingEnabled: false
HttpMethod: GET
LoggingLevel: ERROR
ResourcePath: /test
LiveDomainName:
DependsOn: Deployment__VERSION__
Type: AWS::ApiGateway::DomainName
Properties:
RegionalCertificateArn:
Ref: CertificateArn
DomainName:
Ref: CustomDomainName
EndpointConfiguration:
Types:
- REGIONAL
LivePathMapping:
Type: AWS::ApiGateway::BasePathMapping
Properties:
BasePath: "v1"
DomainName:
Ref: LiveDomainName
RestApiId:
Ref: MyRestAPI
Stage:
Ref: DeployStage
其中__VERSION__
是需要我们每次修改完Template后将其替换成一个不会重复的值。你可以通过Linux命令实现快速替换,如:
sed -i "s|__VERSION__|201905092334|g" ./cloudformation/api-gateway-template.yml
如果,你使用ansible实现cloudformation的部署,那么你可以这个做:
---
- name: set deployment version
command: date +%Y%m%d%H%M
register: timestamp
- replace:
path: ./cloudformation/api-gateway-template.yml
regexp: __TIMESTAMP__
replace: "{{timestamp.stdout}}"
backup: false
- name: setup API gateway stack
cloudformation:
stack_name: "api-gateway-stack"
state: present
region: "{{aws_region}}"
template: cloudformation/api-gateway-template.yml
template_parameters:
DeployStage: "{{deploy_stage}}"
CustomDomainName: "{{domain_name}}"
CertificateArn: "{{certificate_arn}}"
参考:https://currentlyunnamed-theclassic.blogspot.com/2018/12/mastering-cloudformation-for-api.html
网友评论