美文网首页
AWS Cloudformation 神坑 - API Gate

AWS Cloudformation 神坑 - API Gate

作者: 水雨田 | 来源:发表于2019-05-09 23:43 被阅读0次

    几天前我在我们项目的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

    相关文章

      网友评论

          本文标题:AWS Cloudformation 神坑 - API Gate

          本文链接:https://www.haomeiwen.com/subject/hxhwoqtx.html