はじめに
※使用ライブラリを、AWS Lambda
, Azure Functions
, IBM Cloud Functions
で使える共通ライブラリにしたので、記事を若干修正しました。(2021/07/07)
AWS Lambda、Azure Functionsに続き、今回、IBM Cloud FunctionsのアプリケーションをElixirで簡単に実装できるライブラリを作ってみたので、それを使ってIBM Cloud Functionsへのデプロイまでをやってみたいと思います。
ライブラリはこちらで公開
→ https://github.com/imahiro-t/faas_base
→ https://hex.pm/packages/faas_base
以前の投稿
→ Azure FunctionsをElixirで
→ ElixirでAWS LambdaでGithubActionsで継続デプロイ
ElixirでIBM Cloud Functions
IBM Cloud FunctionsはApache OpenWhiskというサーバレス基盤のオープンソース(と言ってもIBMが主体となって進めていたもの)に準拠していて、現時点でElixir用のライブラリは用意されてはいませんが、それ以外の言語であっても、CLIアプリケーションのようなバイナリを用意してあげることでDockerイメージとして動作させることができるようです。
この辺りについては作成したライブラリ側で吸収しているので、今回はこの辺りをあまり意識せずにデプロイまでやっていくことになります。
やってみる
IBM Cloud CLIのセットアップ
IBM Cloudをターミナルからデプロイしたりできるツールです。こちらの手順に従ってセットアップします。
続いて、下記コマンドでCloud Functions CLIプラグインのセットアップを行います。
$ ibmcloud plugin install cloud-functions
IBM Cloud Functions環境の作成
下記コマンドでIBM Cloud Functionsをデプロイする名前空間を作成・設定します。
$ ibmcloud fn namespace create my_namespace
$ ibmcloud fn property set --namespace my_namespace
アプリケーション作成
まずはmix new upcase
とプロジェクトを作成し、mix.exs
にIBM Cloud Functions用のライブラリを追加します。
defp deps do
[
{:faas_base, "~> 1.0.2"}
]
end
$ mix deps.get
次にupcase.ex
に下記のようにハンドラを実装します。(サンプルとして小文字を大文字にするだけの簡単な関数アプリです)
defmodule Upcase do
use FaasBase, service: :ibm
alias FaasBase.Logger
alias FaasBase.Ibm.Request
alias FaasBase.Ibm.Response
@impl FaasBase
def init(context) do
{:ok, context}
end
@impl FaasBase
def handle(%Request{body: body} = request, event, context) do
Logger.info(request)
Logger.info(event)
Logger.info(context)
{:ok, Response.to_response(body |> String.upcase, %{}, 200)}
end
end
IBM Cloud Functionsのランタイムからはリクエスト情報が格納されたevent
と環境変数等の情報が格納されたcontext
がMap型で渡ってきます。
第一引数のrequest
にはevent
情報のbody
やらheaders
やらmethod
やらを扱いやすいように展開して格納しています。
戻り値は{:ok, String.t}か{:error, String.t}のように文字列のみを返すか、{:ok, FaasBase.Ibm.Response.t}か{:error, FaasBase.Ibm.Response.t}のようにヘッダー情報やステータスコードを含めた情報を返す必要があります。
試しにコンソールでハンドラの動作確認をしてみます。
$ iex -S mix
iex> alias FaasBase.Ibm.Request
iex> Upcase.handle(%Request{body: %{"data" => "hello"}}, %{}, %{})
{:ok,
%FaasBase.Ibm.Response{
body: "{\"DATA\":\"HELLO\"}",
headers: %{},
status_code: 200
}}
問題なさげです。
デプロイ
IBM Cloud Functionsの実行環境はLinuxになるので、ビルドは、Linux上で行う必要があります。ライブラリには、ビルド用のmixタスクが用意されていて、こちらを実行すると、ビルドとともに、にデプロイに必要なzipファイルが作られます。
$ handle_module=Upcase
$ mkdir -p _build
$ docker run -d -it --rm --name elx erintheblack/elixir-ibm-functions-builder:1.10.4
$ docker cp lib elx:/tmp
$ docker cp mix.exs elx:/tmp
$ docker exec elx /bin/sh -c "mix deps.get; MIX_ENV=prod mix ibm.release ${handle_module}"
$ docker cp elx:/tmp/_ibm ./_build
$ docker stop elx
下記コマンドでWEBアクションとしてデプロイします。
$ cd ./_build/_azure
$ ibmcloud fn action create upcase upcase-0.1.0.zip --native --web true --param LOG_LEVEL info
エンドポイントは下記コマンドで取得できます。
$ ibmcloud fn action get upcase --url
ok: got action upcase
https://us-south.functions.appdomain.cloud/api/v1/web/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/default/upcase
動作確認
curlで叩いて正常に動作することを確認します。
$ curl -H "Content-Type: application/json" https://us-south.functions.appdomain.cloud/api/v1/web/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/default/upcase -d '{"data": "hello"}'
{"DATA":"HELLO"}I
さいごに
FaaSのElixirのライブラリ作成は今回で3つ目ですが、毎回異なるアプローチでカスタム言語に対応した基盤が作られている感じがします。
今回特徴的だったのが、毎回出力して終了するCLIのようなアプリを作成するというもので、Azure Functionsと同様に簡単にWEBからのリクエストが受けられるとは言え、Elixirで書けるからという理由だけで選択するには、かなりシチュエーションを選ぶ気がします。
また、現状では環境変数がこちらから設定できないみたいで、デプロイ時にパラメータとしての設定は可能ですが、リクエスト時にPOSTデータと混在して渡されるなど、使い勝手も。。。な感じでした。
デプロイ時のフラグに--web-secure true
を付けるだけで、リクエストヘッダーにセキュアな値設定を必須にできるなど、痒いところに手が届く機能もあるようなので、オープンソースを活かし今後どんどん使い勝手が良くなっていくかもです。
残るはIBMじゃない方のCloud Functionsですが、こちらはまだカスタム言語に対応していないみたいです。対応予定はあるのかな。。