低コストで自作する阿里雲関数計算FC+OSSのイメージホスティング

まずは三つだけ:
- このイメージホスティングは機能もシンプルで、設定も容易、安全性も十分です。もし足りなくなったら、ソースコードを AI に渡すだけで機能の追加・削除はかんたんにできます。
- 本文中の
AccessKey、パスワード、ドメイン、バケット名はすべてダミーです。そのまま写さず、自分の値に置き換えてください。 - コードは Node.js のシングルファイル(約380行)で、依存は FC のレイヤー(Layer)から注入します。ビジネスコードにはハードコードを一切置かず、設定はすべて環境変数で行います。
一、なぜ自前で作るのか
静的ブログには、おおむねどれかのイメージホスティングが必須です。お金を節約したい気持ちもわかりますから、他人の「永久無料」イメージホスティングを使う人も多い。しかし、そんなに偉い善人が永遠に逃げない保証はどこにもありません。しかもデータは他人の手にあり、こちらには何の主導権もない——だからなんとなく心配になります。
いろいろハマった末に、極低成本で自分で運用できるイメージホスティングを作ることにしました。目的はとにかく、安く、容量無制限、そして安全です!
自前でイメージホスティングを作る方法の多くは、自分でサーバーを用意する必要があります。月額数十元のサーバー代がかかるうえ、パッチ当ても自分でやる羽目になる。あるいはローカルのクライアントでしか動かないものだと、パソコンを変えたらアップロードすらできません。結局私は、サーバーレスなクラウドのイメージホスティングを作ることにしました。場所を選ばず、思い立ったときにいつでもアップロードできるように。
阿里雲関数計算 FC + OSS + CDN
FC は従量課金(低トラフィックなら≈0元)+データベース不要+ノーメンテナンスで、デプロイパッケージはわずか 6KB。アップロード頻度が低ければ実質ゼロ円です。
OSS の画像ストレージも従量課金で、数十 GB の画像でも 1 か月で数元程度です。
CDN をかませば、画像の読み込みは飛ぶように速くなります!
個人用のイメージホスティングなので、攻撃される価値もさほどありませんが、多層のセキュリティ設計は入れています:強制 PoW によるブルートフォース対策、マジックナンバー検査による偽装対策、三重のサイズ検査による容量ガード、1 日あたりのクォータによる悪用対策、Token の 2 時間失効による盗用対策。
この方法にも欠点はあります:マルチユーザーはなく、アルバム管理はなく、使用量の統計もない。画像の削除にすら、OSS コンソールに入って手動で削除するしかありません。
でも、小さくて安く、個人で使うには十分です。
二、アーキテクチャとリクエストの流れ
ブラウザ upload.html
│ ① POST /login {user, pass}
│ ← 200 {token}(有効期限 24h)
│ ② POST /upload Authorization: Bearer *** + 画像バイナリ
▼
阿里雲関数計算 FC(Node.js 18、HTTP トリガー)
│ 認証 / レート制限 / マジックナンバー検査 / サイズ検査
│ ③ ossClient.put(key, stream)
▼
OSS バケット(プライベート読書き、パブリック読み取り禁止)
│ ④ CDN がオリジンへ(内網、無料)
▼
https://img.example.com/m/26j13/ab12cd.jpg ← 外部アクセス用ドメイン
設計上の判断は三つ:
- バケットをプライベートに:誰にも直リンクの読み取り権限を与えず、画像には CDN 用ドメインに紐づいた URL 経由でのみアクセスできるようにします。パブリック読み取りにすると、あなたのバケットは公共の資源になり、他人に一覧を取られ、トラフィックを無断で使い放題にされます。
- アップロードにはログイン必須:そうしないと、あなたの API ドメインが無料のイメージホスティングとして他人に使い放題され、コストもストレージも制御不能になります。
- ビジネスコードは鍵に触れない:AK/SK は環境変数の中にのみ存在し、コードにもリポジトリにも二度と現れません。
三、準備
3.1 サービスの開通
関数計算 FC と オブジェクトストレージ OSS を開通します(どちらも従量課金、前払いなし)。
3.2 バケットの作成
- 名前:グローバル一意。例:
my-img - リージョン:自分・利用者に近いものを。例:
華東1(杭州)→oss-cn-hangzhou - 読み書き権限:プライベート(重要)
- ストレージタイプ:標準ストレージ(アクセス頻度の高い画像にはこれ。低頻度/アーカイブを選ばないこと——あれらには最短保管期間があり、早く削除すると逆に課金されます)
3.3 RAM ユーザーを作り、単一バケットのみに権限を限定する(強く推奨)
数多くのチュートリアルはメインアカウントの AccessKey をそのまま使わせますが——やめてください。メインアカウントの AK が漏れれば、阿里雲アカウント全体が敵のものになります。
RAM コンソール → 権限ポリシー → 権限ポリシーの作成 → JSON タブ:
{
"Version": "1",
"Statement": [
{
"Effect": "Allow",
"Action": [
"oss:GetObject",
"oss:PutObject",
"oss:DeleteObject",
"oss:ListObjects",
"oss:HeadObject"
],
"Resource": [
"acs:oss:*:*:my-img",
"acs:oss:*:*:my-img/*"
]
}
]
}
ポリシー名は ImageHostSingleBucket。続いて:
- RAM → ユーザー → ユーザーの作成で 「永続的な AccessKey によるアクセス」 にチェック
- このユーザーには上のポリシー一つだけを割り当てる
- AK/SK を生成し、すぐにローカルのパスワードマネージャーへ保存
意図的に外した部分:
oss:ListBucketsは与えない —— 漏えいしても、どのバケットがあるかすら読まれないoss:CopyObjectは与えない —— イメージホスティングにコピー権限は不要。与えなくてよいものは与えないAliyunOSSFullAccessは付けない —— そうすると、イメージホスティング用の AK であなたの全データバケットを読めるようになってしまう
四、依存レイヤー(Layer)の準備
ビジネスコードの依存は ali-oss のみ。これを Layer にすれば、コードパッケージは数 KB に収まります。
方法 A:Release アタッチメントを直接ダウンロード(推奨)
プロジェクトでは Release を公開しており、layer.zip とコードパッケージはそのままダウンロードできます:
https://github.com/ezzty/oss-upload/releases/latest
| アタッチメント | サイズ | 内容 |
|---|---|---|
layer.zip |
3.25 MB | ali-oss@6.23.0 とその依存、計 2338 ファイル |
oss-upload-code.zip |
約 6 KB | index.js + package.json、関数デプロイパッケージ |
利点:Node.js を入れる必要も、zip を作る必要もなく、ローカルの npm install で新しい SDK が入って「作者が検証したのと違う」という問題も起きません。レイヤーに含まれるのは npm パッケージのソースだけで、鍵はありません。
方法 B:自分でビルド
依存のバージョンを変えたいとき、あるいは既成のパッケージを信頼できないときは自分で作ります:
mkdir -p layer-build/nodejs
cd layer-build
npm init -y
npm install ali-oss
# ディレクトリはこの構造でなければならない:
# nodejs/
# node_modules/
# ali-oss/
# ...その推移依存
cd nodejs && zip -r ../layer.zip node_modules && cd ..
アップロード
FC コンソール → レイヤー管理 → レイヤーの作成:
- 名前:
oss-upload-deps - ランタイム:Node.js 18
layer.zipをアップロード
レイヤーは一度作れば OK です。今後
ali-ossのバージョンを上げるときは、新しいバージョンを再パッケージングして公開し、関数を新しいバージョンに向けるだけ。ビジネスコードは触りません。
五、関数のデプロイ
5.1 ビジネスコードの取得
Release アタッチメントの利用を推奨:https://github.com/ezzty/oss-upload/releases/latest から oss-upload-code.zip(約 6 KB)をダウンロードして展開すれば標準的なデプロイパッケージです。
自分でパッケージングする場合、zip のルートにはこの二ファイルだけを置いてください(フォルダーで包むと Handler が index.handler を見つけられません):
code.zip
├── index.js ← ビジネスソース
└── package.json ← 依存の宣言(参考用。実行時はレイヤーから読む)
zip code.zip index.js package.json
5.2 関数の作成
- ランタイム:Node.js 18
- ハンドラー:
index.handler - スペック:0.05 vCPU / メモリ 128 MB / ディスク 512 MB(実測で足りる最小スペック)
- レイヤー設定:先ほど作った
oss-upload-depsを紐づける
メモリは注意が必要です。コードは body 全体をメモリに読み込み(
bodyBuf)、base64 デコード時にはさらにコピーが作られます。通常の画像なら 128 MB で問題ありませんが、アップロード上限を 20 MB に引き上げて上限付近の大きな画像を頻繁に送るなら、メモリも一緒に上げてください。
5.3 環境変数(必須 8 個、一つ欠けても起動しない)
関数設定の環境変数には必須が 8 つあります。下の画像のとおりに設定してください。
AUTH_USERadmin—— ログインユーザー名AUTH_PASS<強力なパスワードを設定>—— ログインパスワード。ランダムな長い文字列をJWT_SECRET<32文字以上のランダム文字列>—— トークン署名用の鍵OSS_ACCESS_KEY_IDLTAIxxxxxxxxxxxxxxxx—— RAM ユーザーの AKOSS_ACCESS_KEY_SECRET<あなたの AccessKey Secret>—— RAM ユーザーの SKOSS_BUCKETmy-img—— バケット名OSS_REGIONoss-cn-hangzhou—— バケットのリージョン IDCDN_DOMAINhttps://img.example.com—— プロトコル頭必須。無いと起動即失敗
任意 1 個:
ALLOWED_ORIGINSexample.com,myblog.net—— 許可する CORS オリジン。カンマ区切り。未設定でも起動します。コードレベルの CORS を有効にしたときだけ判定に関わります(デフォルトの CORS はプラットフォームがOriginをそのまま返すだけなので、isValidOriginは根本的に呼ばれません)。コードレベルの CORS を有効にするなら必ず設定を。さもないとフォールバック値のexample.comがブラウザからのアップロードを全部遮断します。

5.4 カスタムドメインの紐づけ
FC コンソール → ドメイン管理 → ドメインの追加 → 案内に従って CNAME と証明書を設定。こうして初めて https://api.example.com のような API アドレスが持てます。
六、フロントエンドページ
upload.html はシングルファイルのフロントエンドなので、自分のサイトのディレクトリに置くだけです。
必ず直す場所が一つ、_FC_PARTS を探してください:
// あなたの関数計算のカスタムドメインに変更
const _FC_PARTS = ['https://', 'api.example.com'];
サイズ上限はバックエンドと揃えること(デフォルトはどちらも 20MB):
if (file.size > 20 * 1024 * 1024) { /* 遮断 */ }
前後にそれぞれ制限があり、小さい方を基準にします。フロントだけ直してバックエンドを触らないと、大きなファイルはサーバー側で 413 になります。バックエンドだけ直すと、ブラウザ側で先に遮断されてそもそも送信できません。
七、深く知りたいなら、核心はソースの README に
以上が自前イメージホスティングを動かす一連の流れです。コードがなぜこう書かれているのか、各層の防御の実装詳細、完全なチェックリストはすべてソースリポジトリの README にまとめてあるので、必要なときに見比べてください:
https://github.com/ezzty/oss-upload → README.md の「核心実装(コードレベル)」と「リリース前検収」
ブログ本文では「なぜ自前で、どう動かすか、何に気をつけるか」だけを分かりやすく伝えたければ十分。実際にデプロイするなら、README がそのまま完全な技術ドキュメントです。
八、コスト
従量課金で、大きく三つ:
| 項目 | 課金方式 | 目安 |
|---|---|---|
| FC 計算 | 計算 CU(vCPU + メモリ × 時間)+ 呼び出し回数 | 無料枠あり。個人のイメージホスティングならおおむね枠付近 |
| OSS ストレージ | 元/GB/月 | 標準ストレージで約 0.09〜0.12 元/GB/月 |
| OSS リクエスト | 回数制 | 極めて低く、無視してよい |
| CDNトラフィック | 元/GB | 下記参照 |
トラフィックが請求の大頭で、最も削減しやすい部分です:
- OSS へのアップロード:無料
- OSS 内網(FC ↔ OSS が同一リージョン):無料
- OSS の外部流出:有料(約 0.5 元/GB のオーダー、時間帯とリージョンで変動)
- CDN を経由:CDN のオリジンフェッチは内網を通るためフェッチ流量は無料。課金されるのは CDN トラフィックのみ
結論:イメージホスティングには必ず CDN を付けること。 速度が上がり、OSS の外部流出費も回避できます。また FC と OSS は必ず同一リージョンに。リージョンをまたぐとインターネットを経由してしまい、遅い上に余計なトラフィックが発生します。
私自身の実測では、1 ヶ月に数十枚アップロードした場合の費用はおよそ 1 元程度でした。
九、保守と更新
ビジネスコードを変えるとき(画像リンク形式など):
- ローカルの
index.jsを修正 zip code.zip index.js package.json- コンソール → コードデプロイ → アップロード → デプロイ
- README の検収プローブを実行して、新しいバージョンが効いていることを確認
フロントを変えるとき:
upload.htmlを修正- サイトへアップロード。
Cache-Control: no-cacheを設定することを推奨(さもないとユーザーが古い版を受け取る可能性がある)
依存を更新するとき:
ali-ossを再npm installしてlayer.zipをパッケージング- コンソールで新しいレイヤーバージョンを公開
- 関数を新しいレイヤーバージョンへ切り替え
鍵のローテーション(定期的に):
- RAM で新しい AK/SK を生成
- FC の環境変数を更新 → デプロイ
- サービスが正常に動いていることを確認した後、RAM コンソールで旧 AK を無効化