Spin 1.0â无服务器启动的开发人员工具
#javascript #python #serverless #webassembly

This article originally appeared on the Fermyon blog.

Spin 1.0

我们很高兴推出Spin 1.0,这是用于使用WebAssembly(WASM)构建无服务器应用程序的开源开发工具的第一个稳定版本!自从我们第一次introduced Spin last year以来,我们一直在与社区一起努力工作,以建立无摩擦开发人员的体验,用于使用WASM构建和运行无服务器应用程序。

在此版本中,我们专注于建立对新编程语言的支持(例如JavaScript,Typescript,Python或C#,除了Rust and Go),连接到数据库,使用流行注册表进行分发应用程序,这是一种构建 - 在持续状态的密钥/值存储中,在kubernetes上运行应用程序或与Hashicorp库集成以管理运行时配置。

无服务器和WebAssembly

当Serverless进入主流计算时,它承诺要提供一个了不起的用户故事:作为开发人员,我可以专注于编写应用程序的业务逻辑,而不必担心我的应用程序在哪里运行,仅在需要时才能执行我的代码
现代无服务器计算产品的日益普及表明,开发人员以这种方法产生共鸣。
但是,当今使用无服务器时,开发人员仍然会感知一些基本限制,例如供应商锁定,可移植性或cold starts

于2017年启动,WebAssembly (Wasm)是一种快速有效的字节码格式,该格式最初旨在在浏览器中运行非JavaScript代码。它的near-native speedfast startup time,true Protability和sandboxed executionthe abundance of programming languages with emerging support for compiling to Wasm相结合,使其成为浏览器外运行代码的引人入胜的包装和执行格式。

我们相信今天,WASM具有可以解决无服务器的局限性的运行时特征,并且通过旋转,我们希望为无服务器WASM构建最佳的开发人员体验。

你好,旋转!

spin是一种开源开发人员工具和框架,可以通过WASM来帮助用户创建,构建,分发和运行无服务器的应用程序。我们可以使用spin new来创建一个基于启动模板的新应用程序,spin build将我们的应用程序编译为WASM,而spin up在本地运行该应用程序。

Spin steps

以下是使用spin Cli创建新的Spin Python应用程序的示例,然后添加JavaScript组件:

# Create a new Spin application based on the Python language template.
$ spin new http-py hello-python
# Add a new JavaScript component based on the language template.
$ spin add http-js goodbye-javascript

运行spin add命令将为我们的组件生成适当的配置,并将其添加到koude5 manifest file中。例如,这是我们Python组件的spin.toml部分:

[[component]]
# The ID of the component.
id = "hello-python"
# The Wasm module to instantiate and execute when receiving a request.
source = "hello-python/app.wasm"
[component.trigger]
# The route for this component.
route = "/hello"
[component.build]
# The command to execute for this component with `spin build`.
command = "spin py2wasm app -o app.wasm"
# The working directory for the component.
workdir = "hello-python"

我们现在可以使用spin build构建我们的应用程序,然后用spin up
本地运行它

# Compile all components to Wasm by executing their `build` commands.
$ spin build
Executing the build command for component hello-python: spin py2wasm app -o app.wasm
Executing the build command for component goodbye-javascript: npm run build

Successfully ran the build command for the Spin components.
# Run the application locally.
$ spin up
Logging component stdio to ".spin/logs/"

Serving http://127.0.0.1:3000
Available Routes:
  hello-python: http://127.0.0.1:3000/hello
  goodbye-javascript: http://127.0.0.1:3000/goodbye

应用程序运行后,我们可以通过向其组件发送请求来开始对其进行测试:

# Send a request to the Python component.
$ curl localhost:3000/hello
Hello, Python!
# Send a request to the JavaScript component.
$ curl localhost:3000/goodbye
Goodbye, JavaScript!

处理请求时,Spin将创建一个与匹配组件的WASM模块相对应的新的隔离WASM实例,执行处理程序函数,然后终止实例。每个新请求都将获得一个新的WASM实例,我们可以这样做,因为WASM实例的启动时间非常快。

让我们看一下Spin 1.0!

随附的一些功能

语言支持

Spin languages

旋转应用程序由一个或多个组件组成。我们可以使用带有旋转SDK(例如Rust, TypeScript and JavaScript, Python, Go, or C#)的任何语言编写旋转组件,也可以使用any other language that compiles to WASI

首先,让我们看一下上一个示例中的python组件,该示例返回了“你好,python!”这是一个将http请求作为参数的python函数,然后返回响应:

# Import the Response object from the Spin SDK.
from spin_http import Response

# The handler function that returns the response.
def handle_request(request):
    # Return an HTTP response with a status, headers, and body.
    return Response(200,
                    [("content-type", "text/plain")],
                    bytes(f"Hello, Python!", "utf-8"))

在JavaScript中,我们可以使用路由器来遵循HTTP处理程序的流行模式来编写一个应用程序:

// Handle the /api/hello route.
router.get("/api/hello", async (req) => {
  return {
    status: 200,
    body: `Hello, Spin! Handling route ${req.url}`
  }
});

// Handle the /api/projects/:id route, and extract the route parameter.
router.get("/api/products/:id", async (req) => {
  return {
    status: 200,
    body: `Handling product ID: ${req.params.id}`
  }
});

处理程序功能可以执行诸如fetching data from external APIsserving files,连接到数据库(例如relational databasesRedis)或persisting state using Spin's built-in key/value storage的操作。

我们正在继续工作以改善the Spin language SDKs,我们感谢社区继续努力帮助和改善我们的语言SDK。

旋转应用程序的持续状态

正如我们在以前的部分中所学到的那样,Spin将为每个请求创建一个新的WASM实例,这使其最适合无状态,请求/响应类型的工作负载。要解决管理状态,persisting and retrieving non-relational data from a key/value store有一个内置的API,并且Spin 1.0带有一个默认的,内置的商店,可用于每个旋转应用程序,具有最小的配置。

让我们看一个从锈分组件中使用此API的示例:

// key for the application state
const DATA_KEY: &str = "app-data";

// application state
#[derive(Serialize, Deserialize, Default)]
struct Data {
    views: u64,
}

/// A simple Spin HTTP component.
#[http_component]
fn hello_kv(req: Request) -> Result<Response> {
    // Open the default KV store.
    let kv = Store::open_default()?;

    // Check whether the key already exists.
    let mut data = match kv.exists(DATA_KEY)? {
        // If it exists, get the value and deserialize it.
        true => serde_json::from_slice(&kv.get(DATA_KEY)?)?,
        false => Data::default(),
    };

    // Update the key/value pair using the new data and set its new value.
    data.views += 1;
    let body = serde_json::to_string(&data)?;
    kv.set(DATA_KEY, serde_json::to_vec(&data)?)?;
    ...
}

除了内置的密钥/值存储外,旋转应用程序还可以连接到外部数据库(例如relational databasesRedis),也可以连接到在HTTP上公开其连接的新的无服务器数据库。让我们看看using a PlanetScale database from TypeScript的示例:

// Full example at https://github.com/radu-matei/spin-planetscale-f1
import { HandleRequest, HttpRequest, HttpResponse } from "@fermyon/spin-sdk";
// Use the official PlanetScale client.
import { connect } from "@planetscale/database";

export async function handler(_req, res) {
  let conn = connect(auth);

  // Send the queries to the database.
  let [results, races, standings] = await Promise.all([
    conn.execute("SELECT * FROM results"),
    conn.execute("SELECT * FROM races"),
    conn.execute("SELECT * FROM standings"),
  ]);

  // Construct the response.
  let data = {
    races: races.rows,
    results: results.rows,
    standings: standings.rows,
  };

  // Return the response.
  res.status(200).header("content-type", "application/json").body(JSON.stringify(data));
};

随着Wasi的成熟,我们对upcoming work in WASI Preview 2 that will bring networking support to WASI感到兴奋,这将使使用流行的数据库驱动程序和WASM和旋转应用程序。

使用注册表服务分发旋转应用程序

诸如GitHub Container RegistryDocker HubAWS ECR之类的注册表服务无处不在,许多人已经将它们用作部署云本机应用程序的工作流程的一部分。

旋转1.0 supports distributing applications using such registry services我们可以使用spin registry push命令来推动应用程序:

$ spin registry push ghcr.io/radu-matei/hello-spin:v2
Pushed with digest sha256:6f886e428152a32ada6303e825975e1a9798de86977e532991a352d02c98f62c

推出应用程序后,我们可以通过运行spin up -f <reference>
来使用注册表参考来运行它。

$ spin up -f ghcr.io/radu-matei/hello-spin:v2
Serving http://0.0.0.0:3000
Available Routes:
  hello-python: http://0.0.0.0:3000/hello
  goodbye-javascript: http://0.0.0.0:3000/goodbye

使用Sigstore签名和验证应用程序

由于我们可以使用流行的注册表服务分发旋转应用程序,因此我们还可以利用诸如SigstoreCosign之类的生态系统工具,该工具通过使用Sigstore的新keyless signatures(使用OIDC Identity sidentity sidentity sidentity sidentity sidentity sidentity sientity sientity sientity sientity dokens签署和验证应用程序)来解决软件供应链问题。诸如github之类的提供商。

我们可以使用github身份签署旋转应用程序,并保证将应用程序推向注册表的演员的身份,以及推动的内容的完整性:

# Sign the application using the GitHub identity signed in your browser.
# If running in automation such as GitHub Actions, this can use the OIDC token
# GitHub makes available for workflows.
$ cosign sign ghcr.io/radu-matei/hello-spin@sha256:6f886e428152a32ada6303e825975e1a9798de86977e532991a352d02c98f62c
Generating ephemeral keys...
Retrieving signed certificate...

Successfully verified SCT...
tlog entry created with index: 15825036
Pushing signature to: ghcr.io/radu-matei/hello-spin

现在,我们可以通过运行cosign verify并传递预期的发行人以及推动该应用程序的身份来验证应用程序的完整性:

# Verify the content of the artifact at the given digest, as well as the fact
# that the signature has been created by a GitHub actor with the given email.
$ cosign verify ghcr.io/radu-matei/hello-spin@sha256:6f886e428152a32ada6303e825975e1a9798de86977e532991a352d02c98f62c \
  --certificate-oidc-issuer https://github.com/login/oauth \
  --certificate-identity radu@fermyon.com

Verification for ghcr.io/radu-matei/hello-spin@sha256:6f886e428152a32ada6303e825975e1a9798de86977e532991a352d02c98f62c --
The following checks were performed on each of these signatures:
  - The cosign claims were validated
  - Existence of the claims in the transparency log was verified offline
  - The code-signing certificate was verified using trusted certificate authority certificates

验证签名并验证应用程序的完整性后,我们可以通过摘要运行应用程序而不是使用其标签:

$ spin up -f ghcr.io/radu-matei/hello-spin@sha256:6f886e428152a32ada6303e825975e1a9798de86977e532991a352d02c98f62c

您可以将此工作流程集成到分布和运行旋转应用程序的过程中。

将旋转应用程序部署到Fermyon Cloud

Fermyon Cloud是Fermyon的云平台,用于运行无服务器WASM。您可以在几秒钟内部署旋转应用程序并具有运行应用程序。注册免费帐户所需要的只是运行spin login && spin deploy

# Log in to your account or sign up for a new one using `spin login`.
$ spin login
Copy your one-time code: xxxx and open the authorization page in your browser: 
https://cloud.fermyon.com/device-authorization

Device authorized!

# Deploy your application to Fermyon Cloud using `spin deploy`.
$ spin deploy
Uploading hello-spin version 0.1.0+r4a828e1a
Deploying...
Waiting for application to become ready........... ready
Available Routes:
  hello-python: https://hello-spin-feiccjxm.fermyon.app/hello
  goodbye-javascript: https://hello-spin-feiccjxm.fermyon.app/goodbye

我们launched Fermyon Cloud in open beta at the end of last year,我们非常高兴看到社区的积极反应!随着旋转项目和社区的发展,我们很高兴能够建立使用WASM运行无服务器应用程序的最佳场所!

将旋转应用程序部署到Kubernetes

除了在本地部署应用程序并部署到Fermyon Cloud外,还可以使用the new Containerd integration for Spin部署Spin应用程序。

您可以在自己的群集(或使用KWasm)上部署此功能,也可以使用可以运行旋转(和slight)应用程序的Azure Kubernetes Service

要开始,请首先根据集装箱集成配置新的运行时类:

apiVersion: node.k8s.io/v1
# Create a new runtime class based on the containerd Wasm integration
# that can run Spin applications.
kind: RuntimeClass
metadata:
  name: "wasmtime-spin-v1"
handler: "spin"
scheduling:
  nodeSelector:
    "kubernetes.azure.com/wasmtime-spin-v1": "true"

然后,创建一个使用新运行时类的部署:

apiVersion: apps/v1
# Create a new Kubernetes deployment.
kind: Deployment
metadata:
  name: wasm-spin
spec:
  replicas: 3
  selector:
    matchLabels:
      app: wasm-spin
  template:
    metadata:
      labels:
        app: wasm-spin
    spec:
      # Specify the runtime class for this deployment.
      runtimeClassName: wasmtime-spin
      containers:
        - name: hello-spin
          image: ghcr.io/radu-matei/hello-spin-kubernetes
          command: ["/"]

要访问该应用程序,您还需要创建Kubernetes服务并有可能成为入口。为了简化此操作,我们正在为Kubernetes开发一个旋转插件,该插件将自动脚手架并部署所需的Kubernetes对象 - 查看koude13我们期待社区关于如何改善对Kubernetes的部署旋转应用程序的反馈!如果您对此功能感兴趣,请确保到reach out on Discordon Twitter

使用外部触发器和插件扩展旋转

Spin plugins是可执行的,可以扩展自旋的功能而无需修改自旋代码库。在之前的示例中,我们使用了两个这样的插件,当构建Python和JavaScript应用程序时:spin py2wasmspin js2wasm以及spin k8s插件。
任何人都可以write a Spin plugin来建立对新语言SDK的支持(例如Python和JavaScript插件),也可以构建新的Spin Triggers。

我们对即将建立外部队列触发器的工作特别兴奋,例如the experimental AWS SQS trigger。稳定后,在安装了插件后,spin up将能够启动在给定队列上具有特定属性的新消息上触发的应用程序。

超越自旋1.0

旋转1.0的主要重点一直在稳定spin new-> spin build-> spin up工作流程的核心部分。结果,我们期待添加一些功能,以添加即将推出的旋转版本,例如更多的触发器,koude21 command to further improve the inner development loop,语言SDK的功能奇偶校验以及新的持久服务(包括旋转密钥/值和关系数据库中apis)。

,但也许最令人兴奋的是使用WASI Preview 2the Wasm component model的过渡,这将带来许多改进和新功能!我们希望在接下来的几个月中在这里取得重大进展,因此请继续关注!

如果您有想法或请求我们可以旋转的功能或改进,请确保reach out on Discordon Twitter

建立开源和社区

旋转是使用Wasmtime建造的,Wasmtime是由Bytecode AllianceWasm component model proposal构建的流行的WASM运行时,如果没有这些项目的维护者的惊人努力,则无法进行旋转。

我们很高兴能够回到WASMTIME和组件模型,以及在该领域中出现的新项目和提案(例如新WASM建议,例如WASI Preview 2koude22koude23koude23koude24)。

没有超过50 contributors的旋转,他们专门用于编写代码和文档,并且没有所有使用该项目的人。

给它一个旋转!

我们希望这篇文章让您对旋转项目的可能性感到兴奋! Head over to the Spin documentation并立即在您喜欢的编程语言中构建您的第一个旋转应用程序!

我们迫不及待地想看看您使用Spin 1.0!

看到您构建的内容

如果您对Spin,Fermyon Cloud或其他Fermyon项目感兴趣,请在Fermyon Discord server中加入聊天,并在Twitter @fermyontech@spinframework上关注我们!

Spin 1.0