用Rust编写DevOps工具的初级指南

909 阅读5分钟

简介

在这篇博文中,我们将介绍一些Rust的基本DevOps用例,以及为什么你想使用它。 作为其中的一部分,我们还将介绍一些你可能会在AWS的基于Rust的DevOps工具中使用的常见库。

如果你已经熟悉用其他语言编写DevOps工具,这篇文章将解释为什么你应该尝试Rust。

我们将介绍为什么Rust是一种特别适合编写DevOps工具和关键云基础设施软件的语言。 我们还将通过一个用Rust编写的小型DevOps工具演示。 这个项目将着眼于帮助刚进入语言生态系统的人熟悉Rust项目的结构。

如果你是Rust的新手,并且有兴趣学习这门语言,你可能想从我们的Rust速成班电子书开始。

是什么使Rust语言变得独特

Rust是一种系统编程语言,专注于三个目标:安全、速度和并发。它在没有垃圾收集器的情况下保持了这些目标,使它成为其他语言不擅长的一些用例的有用语言:嵌入其他语言,具有特定空间和时间要求的程序,以及编写低级别的代码,如设备驱动程序和操作系统。

Rust书(第一版)

Rust最初是由Mozilla创建的,后来得到了广泛的采用和支持。正如Rust书中的那句话所暗示的那样,它的设计是为了填补与C++或C相同的空间(因为它没有垃圾收集器或运行时)。 但Rust也包含了零成本的抽象和许多你期望在更高层次的语言(如Go或Haskell)中的概念。 由于这个原因,以及许多其他原因,Rust的用途已经远远超出了作为低级安全系统语言的最初空间。

Rust的所有权系统对于编写正确和资源高效的代码非常有用。所有权是Rust语言的杀手锏之一,它可以帮助程序员在编译时发现其他语言所遗漏或忽略的资源错误。

Rust是一门性能和效率极高的语言,可以与你看到的日常C或C++语言的速度相媲美。 而且由于Rust中没有垃圾收集器,所以更容易获得可预测的确定性性能。

Rust和DevOps

Rust的独特之处在于它在机器人和火箭等领域都非常有用,但这些品质与DevOps有关吗? 我们是否关心我们是否有高效的可执行文件或对资源的精细控制,或者Rust对于我们在DevOps中通常需要的东西来说有点矫枉过正?

是的,也不是

Rust对于那些性能至关重要,并且需要以确定和一致的方式进行操作的情况来说显然是有用的。这显然是指以前C和C++是镇上唯一的游戏的低级别地方。 在这些情况下,在Rust之前,人们只是不得不接受用这些语言处理大量代码库的固有风险和额外的开发成本。 Rust现在允许我们在这些领域操作,但没有C和C++可能带来的风险。

但对于DevOps和基础设施编程,我们并没有受到这些要求的限制。对于DevOps,我们已经能够从Go、Python或Haskell等语言中进行选择,因为我们没有被使用情况严格限制在没有垃圾收集器的语言中。既然我们可以接触到其他语言,你可能会认为使用Rust有点矫枉过正,但让我们回顾一下几点来反驳这一点。

为什么你想用Rust编写你的DevOps工具

  • 相对于Go或Java等其他选择,可执行文件较小
  • 易于在不同的操作系统目标之间进行移植
  • 资源效率高(这有助于减少你的AWS账单)
  • 最快的语言之一(即使与C语言相比)。
  • 零成本的抽象--Rust是一种低级别的高性能语言,它的泛型和抽象也给我们带来了高级语言的好处。

为了进一步阐述其中的一些观点。

操作系统目标和针对不同架构的Rust交叉编译

对于DevOps来说,还值得一提的是,你可以在不同的架构和不同的操作系统之间移植你的Rust代码(相对)容易。

使用官方的Rust工具链安装程序rustup ,很容易获得目标平台的标准库。Rust支持大量不同层次的平台rustup 工具的文档中有一节介绍了如何获得各种架构的预编译工件。要安装一个架构的目标平台(除了默认安装的主机平台),你只需要运行rustup target add

$ rustup target add x86_64-pc-windows-msvc 
info: downloading component 'rust-std' for 'x86_64-pc-windows-msvc'
info: installing component 'rust-std' for 'x86_64-pc-windows-msvc'

交叉编译已经默认内置于Rust编译器中。 一旦安装了x86_64-pc-windows-msvc 目标,你就可以使用cargo 构建工具使用--target 标志为Windows进行构建。

cargo build --target=x86_64-pc-windows-msvc

(默认的目标始终是主机架构)

如果你的某个依赖项链接到本地(即非Rust)库,你需要确保这些库也能交叉编译。做rustup target add,只为该目标安装Rust标准库。然而,对于交叉编译时经常需要的其他工具,有一个方便的github.com/rust-embedd…工具。 这基本上是一个围绕cargo的包装器,它在docker镜像中进行所有交叉编译,并安装了所有必要的位(链接器)和部件。

小型可执行文件

Rust的一个关键特点是它不需要运行时或垃圾收集器。 与Python或Haskell等语言相比,Rust缺乏任何运行时依赖(Python)或系统库(如Haskell),这对可移植性是一个巨大的优势。

就实际情况而言,就DevOps而言,这种可移植性意味着Rust可执行文件比脚本更容易部署。 与Python或Bash相比,使用Rust,我们不需要提前为我们的代码设置环境。这使我们不必担心语言的运行时依赖性是否已经设置好。

除此之外,使用Rust,你能够使用MUSL libc为Linux制作100%的静态可执行文件(默认情况下,Rust会静态链接所有Rust代码)。 这意味着你可以在你的Linux服务器上部署你的Rust DevOps工具的二进制文件,而不必担心是否事先安装了正确的libc 或其他库。

为Rust创建静态可执行文件很简单。正如我们之前讨论的,在讨论不同的操作系统目标时,Rust很容易切换你要构建的目标。 要为Linux MUSL目标编译静态可执行文件,你需要做的就是添加musl 目标。

$ rustup target add x86_64-unknown-linux-musl

然后你就可以使用这个新的目标来构建你的Rust项目,作为一个完全静态的可执行文件。

$ cargo build --target x86_64-unknown-linux-musl

由于没有运行时或垃圾收集器,Rust的可执行文件可以非常小。例如,有一个常见的开发工具叫CredStash,最初是用Python写的,但后来被移植到Go(GCredStash)和现在的Rust(RuCredStash)。

比较Rust和Go实现的CredStash的可执行大小,Rust的可执行大小几乎是Go的四分之一。

实现可执行文件大小
Rust CredStash: (RuCredStash Linux amd64)3.3 MB
Go CredStash: (GCredStash Linux amd64 v0.3.5)11.7 MB

项目链接:

这绝不是一个完美的比较,8MB可能看起来不是很多,但考虑到自动拥有可执行文件的优势,其大小是你通常期望的四分之一。

这减少了你的Docker镜像、AWS AMI或Azure VM镜像所需的大小--这有助于加快旋转新部署的时间。

对于这种规模的工具,拥有一个比其他方式小75%的可执行文件并不明显。在这个规模上,8MB的差异仍然是相当便宜的。 但对于更大的工具(或工具和基于Rust的软件集合),好处会增加,差异开始成为一个实际和值得考虑的问题。

Rust的实现也没有严格考虑到可执行文件的大小。因此,如果可执行文件的大小是一个更重要的因素,可以做其他的改变--但这超出了这篇文章的范围。

Rust是快速的

为什么你不想用Rust编写你的DevOps工具?

对于大中型项目来说,像Rust中的类型系统和编译时检查与Python或Bash这样的语言相比是很重要的。 后者的语言让你更容易摆脱一些东西。这使得开发在某种意义上更 "快"。

在某些情况下,特别是那些涉及小项目代码库的情况下,使用解释性语言会更有好处。在这些情况下,能够快速改变代码的片段而不需要重新编译和重新部署项目,超过了Rust等语言带来的好处(在安全性、执行速度和可移植性方面)。

在这种情况下,如果频繁但小的代码库变化,使用和迭代Rust代码库将是不必要的耗时 如果你有一个小的代码库,很少或没有运行时依赖,那么使用Rust就不值得了。

AWS的DevOps项目演示

我们将在这里简要介绍一些通常用于AWS的DevOps工具的库,并对一个小型的Rust演示项目进行走访。 这旨在提供一个小例子,使用一些你可能需要的库,如果你正在用Rust编写一个基于CLI的DevOps工具。具体到这个例子,我们将展示一个对AWS S3进行一些基本操作的工具(创建新桶,向桶中添加文件,列出桶的内容)。

项目结构

对于AWS的集成,我们将利用Rusoto库。 具体来说,对于我们的小型演示Rust DevOps工具,我们将使用rusoto_corerusoto_s3crate(在Rust中,crate类似于一个库或包)。

我们还将使用structoptcrate 作为我们的 CLI 选项。这是一个方便的、包含电池的CLI库,使得围绕Rust结构创建一个CLI接口变得容易。

该工具通过匹配CLI选项和用户传入的参数来操作,表达式为match

然后我们可以用它来匹配我们定义的CLI选项结构的那一部分,并为该选项调用相应的函数。

match opt {
    Opt::Create { bucket: bucket_name } => {
        println!("Attempting to create a bucket called: {}", bucket_name);
        let demo = S3Demo::new(bucket_name);
        create_demo_bucket(&demo);
    },

这个匹配在 Create Opt 枚举的变体。

然后,我们使用S3Demo::new(bucket_name) 来创建一个新的S3Client ,我们可以在我们定义的独立的create_demo_bucket 函数中使用,这将创建一个新的S3桶。

该工具相当简单,大部分代码位于src/main.rs中。

构建Rust项目

在你构建本项目的代码之前,你需要安装Rust。 请遵循这里的官方安装说明

Rust的默认构建工具叫做Cargo。熟悉Cargo的文档是值得的,但这里是构建项目的一个快速概述。

要构建项目,从git repo的根目录下运行以下程序。

cargo build

然后你可以用cargo run 来运行代码,或者直接用./target/debug/rust-aws-devops 来执行代码。

$ ./target/debug/rust-aws-devops 

Running tool
RustAWSDevops 0.1.0
Mike McGirr <mike@fpcomplete.com>

USAGE:
    rust-aws-devops <SUBCOMMAND>

FLAGS:
    -h, --help       Prints help information
    -V, --version    Prints version information

SUBCOMMANDS:
    add-object       Add the specified file to the bucket
    create           Create a new bucket with the given name
    delete           Try to delete the bucket with the given name
    delete-object    Remove the specified object from the bucket
    help             Prints this message or the help of the given subcommand(s)
    list             Try to find the bucket with the given name and list its objects``

这将输出由structopt 为我们自动创建的漂亮的CLI帮助输出。

如果你已经准备好构建一个发布版本(开启了优化功能,这将使编译时间稍长),请运行以下程序。

cargo build --release

结论

正如这个小演示所显示的,开始使用Rust编写DevOps工具并不困难。即使如此,我们也不需要在开发难度和高性能的快速代码之间做出权衡。

希望下次你在编写新的DevOps软件时,无论是针对特定DevOps操作的简单CLI工具,还是编写下一个Kubernetes,你都会考虑使用Rust。