【OCI】API Gateway経由でFunctionsのAPIを呼び出してみる

# はじめに

今回はOracle Cloud Infrastructure（OCI）のFunctionsとAPI Gatewayを使って
簡単なAPIを作成する方法を紹介したいと思います。
※Functionsで実行される言語はNode.jsを使用します。

■実行環境（クライアントPC）
Windows10, Postman（REST APIを呼び出せるツールならなんでもOK）

■参考
・Functions
https://docs.oracle.com/ja-jp/iaas/Content/Functions/Concepts/functionsoverview.htm

・API Gateway
https://docs.oracle.com/ja-jp/iaas/Content/APIGateway/Concepts/apigatewayoverview.htm

■構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/3a8bb43a-ce4e-0846-b7bc-53b61500c3fc.png)

今回はプライベートサブネットに作成したFunctionsを外側から
呼び出しできるようにパブリックサブネットにAPI Gatewayを
配置する構成となります。
イメージの保存先はContainer Registryを使用します。

■事前準備
VCN、サブネット（プライベート、パブリック）、セキュリティリスト等を
作成しておき、インターネットからHTTPS(Port 443)でアクセスできるように
準備しておきます。

作成方法についてはこちらを参考にしてください。
https://oracle-japan.github.io/ocitutorials/beginners/creating-vcn/

◆目次
1.ポリシーの設定
2.Container Registryの作成
3.Functionsの作成
4.API Gatewayの作成
5.クライアントPCからのAPI呼び出し

## 1. ポリシーの設定
API GatewayとFunctionsを連携させるには最初にポリシーの設定をします。

OCIではAPI GatewayとFunctionsのように異なるサービス間でリソースを
共有する場合、リソースプリンシパルと呼ばれるポリシー設定が必要となります。

左上メニュー[Ξ]からアイデンティティとセキュリティを選択し、
ポリシーをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/89440c63-decb-24ab-29d0-0446564a9e14.png)

対象のコンパートメントを選択し、ポリシー作成ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/baf0d07d-c9bf-80f4-f65a-0410bfed5744.png)

名称、説明に任意の文字列を入力し、手動エディタの表示をONにして
ポリシー・ビルダーにポリシーを入力してから作成ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/ce007ebc-2e4a-1ac6-b47a-865fb0f4a33f.png)
[ポリシー]
allow any-user to use functions-family in compartment <コンパートメント名> where all { request.principal.type= 'ApiGateway', request.resource.compartment.id = '<コンパートメントのOCID>' }

※コンパートメントのOCIDは以下から確認できます。
アイデンティティ > コンパートメント > コンパートメントの詳細

以上でポリシーの設定は終了です。

## 2. Container Registryの作成
Functionsを動かすコンテナのイメージを格納するために
Container Registry(OCIR)を作成します。

左上メニュー[Ξ]から開発者サービスを選択し、
コンテナ・レジストリをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/1c18378d-3021-0ff2-0640-6d9a51ee039c.png)

コンパートメントを選択し、リポジトリの作成をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/5bdd1a9c-18a9-e4dc-2447-a1d3be0fdb9c.png)

以下設定してから作成ボタンを実行します。
アクセス = プライベート
リポジトリ名 = repo_fc01
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/37b64e2e-cd47-24aa-df55-e00053dad363.png)

リポジトリの作成が完了すると次のような画面となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/81bfddb1-6657-1c60-f5d5-b86144d360c8.png)

以上でリポジトリの作成は終了です。

## 3. Functionsの作成
実際にAPIの処理を実行するためにFunctionsを作成します。

今回は簡単な四則演算でパラメータで渡した２つの値を
加算させて結果を返すという処理を行いたいと思います。

左上メニュー[Ξ]から開発者サービスを選択し、
ファンクションをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/2e0ad43f-6ae7-b13b-e08b-33debe8620f7.png)

対象のコンパートメントを選択し、ファンクションの作成ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/9a2d79cd-3f0c-4ab9-e2c9-99b054d14081.png)

以下の項目を入力して作成ボタンを実行します。
名前 = fc01
VCN = ※既存の作成されているVCN
サブネット = ※既存の作成されているプライベートサブネット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/15884fbf-f616-4ad9-d8e6-770bd36c3467.png)

Functionsが作成されると次のような画面が表示されます。
スタートガイドの手順に沿って処理を行うとAPIの処理を作ることができますが、
ここでは順を追って説明していきたいと思います。

まずはCloud Shellを起動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/ee3932a6-7378-1a74-9e4c-2987796430fd.png)

起動すると次のようなブラウザからアクセスできるコマンドラインが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/74e0a026-f1a9-5c14-4bfc-4d9e3b2732cd.png)

①ファンクションのコンパートメントIDでコンテキストを更新
コマンドラインで以下を実行
```
fn update context oracle.compartment-id <コンパートメントのOCID>
```
※スタートガイドのステップ3に表示されているのでコピーできます。

②レジストリ／リポジトリを指定
コマンドラインで以下を実行
```
fn update context registry kix.ocir.io/<テナンシー名>/[repo-name-prefix]
```
※スタートガイドのステップ4に表示されているのでコピーできます。

[repo-name-prefix]は前述のContainer Registryの作成で設定した
repo_fc01にします。

③ディレクトリ作成
次のコマンドでディレクトリを作成します。
```
mkdir /home/<ユーザ名>/fc01
mkdir /home/<ユーザ名>/fc01/plus
```
ディレクトリ作成後にcdで移動しておきます。
```
cd /home/<ユーザ名>/fc01/plus
```
④ファンクション作成
次のコマンドでFunctionsのNode.js実行テンプレートを作成します。
```
fn init --runtime node
```
ファイル表示[ls -l]を実行すると次のようなファイルが生成されている
ことが確認できます。
```
-rw-r--r--. 1 <ユーザ名> oci 228 Aug  4 09:27 func.js
-rw-r--r--. 1 <ユーザ名> oci 154 Aug  4 09:27 func.yaml
-rw-r--r--. 1 <ユーザ名> oci 198 Aug  4 09:27 package.json
```
⑤Node.js修正
次のコマンドでNode.jsファイルの中身を丸ごと変更します。
vi func.js
```
const fdk=require('@fnproject/fdk');

fdk.handle(function(input){
  let l = 0
  let r = 0
  if (input.left) {
    l = Number(input.left)
  } 
  if (input.right) {
    r = Number(input.right)
  }
  const result = l + r
  return {'result': String(result)}
})
```

func.jsの修正が終わったら、ディレクトリのトップにcdで移動しておきます。
```
cd /home/<ユーザ名>/fc01
```

⑥Functionsのデプロイ
次のコマンドでデプロイを実行します。
(実行するディレクトリ: /home/<ユーザ名>/fc01)
```
fn -v deploy --app fc01 plus
```

処理が正常に終了すると次のようなメッセージが表示されます。
```
Updating function plus using image kix.ocir.io/<テナンシー名>/repo_fc01/plus:0.0.1...
Successfully created function: plus with kix.ocir.io/<テナンシー名>/repo_fc01/plus:0.0.1
```

念のためFunctionsが機能しているか確認します。
デプロしたディレクトリ（/home/<ユーザ名>/fc01）と同じ階層で実行します。
```
echo -n '{"left":"5", "right":"10"}' | fn invoke fc01 plus
```

正常に処理が完了すると次の結果が返ります。
```
{"result":"15"}
```

以上でFunctionsの作成は終了です。

## 4. API Gatewayの作成
先程作成したFunctionsを外から呼び出しできるようにAPI Gatewayを作成します。

左上メニュー[Ξ]から開発者サービスを選択し、
ゲートウェイをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/1533cc12-5a88-d644-59d2-7272b5c8a495.png)
使用するコンパートメントを選択し、ゲートウェイの作成ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/4bc6449a-2d90-d18a-699f-8ba3054b6757.png)
以下の項目を入力してゲートウェイの作成ボタンを実行します。
名前 = fc01
タイプ = パブリック
仮想クラウドネットワーク = ※既存の作成されているVCN
サブネット = ※既存の作成されているパブリックサブネット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/874d9755-224e-6cab-d2a3-822accff1b16.png)
API Gatewayの作成が完了すると次のような画面となりますので、
左下のデプロイメントリンクをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/70812c7a-7be0-541b-698a-02f7a8b05864.png)
続いて、デプロイメントの作成ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/19af4bd6-37bf-d8a5-c693-2bf5a5feaf8c.png)
トップの選択項目を「最初から」とし、以下の項目を入力して
次ボタンをクリックします。
名前 = fc01_deploy
パス接頭辞 = /calc
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/c4ae9bd4-2249-11c5-63c3-d2d1b05db8d7.png)
認証なしを選択し、次ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/aa854ff7-5786-d3bf-da5b-72464fa0f094.png)
以下の項目を入力・選択して次ボタンを実行します。
パス = /plus
メソッド = POST
バックエンド・タイプ = Oracleファンクション
アプリケーション = fc01
関数名 = plus
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/4d8952e3-0b8c-a3ad-9dff-f8a5c5c375d9.png)
最後に確認をして、作成ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/b5eaf5c3-ad1e-11a1-be66-c66dd524befe.png)
次の画面が表示されたらAPI Gatewayが正常に作成されたことが確認できます。
あとで使用するため、エンドポイントのコピーで情報を保存しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/1c84e89e-bee6-fa66-d188-27b8abdeeae7.png)

以上でAPI Gatewayの作成は終了です。

## 5.クライアントPCからのAPI呼び出し
最後に作成したAPI Gatewayが正常に動作するか確認します。

Postmanを起動したら新規リクエストを作成し、以下の項目を入力します。
メソッド = POST
エンドポイント = API Gateway作成せ取得したエンドポイント + /plus
ボディ = {"left":"30", "right":"70"}
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3493138/23744db3-30c3-65e0-b1ac-5272a9feb99e.png)
Sendボタンをクリックすると結果が返ってきます。
{
    "result": "100"
}

## おわり
以上、API Gateway + Functionsで簡単にAPIを作成することができました。