Code EngineのFunctionsを使ってみよう

# はじめに
気づいたらCode EngineのProjectを開くと `Functions` というサービスが増えていました。何これ、と思ったので少し弄った結果を共有します。なお、[リリースノート](https://cloud.ibm.com/docs/codeengine?topic=codeengine-codeengine-relnotes#codeengine-jun2923)を確認する範囲では2023/6/29にリリースされていた模様です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/0e0cfe16-1384-b265-c9cc-86bff90e2d62.png)

# ApplicationsやJobsとの違いは？

[ドキュメント](https://cloud.ibm.com/docs/codeengine?topic=codeengine-cebatchjobs#batchjob-compare)より
|特性 |Applications|Jobs|Functions|
|---|---|---|---|
|実行時間 (期間) |長期実行 (要求ごとに 10 分) |長期実行 (最大 24 時間) |短時間実行 (2 分以下)|
|始動待ち時間 |中 |予定開始日 |低|
|終了 |継続的に実行 |実行から完了まで |実行から完了まで|
|呼び出し |要求時または永続的に実行中 |スケジュール済み |要求時、インスタント|
|プログラミング・モデル |コンテナー・ベースのビルドと実行 |コンテナー・ベースのビルドと実行 |言語固有のソース・コード・ファイルおよび依存関係メタデータ|
|並列処理 |並列実行、柔軟性 |低から中程度の並列実行 |高並列実行|
|スケールアウト |要求の数に基づく |ジョブ・ワークロード定義に基づく |イベントまたは直接呼び出しに基づく|
|最適化対象 |長時間実行され、非常に複雑なワークロードとオンデマンド・スケールアウト |リソース要求が高い、スケジュールされたワークロードまたは計画されたワークロード |起動時間と迅速なスケールアウト|

`Application` は簡易なWebサイトを構成して、アクセスがあった時だけコンテナを稼働して見せる、といったことが可能で、`Jobs` はバッチ処理させたい時に便利でした。私の場合は特に定期的な機械学習処理を動かしたり、Webサイトの情報収集を定期実行させるのに便利に使っています。

では `Functions` は、どんなシチュエーションで使えるでしょうか？
簡単に想像がつくのは `Webhook` や `API` の公開で便利かなと思いました。
今まで何かしらのサービスから一時的に呼び出された場合に必要な処理を行うWebhookをCode Engineで実装する場合、Applicationを作成してそのURLを公開し、Applicationに必要な実装を行っていました。この時Applicationで動かすコンテナは、 `httpのリクエストを受け取って、それを必要な形でハンドリングする仕組み` も含めて実装する必要がありました。これはフレームワークを使えば難しい話ではないですが、それでもビジネスロジック以外の実装が必要になるので、実装の負担が少しありました。
`Functions` では、こちらの記事を執筆する2023年10月時点では、 `Node.js`、 `Python` に限定されたランタイムのみ提供されますが、コードに内包された `main()` 関数に対しhttpのリクエストに関する情報を渡した上で動作させる仕組みのため、httpのハンドリングはFunctions側が担ってくれて、あくまで必要なロジックのみを実装するだけでサービスの利用が可能です。

また、 `Functions` では、呼び出されるたびに、都度コンテナを立ち上げるのではなく、Code Engineのサービスで稼働しているコンテナの中に、指定されたコードを差し込んで動かす形なので、言ってしまうと `Applications` に比べて、該当のコードが起動するまでは早くなります。この点においても `Webhook` や `APIの公開` といった使い方が良いのでは、と考えます。

# Functionsを作ってみよう
Code EngineのProjectから `Functions` を選んで、そこから新規作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/15ece7d0-b237-367e-8620-3bfe78490af0.png)

ちょっと縮尺を小さくして見づらいですが、 `名称` `ランタイム` `リソースサイズ` `タイムアウト`の設定が必要です。
- 名称：半角英数字、もしくは `-`のみ入力可能です
- ランタイム： `Node.js 18` `Python 3.11` のみ指定可能です
- リソースサイズ： デフォルトでは `0.25 vCPU / 0.5 GB`が選択されますが、必要なスペックを指定してください
- タイムアウト：これは動作するコードが指定された時間以内の応答を返さない場合にタイムアウトになるものです

画面下部で環境変数を設定できます、これはその場でKey/Valueで指定しても良いですし、Code EngineのProjectに登録されているConfigMapやSecretを選択しても構いません。作成した後からでも指定が可能です。今回は特に指定せずに動かします。

必要な設定を行い `Create` しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/0d476063-503c-df0a-2320-52bf73c1a8af.png)

このように作成が完了しました。これで作成自体は完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/bf9e2c0f-5a2c-3a26-23d3-23052c06b94c.png)

# 作成された内容を確認しましょう
Node.jsのコードが自動生成されて、そこに `main()` が定義されて、このFunctionが呼び出されると、このコードが実行される形になります。
自動生成されたコードは、この関数に渡された引数 `args` と、環境変数の `process.env` の内容を標準出力した後に、リクエスト元に応答するようです。
このコードはポータルのGUI上で編集が可能です。
```Javascript:main.js
function main(args) {
  const body = {
    args,
    env: process.env,
  };

  console.log(`Return body: ${JSON.stringify(body, null, 2)}`);

  return {
    statusCode: 200,
    headers: { 
      'Content-Type': 'application/json', 
    },
    body,
  };
}

module.exports.main = main;

```

このFunctionを試験的に呼び出す方法は2つあって、ポータル上で呼び出すか、利用者でブラウザ等でアクセスするかの方法があります。

画面右上の `Test function` を押すと次のような画面が表示されます。
- Send request：ポータル上で作成したFunctionにGET要求を実行した結果を確認できます
- Function URL：これはブラウザだと別タブで公開されたFunctionのURLにアクセスします

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/dcb0f8fc-4ae6-d1d2-3c5f-15f3baf78f5f.png)

前者を実行すると次のようなイメージになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/5eb06b91-22cd-3f87-1367-efbb274f9ec2.png)
後者はブラウザで開くと次のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/02f0c382-2163-e94b-eaf6-47a1d4bfbca2.png)

ブラウザでアクセスした応答内容を見ると次のようになっています。今回はURLのパラメータに特に何も指定していないですが、 `args` の中にいくつか情報があるので、それを活用して今後は処理を分けれそうですね。
- `args.__ce_method`：アクセスのメソッドがわかるので、REST APIのようにメソッドに分けて実装ができそうです
- `args.__ce_path`：アクセスしている公開したFunctionへのパスが分かるので、これに合わせて機能を分けることができそうです

```json
{
  "args":{
    "__ce_headers":{
      "Accept":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
      "Accept-Encoding":"gzip, deflate, br",
      "Accept-Language":"en-US,en;q=0.9,ja;q=0.8",
      "Sec-Ch-Ua":"\"Google Chrome\";v=\"117\", \"Not;A=Brand\";v=\"8\", \"Chromium\";v=\"117\"",
      "Sec-Ch-Ua-Mobile":"?0",
      "Sec-Ch-Ua-Platform":"\"Windows\"",
      "Sec-Fetch-Dest":"document",
      "Sec-Fetch-Mode":"navigate",
      "Sec-Fetch-Site":"cross-site",
      "Sec-Fetch-User":"?1",
      "Upgrade-Insecure-Requests":"1",
      "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
      "X-Request-Id":"808b1e44-1663-4f13-b037-9b20dfc2cbea"},
    "__ce_method":"GET",
    "__ce_path":"/"
  },
  "env":{
    "CE_ENABLE_INIT_INSTALL":"false",
    "HOME":"/root",
    "PATH":"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/lib/nodejs/bin",
    "PWD":"/nodejsAction",
    "SHLVL":"0",
    "_":"/usr/local/lib/nodejs/bin/node",
    "__OW_ALLOW_CONCURRENT":"true",
    "container":"oci"
  }
}
```
ブラウザでアクセスするURLにパラメータを適当に入れるとちゃんと `args` で受け取れていることがわかります。
`?id=testid&value=12345`をURLの末尾に付与しましたが見えています。
`args.id` : `testid`
`args.value` : `12345`

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/651598/9e4281eb-0ce1-3545-f194-d983bce53b91.png)

httpのリクエスト内容からも情報が取得できているので、活用できそうです。

# ポータルのコード編集ではできないこと
今回選択したランタイムでは `Node.js 18` を使っていますが、自動生成されたコードは Code Engine が裏で実行する際に `await` が付与されているわけでもなく、またポータルで編集可能なコードの `main()` に `async` を付与するとコードエラーでデプロイできなくなります。せっかく Node.js のv18を使ってるなら、`fetch()`を使ってコードを書きたいのに、何だかもったいない。。。
つまり、このままでは非同期処理を実行することが出来ません。やり方はあるのですが、それは次の記事で紹介したいと思います。

# さいごに
いかがでしたでしょうか？ Code Engine の `Functions` を使うことで、簡単にWebhookやAPIを公開する道が開けたように思います。
なるべくビジネスロジックの開発に専念して、周辺はクラウドサービスを活用していくのも面白いと思いますね。ぜひ次の記事でも活用方法を紹介できればと思います。

- Code Engine次回記事(2023/11/7)

https://qiita.com/katahiro/items/b2866df4225bbdd8334d