Cloud Intelligence™Cloud Intelligence™

Cloud Intelligence™

GCP Cloud FunctionsとGoogle Cloud CDNで画像を動的にリサイズする

By Avi KeinanAug 28, 20257 min read

このページはEnglishDeutschEspañolFrançaisItalianoPortuguêsでもご覧いただけます。

本記事では、Webサイトで画像をリサイズすべき理由と、Google Cloud FunctionsをGCPロードバランサーおよびCloud CDNと組み合わせて画像を動的にリサイズ配信し、サイトのパフォーマンス向上、ストレージコストの削減、ページ読み込み時間の短縮を実現する方法を解説します。

ChatGPT 5で生成した画像

大手のWebサイトのほとんどは掲載画像をリサイズしています。その理由は主に次の3点です。

  • サイズの大きい画像をダウンロードさせると、帯域使用量が増え、データ転送コストもかさみます。
  • HTML側で画像を縮小表示する場合、クライアントのブラウザが画像全体をダウンロードしたうえでCPUを使ってリサイズする必要があり、サイトの読み込みが遅くなります。
  • 動的リサイズを使わない場合、デスクトップ・モバイル・タブレット・メール(ニュースレター)向けに同じ画像のサイズ違いを保持する必要があります。クラウドのストレージ料金は月額0.02ドル/GBからとはいえ、積み重なれば無視できないコストになります。

画像最適化の詳細は、Steve Souders著 『Even Faster Web Sites』 第10章をご覧ください。

Google Cloud Content Delivery Network(CDN)を活用すれば、世界中に分散したエッジネットワーク上にオブジェクトをキャッシュでき、静的コンテンツの配信を高速化できます。

Google Network

本記事のソリューションでは、次のサービスを使用します。

  • Google Cloud Storage — 画像をフルサイズで保存します。
  • Cloud Function — オブジェクトを動的にリサイズします。
  • Cloud CDNを有効化したGoogle Cloud Load Balancer — ユーザーはこのロードバランサー経由で画像を取得します。

GCP Cloud Load Balancer(LB)を作成し、Cloud Functionを呼び出すように設定します。FunctionがGoogle Cloud Storage(GCS)から画像を取得してリサイズし、LBはリサイズ後の画像を返すと同時にエッジへ保存し、以降のリクエストに備えます。

デプロイ手順

まずGoogle Cloud Storage(GCS)バケットを作成し、すべてのオブジェクト(画像)を元のサイズでアップロードします。

バケット名を入力し、リージョンを指定したらCreateボタンをクリックします。

例として、nasdaq.jpg(2019年に筆者が撮影したタイムズスクエアのNasdaqビル)を、6.3 MBの元サイズのままこのバケットにアップロードしました。

次にFunctionを作成します。Cloud Functionsコンソールを開き、Write a functionをクリックします。

Cloud Functionsを初めて利用する場合、「Cloud Functions APIが有効化されました」というメッセージが表示されることがあります。これは、追加費用なしでプロジェクト内でCloud Functionsの機能が有効化されることを示しています。

このステップでは、Functionを次のように設定します。

  1. インラインエディタを使用します。
  2. Functionの名前を設定します。
  3. リージョンはGCSと同じリージョンを選択します。
  4. ランタイムを設定します。本デモではPythonを使用します。
  5. LBからFunctionを呼び出せるよう、IAM認証のチェックを外します。

Createボタンをクリックします。GCPサービスを初めて利用する場合は、いくつか追加でAPIを有効化する必要があるかもしれません。

本例では、Functionを実行するコンテナをビルドするためにCloud Buildを有効化する必要があります。

ここからが本題です。GCSからファイルを取得し、リサイズして返すコードを書いていきます。

これをすべてChatGPTに任せられるか試したかったので、以下のプロンプトでLLMと数回やり取りしました。

「指定された文字列に基づいて、jpg/png画像をx, yにリサイズするPythonコードを書いてください。」

「Google Cloud Functionで、パラメータをクエリ文字列で受け取り、ファイルはGCSから読み込み、結果は適切なMIMEタイプでロードバランサーに返す前提でお願いします。」

「このコードのrequirements.txtには何を書けばよいですか?」

「『Error processing image: module \'PIL.Image\' has no attribute \'ANTIALIAS\'』というエラーが出ます。」

結果として得られたのが以下のコードです。9行目で使用するバケットを指定します。

こちらは、Cloud Functionsがコンテナをビルドする際に使用するPythonライブラリを指定するrequirements.txtです。

main.pyのコードとrequirements.txtのライブラリを更新すると、「指定された関数(エントリポイント)がソースコード内に存在しない可能性があります。コード内のエントリポイントが入力フィールドと一致していることを確認してください」という警告が表示されます。

これは、コード側がresize_imageを呼び出しているのに対し、Cloud Functionsのデフォルトの関数名がhello_httpになっているためです。

Function entry pointresize_imageに変更し、Save and redeployをクリックします。

Cloud Functionsがコードのコンテナをビルドします。数分かかる場合があり、ビルドの状況はダッシュボード上部で確認できます。

注: Cloud FunctionはFunctionを呼び出します。FunctionはCompute Engineデフォルトサービスアカウントを使用しており、これによりプロジェクト内のすべてのGCSバケットへのアクセス権が付与されます。本記事では扱いませんが、ベストプラクティスとしては、最小権限の原則に従って専用のサービスアカウントを使用すべきです。

次は、ロードバランサーを作成します。コンソールの検索バーで「Load Balancer」を検索し、ロードバランサーのダッシュボードを開いてCreate load balancerをクリックします。

ここでは、グローバルかつパブリック向けのアプリケーションロードバランサーを作成します。ロードバランサーの種類でApplication Load Balancer (HTTP/HTTPS)を選択します。

世界中のユーザーがロードバランサーにアクセスできるよう、Public Facing (external)を選択します。

リサイズ済み画像をエッジサーバーにキャッシュできる(追加費用なし)Google Cloud CDNを活用するため、グローバルロードバランサーを選択します。

続いて、最新世代のロードバランサーを選択し、NextCreateの順にクリックします。

ここでは簡略化のため、ロードバランサーのHTTP frontendエンドポイントを作成します。

ロードバランサーとフロントエンドIPの名前を設定します。

Backend Configurationをクリックし、Backend services & backend bucketsのボックス内でCreate a backend serviceをクリックします。

バックエンドサービスは、ロードバランサーがリクエストを転送する先のリソースを指定するもので、本構成ではCloud Functionが対象になります。

バックエンドサービスに名前と説明を入力し、Backend typeServerless network endpoint groupに変更します。

Backendsセクションまでスクロールします。New backendの下のServerless network endpoint groupsをクリックし、Create Serverless network endpoint groupをクリックします。

エンドポイントの名前を設定し、Cloud Functionを作成したリージョンを選択します。

これまでに一度もFunctionを呼び出したことがない場合、次のエラーがポップアップ表示されます。

エラーメッセージ内のリンクから、Cloud Function APIを有効化します。

Serverless network endpointページに戻り、エンドポイント名とFunctionが配置されているリージョンを設定し、Cloud Runを選択します(紛らわしいですが、Cloud Functionsは現在Cloud Run Functionsという名称になっており、同じ基盤技術を使用しています)。

Functionを選択し、Createをクリックします。

バックエンドのページで、Cloud CDNのキャッシュ設定を構成できます。

CDNには、指定の期間(スクリプト54行目では1時間)、もしくはより長い期間レスポンスをキャッシュするよう設定できます。

ここで強調しておきたいのは、CDNはウォームキャッシュ方式であるという点です。つまり、頻繁にリクエストされるコンテンツのみがCDNキャッシュに保持されます。1年間のキャッシュ期間を設定しても、リクエスト頻度が低ければ、しばらく経つとCDNはオリジン(Function)から再取得することになります。

上の図で矢印が指しているLogging機能にも注目してください。デバッグや使用状況の追跡に便利ですが、コストも高めです(ログ1 TBあたり512ドル)。

リクエスト数が多い場合は、Sample rate0.01に設定すれば、100リクエストにつき1件をログに記録できます。

続いてSecurityまでスクロールします。

デフォルトでは、Googleはバックエンドに対してWebアプリケーションファイアウォール(WAF)であるCloud Armorを有効にしていますが、追加費用が発生するうえ、本記事はデモのため、Cloud Armor backend security policyをクリックしてNoneに変更し、無効化します。

次にCreateをクリックし、ロードバランサーのダッシュボードでもう一度Createをクリックします。

注: GCPでロードバランサーを作成・更新すると、設定が反映されるまで最大15分ほどかかることがあります。

ロードバランサーの準備が整ったら、ロードバランサー名をクリックすると、生成されたIPを確認できます。

このIPにアクセスし、画像名と高さ・幅を指定します。

http://34.49.21.153/?image=nasdaq.jpg&size=500x600

結果は次のとおりです。6.5 MBのファイルではなく、リサイズされた63 KBの画像が返されました。

筆者の仕事は、お客様のクラウド活用を支援することです。私たちにできることは doit.com/services でぜひご覧ください。