做为程序员,总想着将一切变成代码,于是有各种 xxx as code 方案。就连画图,都喜欢用代码来完成,于是就有了 diagram as code,我以前在《使用 tplant 快速概览代码的领域模型 - Jeff Tian的文章 - 知乎 》里提到过一些相关的内容。
这还没完,我们还希望将密码都放在代码仓库里!Secret as code。当然,肯定不能明文存储,所以 SOPS 是一个非常好用的工具,让我们可以使用 git 来跟踪密码的变化,详见《加密 Kubernetes 集群中的敏感信息 - Jeff Tian的文章 - 知乎 》。
这样就够了吗?没有!我们还希望能够将基础设施也通过代码来完成分配并对其变化使用 git 跟踪,所谓 GitOps,或者 Infrastructure as code,简称 IaC。
IaC,你们是怎么做的呢?欢迎留言分享。
我先说我们是怎么做的。
一开始比较土,使用 shell 或者 PowerShell 脚本来完成,这是一种纯命令式的做法,一边改脚本一边实际验证,来来回回非常麻烦。头不只一个大。
后来使用 Terraform,以及主要用了 AWS 云,所以用了 Cloudformation。这是一种声明式的做法,虽然好了很多,但是在自动化测试方面差了点。而且,写声明文件也比较乏味,毕竟不是在做“编程”工作,而是像在十几年写 HTML 一样的。严格来说,HTML 只是一种标记语言而并不是编程语言。
最近呢,团队经过多种方案比较,最终选择了使用 Pulumi 来做 IaC。虽然还不太懂,但体验下来还是很惊艳的,主要在以下几个方面。

Secret as Code 比 SOPS 更香

详见《加密 Kubernetes 集群中的敏感信息 - Jeff Tian的文章 - 知乎 》,我了解到 SOPS 之后,就用在好几个实际的项目中了。但是用多了之后,发现一个很大的问题,就是经常在提交代码之前,会忘记加密码加密,导致提交了明文到代码仓库。虽然有一些办法可以规避,但是却留下了犯错的空间。
用 Pulumi,没想到可以不使用 SOPS。它有和 SOPS 同样的功能,即会对代码里的密码加密,而不是保存明文。但是用起来比 SOPS 更简单,并且没有犯错空间。因为它提供了获取明文密码的其他方式,而不是在文件里显示它。

自动化测试

没想到 IaC 也可以像写业务代码一下,写自动化测试了!就是说,你也可以在 IaC 项目中进行 TDD 了!
比如,你在 secrets.json文件里定义了这样的密码信息: json { secrets: [ { pulumiSecretName: rds-credentials, awsSecretName: /rds/{env}-{region}, createAwsSecret: true, kmsKeyIdOrAlias: null, keys: [ { name: username, value: master, configKey: null }, { name: password, value: null, configKey: rds-password } ], description: Secret storing credentials to RDS used by human users to access the database, tags: { from: passwordstate, } },...] }

以上的密码定义中,我们添加了一个 tag。你在真正部署到线上环境前,可以通过自动化测试来验证最终生成的资源包含了预期的 tag: csharp [Theory] [ClassData(typeof(StackTestData))] public async Task Should_CreateSecretsWithProperTags(string stackName) { // act var resources = await RunDeployment(stackName); var names = GetService();

// assert
var secrets = resources.OfType<Secret>().ToList();
var expectedSecretsCount = Config.SecretsConfig.Secrets.Count * Config.DeploymentEnvironment.Environments.Count;
secrets.Count.Should().Be(expectedSecretsCount);

foreach (var secret in secrets)
{
    var secretName = await secret.Name.GetValue();
    // ignore created secrets for different environments
    if (!secretName!.Contains(Config.DeploymentEnvironment.CurrentEnvironment.Name))
    {
        continue;
    }

    var secretConfig = Config.SecretsConfig.Secrets.Single(x => names.SecretName(x.AwsSecretName) == secretName);

    var secretTags = await secret.Tags.GetValue();
    secretTags.Should().NotBeNull();
    foreach (var tag in Config.DefaultResourceTags)
    {
        secretTags.Should().Contain(secretTag => secretTag.Key == tag.Key && secretTag.Value == tag.Value);
    }

    foreach (var tag in secretConfig.Tags)
    {
        secretTags.Should().Contain(secretTag => secretTag.Key == tag.Key && secretTag.Value == tag.Value);
    }

    secretTags.Should().Contain(secretTag => secretTag.Key == pulumi-secret-name && secretTag.Value == secretConfig.PulumiSecretName);
}

}

彩蛋:意想不到的 AI 功能

团队决定使用 Pulumi 的 C# 版本,这让我有点畏难。不过我发现 Pulumi 提供了 AI 功能,凡是不懂的,我就问它。从 AI 的引导来看,它专注于写代码,写得实在太靠谱了,没有让我在团队中露陷(35+ 岁了还没被开除)。
WX20240229-142711@2x.png
更棒的是,虽然界面上看它好像只会写代码,但其实这个 AI 可以做任何事情。比如我看到不认识的英文,就让它翻译:
WX20240229-142802@2x.png
看到没?不仅可以翻译,甚至比其他我试过的 AI 都更加贴心:那就是给予了非常多的解释,并且还标注了汉语拼音!
当然,英语不好也是可以直接使用它的,因为它也会中文:
image.png
最最关键的是,不像 OpenAI 的 ChatGPT,也不像其他很多国外的 AI 助手,不让国内用户访问,它是可以直接从国内访问的!
另外一个令人发指的彩蛋就是:不需要登录即可使用!这你敢信?
最后,它没有任何限额限制!这真是大方到家了,我用了差不多一个月了,从没拒绝过回答我的问题!连登录都不用,更别说收钱了。
一个字:绝!