Cloudinaryで一度fetchしたオリジン画像を更新する

CloudinaryのFetch機能を利用すると、リモートサーバーにある画像をオンデマンドで取得します。Upload APIのDestoryメソッドを呼び出し、リモートで更新されたリソースを即座に配信する方法を紹介します。
2019.09.13

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

Cloudinaryではリモートサーバーにある画像をオンデマンドで取得する機能が2種類存在します

  • Fetch : リモートのリソースをマスターとして活用。Cloudinaryで一度キャッシュされると、定期的(デフォルトでは7日)に再取得
  • Auto-Upload : Cloudinaryに一度取り込まれると、以降はCloudinaryのリソースをマスターとして利用。

前者の Fetch 機能を利用すると、リモートサーバーのリソースが更新されても、Cloudinaryが定期的にプルするまで更新リソースが配信されません。

今回は Upload API の destory メソッドを呼び出し、即座に新しいリソースを配信する方法を紹介します。

やってみた

以下のシナリオで動作確認します。

  1. リモートのリソースを fetch → 新規にfetchされる
  2. リモートのリソースを更新
  3. fetch URL をもう一度リクエスト → fetch 済み & CDN キャッシュ済みリソースが返される
  4. Upload API の Destory メソッドを実行
  5. fetch URL をもう一度リクエスト → 再度 fetch し、更新したリソースが返される

1. リモートのリソースを fetch

Fetch 機能を利用し、リモートサーバーのリソース(https://example.com/dog.png)を fetch します。

$ curl -I https://res.cloudinary.com/demo/image/fetch/https://example.com/dog.png
HTTP/2 200
access-control-allow-origin: *s
cache-control: public, max-age=604800
content-type: image/png
edge-cache-tag: 344131525993202736795913186797227682751,2ec1e12c84e9ec6781dfb3e75d59aba9
etag: "e1a0f56a8bd70af6335a0ba7eb24803e"
last-modified: Thu, 12 Sep 2019 17:06:39 GMT
server: cloudinary
status: 200 OK
timing-allow-origin: *
x-request-id: 342bd4182ec8c368
accept-ranges: bytes
date: Thu, 12 Sep 2019 17:06:38 GMT
via: 1.1 varnish
age: 0
x-served-by: cache-hhn4035-HHN
x-cache: MISS
x-cache-hits: 0
x-timer: S1568307998.203637,VS0,VE724
access-control-allow-headers: X-Requested-With,Range,User-Agent
access-control-expose-headers: Content-Length
content-length: 8311

初めてのリクエストのため、キャッシュミスしています(x-cache: MISS)。

また、このリソースは

  • etag: "e1a0f56a8bd70af6335a0ba7eb24803e"
  • content-length: 8311

であることがわかります。

2. リモートのリソースを更新

リモートのリソースを更新し、リモートサーバーに直接 GET リクエストします。

$ curl -I https://example.com/dog.png
HTTP/1.1 200 OK
x-amz-id-2: MwDPqXCPzDQfqkp0mEaoCPSwAkdrtZ762IdHA2mqybY+Ub1ecMllDsyfP56pRFfQL3Q8IvJlTrQ=
x-amz-request-id: FB4BF2113639BFA3
Date: Thu, 12 Sep 2019 17:10:09 GMT
Last-Modified: Thu, 12 Sep 2019 17:09:24 GMT
ETag: "44eb7c121b77323596bca1cea1add715"
Accept-Ranges: bytes
Content-Type: image/png
Content-Length: 154414
Server: AmazonS3

更新後のリソースは

  • etag: "44eb7c121b77323596bca1cea1add715"
  • content-length: 154414

であることがわかります。

3. fetch URL をもう一度リクエスト

ステップ #1 の fetch URL をもう一度リクエストします。

$ curl -I https://res.cloudinary.com/demo/image/fetch/https://example.com/dog.png
HTTP/2 200
access-control-allow-origin: *
cache-control: public, max-age=604800
content-type: image/png
edge-cache-tag: 344131525993202736795913186797227682751,2ec1e12c84e9ec6781dfb3e75d59aba9
etag: "e1a0f56a8bd70af6335a0ba7eb24803e"
last-modified: Thu, 12 Sep 2019 17:06:39 GMT
server: cloudinary
timing-allow-origin: *
accept-ranges: bytes
date: Thu, 12 Sep 2019 17:07:20 GMT
via: 1.1 varnish
age: 3
x-served-by: cache-fra19174-FRA
x-cache: HIT
x-cache-hits: 1
x-timer: S1568308041.799637,VS0,VE0
access-control-allow-headers: X-Requested-With,Range,User-Agent
access-control-expose-headers: Content-Length
content-length: 8311

x-cache: HIT からキャッシュヒットしていることがわかり、etagcontent-lengthから fetch 済みリソースが返されていることがわかります。

4. Upload API の Destory メソッドを実行

Upload API の Destory メソッドを呼び、Cloudinary にキャッシュされた古いリソースを削除し、Cloudinary の CDN からパージします。

Python 版 Destroy プログラム

destroy.py

import cloudinary
import cloudinary.uploader

cloudinary.config(
  cloud_name = "XXX",
  api_key = "XXX",
  api_secret = "XXX"
)

public_id = "https://example.com/dog.png"

result = cloudinary.uploader.destroy(
    public_id,
    type="fetch",
    invalidate=True)
print(result)

フェッチリソースの場合、 type="fetch" を指定し、public_id にはリモートリソースの URL を指定します。

Cloudinaryは画像・動画配信のワンストップソリューションで、CDN も内蔵しています。CDN キャッシュをパージするため、 invalidate=True の引数も渡します。

Cloudinary の Python SDK の使い方は、次の過去記事を参照ください。

PythonからCloudinaryを操作してみた

Destroy の実行

このプログラムを呼び出します。

$ python destroy.py
{'result': 'ok'}
$

5. Destroy 後に fetch URL をリクエスト

Cloudinary 内のリソースを削除し、CDN キャッシュをパージすると、レスポンスはどのように変わるでしょうか?

$ curl -I https://res.cloudinary.com/demo/image/fetch/https://example.com/dog.png
HTTP/2 200
access-control-allow-origin: *
cache-control: public, max-age=604800
content-type: image/png
edge-cache-tag: 344131525993202736795913186797227682751,2ec1e12c84e9ec6781dfb3e75d59aba9
etag: "44eb7c121b77323596bca1cea1add715"
last-modified: Thu, 12 Sep 2019 17:13:14 GMT
server: cloudinary
status: 200 OK
timing-allow-origin: *
x-request-id: 0eb56443c5f7dba1
accept-ranges: bytes
date: Thu, 12 Sep 2019 17:13:13 GMT
via: 1.1 varnish
age: 0
x-served-by: cache-fra19176-FRA
x-cache: MISS
x-cache-hits: 0
x-timer: S1568308393.699648,VS0,VE1105
access-control-allow-headers: X-Requested-With,Range,User-Agent
access-control-expose-headers: Content-Length
content-length: 154414

x-cache: MISSからキャッシュミスしていることがわかり、etagcontent-lengthから更新後の新しいリソースが返されている、つまり、リモートサーバーに fetch したことがわかります。

コンソールからも新しいリソースであることを確認してください。

コンソールからの削除操作

コンソールの Media Library にはフェッチされたリソースも表示されます。

コンソールからこのリソースを削除すると、 Destroy メソッドを実行したのと同等の操作となります。

無効化時に利用できる URL 形式は複数

キャッシュを無効化する時に利用する URL 形式は複数種類あり、アカウントごとに切替可能です。

通常は URL 形式の違いを意識する必要はなく、デフォルトのままで問題ありませんが、オブジェクトのバージョンごとの配信(cache busting)を行っているような場合、配信方式にあった無効化処理が必要です。

詳細は次のドキュメントを参照ください。

What URL conventions are invalidated – Cloudinary Support

最後に

Cloudinary で一度 fetch した画像を更新する方法を紹介しました。

プロダクション運用では、オリジンサーバーの画像系リソースの更新イベントをトリガーに Cloudinary へ Upload::destory メソッドを呼び出し、CloudinaryコンソールのリソースキャッシュとCDNキャッシュを削除すると、サービス全体でコンテンツ更新のタイムラグが発生しなくなるかと思います。

具体的な構成例は次の記事で紹介しています。

AWS Lambda PythonからCloudinaryを操作してみた | Developers.IO

VarnishCloudinary は素晴らしいですね!

それでは。

参考