API GWでOCI Functionsの呼び出し

このシリーズの他の記事は、[オラクル・クラウドの個人ブログ一覧 ](https://qiita.com/letian/items/10152a4fec813c1b7d98)からご覧いただけます。

# 初めに
OCI Functionsを呼び出すのは、以下の様々な方法があります。

- Fn Project CLI
- OCI CLI
- OCI SDK
- HTTPリクエスト
- その他のOCIサービス (Event, Notification, Streamingなど)

今回は、API Gatewayを経由し、OCI Functionsを呼び出す方法を紹介します。
![oracle-funcs-api-gtw-diagram.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/83449ebc-5f28-c9e0-02cb-b39885466d61.png)

__目次__
- [1. 事前準備](#1-事前準備)
- [2. OCI Functionの作成](#2-oci-functionの作成)
- [3. 動的グループとポリシーの作成](#3-動的グループとポリシーの作成)
- [4. API GWからの呼出し](#4-api-gwからの呼出し)

# 1. 事前準備
OCI上に、以下のリソースを作成しておきます。

| 分類        | リソース                     | 名前  | コメント    |
|---------------|---------------------------------|--------------|-------------|
|IAM|Compartment| PoC |root/PoC (作業用)|
||User | 任意 |IDCSユーザか、OCIユーザかどちらでもよい。|
||Auth Token|  |作成後、Tokenをメモしておく（コンソールに再度表示しない）。<br>(作成方法は、[ドキュメント](https://docs.oracle.com/ja-jp/iaas/Content/Functions/Tasks/functionsgenerateauthtokens.htm)をご参照。)|
|ネットワーク|VCN|VCN1 ||
||Subnet| Public Subnet1 |パブリック|
||Internet GW| IGW1 |外部からのHTTPリクエストは必要のため|
||Route Table| Route Table for Public Subnet1 |IGW1を追加|
||Security List| Security List for Public Subnet1 |Ingress Rule: <font color="blue">TCP 443</font>を許可<br>Egress Rule: <strong>All \<region> services</strong> In Oracle Services Network, TCP <strong>All</strong> |

__情報収集__
| 取得項目        | 内容                     |  コメント    |
|---------------|---------------------------------|--------------|
|Region Key|<font color="blue">nrt</font>|確認方法は、[一覧表](https://docs.oracle.com/ja-jp/iaas/Content/General/Concepts/regions.htm)をご参照ください。|
|Region ID|ap-tokyo-1|確認方法は同上|
|Compartment OCID|この例は、root/PoCのOCIDです。|MENU->Identity->Compartments から取得|
|Tenancy Namespace|以下の画面で、ご確認ください。|<font color="blue">Tenancy名ではない</font>ので、ご注意ください。| 

**Tenancy Namespace の取得**
コンソール画面の右上にあるプロファイルメニューから「<font color=blue>テナンシ</font>」をクリックし、詳細情報画面に進んでください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/22b3b366-e8af-1593-3af5-6c0ee568192a.png)

そこで「Object Storage Namespace（オブジェクト・ストレージ・ネームスペース）」の値を確認します。
![Namepace.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/e0cbd6be-29e1-2f08-9471-66723ee7c37a.jpeg)

:::note
※、一部の古いテナンシでは、テナンシ名とネームスペース名が同じ場合がありますが、これらは別のものとして扱う必要がありますのでご注意ください。
:::

__作業ターミナル__
<font color="blue">OCI Cloud Shell</font>に、Docker,Fn Projectなどのツールは既に実装済で、今回の例はCloud Shellを利用します。

```bash
liu_wei@cloudshell:~ (ap-tokyo-1)$ docker -v
Docker version 19.03.11-ol, build f0aae77
liu_wei@cloudshell:~ (ap-tokyo-1)$ fn version
Client version: 0.6.1 is not latest: 0.6.5
Server version:  ?
liu_wei@cloudshell:~ (ap-tokyo-1)$
```

# 2. OCI Functionの作成
__OCI Registoryにログイン__
コマンド：<font color="blue">docker login</font> \<region-key>.ocir.io

コマンド実施後、ユーザ名とパスワードの入力が要求されます。
<strong>ユーザ名</strong>: <font color="blue">\<tenancy-namespace>/\<user-name></font>
\<tenancy-namespace>は、上記の情報収集画面で確認します。
\<user-name>は、IDCSユーザかOCIユーザの名前です。(Cloud Shellに表示されるユーザ名ではありません。)
IDCSユーザの場合、"oracleidentitycloudservice/xxxxxx"という形式で入力してください。
<strong>パスワード</strong>: 事前に作成した<font color="blue">Auth Token</font>の値です。 
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/92baa07e-bd9e-6424-017a-11f61a572d1d.jpeg" width="600">

__Functionの設定__
Contextの一覧を確認します。
コマンド：fn <font color="blue">list context</font>
```
liu_wei@cloudshell:~ (ap-tokyo-1)$ fn list context
CURRENT NAME            PROVIDER        API URL                                         REGISTRY
*       ap-tokyo-1      oracle-cs       https://functions.ap-tokyo-1.oraclecloud.com
        default         oracle-cs
liu_wei@cloudshell:~ (ap-tokyo-1)$
```

利用中のRegionを指定していない場合、以下のコマンドで指定します。
コマンド例：fn <font color="blue">use</font> context ap-tokyo-1

Compartmentを設定します。
コマンド：fn update context oracle.compartment-id \<Compartment-OCID>

```bash
liu_wei@cloudshell:~ (ap-tokyo-1)$ fn update context oracle.compartment-id ocid1.compartment.oc1.<略>
Current context updated oracle.compartment-id with ocid1.compartment.oc1.<略>
liu_wei@cloudshell:~ (ap-tokyo-1)$
```

Registry URLを設定します。
コマンド：fn update context <font color="blue">registry</font> \<region-key>.ocir.io/<font color="blue">\<tenancy-namespace></font>/\<registry-project-name>

```bash
liu_wei@cloudshell:~ (ap-tokyo-1)$ fn update context registry nrt.ocir.io/tenancy-namespace/my-func-prj
Current context updated registry with nrt.ocir.io/tenancy-namespace/my-func-prj
liu_wei@cloudshell:~ (ap-tokyo-1)$ fn list context
CURRENT NAME            PROVIDER        API URL                                         REGISTRY
*       ap-tokyo-1      oracle-cs       https://functions.ap-tokyo-1.oraclecloud.com    nrt.ocir.io/tenancy-namespace/my-func-prj
        default         oracle-cs
liu_wei@cloudshell:~ (ap-tokyo-1)$ 
```

~/.fn/contextsの下に、設定ファイルが作成されました。

```bash
liu_wei@cloudshell:contexts (ap-tokyo-1)$ pwd
/home/liu_wei/.fn/contexts
liu_wei@cloudshell:contexts (ap-tokyo-1)$ ll
total 8
-rw-r--r--. 1 liu_wei oci 228 Apr 20 08:21 ap-tokyo-1.yaml
-rw-r--r--. 1 liu_wei oci  33 Oct 13  2020 default.yaml
liu_wei@cloudshell:contexts (ap-tokyo-1)$ cat ap-tokyo-1.yaml 
api-url: https://functions.ap-tokyo-1.oraclecloud.com
oracle.compartment-id: ocid1.compartment.oc1.略
provider: oracle-cs
registry: nrt.ocir.io/tenancy-namespace/my-func-prj
liu_wei@cloudshell:contexts (ap-tokyo-1)$ 
```

__アプリケーションの作成__
MENU->Developer Services->Functions
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/5316c5f5-0f7d-0927-83f8-c8c2aa8253b6.png)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/a695da73-02fa-9eae-8177-52558ce0841a.png" width="500">

__Functionの作成__
言語はJAVA,Python,NodeのどちらでもOKです。この例は、Pythonを利用します。

作業用ディレクトリを作成します。

```bash
liu_wei@cloudshell:~ (ap-tokyo-1)$ pwd
/home/liu_wei
liu_wei@cloudshell:~ (ap-tokyo-1)$ mkdir my-app
liu_wei@cloudshell:~ (ap-tokyo-1)$ cd my-app
liu_wei@cloudshell:my-app (ap-tokyo-1)$ 
```

Python Functionの作成
コマンド：fn <font color="blue">init</font> --runtime <font color="Magenta">python</font> \<Function Name>

```bash
liu_wei@cloudshell:my-app (ap-tokyo-1)$ fn init --runtime python my-func-helloworld
Creating function at: ./my-func-helloworld
Function boilerplate generated.
func.yaml created.
liu_wei@cloudshell:my-app (ap-tokyo-1)$ ll
total 0
drwxr-xr-x. 2 liu_wei oci 62 Apr 20 09:03 my-func-helloworld
liu_wei@cloudshell:my-app (ap-tokyo-1)$ ll my-func-helloworld/
total 12
-rw-r--r--. 1 liu_wei oci 576 Apr 20 09:03 func.py
-rw-r--r--. 1 liu_wei oci 147 Apr 20 09:03 func.yaml
-rw-r--r--. 1 liu_wei oci   3 Apr 20 09:03 requirements.txt
liu_wei@cloudshell:my-app (ap-tokyo-1)$  
```

<strong>Deploy Function</strong>
コマンド：fn -v <font color="blue">deploy</font> --app \<Application Name>
Function格納用ディレクトリに移動してから、コマンドを実施します。

```bash
liu_wei@cloudshell:my-app (ap-tokyo-1)$ pwd
/home/liu_wei/my-app
liu_wei@cloudshell:my-app (ap-tokyo-1)$ cd my-func-helloworld/
liu_wei@cloudshell:my-func-helloworld (ap-tokyo-1)$ fn -v deploy --app HelloWorld
Deploying my-func-helloworld to app: HelloWorld
<略>
```

__Functionの呼び出し__
コマンド：fn <font color="blue">invoke</font> \<Application Name> \<Function Name>
初回目は、少し時間がかかりますけど、二回目よりすぐ終わります。

```bash
liu_wei@cloudshell:my-func-helloworld (ap-tokyo-1)$ fn invoke HelloWorld my-func-helloworld
{"message": "Hello World"}
liu_wei@cloudshell:my-func-helloworld (ap-tokyo-1)$
```

パラメータで呼び出し

```bash
liu_wei@cloudshell:my-func-helloworld (ap-tokyo-1)$ echo -n '{"name":"Wei"}' | fn invoke HelloWorld my-func-helloworld
{"message": "Hello Wei"}
liu_wei@cloudshell:my-func-helloworld (ap-tokyo-1)$ 
```

Endpointの確認
コマンド：fn <font color="blue">inspect</font> function \<Application Name> \<Function Name>
OCIコンソールでも確認できます。
![endpoint.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/94937a40-ae38-b057-c34a-23183d080332.jpeg)

EndpointのURLをクリックしたら、以下の画面が表示されます。これは想定内の動作です。
![Browser.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/2ad71ad6-ef9e-da0d-94f1-92471054e83a.jpeg)
ここまで、Function自体の作成は完了です。これから、API GWでの呼び出しを設定します。

# 3. 動的グループとポリシーの作成
グループ名：API-GW-DG
ルール： ALL {resource.type = '<font color="blue">ApiGateway</font>', resource.compartment.id = 'ocid1.compartment.\<your-compartment-OCID>'}
![DG.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/0ece3cfb-2d5e-c9f1-b7ec-1cac215b1a22.jpeg)

ポリシー名：API-GW-Policy
ステートメント：
Allow dynamic-group \<Dynamic Group Name> to <font color="blue">use functions-family</font> in compartment \<your-compartment-name>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/7dbcf673-6332-2190-1a10-ca4b614d6a2d.png)

# 4. API GWからの呼出し
__API Gatewayの作成__
MENU->Developer Services->API Management->Gateways
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/d2348e88-f5a3-e9da-3df7-2a7fee0f92f4.png" width="500">

__API Deploymentの作成__
![API-GW.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/1ba0a11b-b8ed-671a-ff9d-89a8704a783b.jpeg)

API GW作成後、Deploymentの作成に進んでください。
API Request Polices: デフォルトのまま
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/3f3f8988-d752-6491-022c-781655b801b6.png" width="600">

次のページ：
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/2352fb1b-ecd0-9bc4-ab39-ab9f9aceb657.png" width="600">

次のページは、確認画面です。確認完了後、作成を開始します。
作成終了後、Endpointをコピーします。
![API-Deployment.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/01d5edb0-7aa7-4a7d-2421-a4292847e3c2.jpeg)

WEBブラウザを開き、Endpointを入力したら、今回は"Hello World"が正しく表示されます。
URLの例：`https://<OCID>.apigateway.ap-tokyo-1.oci.customer-oci.com`<font color="blue">/v1/helloworld</font>
もし、正しく表示されなかったら、以下のことをチェックしてください。
1. Ingress Ruleには、TCP 443ポートを許可していますか？
2. URLの入力間違いがありませんか？"/v1"の後ろ、"/helloworld"を忘れないでください。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1121622/cc7ff5d4-1df5-6a3d-6024-c79f617d4478.jpeg">

CURLでAPI Endpointを経由し、Functionを呼び出します。
コマンド：`curl <Endpoint_URL>`

```bash
liu_wei@cloudshell:~ (ap-tokyo-1)$ curl https://xxxxxxxx.apigateway.ap-tokyo-1.oci.customer-oci.com/v1/helloworld
{"message": "Hello World"}liu_wei@cloudshell:~ (ap-tokyo-1)$
```

注意：もし[STEP-3](#3-動的グループとポリシーの作成)のポリシー作成を漏れたら、上記CURLコマンドを実施すると、以下のエラーが発生されます。
```
{"code":500,"message":"Internal Server Error"}
```

__サマリ__
この例では、Cloud Shell を使って OCI Function を作成しました。もちろん、ローカル端末やクラウド上の仮想マシン（VM）でも実行することは可能です。その場合は、<font color="blue">OCI CLI、Docker、および Fn Project</font> のインストールと設定が必要になります。また、管理者以外のユーザーが実行する場合は、適切なポリシーの追加も必要です。

私自身は、Cloud Shell とクラウド上の VM の両方で作業した経験がありますが、Cloud Shell の方が比較的手軽で、個人的にはおすすめです。

以上

----
__関連記事__
[OCI Functionで郵便番号から住所を検索する](https://qiita.com/liu-wei/items/70dd4f538d7e1b83066d)

__参考ドキュメント__
[Functions: APIゲートウェイを使用したファンクションのコール](https://docs.oracle.com/ja-jp/iaas/developer-tutorials/tutorials/functions/func-api-gtw/01-summary.htm)
[クラウド・シェルでのファンクションのクイックスタート](https://docs.oracle.com/ja-jp/iaas/Content/Functions/Tasks/functionsquickstartcloudshell.htm#functionsquickstart_cloudshell)
[OCI Functionsのトラブルシューティング](https://docs.oracle.com/ja-jp/iaas/Content/Functions/Tasks/functionstroubleshooting.htm#Troubleshooting_Oracle_Functions)