12/05 Azure Functions with Serverless Framework

2020/12/15に公開

この記事は、Serverless Hello World Advent Calendar 2020の5日目です。

Serverless FrameworkでAzure FunctionsのHTTP APIを実装します。
Azure Functions - Quickstartを参考にすすめていきます。

Serverless Framerowkの導入

npmでグローバルにserverlessを入れてあげれば良いです。

サービスの作成

$ sls create -t azure-nodejs -p helloworld
Serverless: Generating boilerplate...
Serverless: Generating boilerplate in "/home/masa/work/serverless-helloworld/05/helloworld"
 _______                             __
|   _   .-----.----.--.--.-----.----|  .-----.-----.-----.
|   |___|  -__|   _|  |  |  -__|   _|  |  -__|__ --|__ --|
|____   |_____|__|  \___/|_____|__| |__|_____|_____|_____|
|   |   |             The Serverless Application Framework
|       |                           serverless.com, v2.15.0
 -------'

Serverless: Successfully generated boilerplate for template: "azure-nodejs"
$ cd 05/helloworld
$ npm install

生成されたファイルの確認

serverless.ymlに2つの関数が登録されていました。

service: helloworld

frameworkVersion: '2'

provider:
  name: azure
  region: West US 2
  runtime: nodejs12

  environment: # these will be created as application settings
    VARIABLE_FOO: 'foo'

plugins: # look for additional plugins in the community plugins repo: https://github.com/serverless/plugins
  - serverless-azure-functions

package:
  exclude:
    - local.settings.json
    - .vscode/**

functions:
  hello:
    handler: src/handlers/hello.sayHello
    events:
      - http: true
        methods:
          - GET
        authLevel: anonymous # can also be `function` or `admin`

  goodbye:
    handler: src/handlers/goodbye.sayGoodbye
    events:
      - http: true
        methods:
          - GET
        authLevel: anonymous

src/handlers/hello.jsが関数の本体です。

'use strict';

module.exports.sayHello = async function(context, req) {
  context.log('JavaScript HTTP trigger function processed a request.');

  if (req.query.name || (req.body && req.body.name)) {
    context.res = {
      // status: 200, /* Defaults to 200 */
      body: 'Hello ' + (req.query.name || req.body.name),
    };
  } else {
    context.res = {
      status: 400,
      body: 'Please pass a name on the query string or in the request body',
    };
  }
};

ローカル環境での動作確認

sls offlineでローカル環境で関数アプリを動かしてみます。

$ sls offline
Serverless: Initializing provider configuration...
Serverless: Configuration warning: Unrecognized provider 'azure'
Serverless:  
Serverless: You're relying on provider plugin which doesn't provide a validation schema for its config.
Serverless: Please report the issue at its bug tracker linking: https://www.serverless.com/framework/docs/providers/aws/guide/plugins#extending-validation-schema
Serverless: You may turn off this message with "configValidationMode: off" setting
Serverless:  
Serverless: Building offline service
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Building binding for function: hello event: httpTrigger
Serverless: Building binding for function: goodbye event: httpTrigger
Serverless: Finished building offline service
Serverless: undefined
Serverless: Spawning process 'func host start'

Azure Functions Core Tools
Core Tools Version:       3.0.3160 Commit hash: 00aa7f43cc5c5f15241b5e6e5363256f19ceb990
Function Runtime Version: 3.0.14916.0


Functions:

        goodbye: [GET] http://localhost:7071/api/goodbye

        hello: [GET] http://localhost:7071/api/hello

For detailed output, run func with --verbose flag.
[2020-12-14T20:24:34.786Z] Worker process started and initialized.

内部でAzure Functions Core Toolsを起動して立ち上がりました。
cURLで叩いてみます。

$ curl -v 'http://localhost:7071/api/hello?name=world'
*   Trying 127.0.0.1:7071...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 7071 (#0)
> GET /api/hello?name=world HTTP/1.1
> Host: localhost:7071
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Mon, 14 Dec 2020 20:28:29 GMT
< Content-Type: text/plain; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
< 
* Connection #0 to host localhost left intact
Hello world%

hello/functions.jsonにAzure Functionsの関数設定が生成されました。

{
  "disabled": false,
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "authLevel": "anonymous",
      "methods": [
        "GET"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ],
  "entryPoint": "sayHello",
  "scriptFile": "../src/handlers/hello.js"
}

デプロイ

自分の環境だと複数サブスクリプションがあるせいか、あらかじめこの手順に従ってService Principalを作成しておかないとエラーになりました。

ひとまずDry Runしてみます。

$ sls deploy --dryrun
Serverless: Initializing provider configuration...
Serverless: Configuration warning: Unrecognized provider 'azure'
Serverless:  
Serverless: You're relying on provider plugin which doesn't provide a validation schema for its config.
Serverless: Please report the issue at its bug tracker linking: https://www.serverless.com/framework/docs/providers/aws/guide/plugins#extending-validation-schema
Serverless: You may turn off this message with "configValidationMode: off" setting
Serverless:  
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Building binding for function: hello event: httpTrigger
Serverless: Building binding for function: goodbye event: httpTrigger
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Logging into Azure
Serverless: Using subscription ID: 7a9f3ee7-623e-4aad-adec-6e8305f54de8
Serverless: -> Creating ARM template from type: consumption
Serverless: 
Resource Group Name: sls-wus2-dev-helloworld-rg
Function App Name: sls-wus2-dev-helloworld
Functions:
        hello
        goodbye
Azure Resources:
{
  "name": "sls-wus2-dev-helloworld",
  "resourceType": "Microsoft.Web/sites",
  "region": "westus2"
},
{
  "name": "sls-wus2-dev-52c01f-appinsights",
  "resourceType": "microsoft.insights/components",
  "region": "westus2"
},
{
  "name": "slswus2dev52c01f",
  "resourceType": "Microsoft.Storage/storageAccounts",
  "region": "westus2"
}

****************************************************************************************************************************
Serverless: Announcing an enhanced experience for running Express.js apps: https://github.com/serverless-components/express.
****************************************************************************************************************************

それでは実際にデプロイしてみます。

$ sls deploy
Serverless: Initializing provider configuration...
Serverless: Configuration warning: Unrecognized provider 'azure'
Serverless:  
Serverless: You're relying on provider plugin which doesn't provide a validation schema for its config.
Serverless: Please report the issue at its bug tracker linking: https://www.serverless.com/framework/docs/providers/aws/guide/plugins#extending-validation-schema
Serverless: You may turn off this message with "configValidationMode: off" setting
Serverless:  
Serverless: Removing .serverless directory
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Parsing Azure Functions Bindings.json...
Serverless: Building binding for function: hello event: httpTrigger
Serverless: Building binding for function: goodbye event: httpTrigger
Serverless: Packaging service...
Serverless: Excluding development dependencies...
Serverless: Logging into Azure
Serverless: Using subscription ID: 7a9f3ee7-623e-4aad-adec-6e8305f54de8
Serverless: Creating resource group: sls-wus2-dev-helloworld-rg
Serverless: Creating function app: sls-wus2-dev-helloworld
Serverless: -> Creating ARM template from type: consumption
Serverless: -> Merging environment configuration
Serverless: Listing deployments for resource group 'sls-wus2-dev-helloworld-rg':
Serverless: -> Deploying ARM template...
Serverless: ---> Resource Group: sls-wus2-dev-helloworld-rg
Serverless: ---> Deployment Name: slswus2devhelloworld-DEPLOYMENT-t1607978610276
Serverless: -> ARM deployment complete
Serverless: Deploying serverless functions...
Serverless: Deploying zip file to function app: sls-wus2-dev-helloworld
Serverless: -> Deploying service package @ /home/masa/work/serverless-helloworld/05/helloworld/.serverless/helloworld.zip
Serverless: Publishing to URI: https://sls-wus2-dev-helloworld.scm.azurewebsites.net/api/zipdeploy
Serverless: Uploading file at '/home/masa/work/serverless-helloworld/05/helloworld/.serverless/helloworld.zip' to container 'deployment-artifacts' with name 'slswus2devhelloworld-ARTIFACT-t1607978610276.zip'
Serverless: Finished uploading blob
Serverless: -> Function package uploaded successfully
Serverless: Deployed serverless functions:
Serverless: -> Function App not ready. Retry 0 of 30...
Serverless: -> Function App not ready. Retry 1 of 30...
Serverless: -> Function App not ready. Retry 2 of 30...
Serverless: -> Function App not ready. Retry 3 of 30...
Serverless: -> Function App not ready. Retry 4 of 30...
Serverless: -> Function App not ready. Retry 5 of 30...
Serverless: -> Function App not ready. Retry 6 of 30...
Serverless: -> goodbye: [GET] sls-wus2-dev-helloworld.azurewebsites.net/api/goodbye
Serverless: -> hello: [GET] sls-wus2-dev-helloworld.azurewebsites.net/api/hello

********************************************************************************************************************************************
Serverless: Announcing an enhanced experience for Serverless Full-Stack Applications: https://github.com/serverless-components/fullstack-app
********************************************************************************************************************************************

表示されたエンドポイントをcURLで叩いてみます。

$ curl -v 'https://sls-wus2-dev-helloworld.azurewebsites.net/api/hello?name=world'                                                                  <main 5:52>
*   Trying 40.64.128.228:443...
* TCP_NODELAY set
* Connected to sls-wus2-dev-helloworld.azurewebsites.net (40.64.128.228) port 443 (#0)

...(省略)...

> GET /api/hello?name=world HTTP/1.1
> Host: sls-wus2-dev-helloworld.azurewebsites.net
> User-Agent: curl/7.68.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Transfer-Encoding: chunked
< Content-Type: text/plain; charset=utf-8
< Request-Context: appId=cid-v1:dfbf56f5-90c1-446c-a8b8-10cc10d770b4
< Date: Mon, 14 Dec 2020 20:52:43 GMT
< 
* Connection #0 to host sls-wus2-dev-helloworld.azurewebsites.net left intact
Hello world%

sls invokeコマンドで直接呼び出すこともできるようです。

$ sls invoke -f hello -d '{"name":"World"}'
Serverless: Initializing provider configuration...
Serverless: Configuration warning: Unrecognized provider 'azure'
Serverless:  
Serverless: You're relying on provider plugin which doesn't provide a validation schema for its config.
Serverless: Please report the issue at its bug tracker linking: https://www.serverless.com/framework/docs/providers/aws/guide/plugins#extending-validation-schema
Serverless: You may turn off this message with "configValidationMode: off" setting
Serverless:  
Serverless: Logging into Azure
Serverless: Using subscription ID: 7a9f3ee7-623e-4aad-adec-6e8305f54de8
Serverless: Invocation url: http://sls-wus2-dev-helloworld.azurewebsites.net/api/hello?name=World
Serverless: Invoking function hello with GET request
Serverless: "Hello World"

Azure上にhello関数をデプロイできました。

Discussion