Help us understand the problem. What is going on with this article?

【AWS】RustでServerlessFrameworkを使ってみる

この記事は Rustその2 Advent Calendar 2019 12/3 の記事です。

以前ブログでも書いたのですが、今回はRustを使ってServerlessFramework用のプロジェクトを作るための最小限のことだけ書いていきます。

環境構築

Rust

バージョン
rustc 1.41.0-nightly
rustup 1.20.2
cargo 1.41.0-nightly

ServerlessFramework

バージョン
yarn 1.19.2
serverless 1.58.0
serverless-rust 0.3.7

Rustインストール

console
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

.profileにPATHが更新されるので確認して、読み込みます。

console
$ cat ~/.profile 

export PATH="$HOME/.cargo/bin:$PATH"

$ source ~/.profile

cargoコマンドが使えれば完了です。

console
$ cargo version
cargo 1.41.0-nightly (8280633db 2019-11-11)

ServerlessFrameworkのインストール

今回はyarnを使って準備していきます。
まずはyarn initpackage.jsonを作ります。
ひとまずすべてデフォルトで作ります。

console
$ yarn init
yarn init v1.19.2
question name (rust_serverless): 
question version (1.0.0): 
question description: 
question entry point (index.js): 
question repository url: 
question author: 
question license (MIT): 
question private: 
success Saved package.json

出来上がるとこんな感じになります。

package.json
{
  "name": "rust_serverless",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT"
}

ここから、ServerlessFrameworkserverless-rustを追加

$ yarn add serverless@1.58.0
$ yarn add serverless-rust@0.3.7

追加後のpackage.jsonはこうなります。

{
  "name": "rust_serverless",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "serverless": "^1.58.0",
    "serverless-rust": "^0.3.7"
  }
}

プロジェクト作成

rustのtemplateは現在ないみたいなので、sls createコマンドは使いません。
まずは、serverless.ymlを手書きします。

serverless.yml
service: rust-sample
provider:
  name: aws
  runtime: rust
  memorySize: 128
  region: ap-northeast-1
plugins:
  - serverless-rust
package:
  individually: true
functions:
  example-function:
    handler: rust-sample
    events:
      - http:
          path: /
          method: GET

次にrustの初期化をします。
serverless.ymlpackage.jsonと同じ位置で以下のコマンドを叩きます。

$ cargo init

ここまでやると、以下のディレクトリ構成になってると思います。

スクリーンショット 2019-12-02 23.40.24.png

コーディング

まず、main.rs用のサンプルのコードになります。
もとのコード消して、こちらに差し替えます。

main.rs
use lambda_http::{lambda, IntoResponse, Request};
use lambda_runtime::{error::HandlerError, Context};
use serde_json::json;

fn main() {
    lambda!(handler)
}

fn handler(
    _: Request,
    _: Context,
) -> Result<impl IntoResponse, HandlerError> {
    Ok(json!({
        "message": "AWS Lambda on Rust"
    }))
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn handler_handles() {
        let request: Request<> = Request::default();
        let expected = json!({
            "message": "AWS Lambda on Rust"
        })
        .into_response();
        let response = handler(request, Context::default())
            .expect("expected Ok(_) value")
            .into_response();
        assert_eq!(response.body(), expected.body())
    }
}

次に、上記のソースに必要なPackageをCargo.tomlに追加します。

Cargo.toml
[package]
name = "rust-sample"
version = "0.1.0"
authors = ["hisayuki <*****@***>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
lambda_runtime = "0.2.1"
lambda_http = "0.1.1"
log = "0.4"
serde_json = "^1"
serde_derive = "^1"

このときに、[package]のnameをserverless.ymlhandler名と一緒にします。

serverless.yml
functions:
  example-function:
    handler: rust-sample
    events:
      - http:
          path: /
          method: GET

この段階で、main.rsの単体テストは出来るので実際にやってみます。
コマンドはcargo testになります。

console
$ cargo test
    Finished test [unoptimized + debuginfo] target(s) in 0.20s
     Running target/debug/deps/rust_sample-eb5e576424cf1ee7

running 1 test
test tests::handler_handles ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Cargo.tomlにpackageを設定してからcargoコマンドを実行していない場合は、ここでpackageのインストールが始まります。

テスト

Lambdaのローカル単体テスト

Lambdaとしてテストスル場合、ローカルであってもリクエストを渡す必要があります。
Requestを渡さないとエラーが返ってきます。

console
{"errorType":"JsonError","errorMessage":"JsonError: invalid type: string \"\", expected struct LambdaRequest at line 1 column 2"}

そのため、本来API Gatewayから送られてくるRequestの簡易版を用意します。

example_request.json
{
  "path": "/",
  "httpMethod": "GET",
  "headers": {
    "Host": "amazonaws.com"
  },
  "requestContext": {
    "accountId": "",
    "resourceId": "",
    "stage": "dev",
    "requestId": "",
    "identity": {
      "sourceIp": ""
    },
    "resourcePath": "",
    "httpMethod": "",
    "apiId": ""
  },
  "queryStringParameters": {}
}

ディレクトリ構成はこんな感じ。

スクリーンショット 2019-12-03 0.48.17.png

プロジェクトのrootで以下のコマンドを打つと実行結果が帰ってきます。

console
$ yarn sls invoke local -f example-function --path test/resources/example_request.json 
yarn run v1.19.2
$ /Users/hisayuki/docker_dev/rust_serverless/node_modules/.bin/sls invoke local -f rust-sample --path test/resources/example_request.json
Serverless: Building native Rust rust-sample func...
    Finished release [optimized] target(s) in 3.04s
  adding: bootstrap (deflated 61%)
Serverless: Packaging service...
Serverless: Building Docker image...
START RequestId: 7cdd45ff-65e6-1b2f-f341-832c8239935c Version: $LATEST

END RequestId: 7cdd45ff-65e6-1b2f-f341-832c8239935c
REPORT RequestId: 7cdd45ff-65e6-1b2f-f341-832c8239935c  Init Duration: 161.04 ms        Duration: 5.20 ms       Billed Duration: 100 ms Memory Size: 1536 MB    Max Memory Used: 10 MB


{"statusCode":200,"headers":{"content-type":"application/json"},"multiValueHeaders":{"content-type":["application/json"]},"body":"{\"message\":\"AWS Lambda on Rust\"}","isBase64Encoded":false}

✨  Done in 54.26s.

API Gateway経由のローカル結合テスト

他言語で使われているserverless-offlineのPackageを追加しようとおもったのですが・・・
Rustはserverless-offline対応していないので、ローカルでは出来ないです。
API GatewayからのテストはAWS上にデプロイして行います。

デプロイ

AWS上にsls deployでデプロイします。

console
$ yarn sls deploy --aws-profile [プロファイル名]
yarn run v1.19.2
$ /Users/hisayuki/docker_dev/rust_serverless/node_modules/.bin/sls deploy --aws-profile [プロファイル名]
Serverless: Building native Rust rust-sample func...
    Finished release [optimized] target(s) in 2.05s
objcopy: stlfi43N: debuglink section already exists
  adding: bootstrap (deflated 60%)
Serverless: Packaging service...
Serverless: Uploading CloudFormation file to S3...
Serverless: Uploading artifacts...
Serverless: Uploading service rust-sample.zip file to S3 (1.06 MB)...
Serverless: Validating template...
Serverless: Updating Stack...
Serverless: Checking Stack update progress...
...........................
Serverless: Stack update finished...
Service Information
service: rust-sample
stage: dev
region: ap-northeast-1
stack: rust-sample-dev
resources: 10
api keys:
  None
endpoints:
  GET - https://a5uhcphfsj.execute-api.ap-northeast-1.amazonaws.com/dev/
functions:
  example-function: rust-sample-dev-example-function
layers:
  None
Serverless: Run the "serverless" command to setup monitoring, troubleshooting and testing.
✨  Done in 59.47s.

deploy完了すると、マネジメントコンソールでも確認できます。

スクリーンショット 2019-12-03 15.37.17.png

AWS上でのLambda単体テスト

deploy後にlocalをつけずにinvokeで実行します。

console
$ yarn sls invoke -f example-function --aws-profile [プロファイル名]
yarn run v1.19.2
$ /Users/hisayuki/docker_dev/rust_serverless/node_modules/.bin/sls invoke -f example-function --path test/resources/example_request.json --aws-profile [プロファイル名]
{
    "statusCode": 200,
    "headers": {
        "content-type": "application/json"
    },
    "multiValueHeaders": {
        "content-type": [
            "application/json"
        ]
    },
    "body": "{\"message\":\"AWS Lambda on Rust\"}",
    "isBase64Encoded": false
}
✨  Done in 2.79s.

このように、Responseが返ってきます。

AWS上でのAPI Gateway経由結合テスト

こちらは、先程のLambdaのdeploy時にAPI Gatewayの作成とEndpointが作成されています。

deploy時のResponse
endpoints:
  GET - https://a5uhcphfsj.execute-api.ap-northeast-1.amazonaws.com/dev/

マネジメントコンソールでも確認できます。

スクリーンショット 2019-12-03 15.39.12.png

テストの方法はcurlで実行をします。

$ curl -X GET  https://a5uhcphfsj.execute-api.ap-northeast-1.amazonaws.com/dev/
{"message":"AWS Lambda on Rust"}

これでAPI Gatewayからの実行テストも完了

まとめ

serverless-offlineが使えないのは残念ですが、RustでのLambda作成もだいぶ整ってきてます。
今回は書きませんでしたが、DynamoDBへの問い合わせ、複数Fanctionの作成方法もあります。
デフォルトで型とテストを備えているRustで、ぜひAPIの作成をしてみてください。

参照Github

http Function
Multi Function

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした