अलीबाबा क्लाउड फ़ंक्शन FC + OSS पर कम लागत का सेल्फ-होस्टेड इमेज होस्ट

image

पहले तीन बातें:

  1. इस इमेज होस्ट की कार्यक्षमता सीधी-सादी है, सेटअप आसान, और सुरक्षा काफ़ी है; नाक़ाफ़ी लगे तो सोर्स कोड AI को थमा दीजिए — फ़ीचर जोड़ना-हटाना बहुत आसान।
  2. इस लेख के सारे AccessKey, पासवर्ड, डोमेन, बकेट के नाम प्लेसहोल्डर हैं; कॉपी करने से पहले अपने वाले डाल लीजिए।
  3. कोड एकल फ़ाइल Node.js है (लगभग 380 लाइन), डिपेंडेंसी FC की लेयर (Layer) से इंजेक्ट होती हैं, बिज़नेस कोड में कहीं हार्डकोड नहीं — पूरा कॉन्फ़िगरेशन एनवायरनमेंट वेरिएबल से चलता है।

सोर्स कोड: https://github.com/ezzty/oss-upload


1. इमेज होस्ट खुद क्यों बनाएँ

हर स्टैटिक ब्लॉग को न कोई न कोई इमेज होस्ट चाहिए ही। बहुत से लोग पैसे बचाने के लिए किसी और का “परमानेंट फ़्री” इमेज होस्ट इस्तेमाल करते हैं — लेकिन कौन सा भला आदमी कभी भागता नहीं? और डेटा दूसरों के हाथ में है, अपनी कोई पकड़ नहीं, तो दिल धक-धक करता ही रहता है।

बहुत गड्ढों में गिरने के बाद मैंने खुद बेहद कम लागत वाला इमेज होस्ट बनाने का फैसला किया। मकसद साफ़ — पैसे बचाना, कोई सीमा नहीं, और सुरक्षा!

ज़्यादातर सेल्फ-होस्टेड इमेज होस्ट के तरीकों में अपना सर्वर चाहिए ही चाहिए — महीने का कुछ दसियों युआन किराया और पैच भी खुद लगाओ; या कुछ इमेज होस्ट सिर्फ़ लोकल क्लाइंट पर चलते हैं, कंप्यूटर बदला तो अपलोड ही नहीं हुआ। आख़िर मैंने एक सर्वरलेस क्लाउड इमेज होस्ट बनाने का फैसला किया — जहाँ चाहें, जब चाहें, बस अपलोड कर दो।

अलीबाबा क्लाउड फ़ंक्शन FC + OSS + CDN

FC यूज़ेज़ के हिसाब से (कम ट्रैफ़िक पर ≈0 युआन) + कोई डेटाबेस नहीं + रखरखाव शून्य, डिप्लॉयमेंट पैकेट सिर्फ़ 6KB तक। कम अपलोड पर खर्च लगभग शून्य।

OSS में इमेज स्टोरेज यूज़ेज़ के हिसाब से — कुछ दसियों GB इमेज का भी महीने में बस कुछ युआन।

ऊपर CDN चढ़ा दिया, तो इमेज उड़ती हुई लोड होती है!

निजी इमेज होस्ट पर हमले की ख़ास वैल्यू नहीं, फिर भी सुरक्षा कई परतों में डाली है: ब्रूट-फ़ोर्स रोकने के लिए मजबूर PoW, नक़ली फ़ाइल रोकने के लिए मैजिक नंबर जाँच, पैकेट भरने से रोकने के लिए तिहरी वॉल्यूम जाँच, ग़लत इस्तेमाल रोकने के लिए रोज़ाना कोटा, और चोरी से इस्तेमाल रोकने के लिए 2 घंटे वाला टोकन।

इस तरीके में कमियाँ भी हैं: कोई मल्टी-यूज़र नहीं, कोई एल्बम प्रबंधन नहीं, कोई यूज़ेज़ स्टैटिस्टिक्स नहीं; इमेज हटानी हो तो OSS कंसोल में जाकर ही हटानी पड़ेगी।

पर यह छोटा है, लागत कम है — निजी इस्तेमाल के लिए काफ़ी है।


2. आर्किटेक्चर और रिक्वेस्ट फ़्लो

ब्राउज़र upload.html
   │  ① POST /login   {user, pass}
   │  ← 200 {token} (मान्यता 24 घंटे)
   │  ② 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. तैयारी

3.1 सर्विस चालू करें

फ़ंक्शन कंप्यूट FC और ऑब्जेक्ट स्टोरेज OSS चालू करें (दोनों यूज़ेज़ के हिसाब से, कोई प्री-परचेज नहीं)।

3.2 बकेट बनाएँ

  • नाम: पूरी दुनिया में अनोखा, जैसे my-img
  • रीजन: अपने/अपने यूज़र्स के नज़दीक वाला चुनें, जैसे East China 1 (Hangzhou) → 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। फिर:

  1. RAM → यूज़र → यूज़र बनाएँ, “स्थायी AccessKey से एक्सेस” वाला चेकबॉक्स लगाएँ
  2. इस यूज़र पर सिर्फ़ ऊपर वाली एक ही नीति लगाएँ
  3. AK/SK बनाएँ और तुरंत लोकल पासवर्ड मैनेजर में सहेज लें

कुछ जानबूझकर किए गए फैसले:

  • oss:ListBuckets नहीं दें — लीक होने पर किसी को यह भी नहीं पता चलेगा कि आपके पास कौन-कौन से बकेट हैं
  • oss:CopyObject नहीं दें — इमेज होस्ट को कॉपी अधिकार की ज़रूरत नहीं, जितना कम दे सकें उतना कम
  • AliyunOSSFullAccess न लगाएँ — इससे आपके इमेज होस्ट वाला AK आपके सारे डेटा बकेट पढ़ सकेगा

4. डिपेंडेंसी लेयर (Layer) तैयार करें

बिज़नेस कोड सिर्फ़ ali-oss पर निर्भर है। इसे लेयर बना लें, तो कोड पैकेट कुछ 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 अपलोड करें

लेयर बस एक बार बनानी होती है। आगे ali-oss का वर्ज़न अपग्रेड करना हो तो नया वर्ज़न बनाकर रिलीज़ करें और फ़ंक्शन को नए वर्ज़न पर मोड़ दें — बिज़नेस कोड बदलने की ज़रूरत नहीं।


5. फ़ंक्शन डिप्लॉय करें

5.1 बिज़नेस कोड पाएँ

सीधे Release अटैचमेंट लेने की सलाह है: https://github.com/ezzty/oss-upload/releases/latest से oss-upload-code.zip (लगभग 6 KB) डाउनलोड करें; अनज़िप करते ही सामने मानक डिप्लॉयमेंट पैकेट मिल जाएगा।

खुद पैक कर रहे हैं तो zip की रूट में सिर्फ़ ये दो फ़ाइलें होनी चाहिए (फ़ोल्डर में मत रखिए, नहीं तो हैंडलर को 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_USER admin — लॉगिन यूज़रनेम
  • AUTH_PASS <मज़बूत पासवर्ड डालें> — लॉगिन पासवर्ड, लंबा रैंडम स्ट्रिंग रखें
  • JWT_SECRET <32 अक्षरों से लंबा रैंडम स्ट्रिंग> — टोकन साइनिंग की चाबी
  • OSS_ACCESS_KEY_ID LTAIxxxxxxxxxxxxxxxx — RAM यूज़र का AK
  • OSS_ACCESS_KEY_SECRET <आपका AccessKey Secret> — RAM यूज़र का SK
  • OSS_BUCKET my-img — बकेट का नाम
  • OSS_REGION oss-cn-hangzhou — बकेट वाले रीजन की ID
  • CDN_DOMAIN https://img.example.com — प्रोटोकॉल ज़रूर लिखें, नहीं तो शुरू होते ही फेल

1 वैकल्पिक:

  • ALLOWED_ORIGINS example.com,myblog.net — क्रॉस-ओरिजिन की अनुमति, कॉमा से अलग करके। न भी सेट करें तो चालू हो जाता है; यह सिर्फ़ कोड-लेवल CORS चालू होने पर ही जाँच में आता है (डिफ़ॉल्ट में CORS प्लेटफ़ॉर्म सीधे Origin लौटा देता है, isValidOrigin कभी कॉल ही नहीं होता)। कोड-लेवल CORS चालू किया तो इसे सेट करना ज़रूरी है, वरना बैकअप वैल्यू example.com ब्राउज़र के सारे अपलोड रोक देगा।

image

5.4 कस्टम डोमेन बाँधें

FC कंसोल → डोमेन प्रबंधन → डोमेन जोड़ें → निर्देशानुसार CNAME और सर्टिफ़िकेट कॉन्फ़िगर करें। तभी आपके पास https://api.example.com जैसा API पता होगा।


6. फ्रंटएंड पेज

upload.html एक फ़ाइल वाला फ्रंटएंड है, सीधे अपनी साइट की डायरेक्टरी में डाल दीजिए।

एक जगह बदलनी ज़रूरी है — _FC_PARTS ढूँढिए:

// अपना फ़ंक्शन कंप्यूट कस्टम डोमेन डालिए
const _FC_PARTS = ['https://', 'api.example.com'];

साइज़ सीमा बैकएंड से मिलाकर रखिए (दोनों की डिफ़ॉल्ट 20MB है):

if (file.size > 20 * 1024 * 1024) { /* रोक दो */ }

फ्रंट और बैकएंड दोनों पर लिमिट है, छोटी वाली मान्य। सिर्फ़ फ्रंट बदला तो बड़ी फ़ाइल सर्वर पर 413 से कट जाएगी; सिर्फ़ बैकएंड बदला तो यूज़र ब्राउज़र में ही रुक जाएगा, भेज ही नहीं पाएगा।


7. गहराई में जाना हो तो — पूरी बारीकियाँ सोर्स README में हैं

यही था सेल्फ-होस्टेड इमेज होस्ट चालू करने का पूरा फ़्लो। कोड ऐसा क्यों लिखा है, हर परत की सुरक्षा कैसे बनी है, पूरा वैलिडेशन चेकलिस्ट — सब सोर्स रिपॉज़िटरी के README में जमा है, ज़रूरत पड़ने पर सामने रखकर देखिए:

https://github.com/ezzty/oss-upload → README.md के «कोर इम्प्लीमेंटेशन (कोड-लेवल)» और «लॉन्च वैलिडेशन» वाले हिस्से

ब्लॉग पोस्ट में बस यही साफ़ करना था कि “खुद क्यों बनाएँ, कैसे चालू करें, किन बातों का ध्यान रखें”; सच में हाथ उठाकर डिप्लॉय करना हो तो README ही पूरा तकनीकी दस्तावेज़ है।


8. लागत

यूज़ेज़ के हिसाब से, तीन हिस्से:

मद बिलिंग अनुमानित मात्रा
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 युआन के आसपास रहता है।


9. रखरखाव और अपडेट

बिज़नेस कोड बदलना हो (जैसे इमेज लिंक का ढाँचा):

  1. लोकल index.js में बदलाव करें
  2. zip code.zip index.js package.json
  3. कंसोल → कोड डिप्लॉय → अपलोड → डिप्लॉय
  4. README के वैलिडेशन प्रोब चलाकर पुष्टि करें कि नया वर्ज़न असरदार है

फ्रंटएंड बदलना हो:

  1. upload.html में बदलाव करें
  2. साइट पर अपलोड करें; Cache-Control: no-cache सेट करने की सलाह है, वरना यूज़र को पुराना वर्ज़न मिल सकता है

डिपेंडेंसी अपग्रेड:

  1. फिर से npm install ali-oss करके layer.zip पैक करें
  2. कंसोल में नया लेयर वर्ज़न रिलीज़ करें
  3. फ़ंक्शन को नए लेयर वर्ज़न पर स्विच करें

चाबियाँ बदलें (नियमित रूप से):

  1. RAM में नया AK/SK बनाएँ
  2. FC के एनवायरनमेंट वेरिएबल अपडेट करें → डिप्लॉय
  3. सेवा सामान्य चलती देखकर, RAM कंसोल में पुराना AK निष्क्रिय कर दें

10. संदर्भ