Skip to content

FA Serverless

Arian Omrani edited this page Oct 11, 2026 · 6 revisions

سرورلس: Vercel و Cloudflare Workers

زبان: English · فارسی

pulsecheck روی Vercel (cron job به‌علاوه‌ی تابع پایتون) و Cloudflare Workers (cron trigger به‌علاوه‌ی Python Worker) اجرا می‌شود. هر دو محیط محدودند و بعضی قابلیت‌ها در آن‌ها نیست. این صفحه می‌گوید چه چیزی کار می‌کند، محدودیت‌های مهم چیست (هم از مستندات خود پلتفرم‌ها و هم از deployهای واقعی) و چطور راه بیندازیدش.

کپی deployشده‌ی خودتان را خصوصی نگه دارید. تنظیماتی که deploy می‌کنید نام سرورهای شما را دارد. از یک مخزن خصوصی یا یک پوشه‌ی محلی deploy کنید، نه از قالب عمومی.

چه چیزی واقعاً تست شد (۱۱ اکتبر ۲۰۲۶)

  • Cloudflare: با cf deploy deploy شد و از cron trigger خودش هر ۵ دقیقه اجرا شد. سایت‌ها را بررسی کرد، وضعیت را در KV نوشت، یک یادآوری را از راه وب‌هوک فرستاد (HTTP 200) و در Workers Logs لاگ زد. یک هشدار به ntfy.sh جواب 429 گرفت و بدون اینکه اجرا متوقف شود به‌صورت notify failed لاگ شد.
  • Vercel: deploy شد؛ endpoint بدون secret جواب 401 داد و با secret درست تنظیمات را خواند. یک تابع هم shoutrrr واقعی را دانلود و اجرا کرد و پیام فرستاد. با یک Upstash Redis واقعی، دو اجرای اجباری هر دو سایت را بررسی کردند، وضعیت بین اجراها ماند، هشدار از راه وب‌هوک رفت و /api/status وضعیت را از Redis خواند (?format=text و ?tag=).
  • handler مربوط به Vercel و خود Worker در تست‌های خودکار هم با داده‌ی ساختگی اجرا می‌شوند.

چه چیزی کجا کار می‌کند

قابلیت CLI / CI / cron Vercel Cloudflare Worker
بررسی HTTP بله بله بله
انقضای گواهی TLS بله بله خیر (fetch گواهی را نشان نمی‌دهد)
بررسی TCP بله بله خیر (رد می‌شود)
ip_version (اجبار IPv4 یا IPv6) بله بله، اگر تابع آن اتصال را داشته باشد (تست نشده) خیر (این سایت‌ها رد می‌شوند)
بررسی ping بله خیر (تابع برنامه‌ی ping ندارد) خیر (رد می‌شود)
هشدار همه‌ی سرویس‌های shoutrrr ارسال‌کننده‌ی داخلی برای ۱۲ سرویس، و برای بقیه shoutrrr واقعی که در اولین استفاده دانلود می‌شود فقط ارسال‌کننده‌ی داخلی برای ۱۲ سرویس (فهرست)
ذخیره‌ی وضعیت فایل SQLite Upstash Redis (REST) Workers KV
status دستور GET /api/status GET /status
خروجی OpenTelemetry، messages، برچسب، یادآوری، maintenance بله بله بله
گزارش با p95 بله خیر (درصد آپتایم و میانگین؛ ۹۰ روز) خیر (همان)

سایت‌هایی که رد می‌شوند در لاگ اجرا فهرست می‌شوند. روی Cloudflare max_redirects نادیده گرفته می‌شود، چون fetch ریدایرکت را با حد خودش دنبال می‌کند.

محدودیت‌هایی که پیدا شد

Vercel

محدودیت مقدار کاری که کرده‌ایم
تناوب cron Hobby: روزی یک‌بار، در هر لحظه‌ی آن ساعت. Pro و Enterprise: هر دقیقه vercel.json با زمان‌بندی روزانه می‌آید تا روی هر پلنی deploy شود. روی Pro مقدار */5 * * * * را بگذارید. روی Hobby از workflow گیت‌هاب trigger-vercel.yml استفاده کنید (پایین‌تر).
تحویل cron تضمینی نیست: ممکن است اجرایی از دست برود یا دوبار انجام شود؛ تلاش مجدد ندارد و ریدایرکت را دنبال نمی‌کند اجراها تکرارپذیرند، اما گاهی فاصله‌ی بی‌اجرا ممکن است.
پایتون Vercel نسخه‌های ۳٫۱۲ (پیش‌فرض)، ۳٫۱۳ و ۳٫۱۴ را دارد. .python-version ما (۳٫۱۵) build را خراب کرد (No interpreter found for Python 3.15). خود تابع روی ۳٫۱۲٫۱۴ اجرا شد در vercel.json مقدار build.env.UV_PYTHON=3.14 گذاشته شده. کد روی ۳٫۱۲ هم کار می‌کند.
مدت اجرا، حافظه، حجم بسته پیش‌فرض ۳۰۰ ثانیه (حداکثر Hobby برابر ۳۰۰ و Pro برابر ۸۰۰)؛ ۲ گیگ (در Pro تا ۴ گیگ)؛ ۵۰۰ مگابایت برای بسته‌ی پایتون برای این کار بیش از کافی است.
File descriptor ۱۰۲۴ عدد، مشترک بین همه‌ی اجراهای هم‌زمان بررسی‌ها دسته‌های ۲۰ تایی اجرا می‌شوند.
دیسک، Docker، ping /tmp قابل نوشتن است؛ Docker و برنامه‌ی ping نیست وضعیت در Redis می‌ماند؛ shoutrrr فقط وقتی آدرس هشداری لازمش داشته باشد در /tmp دانلود می‌شود (با بررسی SHA-256).
حجم deploy یک فایل ۲۰ مگابایتی در بسته باعث شد vercel deploy روی یک اتصال کند بیش از ۲۵ دقیقه گیر کند چیز بزرگی آپلود نمی‌شود؛ فایل اجرایی هنگام اجرا دانلود می‌شود.
IP خروجی پویا بدون IP ثابتِ پولی Vercel نمی‌شود IP مانیتور را در allow-list گذاشت.
vercel dev cron job اجرا نمی‌کند endpoint را خودتان صدا بزنید.

پلن رایگان Upstash ماهی ۵۰۰ هزار دستور و ۲۵۶ مگابایت می‌دهد. هر اجرا ۲ دستور مصرف می‌کند، یعنی با زمان‌بندی ۵ دقیقه‌ای حدود ۱۷ هزار دستور در ماه.

Cloudflare Workers (پایتون)

محدودیت مقدار کاری که کرده‌ایم
ابزار deploy CLI جدید cf (بتا) هنوز نمی‌تواند Python Worker بسازد، چون بسته‌ی dev server پایتونش منتشر نشده. cf deploy --prebuilt اما خروجی دست‌ساز را قبول می‌کند deploy/cloudflare/build.py خروجی Build Output را می‌نویسد و cf deploy --prebuilt آن را آپلود می‌کند.
ماژول‌ها در Python Worker فقط ماژول اصلی import می‌شود؛ فایل‌ها و بسته‌های دیگر در محیط واقعی import نشدند build.py کل پکیج را در یک entry.py می‌گذارد.
بسته‌ها Pyodide (پایتون ۳٫۱۳ روی WebAssembly). نمایه‌ی بسته‌های Pyodide از بعضی شبکه‌ها در دسترس نبود Worker هیچ بسته‌ی شخص ثالثی ندارد. YAML هنگام build به JSON تبدیل می‌شود.
scheduled(controller, env, ctx) آرگومان env هنگام اجرا None است (در لاگ‌ها دیده شد) Worker از self.env استفاده می‌کند.
thread، subprocess، socket قابل استفاده نیست فقط asyncio و fetch هست؛ پس بررسی TCP، ping و انقضای TLS ندارد.
CPU مستندات: رایگان ۱۰ میلی‌ثانیه برای هر اجرای cron، پولی ۳۰ ثانیه. اجرای دو سایت حدود ۲۴ میلی‌ثانیه CPU مصرف کرد و بدون خطا تمام شد حسابی که با آن تست کردیم بدون مشکل اجرا کرد (پلن آن را بررسی نکردیم). برای استفاده‌ی واقعی پلن پولی (۵ دلار در ماه) را در نظر بگیرید.
راه‌اندازی هنگام deploy بین ۰٫۶ تا ۱٫۱ ثانیه گزارش شد فقط در هر cold start یک‌بار است، نه برای هر سایت.
subrequest رایگان ۵۰، پولی ۱۰٬۰۰۰ در هر اجرا هر سایت، هشدار، فراخوانی KV و ارسال تلمتری یکی حساب می‌شود. در پلن رایگان زیر حدود ۴۰ سایت بمانید.
اتصال هم‌زمان ۶ بررسی‌ها دسته‌های ۶ تایی اجرا می‌شوند.
cron trigger رایگان ۵ تا برای کل حساب، پولی ۲۵۰ تا؛ به وقت UTC؛ اعمال تغییرها ممکن است چند دقیقه طول بکشد –
KV رایگان: ۱۰۰۰ نوشتن در روز (هر اجرا یک نوشتن دارد؛ cron پنج‌دقیقه‌ای ۲۸۸ نوشتن در روز می‌شود و cron یک‌دقیقه‌ای پلن پولی می‌خواهد) –
workers.dev درخواست‌ها از ماشین تست به Worker می‌رسید (لاگ می‌زد) ولی پاسخ برنمی‌گشت؛ برای همین /status و /run فقط با داده‌ی ساختگی تست شدند از دامنه‌ی خودتان استفاده کنید، یا از شبکه‌ی دیگری امتحان کنید.

راه‌اندازی Vercel

  1. در یک fork یا clone خصوصی از کل مخزن (قالب حداقلی فایل‌های Vercel و Cloudflare را ندارد)، pulsecheck.example.yml را به pulsecheck.yml کپی کنید، ویرایشش کنید (برای رمزها ${VAR}) و commit بزنید.
  2. دستور vercel را اجرا کنید (یا مخزن را در داشبورد import کنید). Vercel خودش app.py را پیدا می‌کند.
  3. ذخیره‌ساز وضعیت: vercel integration add upstash/upstash-kv. بار اول لینکی که چاپ می‌شود را باز کنید، شرایط Upstash را بپذیرید و دستور را دوباره اجرا کنید. این کار KV_REST_API_URL و KV_REST_API_TOKEN را به پروژه اضافه می‌کند (UPSTASH_REDIS_REST_URL و UPSTASH_REDIS_REST_TOKEN هم پذیرفته می‌شود).
  4. متغیرهای محیطی: CRON_SECRET (تصادفی و دست‌کم ۱۶ کاراکتر؛ Vercel آن را به‌شکل Authorization: Bearer … می‌فرستد و endpointها هر درخواست دیگری را رد می‌کنند) و هر ${VAR} که در YAML به کار برده‌اید.
  5. زمان‌بندی: vercel.json را ویرایش کنید. روی Pro: "schedule": "*/5 * * * *". روی Hobby همان زمان‌بندی روزانه را نگه دارید و از .github/workflows/trigger-vercel.yml استفاده کنید (متغیر گیت‌هاب VERCEL_CRON_URL برابر https://<project>.vercel.app/api/cron و secret به نام CRON_SECRET).
  6. vercel --prod را اجرا کنید و بعد curl -H "Authorization: Bearer $CRON_SECRET" https://<project>.vercel.app/api/cron را بزنید (با ?force=1 یا ?tag=x). وضعیت در /api/status?format=md است. اگر Deployment Protection جلوی curl را گرفت، از vercel curl /api/cron استفاده کنید.

راه‌اندازی Cloudflare

  1. npm install --global cf و بعد cf auth login. uv هم لازم است.
  2. pulsecheck.example.yml را به pulsecheck.yml کپی و ویرایش کنید (سایت‌های HTTP؛ آدرس‌های هشدار از فهرست داخلی).
  3. هر ${VAR} که YAML به کار برده را export کنید، و اگر /status و /run را می‌خواهید STATUS_TOKEN را هم.
  4. deploy/cloudflare/deploy.sh pulsecheck.yml را اجرا کنید (گزینه‌ها: --cron "*/5 * * * *" و --name pulsecheck). اولین deploy فضای KV به اسم <name>-state را می‌سازد و deployهای بعدی از همان استفاده می‌کنند. secretها در هر deploy از محیط شما آپلود می‌شوند.
  5. لاگ‌ها: cf o11y telemetry query --query-id q --view events --limit 20 --timeframe-from <ms> --timeframe-to <ms> --parameters-datasets cloudflare-workers --parameters-order-by-value timestamp --parameters-order-by-order desc.
  6. وضعیت: cf kv keys get pulsecheck --namespace-id <id> --text. اگر اجرایی کرش کند، دلیلش زیر کلید pulsecheck-error می‌ماند.
  7. endpointها (با هدر Authorization: Bearer $STATUS_TOKEN): GET /status?format=text|md|json&tag=x و GET /run?force=1&tag=x.

نکته‌ی YAML

داخل mappingهای درون‌خطی { ... } هر مقداری که ${VAR} دارد را در کوتیشن بگذارید: {name: api, url: "https://x.example.com/${TOKEN}"}.

Clone this wiki locally