まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:code

3月31日に IBM Cloud の新しいサーバーレス環境である Code Engine が正式リリースされました:
2021040215


ベータ公開の段階から利用レポートを見かけることもありましたが、価格も公開された状態で改めて利用してみました。その利用手順含めて紹介します。 なお詳しくは後述しますが、この Code Engine は無料枠を使って利用することも可能ですが、ライトプラン契約では利用できません。利用するにはクレジットカードを登録するなどしてベーシックプランに切り替えて利用する必要がある点に注意してください。

【Code Engine とは】
Knative ベースのサーバーレスエンジンです。実行エンジンそのものがコンテナオーケストレーションである Kubernetes 環境上に構築されており、Kubernetes をほとんど意識することなくイベント駆動型のアプリケーションをデプロイ/管理することができると同時に、Kubernetes による自動スケールも行われることで詳しい設定をすることなく安定した稼働を実現することができます。利用料金は後述しますが、「CPU やメモリを実際につかったぶんだけ」課金されます(使わない場合は無料です、加えて無料枠もあるのである程度の稼働は無料枠内で実現できます)。

エンジン上で動かすアプリケーションはコンテナイメージの URL を指定する形でもできますし、イメージ化されていなくてもソースコードが公開されていれば(Dockerfile を用意することで)コンテナをビルドして動かすことも可能です。


【利用手順】
IBM Cloud にログインし、「リソースの作成」を選択します:
2021040201


サービスのカタログ画面が表示されます:
2021040202


検索バーで "Code Engine" を指定して検索します("Code" くらいまで入力すると検索候補に表示されるので、これを選択します):
2021040203


Code Engine の説明ページが表示されます。実際に利用するには「作成の開始」ボタンをクリックします:
2021040204


作成する対象を選択します。ここでは通常の(利用者がウェブブラウザからアクセス可能な)ウェブアプリケーションを作ることにするので「アプリケーション」を選択します。また必要に応じて名前を指定します(デフォルトのままでも構いません):
2021040205


名前確定後に、プロジェクトを選択または作成します。初回ではまだプロジェクトは存在していないので「プロジェクトの作成」をクリックします:
2021040206


画面右にプロジェクト作成のダイアログが表示されます:
2021040207


このダイアログ内でエンジンを稼働させるロケーション(下図では「東京」)、およびプロジェクトを管理するための名称を指定します。最後に「作成」をクリック:
2021040208


作成したプロジェクトが選択された状態になります。続けてこのサーバーレス環境で実行するコードを指定します。コンテナイメージか、(Docker ファイルのある)ソースコードを指定できますが、ここではコンテナイメージを選択しています。コンテナイメージの場合は実際に公開されているコンテナイメージの URL および稼働ポート番号を指定します:
2021040209


※上の例ではコンテナ URL として docker.io/dotnsf/hostname を指定しています。このイメージは私が作って公開しているもので、「8080 番ポートに HTTP アクセスすると /etc/hostname の内容を表示する」というだけのシンプルなアプリケーションです。負荷少なく動くという意味で動作確認向きなので、よかったら使ってください。


コードの指定に続けてランタイムの設定を行います。稼働インスタンス数や各インスタンスのスペックを指定します(複数インスタンスでの稼働に未対応など、スケールアウトしてほしくない場合はインスタンス最大数を1に設定します)。最後に画面右のアプリケーションの「作成」をクリック:
2021040210


画面が切り替わって、プロジェクトのダッシュボードが表示されます。作成直後は「デプロイ中」というステータスになっているはずです:
2021040211


しばらく待つとコンテナのデプロイが完了し、「準備完了」というステータスに切り替わります。またこうなると画面右側に「アプリケーションの URL を開く」というホットスポットが表示されるので、ここをクリックしてアプリケーションにアクセスします:
2021040212


新しいタブが開いて、Code Engine 内で稼働しているアプリケーションにアクセスできました(このアプリケーションの場合はコンテナの /etc/hostname ファイルの内容が表示されています)。特別な設定はしていませんが、ホスト名が自動的に割り当てられて HTTPS でアクセスできています:
2021040213


実際には Kubernetes 上で動いているため、アプリケーションの負荷に応じた自動スケールなどは、ほぼ意識することなく(上記設定だけで)実現できています。Kubernetes を抽象化した形で利用しているため、非常に使いやすいプラットフォームを実現できています。


なお、この Code Engine の料金は以下のようになっていました(2021/04/03 時点)。CPU 稼働時間、利用メモリ、および HTTP リクエスト数について1ヶ月ごとの無料枠と、無料枠を超えて利用した場合の価格が表示されています。このあたりはプロジェクト作成時のインスタンスリソース指定内容とも関わってくるので、アプリケーションの特性に応じてカスタマイズする余地があると思っていますが、試験的に利用する想定であればかなりお手軽な価格帯のように感じます:
2021040214







 

とある REST API を使っていて気付いたこと/考えさせられたことをまとめてみました。明快な結論や提案があるわけではなく、グダグダに感じられる内容かもしれないので、あらかじめご了承ください。


そのとある REST API を使ったウェブアプリケーションを作って運用している中で、おかしな挙動に気付くことがありました。以前は問題なく動いていたのに、あるタイミングで実行すると期待通りに動かない、という「まあまあよくある」ケースです。自分のケースでは動かないというよりも、タイムアウトを起こすような挙動になっていました。ただ REST API そのものが止まっている様子はない、というケースでした。

自分のソースコードでは一応エラーハンドリングはしていたつもりでしたが、REST API がエラーを起こしている様子もなく、原因究明に時間を要するものでした。結論としては自分のアプリケーションコードを見ていてもよく分からず、REST API のユニットテストのようなツールを動かした結果気付くことがありました。

それがこちら:
20170207


・・・わかるでしょうか? REST API を実行したレスポンス本文が Response Body に、ステータスコードが Response Code に記述されています。これによるとレスポンス本文は
{
  "status": "ERROR",
  "statusInfo": "daily-transaction-limit-exceeded"
}

となっていて、"daily-transaction-limit-exceeded" が原因のエラーが発生している、という内容でした。この API には Daily Transaction Limit(1日で使える回数の上限)が決められていて、その上限に達したのでもう実行できない、という内容です。これに関してはそういう条件で使っている API なので、なるほど、エラーの原因はわかりました。

問題はこの REST API を実行したステータスコードが 200 になっている点です。HTTP ステータスコードの分類とその意味についてはウィキペディアなどを参照していただきたいのですが、簡単にいうとこんな感じで分類されています:
コード意味考えられる原因など
2xx(200番台)成功 -
4xx(400番台)クライアントエラー認証が必要、アクセス権がない、URLが間違っている、タイムアウト、、、
5xx(500番台)サーバーエラーアプリケーションエラー、不正なゲートウェイが利用されている、、、


200 番台は成功。400 番台と 500 番台がエラーで、それぞれクライアント側のエラーなのか、サーバー側のエラーなのかを分類しています。

で、今回のケースですが、実行結果はエラーなのに 200 番のステータスコードが返されているのでした。自分のプログラムコードの中では「200 が返ってきたら成功」と決めつけて実装していたため、このようなケースに対処できていなかったのでした。


ここまでが実際に目の当たりにしたエラーとその原因でした。以下はこの件に関して自分が考えたことです。


自分を弁護する意味で「えー、でもそれっておかしくない? エラーなんだからスタータスコードは 400 番台なり 500 番台で返ってくるべきでは?」という考えもないわけではありません。ただ今回のケースではこれも難しいような、つまり 200 番のステータスコードがあながち間違ってはいないような気もしています。

その理由として、まず「これはクライアントエラーなのか?それともサーバーエラーなのか?どちらかに分類できるのか?」という問題です。これに関してはどちらとも言えるし、どちらとも言えないと思っています。

実は 402 番のステータスコードは "Payment Required" 、つまり(お金を払わないといけないんだけど)払ってないエラー、と定義されています。が、実際には定義だけで実装されていない、つまり将来のための定義とされているのでした。また厳密には支払い契約を結んで使っているわけではなく、「この条件で1日○○回使える」というルールの下で利用しているだけなので、402 番に(クライアントエラーに)該当するエラーであるとは考えにくいのです。

※本来の意味とは違いますが、「権限がない」に該当するのではないかと言われると、まあ・・・ という考え方もあるとは思います。ただそれをクライアントエラーとして返してもクライアント側はどうにもできないので、やはり 400 番台エラーには該当しないと思います。

ではサーバーエラーなのか?というと、これも該当しないと思います。プログラミングにミスがあったわけではなく、「実行したら、『実行できない』という結果が返ってきた」のは、正しく実行された(結果が期待通りではなかった)とも言えます。そう考えると、そもそも今回の件は REST API レベルではエラーですらないとも言えます。


そう考えると、今のように API を組み合わせてアプリケーションを作ることが珍しくない環境においては、200 番のステータスコードが返ってきてもエラーの可能性を疑ってコーディングする必要があるのかも、と思うようになりました。このケースであれば実装側が工夫すれば(というか、そもそもちゃんと色んなケースを想定してエラーハンドリングしていれば)防げるものです。

そういう意味でもいい反省の機会でした。 でも今後は同様のケースを想定した「成功でもエラーでもないステータス」が出て来る可能性もありますよね。。


このページのトップヘ