AWS Cloudformation 神坑 - API Gateway Deployment

幾天前我在我們項(xiàng)目的API Gateway Cloudformation中添加了一些新的資源配置,然后使用這個(gè)Cloudformation更新了Cloudformation Stack,由于我只是新增了一些資源并且不會(huì)影響其它任何資源,所以運(yùn)行完我就悠哉的回家了。

由于第二天早上有事,所以中午才到公司,剛到公司有同事告訴我,我昨天API Gateway的更新使得我們有一些API Path無法訪問,我一臉茫然,我只不過是添加了一些新的資源怎么會(huì)導(dǎo)致API Gateway的API Path無法訪問,我并沒有對(duì)它做任何修改。

這時(shí),我的同事告訴我他們已經(jīng)把這個(gè)問題修改好了。在我還茫然于到底發(fā)生了什么事情的時(shí)候他們竟然已經(jīng)把問題解決了,我心中不禁有一絲羞愧。我問同事到底發(fā)生了什么,你們又是如何解決的,同事告訴我,修改API Gateway Cloudformation并更新后API Gateway Stage上的一些Resource奇怪的就不見了,但如果手動(dòng)在AWS Web Console上進(jìn)行一次Deployment后,所有的Resource就會(huì)正常發(fā)布出來。

這么神奇?此時(shí),這里有這么幾個(gè)問題:

  • 我們不是在Cloudformation里定義了AWS::ApiGateway::Deployment嗎?為什么還需要手動(dòng)在AWS Web Console上進(jìn)行Deployment?
  • 我沒有修改Cloudformation里定義的AWS::ApiGateway::Deployment和相關(guān)配置,為什么更新Cloudformation的Stack后,Stage上的一些Resource奇怪的就不見了?

經(jīng)過分析之后,我們發(fā)現(xiàn)通過API Gateway Cloudformation更新Cloudformation stack后,Stage里的Resource會(huì)變成你第一次運(yùn)行這個(gè)Cloudformation所創(chuàng)建出來的Deployment里的Resource配置。說到這里你可能已經(jīng)明白了使用Cloudformation所創(chuàng)建出來的同一個(gè)Deployment一經(jīng)創(chuàng)建,便再也無法更新。

找到這個(gè)問題的原因后,再來反推一下這個(gè)事情為什么會(huì)發(fā)生:

  • 程序員A創(chuàng)建這個(gè)API Gateway Cloudformation然后運(yùn)行,得到正確的API Gateway配置。
  • 程序員B修改這個(gè)API Gateway Cloudformation然后運(yùn)行,發(fā)現(xiàn)API Gateway Stage并沒有被部署,于是程序B手動(dòng)在AWS Web Console上進(jìn)行Deployment發(fā)現(xiàn)這樣可以部署更新。
  • 程序員C follow了程序員B的做法。所以每當(dāng)修改API Gateway Cloudformation并更新stack后,都需要手動(dòng)進(jìn)行一次Deployment。
  • 到了我修改的時(shí)候,我并不知道這個(gè)上下文,所以我并沒有手動(dòng)進(jìn)行Deployment。

我要這樣繼續(xù)follow下去嗎?當(dāng)然不,這樣不make sense的事情,慘劇肯定還會(huì)發(fā)生。這樣做還有一個(gè)壞處就是它帶來了API Gateway 更新時(shí)API Path無法訪問,當(dāng)然這個(gè)時(shí)間取決于你手動(dòng)進(jìn)行Deployment的速度。

我真正想要的是通過Cloudformation自動(dòng)化完成Stage Deployment。怎么做呢?

讓我們先看看我們當(dāng)時(shí)使用的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

既然,同一個(gè)AWS::ApiGateway::Deployment無法更新,那我我每次更改的時(shí)候使用一個(gè)新的AWS::ApiGateway::Deployment可不可以呢?

于是我修改了Deployment這個(gè)Resource名稱到Deployment201905092310,運(yùn)行更新后我得到了這樣的Error: StageDescription cannot be specified when stage referenced by StageName already exists。

好吧,這種方式會(huì)將Stage和Deployment綁定在一起,我只想創(chuàng)建一個(gè)新Deployment但我并不想也創(chuàng)建一個(gè)新的Stage??磥?,我尋找一種方式將Stage配置分離出來,正好這里就有一個(gè)API Gateway Resouce Type就是AWS::ApiGateway::Stage。

經(jīng)過修改,我們的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后將其替換成一個(gè)不會(huì)重復(fù)的值。你可以通過Linux命令實(shí)現(xiàn)快速替換,如:

sed -i "s|__VERSION__|201905092334|g" ./cloudformation/api-gateway-template.yml

如果,你使用ansible實(shí)現(xiàn)cloudformation的部署,那么你可以這個(gè)做:

---
  - 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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容