Pensieve: 2005

2020-05-28 21:20

所观所读所听

看完了The Last Dance, 想想97年天王山爵士主场的flu game, 真不容易.

仍在读Harry Potter 2, 进度过半.

这个月, 我的Apple Music里刷新了金大哥的三张专辑(爱呀, 完美世界和七大电台), 直接原因是我的Apple Music里有几首我播放量很高的歌因为版权原因灰掉了, 所以只好找了重刷进去. 过程中发现惊天动地这首歌还由原作者配了故事, 写得也还算不错. 最让人觉得不可思议的是, 有些歌曲作者直接在豆瓣上接了网盘放了音乐下载, 而且那些个mp3文件做得很是一丝不苟, 从歌曲到id3 tag都完美无暇, 单论这一点就觉得这个作者真值得粉.

另外, 之前我听到了The Piano Guys的曲子, Story of my life. Apple Music里面也有这个曲子的MV, 我听的时候觉得这首挺积极向上而又隽永不绝. 而这个月机缘巧合下听到了原唱, 发现是一个讲失恋的歌曲, 反复听后也没从曲子里听出歌词里面的绝望与失望. 谈不上奇怪, 因为我对北京人在纽约的片头千万次的问也有完全一样的感觉: 如果只听曲子也很是积极向上, 但是看看歌词就是不太搭.

所玩

这个月很少有空能够玩游戏. 唯一值得一提的是月头的时候在手机上买了Battle Chaser Nightwar, 不过玩了大概30%左右就放下了, 玩过荒野之息后, 对这种线性的RPG已经完全提不起兴趣了. 买之前看过评测, 说有这样那样的创新云云, 但是真正上手后, 仍改变不了自己被一个简单的线性逻辑玩的感觉. 几十年前游戏产业刚刚发轫的时候, 有这样的幼稚游戏, 大家也许会买账, 但是我不认为现在仍是那样的时代.

月底看到了这个解数独的视频, 觉得很棒.

Buildkite相关

之前写的一篇描述我们设置Buildkite的长文章已经发了. 在这儿可以看到. 这算是自己最近几年写得最长的一篇文章了.

这个月BK的社区经理在Slack里面问大家有没有使用Partner Eventbridge的例子. 我毛遂自荐把自己之前写的几篇文章发了过去. 这些文章后来被传给了AWS的同学, 反馈也还不错.

基于Eventbrdige的多AWS账户自动化管理

这个目前只是构思, 有些前期的调研和demo, 但是没有开始写.

说这个之前, 先说下我之前提出的用一个repo来管理所有ECR的方案. 在AWS环境下, 我们应该充分利用ECR来放docker镜像, 好处是和AWS的IAM整合得很好, 可以很方便地控制权限. 但是使用一段时间后你会发现, 如果没有中心化的管理, 每个人都能够创建自己的ecr的话, 那么所有的这些镜像到头来很可能是没法管理的. 所以我提出的方案是定义一个简单的DSL, 类似这样:

- name: plat/payid-service
  policies:
    - dev-: "7 days"
    - untagged: "7 days"
  permissions:
    pull:
      - payid-nonprod
  cfn-tags:
    owner: platform
    cost_center: platform
    description: PayId Service

这样强制要求每个项目都定义好自己的权限, 自己的生命周期管理方式, 保证我们更高效地管理ecr. 我们有一个简单的脚本来解析这个yml文件, 生成Cloudformation模板, 这样每次主干上有提交的话, 我们都能够自动推送到生产环境. 工作流变成了程序同学往这个DSL所在的repo提交PR, 我们审核没问题后merge进主干后直接部署, 整体用起来感受很好, 维护成本也很低.

现在, 我们想要有一个类似的DSL来管理我们的AWS账户, 我们目前有一个pipeline来处理用户的新AWS账户申请, 每个用户在pipeline的提示框里输入账户信息, 我们自动化创建好一个AWS账户交付给用户. 这个pipeline已经运行了一段时间, 目前看起来还行, 但是总觉得差点劲, 因为这个工作流只负责部署, 没有后面的维护, 而且可自定义性不高, 几个flag都是手工控制的. 为此, 我提出了一个类似这样的DSL:

Default:
  create_vpc: false
  has_deploy_role: false
  region: ap-southeast-2

Scopes:
  infra:
    create_vpc: true
    has_deploy_role: true
  dev:
    create_vpc: true
    has_deploy_role: true
  std:
    create_vpc: true
    has_deploy_role: true

Accounts:
  payid-nonprod:
    scope: dev

这样, 每个账户继承了全局性的账户属性定义, 也可以自己覆盖, 有了这个DSL后, 所有AWS账户属性的权威信息就存放到了一个repo里面. 我们的设想是, 这个repo同样可以提供给程序同学发PR, 程序同学发完PR后我们就可以自动化地创建所有的账户. 当然, 这儿有一个难点是怎么样把要创建的账户的信息交给master账户, 安全地在master账户里做创建新用户的API请求.

我们现在使用的方法是master账户里有一个特殊的IAMRole, 可以供CICD所在的AWS账户调用. 这个Role的scope限制得比较死, 只能做特定的IAM操作, 但是我们仍然不是特别满意, 因为这仍是一个运行风险. 另外一种可以使用的方式是在master账户里创建一个S3, CICD账户能够写这个S3, 进而触发一个预先定义好的lambda来执行操作. 相对于我们现在正在使用的方式, 算是一个进步, 至少没有直接可用的IAMRole, 但是我们得把消息的格式预先定义好. 所以仍称不上完美. 不过说起传消息, 我们还可以考虑SNS和Eventbridge, 现在看来, Eventbridge就是一个富人版的SNS, 因为Eventbridge不仅支持消息过滤, 而且支持的目标更多更全面, 总体来说可玩性更高.

所以回头来说我们的设想吧. 我们的想法是每个账户定义好一个Eventbridge, 这些Eventbridge的名字都一样, 用途也是单一的账户管理. 只不过我们加上了IAM级的各种限制, 例如master账户只能收来自CICD的消息等等. 这些Eventbridge的部署通过手工运行Cloudformation来完成, 同时我们会定义一些lambda和一些IAMRole来作为目标, 定义好规则来保证只有特定的消息能触发这些lambda. 各个账户部署好这些工具性的内容后, 我们就可以实现在CICD账户发消息来触发各个账户运行不同的lambda, 不管是收集信息供决策使用还是直接运行某些功能, 都可以很方便地实现了. 另外, 我们也可以把所有经过这些Eventbridge的消息全部转存到S3, 供合规检查时使用.

JFrog

这个月还玩了下JFrog, 我们使用的是最低级的Pro版, 每个月差不多100块, 2G存储空间不限用户使用. 比较坑的是这货的初始化比较恶心人, 很基本的创建一个虚拟用户并给每个用户一个access token的功能都不能在网页上完成, 只能通过生成一个admin token然后自己通过API来搞. 于是一怒之下搞了一个脚本, 自动创建这些用户和组, 再创建和打印token. 做到这一步了, 一不做二不休, 还把创建repo的工作也自动化了, 这样也挺好, 新创建一个账户以后什么设置都可以自动搞定了, 主干逻辑大概是这样:

repositories = [
    Repository("go"), Repository("gems"),
]
for repo in repositories:
    repo.create()
    repo.create_groups()
    repo.set_permission()
    repo.create_tokens()

Repository这个类有上面提到的这几个方法, 比如, 创建repo的方法类似:

def create(self):
    """Create repository."""
    headers = {
        "Authorization": f"Bearer {os.environ['TOKEN']}",
        'Content-type': 'application/json',
        'Accept': 'text/plain',
    }
    data = {
        "rclass": self.repo_type,
        "packageType": self.package_type,
        "repoLayoutRef": self.repo_layout,
        "description": f"Internal {self.package_type} repository"
    }
    response = requests.put(
        headers=headers,
        url=f"{JFROG_URL}/api/repositories/{self.repo_key}",
        data=json.dumps(data)
    )
    if response.status_code not in [200, 400]:
        raise RuntimeError(
            f"Create {self.repo_key} failed with {response.status_code}")

JFrog的API使用过程中有几个小坑, 不过不算大问题.