Pensieve: 2105

2021-05-29 11:54

所观所读所听

看完了Blind watchmaker, 如是而已. 另外看完的一本书是Mastering AWS Cloudformation, 我搜索Stackset的一些信息时看到这本, 正好那天也比较闲, 就找到然后读完了. 我本希望这是一本进阶的书, 或者至少能够给我拓展一下视野, 让我看看其他人用Cloudformation有什么心得体会. 不过这本书是一本对我来说太过基本的书, 不客气地说, 我写一本Cloudformation的书都会比这本好. 唯一值得一提的是这本书让我知道了Stackset还有TAG这样的东西, 由于这个是对整个账号而不是针对单个stackset的, 我想不到这个功能的用途(可能这也是为什么我不知道有这个功能).

影视方面, 看完了爱死机器人的第二季, 挺一般, 没看出什么特别有创造力的东西, 有点失望. 还看了最近刚结婚的国民老婆演的恋爱回旋, 没觉得好看, 感觉远不如留在影视库里的逃避虽然可耻但是有用. 算了, 不提也罢. 最近这一两天看完了Friends的重聚, 满是回忆, 很喜欢.

CMDB

这个是在单位做的一点工作, 到头来效果比我想象得还要好, 所以拿出来说一下.

CMDB即Configuration Management Database, 用来放configuration信息. 我之前也说过, 单位采用的策略是使用多个AWS账号, 一个AWS账号里面最多只有一个VPC, 里面只跑一个服务. 那么一个现实的问题是如何跨账号共享一些信息, 比如, 如果在同一个账号里面, 一个服务的SQS要共享给另一个服务, 那么在Cloudformation里面加一个output, export出来就可以了, 但是如果这个SQS是在另一个账号里面, 而你又有洁癖想要避免把SQS的Arn写死到配置文件里面, 就比较难做了.

我们采用的解决方案是新开一个账号, 里面跑APIGW/DynamoDB和lambda. 这个APIGW会接受来自各个账号的请求, 调用lambda来处理逻辑, 最后这些配置信息存储到DynamoDB里面去. 安全方面来说, 我们定义了APIGW的resource policy, 用IAM来做鉴权, 请求必须来自同一个Organization下的账号才会被处理. 这要求客户端那边必须用当前的IAM role做一个签名, 然后请求头里面带上这个签名过来. 到了lambda这一层, 我们能够拿到来源用户的账号和Role并做进一步限制.

客户端那边, 为了让用户的使用更方便, 我们还提供了一个Cloudformation custom resource来简化操作, API类似:

  # Getting value from CMDB.
  ESServiceName:
    Type: 'Custom::CmdbValue'
    Version: '1.0'
    Properties:
      ServiceToken:
        Fn::ImportValue: "ap-lambdas-cmdb-lambda"
      Service: some-service
      ExportName: es-service-name

  # Saving value to CMDB.
  ESServiceName:
    Type: 'Custom::CmdbValue'
    Version: '1.0'
    Properties:
      ServiceToken:
        Fn::ImportValue: "ap-lambdas-cmdb-lambda"
      ExportName: es-service-name
      ExportValue: !Join
        - "."
        - - "com.amazonaws.vpce"
          - !Ref AWS::Region
          - !Ref VPCES

这个方案有几个优点:

  1. 跨账号, 安全性高.
  2. 便宜, 我刚查了下, 由于这个方案里的几个服务都是按用量计费的, 所以每个月的使用费用不到$5, 对于企业来说, 运行成本可以忽略不计.
  3. 好用. 这个方案在概念上很简单, 我们为各个账号提供了键值对的存储, 具体实现细节用户可以不用管.

这个方案大大简化了我们在设置Privatelink时的操作.

比如我们的用例里面, apigw要通过Privatelink连到微服务账号里面去. 一般来说这个操作需要微服务Stack的配置文件里面写死访问者的账号, 然后创建VPCEndpointService, 然后回到apigw账号里面去, 写死VPCEndpointService的名字, 然后创建Stack连接到这个VPCEndpointService, 创建PrivateLink. 这个方案耦合度很高, 太多操作需要人肉写死, 很不美观. 在新方案里面, apigw分成base stack和几个和微服务连接的子stack. 在base stack里面发布账号到CMDB, 微服务里面创建VPCEndpointService时信任apigw账号, 然后发布另外几个值到CMDB. Apigw连接这个微服务的子stack里面拿到这几个值, 完成连接的创建. 听起来没什么大的变化, 但是我们还使了一些小聪明, 比如apigw的base stack里定义的东西很少, 基本不会变, 所以对于新的微服务而言, 这个值是永远在那儿的. 而在apigw的子stack里面, 我们在部署前加了逻辑, 如果需要的键值对不存在于CMDB时, 我们不创建这个Stack. 所以在实际使用时, 这理论上紧密跨账号耦合的PrivateLink被我们简化成可以基本无视耦合度来部署, 体验很好.

APIGW和PrivateLink

前面已经提到了, 这一个月在做APIGW相关的工作. 我们想实现的是在一个账号里面部署APIGW, 然后连到另外一个账号里的微服务, 我们用PrivateLink来连接这两个账号的VPC. 整个连接有点zero-trust的意思, 详述如下.

客户端连到apigw的时候, 匹配到了某个路径后被反向代理到一个URL, 我们要通过这个URL连到微服务, 要求是通过AWS骨干网不走公网, 而且走AWS骨干网的内容有HTTPS加密. 为了实现这一点, 我们需要注意下面几个细节:

  1. 微服务账号需要将一些信息发布到CMDB, 除了PrivateLink相关的信息外, 还需要将NLB上SSL的域名写到CMDB.
  2. apigw账号连接到微服务时, 需要创建一个DNS Private Zone, 绑定到VPC. apigw通过VpcLink连到这个账号里面的VPC, 然后设置反向代理时写NLB上SSL的域名.
  3. 将VPCEndpointService的ENI的域名加到一个target group, 再把这个target group连到NLB.
  4. 在微服务账号里面, 一定记得把NLB的跨AZ路由打开, 这样不会导致因为微服务只跑在一两个AZ里有请求失败.

做了这些设置后, apigw里面设了反向代理后, 流量通过VpcLink进了Vpc, 在Vpc里面由于Private Zone的存在, 流量被引到了VPCEndpointService的网卡上, 再走到了微服务账号的NLB上, 解开SSL证书后发给了后面的Task.

这个过程中踩的一个小坑是上面的细节4, 因为没有跨AZ的路由, 而我们又是在dev账号里面测试, 里面只有一个ECS task, 所以只有三分之一的请求会走到task, 其余的请求都报500错误, 因为流量进了微服务账号的NLB后, 有三分之二的AZ没有task可以处理这个请求.