本文旨在为使用 Go 语言提供一个新的视角。你不要指望在这篇文章里看到代码或者学到什么新东西,我只是提供了一个开放的新视角,并将它分享给正在寻找新解决方案的系统运维/DevOps/Observability 工程师们。
不久前,系统运维还是一个很常见的工作岗位,这个岗位负责更新软件、配置网络和编写 bash 脚本。然后 DevOps 出现了——这是开发人员与运维人员之间的一个交汇点。现在,随着系统变得越来越分布式且越来越复杂,可观察性成为有助于你了解系统健康状况的一个重要因素。
定义可观察性
人们早就开始了有关可观察性的讨论,并且现在还在继续。我从中学到了很多东西,并且很高兴能够参与其中。有些人可能不同意这个定义,而且在未来,我可能还会有不同的想法。
监控是指反复检查系统及其输出,以确保它们处于良好的状态。它可以说是运维版的集成测试,只是偏重于中断事故以及与警报和日志等工具相关的问题。
在控制理论中,可观察性是一种系统度量,即根据系统外部输出来判断系统内部的健康状况。系统的可观察性和可控性是一组数学对偶。
可观察性是关于了解软件和系统的内部工作情况,提出问题,并通过外部观察获得问题的答案。可以是任意问题,不仅限于警报之类的问题。
很多现代用于运维和可观察性的工具都是用 Go 语言开发的。
这种转变如何在代码中体现出来?
一切都是从在代码中添加日志开始的,这绝对是所有调试工具的始祖。
我们还会经常写一些简单的 bash 脚本。
有时候你可能会想:“要解决这个问题,写个一次性小脚本就可以了”。但随着系统变得越来越复杂,我们的工作越来越复杂,这些脚本也越来越复杂。它们成倍增加,最终出现了一大堆遗留脚本。我们可以使用编程语言而不是脚本语言写些真正的代码,而不只是脚本。
就像你无法想象一个餐厅厨师在一个高可用、高响应的复杂架构中使用常规的刀具维护多个服务——这需要一个易于维护的解决方案。Go 语言提供了很多这方面的好处,接下来我们将详细介绍。
工具
这里列出的所有工具都是开源的。
使用 Go 语言重写的 Ops 工具
Kubernetes
谷歌在 2003 年开发了 Borg,作为集群管理器,Kubernetes 旨在能有效地调度数十万个作业,并让计算更加高效。它最初是用 C++编开发的。
2014 年,Kubernetes 作为一个容器编配器发布,大型公司用它在生产环境中运行分布式服务。
Kubernetes 是一个用于自动化容器化应用程序的部署、扩展和管理的系统。它将构成应用程序的容器分组为逻辑单元,以便于管理和发现。Kubernetes 可以在不增加运维团队规模的情况下扩展和添加无限量的资源。它可以部署在本地,也可以在混合或公共云基础设施上运行。
Prometheus
Borg Mon 也于 2003 年诞生,作为 Borg 的内部监控系统,它最初也是用 C++开发的。
2012 年,Prometheus 开源:作为一个时间序列数据源,用于收集指标和生成警报。
时间序列通过指标名称和一组键值对来标识,从而可以支持强大的查询和高效存储,并内置了警报功能,它支持 10 种语言,可以从 Docker 和 StatsD 等来源导入数据。
Etcd
Chubby 于 2006 年创建,是一个分布式锁管理器。有趣的是,它的 SLO 是 99.99(4 个 9),为了达到这个 SLO,开发团队经常不得不每季度执行长达 13 分钟的计划内中断!
Etcd 是在 2013 年开发的:一个强大的一致性分布式键值存储,你可以读取和写入数据,而且为在一组计算机上存储数据提供了可靠的方法。
Etcd 是 Kubernetes 的核心组件,被用于关键任务型分布式系统中,因为它可以优雅地处理网络分区导致的首领选举,并容忍机器故障。一个简单的用例是将数据库连接信息作为键值对保存在 Etcd 中。你可以监控这些值,在它们发生变更时,你的应用程序就可以使用新的值。Etcd 现在成为 CNCF 的一个项目!
Docker
2006 年,谷歌推出了 cgroups,让你可以隔离进程,并限制其资源。2008 年又推出了 lxc,为内核模块带来了额外的功能。lxc 用于命名空间,并限制进程可以看到哪些东西。
2013 年,Docker 发布了:它可以完成上述所有的工作,另外还提供了管道和工具来构建镜像和控制端口映射。
Docker 用于容器化——操作系统级别的虚拟化。它是目前市场上最受欢迎的容器运行时,也是 Kubernetes 的核心组件。
使用 Go 语言开发的 Ops 工具
Helm
Helm 是 Kubernetes 的包管理器,它是查找、共享和使用为 Kubernetes 开发的软件的最佳方式。Helm Charts 可用于定义、安装和升级复杂的 Kubernetes 应用程序,它支持版本控制、共享和发布配置文件,不会让你迷失在意大利面式的代码中。
Grafana
Grafana 是一个时间序列分析、监控和可视化平台。官方支持的一些数据源包括:Prometheus、InfluxDB 和 Elasticsearch。每个数据源都有一个根据其特性和功能定制的查询编辑器。每个数据源都绑定到一个面板,所有面板组合在一起构成一个仪表盘,可以控制时间段,也可以将时间序列数据与其他事件相关联。
Open Tracing Project
用于在分布式软件架构(例如微服务)中分析和监控应用程序,通过精确定位发生故障的位置来帮助调试复杂系统,并通过发现导致性能下降的原因来做出优化。
Open Tracing API 项目正致力于创建更标准化的 API 和增强工具,它包含 API 规范、实现了规范的框架和库,以及项目文档。开发人员可以使用与厂商无关的 API 将增强添加到代码中。
Jaeger
Jaeger 与 Open Tracing 兼容,是一种端到端的分布式跟踪,用于监控和排除复杂分布式系统中的事务。
Jaeger 可用于分布式事务监控和上下文传播、性能和延迟优化、故障分析和服务依赖性分析。
Istio
一个服务网格框架,可配置的基础设施层,用于微服务架构。Istio 使服务实例之间的通信变得更灵活、可靠、快速和安全。在将它添加到应用程序时,只需要为每个服务部署一个边车(代理),不需要更改应用程序代码!
它支持自动发现设备和服务、跟踪、监控、日志记录、平台可观察性、基于访问控制和身份验证的安全性、访问策略控制和流量控制。
CNCF
云原生计算基金会是上述很多项目的“大本营”。它是一个开源软件基金会,致力于推广云原生计算。该基金会构建了可持续发展的生态系统,并围绕一系列高质量的开源项目建立了一个社区,这些项目将容器作为微服务架构的一部分进行编排。
云原生计算使用开源软件将应用程序作为微服务来部署,将每个部分打包到容器中,并动态编排这些容器以优化资源利用。
Go 语言为 SRE 带来的好处
简单地说就是:简单、可靠、快速。
使用 Go 语言——无论你在构建什么,专注于手头的工作,不要操心那些工具。
简单
可读性:Go 语言代码具有很高的可读性,即使有多个运维团队共享代码库,这些代码也很容易读懂,因为它们不管在哪里看起来都是一样的。可以使用 gofmt(内置的 linter)让代码变得更容易阅读。
内置了测试、分析和基准工具:为了遵循 Go 语言的最佳实践,你可能会进行 TDD(测试驱动开发)。你不一定需要使用断言,但如果你想要用它,随时都可以使用。测试、分析(CPU 和内存)和基准工具都是内置的,因此不需要学习新语言、新命令或使用新工具,并且还有一些很好的工具可用于可视化测试结果。
一个二进制文件搞定一切:Go 语言使用了静态链接,也就是说它不需要依赖外部库,不需要复制依赖项或操心导入问题。所有代码及其依赖项都在二进制文件中。而且,作为一个纯粹的同质环境,它不依赖语言版本和发布版本。
交叉编译:将所有东西塞进二进制文件中让事情变得简单了,而能够对其进行交叉编译则让事情变得更加简单:为了支持在不同的操作系统上运行二进制文件,只需要设置 2 个环境变量:GOARCH。不需要虚拟环境、包管理器或管理依赖项。这是 CLI 提供的一项很棒的功能,实际上,一些流行的工具也在使用它,如 etcdctl、kubectl 和 docker。
这是部分支持的平台清单:
组合,而不是继承:避免继承造成的混淆。
标准库:标准库中的很多软件包都是 Ops 工具(比如处理基于不同的协议——如 HTTP/HTTP2——的 Web 服务,以及文件处理:路径、下载、打开、进程、时间、json、正则表达式等)的构建块。不需要跟踪用于基本操作的标准包或外部软件,或者在这些包被弃用后切换到其他包。因为这些都在标准库中——它们得到了良好的维护,提供了良好的文档。虽然标准库不大,但对于 OPS/SRE 工程师来说已经很好用了。
可靠
有指针,但没有指针算术:保持安全!
错误处理:明确规划错误,并将其作为值来对待,而不是抛出错误异常,从而让代码执行更加顺畅。
开源:Go 语言背后有一个强大的社区在提供支持,包括多年来使用 Go 语言的公司以及谷歌、微软、苹果等行业巨头(你知道 SpaceX 正在使用 Go 语言吗?)。
数据类型:Go 语言是类型安全和强类型的语言,这意味着对 int 进行字符串操作是不允许的,因为这样无法通过编译。基于数组类型构建的内存高效抽象的额外好处是让一些操作明显变快了。
快速
快速编译和执行:如果有未使用的导入,编译器将运行失败。编译时间很短,得到的二进制文件很小。由于代码被编译为机器码,所以执行速度很快。想想通过 sed 或 bash 循环运行数百万个输入,如果使用 Go 语言会有多快?
垃圾回收:与很多其他语言一样,Go 语言中的垃圾回收也是一个争议性话题。简单地说就是出于性能优化的目的会对默认值做出修改。
导入定义好的依赖项:所有依赖项都包含在二进制文件中,这样可以省掉协调二进制文件和依赖项的额外步骤。
性能:现在已经有很多基准测试,这里有一个相当全面的清单(https://stackimpact.com/blog/practical-golang-benchmarks/)。
英文原文:https://blog.gopheracademy.com/advent-2018/go-devops/
评论