[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fWUHPFIw5lyprehOBSBueJ_ZdzTRetZK_V-XewCL6X9k":3},{"data":4,"related":106},{"id":5,"title":6,"slug":7,"excerpt":8,"cover":9,"category":11,"author":14,"track":20,"level":23,"reading_minutes":26,"is_featured":27,"published_at":28,"updated_at":28,"body":29,"toc":30,"tags":75,"faqs":90,"related_services":100,"comments_enabled":104,"meta_title":19,"meta_description":105,"canonical_url":19,"noindex":27,"is_live":104},10,"رفع خطای 429 در Trivy DB با راه‌اندازی mirror داخلی","trivy-db-429-rate-limit-mirror","اگر مرحله اسکن Trivy در پایپلاین با خطای 429 Too Many Requests شکست می‌خورد، مشکل از کد یا شبکه شما نیست؛ GHCR نرخ pull ناشناس را محدود می‌کند. در این مقاله پایگاه‌داده Trivy را در رجیستری داخلی mirror می‌کنیم و Trivy را به آن وصل می‌کنیم.",{"url":10,"alt":6},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002Fcd38c70f-05ae-4f49-aa18-24ae7740ace1.webp",{"name":12,"slug":13},"امنیت و DevSecOps","security",{"name":15,"slug":16,"role":17,"bio":18,"linkedin_url":19,"avatar_url":19},"تحریریه کلودیکپ","cloudicap-editorial","تیم محتوای کلودیکپ","تیم فنی و محتوای کلودیکپ؛ مهندسانی که هر روز با DevOps، Kubernetes و زیرساخت ابری کار می‌کنند و تجربه‌هایشان را اینجا می‌نویسند.",null,{"value":21,"label":22},"engineers","مهندسان",{"value":24,"label":25},"intermediate","متوسط",8,false,"2026-10-11T08:54:50+00:00","\u003Ch2 id=\"شرح-مسئله\">شرح مسئله\u003C\u002Fh2>\u003Cp>جاب اسکن امنیتی که ماه‌ها سبز بود، یک‌دفعه قرمز می‌شود و لاگ Trivy چیزی شبیه این نشان می‌دهد:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">2025-01-14T09:12:41Z FATAL Fatal error run error: init error: DB error: failed to download vulnerability DB: database download error: OCI repository error: 1 error occurred:\n    * failed to download artifact: failed to copy: httpReadSeeker: failed open: unexpected status code https:\u002F\u002Fghcr.io\u002Fv2\u002Faquasecurity\u002Ftrivy-db\u002Fblobs\u002Fsha256:9d1...: 429 Too Many Requests\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>قبل از اینکه به سمت شبکه، DNS یا پروکسی شرکت بروید، به این نشانه‌ها دقت کنید:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>خطا قطعی نیست: بعضی اجراها سبز است، بعضی قرمز. یک rerun ساده معمولاً جواب می‌دهد و همین باعث می‌شود مشکل جدی گرفته نشود.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>روی لپ‌تاپ خودتان همان دستور بدون خطا اجرا می‌شود، ولی روی runnerهای CI نه.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>مرحله اسکن کند است: هر اجرا چند دقیقه صرف دانلود پایگاه‌داده می‌شود، حتی وقتی ایمیج کش شده است.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>خطا در فاز \u003Ccode>init\u003C\u002Fcode> رخ می‌دهد؛ یعنی Trivy حتی به اسکن ایمیج نرسیده و پایپلاین بدون هیچ یافته امنیتی شکست خورده است.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>نکته مهم این است که این خطا \u003Cstrong>false positive نیست\u003C\u002Fstrong>؛ پایپلاین واقعاً اسکن نشده و شما در آن لحظه هیچ سیگنال امنیتی از ایمیج ندارید. بدترین واکنش، حذف یا \u003Ccode>allow_failure\u003C\u002Fcode> کردن جاب است.\u003C\u002Fp>\u003Ch2 id=\"علت-ریشه‌ای\">علت ریشه‌ای\u003C\u002Fh2>\u003Cp>Trivy پایگاه‌داده آسیب‌پذیری‌ها را به‌صورت یک artifact از نوع OCI نگه می‌دارد و به‌طور پیش‌فرض آن را از GHCR می‌گیرد:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>پایگاه‌داده اصلی: \u003Ccode>ghcr.io\u002Faquasecurity\u002Ftrivy-db:2\u003C\u002Fcode>\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>پایگاه‌داده مخازن Java: \u003Ccode>ghcr.io\u002Faquasecurity\u002Ftrivy-java-db:1\u003C\u002Fcode>\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>GHCR برای pull ناشناس (بدون احراز هویت) نرخ‌محدودیت اعمال می‌کند و این محدودیت بر اساس IP است. در محیط عملیاتی سه چیز دست‌به‌دست هم می‌دهند و شما را به سقف می‌رسانند:\u003C\u002Fp>\u003Col>\u003Cli>\u003Cp>\u003Cstrong>IP مشترک خروجی:\u003C\u002Fstrong> همه runnerها از پشت یک NAT یا یک IP ثابت شرکت بیرون می‌روند؛ پس سهمیه شما با سهمیه کل سازمان جمع می‌شود.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>موازی‌سازی:\u003C\u002Fstrong> چند پایپلاین هم‌زمان روی چند برنچ، یا چند جاب در یک stage، هم‌زمان درخواست دانلود می‌دهند و نرخ لحظه‌ای را منفجر می‌کنند.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>نبود کش:\u003C\u002Fstrong> runnerهای ephemeral هیچ چیز را بین اجراها نگه نمی‌دارند. Trivy در هر اجرا ابتدا manifest مخزن را چک می‌کند تا ببیند DB عوض شده یا نه؛ همان چک هم یک درخواست به GHCR است و به سهمیه شما اضافه می‌شود. اگر دایجست عوض شده باشد، دانلود کامل هم اضافه می‌شود.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Fol>\u003Cp>خلاصه: این یک باگ در کد شما یا یک مشکل گذرای شبکه نیست؛ یک وابستگی معماری به یک رجیستری عمومی با نرخ‌محدودیت است. راه‌حل درست، حذف این وابستگی است، نه retry کردن آن.\u003C\u002Fp>\u003Ch2 id=\"راه‌حل-گام‌به‌گام\">راه‌حل گام‌به‌گام\u003C\u002Fh2>\u003Ch3 id=\"گام-۰-تأیید-اینکه-خطا-واقعا-۴۲۹-است\">گام ۰: تأیید اینکه خطا واقعاً ۴۲۹ است\u003C\u002Fh3>\u003Cp>قبل از هر تغییری، مطمئن شوید خطا از نرخ‌محدودیت می‌آید و نه از timeout پروکسی یا خطای TLS:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">trivy image --debug python:3.11-slim 2&gt;&amp;1 | grep -i -E \"429|too many requests|rate\"\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>اگر خروجی خالی بود، مشکل جای دیگری است و ادامه این مقاله کمکی نمی‌کند.\u003C\u002Fp>\u003Ch3 id=\"گام-۱-mirror-کردن-پایگاه‌داده-در-رجیستری-داخلی\">گام ۱: mirror کردن پایگاه‌داده در رجیستری داخلی\u003C\u002Fh3>\u003Cp>برای جابه‌جایی artifactهای OCI از ابزار \u003Ccode>oras\u003C\u002Fcode> استفاده می‌کنیم. یک کاربر با دسترسی push روی رجیستری داخلی بسازید و اسکریپت زیر را روی یک ماشین با اینترنت آزاد (مثلاً سرور mirror) قرار دهید:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">#!\u002Fusr\u002Fbin\u002Fenv bash\n# \u002Fusr\u002Flocal\u002Fbin\u002Ftrivy-db-mirror.sh\nset -euo pipefail\n\nREGISTRY=\"registry.example.com\"\nSRC_DB=\"ghcr.io\u002Faquasecurity\u002Ftrivy-db:2\"\nDST_DB=\"${REGISTRY}\u002Fsecurity\u002Ftrivy-db:2\"\nSRC_JAVA=\"ghcr.io\u002Faquasecurity\u002Ftrivy-java-db:1\"\nDST_JAVA=\"${REGISTRY}\u002Fsecurity\u002Ftrivy-java-db:1\"\n\nprintf '%s' \"$REGISTRY_PASSWORD\" | oras login \"$REGISTRY\" \\\n  --username \"$REGISTRY_USER\" --password-stdin\n\noras cp \"$SRC_DB\"   \"$DST_DB\"\noras cp \"$SRC_JAVA\" \"$DST_JAVA\"\n\necho \"$(date -Is) trivy db mirror ok\"\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>دو نکته عملی:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>اگر \u003Ccode>oras cp\u003C\u002Fcode> با خطای platform شکست خورد، فلگ \u003Ccode>--platform linux\u002Famd64\u003C\u002Fcode> را اضافه کنید؛ این artifactها شامل چند پلتفرم نیستند ولی ابزار گاهی سخت‌گیر می‌شود.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>رجیستری داخلی باید OCI-conformant باشد. Harbor، Nexus و GitLab Container Registry همه گزینه‌های خوبی‌اند. اگر Harbor دارید، به‌جای این اسکریپت می‌توانید proxy cache بسازید و \u003Ccode>--db-repository\u003C\u002Fcode> را به همان پروژه اشاره دهید.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Cp>سپس با cron روزانه sync را اجرا کنید:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\"># \u002Fetc\u002Fcron.d\u002Ftrivy-db-mirror\n15 2 * * * root \u002Fusr\u002Flocal\u002Fbin\u002Ftrivy-db-mirror.sh &gt;&gt; \u002Fvar\u002Flog\u002Ftrivy-db-mirror.log 2&gt;&amp;1\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>دقت کنید که پارامتر \u003Ccode>REGISTRY_PASSWORD\u003C\u002Fcode> را از یک فایل با مجوز \u003Ccode>600\u003C\u002Fcode> یا از secret manager بخوانید، نه داخل خود اسکریپت.\u003C\u002Fp>\u003Ch3 id=\"گام-۲-وصل-کردن-trivy-به-mirror\">گام ۲: وصل کردن Trivy به mirror\u003C\u002Fh3>\u003Cp>Trivy هم با فلگ و هم با متغیر محیطی این آدرس را قبول می‌کند. در GitLab CI:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-yaml\">variables:\n  TRIVY_DB_REPOSITORY: \"registry.example.com\u002Fsecurity\u002Ftrivy-db:2\"\n  TRIVY_JAVA_DB_REPOSITORY: \"registry.example.com\u002Fsecurity\u002Ftrivy-java-db:1\"\n  TRIVY_CACHE_DIR: \"$CI_PROJECT_DIR\u002F.trivy-cache\"\n  TRIVY_USERNAME: \"$REGISTRY_USER\"\n  TRIVY_PASSWORD: \"$REGISTRY_PASSWORD\"\n\ntrivy-scan:\n  stage: security\n  image:\n    name: aquasec\u002Ftrivy:0.58.1\n    entrypoint: [\"\"]\n  cache:\n    key: trivy-db\n    paths:\n      - .trivy-cache\u002F\n  script:\n    - &gt;\n      trivy image\n      --cache-dir \"$TRIVY_CACHE_DIR\"\n      --scanners vuln\n      --severity CRITICAL,HIGH\n      --ignore-unfixed\n      --exit-code 1\n      --format table\n      \"$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA\"\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>متغیرهای \u003Ccode>REGISTRY_USER\u003C\u002Fcode> و \u003Ccode>REGISTRY_PASSWORD\u003C\u002Fcode> را در تنظیمات CI تعریف و masked کنید. توکن فقط به \u003Ccode>pull\u003C\u002Fcode> نیاز دارد؛ دسترسی نوشتن به هیچ پایپلاینی داده نشود. اگر رجیستری داخلی شما روی همان شبکه runnerها و بدون احراز هویت باز است، این دو متغیر را حذف کنید.\u003C\u002Fp>\u003Ch3 id=\"گام-۳-کش-کردن-پایگاه‌داده\">گام ۳: کش کردن پایگاه‌داده\u003C\u002Fh3>\u003Cp>با \u003Ccode>TRIVY_CACHE_DIR\u003C\u002Fcode>، Trivy دیتابیس را یک بار می‌گیرد و اجراهای بعدی فقط دایجست را چک می‌کنند. دو حالت داریم:\u003C\u002Fp>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>runner با دیسک پایدار:\u003C\u002Fstrong> مقدار \u003Ccode>TRIVY_CACHE_DIR\u003C\u002Fcode> را به یک مسیر دائمی مثل \u003Ccode>\u002Fvar\u002Fcache\u002Ftrivy\u003C\u002Fcode> بدهید. بهترین حالت همین است؛ سریع و بدون آرشیو کردن.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>runner ephemeral:\u003C\u002Fstrong> از \u003Ccode>cache\u003C\u002Fcode> خود CI استفاده کنید. توجه کنید که GitLab در هر جاب آرشیو را دانلود و باز می‌کند؛ یک DB چند صد مگابایتی روی شبکه داخلی سریع است ولی بی‌هزینه نیست.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Ch3 id=\"گام-۴-محیط-بدون-اینترنت\">گام ۴: محیط بدون اینترنت\u003C\u002Fh3>\u003Cp>برای runnerهای air-gapped، کش را از بیرون منتقل می‌کنید و از Trivy می‌خواهید به هیچ رجیستری‌ای دست نزند:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\"># روی ماشین متصل: کش را گرم کن\nmkdir -p \u002Fopt\u002Ftrivy-cache\ntrivy image --cache-dir \u002Fopt\u002Ftrivy-cache alpine:3.19\ntar -czf trivy-cache.tgz -C \u002Fopt\u002Ftrivy-cache .\n\n# روی ماشین ایزوله: بازگردانی و استفاده بدون به‌روزرسانی\ntar -xzf trivy-cache.tgz -C \u002Fopt\u002Ftrivy-cache\ntrivy image --cache-dir \u002Fopt\u002Ftrivy-cache \\\n  --skip-db-update --skip-java-db-update alpine:3.19\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>اینجا پایگاه‌داده در زمان اجرا به‌روز نمی‌شود، پس تاریخچه انتقال فایل کش بخشی از فرایند عملیاتی شماست. بدون آن، در عمل روی یک DB ماه‌ها قدیمی اسکن می‌کنید.\u003C\u002Fp>\u003Ch2 id=\"بررسی-اینکه-مشکل-واقعا-حل-شده-است\">بررسی اینکه مشکل واقعاً حل شده است\u003C\u002Fh2>\u003Cp>سه تست زیر را اجرا کنید؛ هر سه باید پاس شوند:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\"># ۱) آیا Trivy واقعاً از رجیستری ما می‌خواند؟\n# مخزن عمداً اشتباه بدهید؛ خطا باید آدرس رجیستری خودتان را نشان بدهد\n# و هیچ fallback ای به ghcr.io رخ ندهد.\ntrivy image --cache-dir \u002Ftmp\u002Ftrivy-verify \\\n  --db-repository registry.example.com\u002Fsecurity\u002Fdoes-not-exist:2 \\\n  alpine:3.19\n\n# ۲) اجرای دوم باید سریع و بدون دانلود باشد\ntime trivy image --cache-dir \u002Ftmp\u002Ftrivy-verify alpine:3.19\ntime trivy image --cache-dir \u002Ftmp\u002Ftrivy-verify alpine:3.19\n\n# ۳) دایجست mirror و منبع اصلی باید یکی باشد\noras manifest fetch registry.example.com\u002Fsecurity\u002Ftrivy-db:2 --descriptor | jq -r '.digest'\noras manifest fetch ghcr.io\u002Faquasecurity\u002Ftrivy-db:2 --descriptor | jq -r '.digest'\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>علاوه بر این، یک تست کاربردی‌تر بزنید: تعداد یافته‌های اسکن قبل و بعد از مهاجرت باید یکسان باشد. اگر mirror ناقص sync شده باشد، Trivy ممکن است بدون خطا با DB ناقص اسکن کند و شما متوجه نشوید:\u003C\u002Fp>\u003Cpre>\u003Ccode class=\"language-bash\">trivy image --format json --output before.json myapp:1.0\n# بعد از تغییر تنظیمات و پاک کردن کش\ntrivy image --format json --output after.json myapp:1.0\njq '[.Results[].Vulnerabilities[]?] | length' before.json\njq '[.Results[].Vulnerabilities[]?] | length' after.json\n\u003C\u002Fcode>\u003C\u002Fpre>\u003Cp>در نهایت، لاگ دسترسی رجیستری داخلی را ببینید: باید از هر runner به‌طور منظم درخواست pull ثبت شده باشد و از سمت GHCR تقریباً هیچ ترافیکی نباشد.\u003C\u002Fp>\u003Ch2 id=\"پیشگیری-و-بهترین-روش‌ها\">پیشگیری و بهترین روش‌ها\u003C\u002Fh2>\u003Cul>\u003Cli>\u003Cp>\u003Cstrong>همه artifactهای خارجی را mirror کنید:\u003C\u002Fstrong> فقط DB آسیب‌پذیری نیست؛ ایمیج‌های پایه، chartهای Helm و مخازن بسته‌ها هم همین رفتار را دارند. یک سیاست egress بگذارید که خروج به \u003Ccode>ghcr.io\u003C\u002Fcode> و \u003Ccode>docker.io\u003C\u002Fcode> را از سمت runnerها ببندد تا وابستگی پنهان دوباره ساخته نشود.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>پیامد شکست sync را جدی بگیرید:\u003C\u002Fstrong> اگر cron شبانه شکست بخورد، پایپلاین‌ها همچنان سبز می‌مانند و کسی متوجه نمی‌شود. روی نتیجه اسکریپت mirror alert بگذارید و سن آخرین sync را در داشبورد نشان بدهید.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>fail-closed رفتار کنید:\u003C\u002Fstrong> اگر DB در دسترس نبود، جاب اسکن باید شکست بخورد، نه اینکه با «بدون یافته» تمام شود. اسکن نکردن را هرگز با پاس شدن برابر نگیرید.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>دیتابیس را بیش از حد قدیمی نگه ندارید:\u003C\u002Fstrong> یک DB دو هفته قدیمی یعنی CVEهای جدید در گزارش شما نیستند. سن پایگاه‌داده را پایش کنید و sync را حداقل روزانه اجرا کنید.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>موازی‌سازی را کنترل کنید:\u003C\u002Fstrong> اگر چند جاب هم‌زمان اسکن می‌کنند، یک جاب مقدماتی دیتابیس را در کش مشترک آماده کند و بقیه فقط مصرف کنند.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>آستانه‌ها را واقع‌بینانه بگذارید:\u003C\u002Fstrong> استفاده از \u003Ccode>--ignore-unfixed\u003C\u002Fcode> و آستانه \u003Ccode>CRITICAL,HIGH\u003C\u002Fcode> باعث می‌شود تیم اسکن را جدی بگیرد. گیت‌هایی که هر اجرا قرمز می‌شوند، دیر یا زود با \u003Ccode>allow_failure\u003C\u002Fcode> خاموش می‌شوند و همان کار را خراب می‌کنند.\u003C\u002Fp>\u003C\u002Fli>\u003Cli>\u003Cp>\u003Cstrong>نسخه Trivy را pin کنید:\u003C\u002Fstrong> ارتقای ناگهانی نسخه اسکنر می‌تواند رفتار گزارش و فرمت خروجی را عوض کند؛ نسخه را در ایمیج CI ثابت و ارتقا را آگاهانه انجام دهید.\u003C\u002Fp>\u003C\u002Fli>\u003C\u002Ful>\u003Ch2 id=\"پرسش‌های-پرتکرار\">پرسش‌های پرتکرار\u003C\u002Fh2>\u003Ch3 id=\"چرا-خطا-فقط-بعضی-وقت‌ها-رخ-می‌دهد\">چرا خطا فقط بعضی وقت‌ها رخ می‌دهد؟\u003C\u002Fh3>\u003Cp>چون نرخ‌محدودیت پنجره‌ای است و به IP شما وابسته است. هر موازی‌سازی در پایپلاین یا هر تیم دیگری که از همان IP خروجی استفاده می‌کند، سهمیه شما را مصرف می‌کند. به همین دلیل retry دادن گاهی جواب می‌دهد و این توهم را ایجاد می‌کند که مشکل گذرا بوده است.\u003C\u002Fp>\u003Ch3 id=\"آیا-احراز-هویت-با-توکن-github-مشکل-را-حل-نمی‌کند\">آیا احراز هویت با توکن GitHub مشکل را حل نمی‌کند؟\u003C\u002Fh3>\u003Cp>سهمیه بالاتری می‌دهد و به‌عنوان مسکن کار می‌کند، ولی وابستگی شما به یک سرویس خارجی و به یک توکن باقی می‌ماند و در محیط با اینترنت محدود یا air-gapped کار نمی‌کند. راه‌حل پایدار همان mirror داخلی است.\u003C\u002Fp>\u003Ch3 id=\"آیا-می‌توانم-دیتابیس-را-داخل-ایمیج-ci-بگذارم\">آیا می‌توانم دیتابیس را داخل ایمیج CI بگذارم؟\u003C\u002Fh3>\u003Cp>فنی است ولی توصیه نمی‌شود؛ پایگاه‌داده روزانه به‌روز می‌شود و شما با هر sync باید ایمیج بسازید و منتشر کنید. کش کردن روی دیسک پایدار runner یا mirror در رجیستری، چرخه به‌روزرسانی را بسیار ساده‌تر می‌کند.\u003C\u002Fp>",[31,35,38,41,45,48,51,54,57,60,63,66,69,72],{"id":32,"text":33,"level":34},"شرح-مسئله","شرح مسئله",2,{"id":36,"text":37,"level":34},"علت-ریشه‌ای","علت ریشه‌ای",{"id":39,"text":40,"level":34},"راه‌حل-گام‌به‌گام","راه‌حل گام‌به‌گام",{"id":42,"text":43,"level":44},"گام-۰-تأیید-اینکه-خطا-واقعا-۴۲۹-است","گام ۰: تأیید اینکه خطا واقعاً ۴۲۹ است",3,{"id":46,"text":47,"level":44},"گام-۱-mirror-کردن-پایگاه‌داده-در-رجیستری-داخلی","گام ۱: mirror کردن پایگاه‌داده در رجیستری داخلی",{"id":49,"text":50,"level":44},"گام-۲-وصل-کردن-trivy-به-mirror","گام ۲: وصل کردن Trivy به mirror",{"id":52,"text":53,"level":44},"گام-۳-کش-کردن-پایگاه‌داده","گام ۳: کش کردن پایگاه‌داده",{"id":55,"text":56,"level":44},"گام-۴-محیط-بدون-اینترنت","گام ۴: محیط بدون اینترنت",{"id":58,"text":59,"level":34},"بررسی-اینکه-مشکل-واقعا-حل-شده-است","بررسی اینکه مشکل واقعاً حل شده است",{"id":61,"text":62,"level":34},"پیشگیری-و-بهترین-روش‌ها","پیشگیری و بهترین روش‌ها",{"id":64,"text":65,"level":34},"پرسش‌های-پرتکرار","پرسش‌های پرتکرار",{"id":67,"text":68,"level":44},"چرا-خطا-فقط-بعضی-وقت‌ها-رخ-می‌دهد","چرا خطا فقط بعضی وقت‌ها رخ می‌دهد؟",{"id":70,"text":71,"level":44},"آیا-احراز-هویت-با-توکن-github-مشکل-را-حل-نمی‌کند","آیا احراز هویت با توکن GitHub مشکل را حل نمی‌کند؟",{"id":73,"text":74,"level":44},"آیا-می‌توانم-دیتابیس-را-داخل-ایمیج-ci-بگذارم","آیا می‌توانم دیتابیس را داخل ایمیج CI بگذارم؟",[76,79,82,85,88],{"name":77,"slug":78},"CI\u002FCD","cicd",{"name":80,"slug":81},"DevSecOps","devsecops",{"name":83,"slug":84},"Trivy","trivy",{"name":86,"slug":87},"رجیستری خصوصی","rgystry-khsosy",{"name":89,"slug":89},"oras",[91,94,97],{"question":92,"answer":93},"چرا خطای 429 فقط بعضی وقت‌ها در پایپلاین ظاهر می‌شود؟","چون نرخ‌محدودیت GHCR پنجره‌ای و بر اساس IP است. تمام runnerهایی که از پشت یک NAT بیرون می‌روند سهمیه مشترک دارند و هر موازی‌سازی در پایپلاین یا هر تیم دیگری روی همان IP، سهمیه شما را مصرف می‌کند. به همین دلیل rerun گاهی سبز می‌شود و مشکل به‌نظر گذرا می‌آید.",{"question":95,"answer":96},"آیا احراز هویت با توکن GitHub مشکل را برطرف می‌کند؟","سهمیه pull بالاتری می‌دهد و به‌عنوان راه‌حل موقت قابل قبول است، ولی وابستگی به ghcr.io و به یک توکن باقی می‌ماند و روی runnerهای بدون اینترنت کار نمی‌کند. راه‌حل پایدار، mirror trivy-db در رجیستری داخلی و تنظیم TRIVY_DB_REPOSITORY است.",{"question":98,"answer":99},"اگر پایگاه‌داده داخلی یک روز به‌روزرسانی نشود چه اتفاقی می‌افتد؟","اسکن با همان نسخه قدیمی انجام می‌شود و ممکن است CVEهای تازه‌منتشرشده در گزارش نیایند؛ خطایی هم دریافت نمی‌کنید. بنابراین sync شبانه باید alert داشته باشد و سن آخرین به‌روزرسانی پایگاه‌داده در داشبورد پایش شود.",[101,102,103],"cloud-infrastructure","devops-consulting","cloud-migration",true,"علت خطای 429 Too Many Requests در دانلود Trivy DB و راه‌حل پایدار آن: mirror کردن trivy-db در رجیستری داخلی، کش کردن DB و پیکربندی پایپلاین CI.",[107,120,134],{"id":108,"title":109,"slug":110,"excerpt":111,"cover":112,"category":114,"author":116,"track":117,"level":118,"reading_minutes":26,"is_featured":27,"published_at":119,"updated_at":119},7,"چرا همگام‌سازی JQL در Jira فقط ۵۰ ایشو برمی‌گرداند؟","jira-jql-search-nextpagetoken-pagination","اگر job همگام‌سازی Jira شما بدون خطا تمام می‌شود ولی فقط ۵۰ ایشو را می‌آورد یا در حلقه بی‌پایان گیر می‌کند، مشکل از JQL نیست؛ اندپوینت search به pagination مبتنی بر nextPageToken منتقل شده و startAt و total دیگر وجود ندارند.",{"url":113,"alt":109},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002Facfa9ea0-bda9-4ade-a1c3-0f1677851587.webp",{"name":115,"slug":78},"CI\u002FCD و اتوماسیون",{"name":15,"slug":16,"role":17,"avatar_url":19},{"value":21,"label":22},{"value":24,"label":25},"2026-10-08T15:34:25+00:00",{"id":121,"title":122,"slug":123,"excerpt":124,"cover":125,"category":127,"author":129,"track":130,"level":131,"reading_minutes":132,"is_featured":27,"published_at":133,"updated_at":133},5,"قفل state ترافورم گیر کرده؛ آزادسازی امن بعد از کیل شدن apply","terraform-state-lock-stuck-force-unlock","وقتی پایپلاین CI وسط اجرای apply کشته می‌شود، قفل state ترافورم در جدول DynamoDB باقی می‌ماند و اجرای بعدی با خطای Error acquiring the state lock متوقف می‌شود. در این مقاله علت ریشه‌ای، روش آزادسازی امن و راه‌های پیشگیری را بررسی می‌کنیم.",{"url":126,"alt":122},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002Fe26789f7-eced-4abb-b6a9-a91adf9b7500.webp",{"name":128,"slug":101},"زیرساخت ابری و IaC",{"name":15,"slug":16,"role":17,"avatar_url":19},{"value":21,"label":22},{"value":24,"label":25},6,"2026-10-08T09:49:26+00:00",{"id":135,"title":136,"slug":137,"excerpt":138,"cover":139,"category":141,"author":144,"track":145,"level":146,"reading_minutes":132,"is_featured":27,"published_at":147,"updated_at":147},9,"پاد Kubernetes در وضعیت Terminating گیر کرده؛ آزادسازی امن","kubernetes-pod-stuck-terminating-finalizer","پاد در حالت Terminating گیر کرده و با kubectl delete پاک نمی‌شود؟ این مقاله علت واقعی (finalizer باقی‌مانده، نود NotReady، ولوم گیرکرده) و راه‌حل گام‌به‌گام امن را نشان می‌دهد.",{"url":140,"alt":136},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002F8e6369a3-ad85-4e7e-8588-183bbd5a31f9.webp",{"name":142,"slug":143},"Kubernetes و کانتینر","kubernetes",{"name":15,"slug":16,"role":17,"avatar_url":19},{"value":21,"label":22},{"value":24,"label":25},"2026-10-10T09:34:08+00:00"]