如何用Go将静态网站部署到AWS上

215 阅读4分钟

想法和概念

我一开始就考虑到了以下特点。

  • 轻量级CLI
  • 简单的认证
  • 上传至S3
    • 可配置不同文件集的缓存头信息
    • 可配置的源文件夹,可选择忽略某些文件
  • Cloudfront无效
    • 可配置的无效URL
  • 干运行选项
    • 在开始上传/失效前测试配置(这可能会产生成本)。

Go在创建轻量级CLI方面非常出色,即使没有任何额外的库或框架。flag 包和方便的I/O通常对这样的小项目是足够的。

对于配置来说,一个简单的config.yml 文件就足够了,用正则表达式来过滤文件,用文件集来过滤头文件。

认证将通过配置文件或环境变量来处理,这很简单,并将安全处理工具的责任放在用户身上。

到目前为止,还不错。

实施

看看这个功能集,我决定采用以下结构。

  • main.go
  • 失效
    • invalidate.go
  • 软件包上传
    • upload.go

这样每个人都可以在需要时自行使用隔离的invalidateupload 包。

main.go

这里真的没有发生什么。我使用flag 包来解析命令行标志,并将config.yml 文件读入以下数据结构。

type Config struct {
    Auth struct {
        Accesskey string
        Key       string
    }
    S3         upload.Config
    Cloudfront invalidate.Config
}

其中S3Cloudfront 代表我的两个worker 包的配置对象。

在解析了配置文件后,main.go 只需用给定的命令行选项和各自的配置调用另外两个包。它还会传入一个logger ,所以这些包不必写到Stdout。这样做的另一个好处是,可以通过传入ioutil.Discard 写入器来实现silent 标志,它可以扔掉所有写给它的信息。

如果有错误发生,它只是被记录下来,然后程序退出。

upload/upload.go

这是它变得更有趣的地方。upload 包有两个公共函数。

  • ParseFiles
    • 根据配置创建并返回一个从文件路径到头文件的地图
  • Do
    • 上传在创建的地图中指定的文件。ParseFiles

upload 包的配置对象看起来像这样。

type Header map[string]string

type Files map[string][]Header

type Config struct {
    Bucket struct {
        Name      string
        Accesskey string
        Key       string
    }
    Parallel int
    Source   string
    Ignore   string
    Metadata []struct {
        Regex   string
        Headers []Header
    }
}

ParseFiles 读取 文件夹,忽略所有与 匹配的文件,如果文件与 匹配,则添加 中给出的 。 出来的是一个 对象,它是一个从文件路径到文件配置的 的映射。Source Ignore Regex Metadata Headers Files Headers

第二个函数,Do 拿到Files 对象,同时(由Parallel 选项指定)为每个条目调用uploadFile ,为每个文件设置适当的头文件。

uploadFileMultipart 文件被创建并发送到AWS S3PUT 端点。最初,我想用io.Pipe ,以便以最小的内存占用有效地串联文件,但AWS的分块上传API与Go标准库的默认实现不兼容。

虽然解决这个问题肯定很有趣,但这似乎是不成熟的优化(特别是考虑到一个静态博客的文件大小在千字节以下)。

请求认证是通过伟大的github.com/smartystree…包完成的,这使得它变得如此简单。

req, err := http.NewRequest(...)
awsauth.Sign(req, awsauth.Credentials{
    AccessKeyID:     config.Bucket.Accesskey,
    SecretAccessKey: config.Bucket.Key,
})

当然,在这个I/O繁重的过程中,可能会发生大量的错误,这些错误都会得到相应的处理。如果出现dry-run ,本来已经上传的文件就会被打印在屏幕上。

invalidate/invalidate.go

invalidate 包只有一个公共函数。

  • Do
    • 创建XML有效载荷并将其发送至AWS Cloudfront API

invalidate 的配置对象看起来像这样。

type Config struct {
    Distribution struct {
        Id        string
        Accesskey string
        Key       string
    }
    Invalidation []string
}

无效化比上传部分要简单一些。首先,XML有效载荷是使用惊人的github.com/beevik/etre…包创建的,然后被发送到Cloudfront API的invalidation 端点。

请求认证的工作方式与upload 包中相同。在dry-run ,会被无效的URL被打印到屏幕上。

结论

在我看来,自己解决像这样的小型自动化问题总是一种有趣的学习经验。我也更喜欢精简的、单一用途的工具,而不是臃肿的软件,只要你把它配置好,它就能做所有的事情(这有时会和自己写一个新工具一样长......)。

解决这个简单的问题也是提高我的围棋技能的一个好方法,有一些有趣的挑战,如同时上传文件,建立一个易于使用的CLI,以及与一个奇怪的基于XML的API一起工作。

这篇文章中描述的工具正是我现在需要的。它使我能够通过一个简单的命令来更新我的博客。

如果有需要的话,我可能会在将来为它添加一些更多的功能,但现在我对它很满意。如果有其他人觉得它有用--那就更好了! :):)