画像スケーリングガイド
このセクションでは、GitLabイメージスケーラの簡単な概要とその操作方法について説明します。
GitLab におけるイメージスケーリングの歴史については、Unfiltered のブログ記事をご覧ください。
なぜイメージスケーリングなのですか?
バージョン13.6以降、GitLabはページのデータ量を減らすために、必要に応じて画像を縮小します。これにより、”オン・ザ・ワイヤー “のデータ量が削減されるだけでなく、レンダリングのパフォーマンスも向上します。
いつ画像を縮小するのですか?
通常、クライアントがクエリ文字列にwidth
パラメータを追加して画像リソースをリクエストすると、画像スケーラが起動します。しかし、特定の種類と形式の画像だけをスケーリングします。画像の拡大縮小を許可するかどうかは、ハードコードされたルールと設定によって決まります。
ハードコードされたルールは、以下のものだけを許可します:
さらに、Workhorseの設定により、以下の場合、画像スケーラがリクエストを拒否することがあります:
- 画像ファイルが大きすぎる場合 (
max_filesize
) 。 - 実行中の画像スケーラが多すぎます(
max_scaler_procs
によって制御されます)。
例えば、GitLabプロジェクトのアバターをオリジナルサイズと64ピクセルに縮小して提供する2つの異なるURLを示します。画像スケーラーが起動するのは二番目のリクエストだけです:
/uploads/-/system/project/avatar/278964/logo-extra-whitespace.png
/uploads/-/system/project/avatar/278964/logo-extra-whitespace.png?width=64
画像のスケーリングはどこで行うのでしょうか?
現在、RailsとWorkhorseは共同でイメージの再スケーリングを行っています。リクエストの認証やバリデーションといった重要なビジネスロジックは Rails で行い、バイナリデータのスケーリングや配信といった「力仕事」は Workhorse で行います。
全体的なリクエストの流れは次のようになります:
Rails
現在のところ、画像のスケーリングはUpload
エンティティ、特に前述のアバターに限定されています。そのため、Railsの画像スケーリング関連のロジックは現在すべてsend_file_upload
コントローラ mixinにあります。Workhorse経由でクライアントからのリクエストを受け取ると、前述の基準に従って画像スケーラーを起動すべきかどうかをチェックし、起動する場合は、Workhorseがスケーリングリクエストを実行するために必要なパラメータを含む特別なレスポンスヘッダーフィールド(Gitlab-Workhorse-Send-Data
)をレンダリングします。Railsがそのリクエストを有効な画像スケーリングリクエストではないと判断した場合は、通常のアップロードに従うことになります。
ワークホース
Railsがリクエストを有効と判断すると、Workhorseが処理を引き継ぎます。Railsのレスポンスでsend-scaled-image
、画像を再スケールする方法を知っている特別なレスポンスインジェクタが呼び出されます。必要な入力は画像の場所(画像がブロックストレージにある場合はパス、そうでない場合はリモートストレージへのURL)と希望の幅だけです。Workhorseが透過的に場所を処理するので、Railsは画像が実際にどこにあるかを気にする必要はありません。
さらに、Railsでの検証を要求するために、Workhorseはいくつかの事前条件チェックを実行し、スケーラープロセスの予算を超過していないことを確認するだけでなく、ファイルが設定された最大許容サイズ制約を満たしているかどうか(メモリ消費を抑えるため)など、実際に画像を再スケーリングできることを確認します。
実際に画像をスケーリングするために、Workhorse は最終的に実際のスケーリング作業を行う子プロセスにフォークし、結果をクライアントにストリームバックします。
再スケーリングされた画像のキャッシュ
現在のところ、スケーリングされた画像はどこにも保存していません。しかしWorkhorseは、クライアントキャッシュ内の画像が最新であればスケーラーをスキップできる、標準的な条件付きHTTPリクエスト戦略を実装しています。そのために、オリジナル画像ファイルのUTCタイムスタンプを持つLast-Modified
ヘッダーフィールドを送信し、クライアントリクエストのIf-Modified-Since
ヘッダーフィールドと照合します。元の画像が変更され、再スケーリングが必要になった場合のみ、スケーラーを再実行します。