Alibaba Cloud(阿里雲)のFunction ComputeでTypeScript-API実装とfun-cli

2019-11-11

Alibaba CloudのFunction Computeについて。これはAWSのlambda、GCPのCloud Functionsに相当する関数型サーバーレス実行環境だ。AWSやGCPを使用せずにAlibaba Cloudを使う1番のメリットは中国本土でのサービス展開のしやすさにあると思われる。

このFunction ComputeはlambdaやCloudFunctionsとなんら変わらない。今回はdockerでnodejs10-typescript環境を作成し、ローカルからAlibabaへデプロイしてみる。

docker環境とfunのインストール

まずはDockerfileを用意。念の為aliyun-cliもインストール。

FROM node:10.14.2-stretch
ENV ALIYUNCLI_VERSION 3.0.16
WORKDIR /usr/local/bin
RUN apt-get update && apt-get install -y curl\
    && curl -L -o aliyun-cli-linux-amd64.tgz https://github.com/aliyun/aliyun-cli/releases/download/v${ALIYUNCLI_VERSION}/aliyun-cli-linux-${ALIYUNCLI_VERSION}-amd64.tgz \
    && tar zxvf aliyun-cli-linux-amd64.tgz

RUN npm install @alicloud/fun tsc typescript -g

そしてdocker-compose.ymlは以下。

version: '3'
services:
  aliyun-fc:
    build: .
    container_name: aliyun-fc
    volumes:
      - ./src:/src
      - ./config.yaml:/root/.fcli/config.yaml
    working_dir: /src
    tty: true

config.yamlってのは認証のために必要なもの。docker buildして立ち上げてアタッチ。

docker exec -it aliyun-fc /bin/bash
# 以下のコマンドでaccountid, accessKeyId, accessKeySecretなど聞かれるので入力
fun config

このfun configコマンドで"/root/.fcli/config.yaml"が作成される。このconfig.yamlをホストマシン側のプロジェクト内に保存してマウントする形をとる。(docker-compose.yml参照)
これでローカル環境の土台は完成。

node,TypeScriptの初期化

以下はdockerコンテナにアタッチしてそれぞれinitしていく

docker exec -it aliyun-fc /bin/bash
# functionの言語・環境を尋ねられる。http-trigger-nodejs10を選択
fun init

# npm初期化(基本全部エンター)
npm init

# tsc 初期化
tsc --init

とりあえず必需なライブラリをインストールして、index.jsをリネームしてindex.ts作成する。tsconfig.jsonの設定は好きなようにすれば良い。

npm install -D @types/node
mv index.js index.ts

ここまでで一旦アリババクラウドにdeployしてみる。

# tsコンパイルで同じディレクトリにindex.jsが作成され、デプロイされる
tsc && fun deploy

コンソール画面でも関数が作成されているのを確認できる。
アリババクラウドfunction

URLも発行されるのでデバッグしてみると正常に動作しているようだ。

Typescriptをちょっといじってみる

index.tsを以下に修正。なんて事のないリクエストbodyをそのまま返すだけのものだ。

import * as getRawBody from 'raw-body'

module.exports.handler = function(req, resp, context) {
    getRawBody(req, function(err, body) {
        resp.setHeader('content-type', 'application/json');
        resp.send(body);
    });
}

また、新たにhead.tsを同じディレクトリに作成する。リクエストヘッダーを返す関数。

module.exports.handler = function(req, resp, context) {
  resp.setHeader('content-type', 'application/json');
  resp.send(JSON.stringify(req.headers));
}

そしてtemplate.ymlで複数の関数を定義してみる。

ROSTemplateFormatVersion: '2015-09-01'
Transform: 'Aliyun::Serverless-2018-04-03'
Resources:
  api:
    Type: 'Aliyun::Serverless::Service'
    Properties:
      Description: 'helloworld'
    index:
      Type: 'Aliyun::Serverless::Function'
      Properties:
        Handler: index.handler
        Runtime: nodejs10
        CodeUri: './'
      Events:
        httpTrigger:
          Type: HTTP
          Properties:
            AuthType: ANONYMOUS
            Methods: ['POST']
    head:
        Type: 'Aliyun::Serverless::Function'
        Properties:
          Handler: head.handler
          Runtime: nodejs10
          CodeUri: './'
        Events:
          httpTrigger:
            Type: HTTP
            Properties:
              AuthType: ANONYMOUS
              Methods: ['GET']

これでGETとPOSTを扱う関数を2つ定義できる。デプロイすると2つの関数とエンドポイントが作成されるのがわかる。REST-APIな感じにする方法はよくわからない。

アリババクラウドのfunction computeについて感想

lambdaやcloudFunctionとほとんど変わりなく使用できると感じた。おそらく上記のyamlをいじる事で色々な構造のものにできると思われる。
functionの起動時間はlambdaやcloudFunctionよりも早いと感じたし、料金もかなりお安くなっている。

ただもうちょっとドキュメントをしっかり用意して欲しい。公式の記述が間違っていたり、古かったりするのが欠点だ。もちろん中国語での情報は多いようだが、検索するのも理解するのもちょっと難しい。また、alibabaCloudのサービスの名前が「Cloud CDN」とか「RDS」とか「Function ~」とか「Elastic ~」とかAWSやGCPと被ってるので、ググっても別の情報が出やすい。