LoginSignup
0

Azure FunctionsをElixirで

Last updated at Posted at 2021-03-05

はじめに

※使用ライブラリを、AWS Lambda, Azure Functions, IBM Cloud Functionsで使える共通ライブラリにしたので、記事を若干修正しました。(2021/07/07)
※Azure Functions Core Tools V4に対応しました(2023/06/24)

以前、AWS LambdaのアプリケーションをElixirで簡単に実装できるライブラリを作ったときに、「ElixirでAWS LambdaでGithubActionsで継続デプロイ」という投稿でAWS Lambdaへのデプロイまでを紹介させていただきました。

今回、Azure FunctionsのアプリケーションをElixirで簡単に実装できるライブラリを作ってみたので、それを使ってAzure Functionsへのデプロイまでをやってみたいと思います。

ライブラリはこちらで公開
→ https://github.com/imahiro-t/faas_base
→ https://hex.pm/packages/faas_base

ElixirでAzure Functions

Azure Functionsでは2020年末にカスタムハンドラーがGAになり、今までサポートされていなかった言語を使ってのAzure Functionsのアプリケーション開発が可能になりました。

ざっくりとした要件としては、Azure Functionsのランタイム上で稼働するwebサーバを使いたい言語で立ち上げて、ある一定のルールでランタイムとやりとりするというもので、ElixirであればCowboyをベースに作成するのが簡単かなと思います。

webサーバの立ち上げやAzure Functionsのランタイムとのやりとりについては、作成したライブラリ側で吸収しているので、今回はこの辺りをあまり意識せずにデプロイまでやっていくことになります。

やってみる

Azure Functions Core Toolsのインストール

Azure Functionsをローカル環境で試してみたり、デプロイしたりできるツールです。ローカル環境はMacなので下記のような感じでインストールしておきます。

brew tap azure/functions
brew install azure-functions-core-tools@4

Azure環境の作成

azureの管理画面にてリソースグループを作成し、関数アプリ(Azure Functions)を作成します。

image.png

image.png

image.png

作成の際には、ランタイム スタックをCustom Handlerに、オペレーティング システムをLinuxにして作成するようにしてください。

アプリケーション作成

まずはmix new upcaseとプロジェクトを作成し、mix.exsにAzure Functions用のライブラリを追加し、アプリケーションとして起動します。

  def application do
    [
      mod: {FaasBase.Azure.Application, []}
    ]
  end

  defp deps do
    [
      {:faas_base, "~> 1.1.1"}
    ]
  end
$ mix deps.get

次にupcase.exに下記のようにハンドラを実装します。(サンプルとして小文字を大文字にするだけの簡単な関数アプリです)

defmodule Upcase do
  use FaasBase, service: :azure
  alias FaasBase.Logger
  alias FaasBase.Azure.Request
  alias FaasBase.Azure.Response
  @impl FaasBase
  def init(_context) do
    # call back one time
    :ok
  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

AzureFunctionsのランタイムからはリクエスト情報が格納されたeventと環境変数等の情報が格納されたcontextがMap型で渡ってきます。

第一引数のrequestにはevent情報をbodyやらheadersやらmethodやらを扱いやすいように展開して格納しています。

戻り値は{:ok, String.t}か{:error, String.t}のように文字列のみを返すか、{:ok, FaasBase.Azure.Response.t}か{:error, FaasBase.Azure.Response.t}のようにヘッダー情報やステータスコードを含めた情報を返す必要があります。

例えばmethodがOPTIONSのレスポンスでCORSのヘッダー情報を返したい場合などは下記のように記述します。

    headers = %{
      "Access-Control-Allow-Origin" => "*",
      "Access-Control-Allow-Methods" => "POST, GET, OPTIONS",
      "Access-Control-Allow-Headers" => "X-PINGOTHER, Content-Type"
    }
    {:ok, Response.to_response("", headers, 200)}

試しにコンソールでハンドラの動作確認をしてみます。

$ _HANDLER=Upcase iex -S mix
iex> alias FaasBase.Azure.Request
iex> Upcase.handle(%Request{body: "{\"data\": \"hello\"}"}, %{}, %{})
{:ok,
 %FaasBase.Azure.Response{
   body: "{\"DATA\":\"HELLO\"}",
   headers: %{},
   status_code: 200
 }}

問題なさげです。

デプロイ

Azure Functionsの実行環境はLinuxになるので、ビルドは、Linux上で行う必要があります。ライブラリには、ビルド用のmixタスクが用意されていて、こちらを実行すると、ビルドとともに、_build/_azureにデプロイに必要な情報が作られます。

$ handle_module=Upcase
$ method_name=upcase
$ mkdir -p _build
$ docker run -d -it --rm --name elx erintheblack/elixir-azure-functions-builder:1.10.3
$ docker cp lib elx:/tmp
$ docker cp mix.exs elx:/tmp
$ docker exec elx /bin/bash -c "mix deps.get; MIX_ENV=prod mix azure.release ${handle_module} ${method_name} 'get post'"
$ docker cp elx:/tmp/_azure ./_build
$ docker stop elx

デプロイします。

$ cd ./_build/_azure
$ func azure functionapp publish ${functionName} --publish-local-settings --overwrite-settings

functionNameには関数アプリ作成時の関数名を指定します。

デプロイが成功すると関数のエンドポイントとなるURLが表示されるのでメモしておきます。

Functions in ********:
    upcase - [httpTrigger]
        Invoke url: https://********.azurewebsites.net/api/upcase?code=u8UF9689a/vHGu1Zd36Fzi0qb5YfHJgKxiZ6jJzHLsP9689X1AZkwQ==

動作確認

curlで叩いて正常に動作することを確認します。

$ curl -X post -H 'content-type:application/json' -d '{"data": "hello"}' https://********.azurewebsites.net/api/upcase?code=u8UF9689a/vHGu1Zd36Fzi0qb5YfHJgKxiZ6jJzHLsP9689X1AZkwQ==
{"data":"HELLO"}

さいごに

AWS LambdaでElixirを動かすのは、実際に運用していたりするので、多少は実績があるのですが、Azure Functionsは運用実績もなく、どこまでできるのか、まだまだ未知数です。

Azure Functionsでは、httpのトリガーだけでなくタイマーや他のトリガーも豊富みたいなので、その辺りの組み合わせや、Azure StorageやAzure Cosmos DBとの連携なども試してみたいと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0