Apache OpenWhisk(Standalone)をブラウザから実行してみる

# 目的
以前の記事でCentOS Stream 9条にStandaloneで起動させたApache OpenWhiskを別の端末からブラウザを用いアクセスし、Web UI上で簡単なサービスを実行できるように環境を用意する。

# 稼働環境
OpenWhiskが起動しているOSのIP、コンテナのIPは以下の通り。コンテナ起動時にOSのIPへバインドしても良かったのですが、どのファイルでdocker runを実行しているのかすぐに把握できなかったためStandaloneを起動した状態でリモート提供する形を模索しました。

```bash
$ ip addr show ens33 | grep "inet "
    inet 192.168.11.55/24 brd 192.168.11.255 scope global dynamic noprefixroute ens33
$ docker ps -a
CONTAINER ID   IMAGE                                 COMMAND                   CREATED         STATUS         PORTS      NAMES
b4ba49dd1a27   openwhisk/action-nodejs-v20:nightly   "docker-entrypoint.s…"   7 minutes ago   Up 7 minutes   8080/tcp   wsk0_9_prewarm_nodejs20
$ docker inspect b4ba49dd1a27 | grep OW_API_HOST
                "__OW_API_HOST=http://172.17.0.1:3233",
```
 
# プロキシの導入と設定
外部から直接アクセスできないサービスを外部に提供する方法として、リバースプロキシで実装していきます。実装にはnginxを導入し、定義を以下のように追加し起動します。

```bash
$ sudo dnf install -y nginx
$ sudo vi /etc/nginx/nginx.conf
〜省略〜
    server {
        listen       13232;
        server_name  _;
        root         /usr/share/nginx/html;

        location / {
            proxy_pass http://172.17.0.1:3232;
        }
    }

    server {
        listen       13233;
        server_name  _;
        root         /usr/share/nginx/html;

        location / {
            proxy_pass http://172.17.0.1:3233;
        }
    }
〜省略〜
$ sudo firewall-cmd --permanent --add-port=13232-13233/tcp
success
$ sudo firewall-cmd --reload
success
$ sudo systemctl start nginx
```

準備ができたのでブラウザで試す前にホストOS上で簡単な動作確認を行います。3232ポート(Controller)、3232ポート(Playground)と2つサービスされている様なので確認してきます。

```bash
$ curl http://192.168.11.55:13232
The requested resource temporarily resides under <a href="/playground/ui/index.html">this URI</a>.
$ curl http://192.168.11.55:13233|jq
〜省略〜
{
  "api_paths": [
    "/api/v1"
  ],
  "description": "OpenWhisk",
〜省略〜
```

次に別の端末のブラウザからそれぞれへリクエストしてみます。いずれも何かしらのコンテンツがレスポンスとして返ってきました。うまくプロキシが動作している様です。では次に、いよいよ機能を使っていきます。
![3233ポートからのレスポンス](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/ccf55cbe-edbc-49e6-9ccf-961931e5d573.png)
![3232ポートからのレスポンス](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/c2ef3c87-da20-4814-b629-538da468eedd.png)

# JavaScriptのRuntimeを用いHello World!を実行
Web UIのコンソールとレスポンスに"Hello World!"を応答するコードを作成し、「Run」ボタンを押下し実行してみます。

![初めてのFaaS JavaScriptでHello World!](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/931adada-1d1f-4b7d-b566-6abaa1e1eb45.png)

。。。待てど暮らせどRunningから状況が変わりません。。そして1分後くらいにエラーと思われる表示に変わり失敗の様です。

![Running](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/59bf56c5-6a77-44f0-8af9-3b2bd071d1ff.png)
![失敗か？](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/2bacb781-a580-43f6-8033-0c5846ac8422.png)

# 原因調査
まずはホストOSでリクエストを受信できているかtcpdumpコマンドで確認してみます。tcpdumpコマンドを実行後にRunを実行しましたが何もパケットが飛んできません。ブラウザからのリクエストがうまくいっていのでしょうか。

```bash
$ sudo dnf install -y tcpdump
$ sudo tcpdump -i any port 3232 or port 3233
tcpdump: data link type LINUX_SLL2
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
```

# ブラウザの開発者機能を用いリクエスト確認
Chromeの「その他のツール」→「デベロッパーツール」を起動し、再度Runを実行して暫く待つと、どうやら特定のURL http://172.17.0.1:3233 へのアクセスが失敗している様です。このIPはホストOS上で起動したOpenWhiskコンテナのIPアドレスです。どこから呼び出されているか引き続き調査すると、playgroundFunction.jsに固定で定義されていました。

![デベロッパーツール](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/4829d6b6-f858-4f66-a469-fc78ae1faf55.png)

![playgroundFunction.js](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/a4f06e03-9a82-4a5d-a67e-00907dbdca82.png)

変数名window.APIHOST=やファイル名playgroundFunctions.jsをキーにgit cloneしてきたからソースを検索してみます。以下の２ファイルで見つかりました。localhost:3233の部分を192.168.11.55:13233に変更すれば良さそうです。srcは以下の方を修正しました。buildは修正してもgradle実行時にsrcのコードで上書きされてしまいます。

```bash
$ find . -type f | grep playgroundFunctions.js |  xargs grep window.APIHOST=
./core/standalone/src/main/resources/playground/ui/playgroundFunctions.js:  window.APIHOST='http://localhost:3233'
./core/standalone/build/resources/main/playground/ui/playgroundFunctions.js:  window.APIHOST='http://localhost:3233'
```

# 再実行
bootRunを再実行し、ブラウザをリロードしてみます。最初にリクエストした時と異なり、サンプルコードが初期表示されています。修正したリソースをデベロッパーツールで参照すると、修正した内容でコンテンツが反映されていました。このままRunを実行してみると正常に動作していることを確認することができました。console.logの出力がどこにされているかはわかりませんでした。

![リロード](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/2caed299-f26f-45e2-9a8e-11f2a7126373.png)

![修正後リソース](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/d6a7d563-1c1c-46ef-b73f-9c0c48c1090f.png)

![実行](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50452/27d12898-f6c0-40f1-bd87-472a1c897564.png)

# 終わりに
この方法が正しいかどうかわかりませんが、動作確認する環境を用意するという当初の目的にはたどり着けた様です。ホストOSを用意せず１つの端末で完結させる方法が一番手っ取り早い方法かと思いますが、私のMacBookではdockerやgradle、java等々色々とエラーが発生したため断念し今回の方法で実現した次第です。