AWS Cloudformation 神坑 - API Gateway Deployment

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,012评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,589评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,819评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,652评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,954评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,381评论 1 210
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,687评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,404评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,082评论 1 238
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,355评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,880评论 1 255
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,249评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,864评论 3 232
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,007评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,760评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,394评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,281评论 2 259

推荐阅读更多精彩内容