تغییر Tag و Digest ایمیج در Helm و رجیستری Infynex
وقتی Tag ساخته یا Promote میشود، دقیقاً چه چیزی در آدرس ایمیجهای چارتهای Helm تغییر میکند؟ تفاوت Tag و Digest چیست، کدام اسکریپت چه کاری میکند و کنترل در چه سطحی انجام میشود؟
۱. خلاصهٔ اجرایی
v1.7.0-rc.2 ← v1.7.0) Tag داخل فایلهای چارت Helm عوض میشود، اما Digest (@sha256:…) تغییر نمیکند و هیچ Build دوبارهای انجام نمیشود. Digest فقط در لحظهٔ Build عوض میشود.نتیجهٔ عملی: بایتهایی که در تست RC E2E را گذراندهاند، عیناً همان بایتهاییاند که با نام v1.7.0 منتشر میشوند.
git.infynex.ir/infynex-io— رجیستری CI/داخلی: خروجی Buildِ PR وmain.ghcr.io/infynex-io/infynex— رجیستری Release: RC، Stable و Nightly برای توزیع.
۲. Tag و Digest چیستند؟
یک مرجع کامل ایمیج (Image Ref) معمولاً سه بخش دارد:
وقتی Tag و Digest هر دو وجود دارند، Pull بر اساس Digest انجام میشود و Tag فقط یک توضیح انسانی است. پس تغییر Tag روی «همان Digest» بهمعنای «همان ایمیج با نام جدید» است.
۳. Tag و Digest چه زمانی تغییر میکنند؟
| مقطع | Tag | Digest | نکته |
|---|---|---|---|
| Build روی PR | pr-<N>-<sha> | جدید | منحصر به Commit همان PR؛ از Tag شناور مشترک استفاده نمیشود تا Pushهای همزمان تداخل نکنند. |
شاخهٔ main | main (شناور) | جدید | build-main.yaml با هر Push؛ در رجیستری CI. |
| Tag پیشانتشار | vX.Y.Z-rc.N | جدید | tags.yaml آن را Build میکند و Digestها را در شاخهٔ مرحلهای release-X.Y.Z-rc.N ثابت میکند. |
| Promote به Stable | vX.Y.Z | ثابت | همان Digest RC؛ فقط بازنویسی/Retag، بدون Build. |
| Nightly Mirror | 0.0.0-nightly.… و nightly | ثابت | کپی بیتبهبیت از رجیستری CI به Release. |
نکته: شاخهٔ release-X.Y یک شاخهٔ Git است نه Tag ایمیج؛ در فایل اولیه Build آن بهعنوان یک ردیف جدا آمده بود، ولی در مستند انتشار اشارهای به Tag ایمیج مستقل برای آن نیست و حذف شده است.
۴. جریان Build، Promote و Mirror
flowchart LR PR["PR build
tag pr-N-sha
NEW digest"] --> MAIN["main build
tag main
NEW digest"] MAIN --> RC["cut-prerelease + tags.yaml
tag v1.7.0-rc.N
NEW digest"] RC --> E2E{"rc-e2e green?"} E2E -- yes --> PRM["promote-rc.yaml
rewrite tags in chart files
SAME digest"] PRM --> FIN["Finalize
retag by digest to v1.7.0
SAME digest"] FIN --> LATEST[":latest only if newest stable"] MAIN -. nightly .-> MIR["nightly-mirror.sh
copy by digest CI to release
SAME digest"]
sequenceDiagram
autonumber
participant CI as CI registry
participant RC as release registry (rc)
participant GIT as release-1.7.0 branch
participant FIN as Finalize
participant ST as release registry (stable)
CI->>RC: tags.yaml builds v1.7.0-rc.2 (digest D1)
RC-->>GIT: Prepare release commit pins digests D1
GIT->>GIT: promote rewrites ":v1.7.0-rc.2@D1" to ":v1.7.0@D1"
GIT->>GIT: second commit pins packages artifact digest
GIT->>FIN: promote PR merged
FIN->>RC: verify candidate and container digests
FIN->>ST: retag by digest: v1.7.0 points to D1
۵. در فایلهای Helm دقیقاً چه عوض میشود؟
مثال قبل و بعد از Promote
| پیش از Promote (RC) | پس از Promote (Stable) |
|---|---|
infynexAPI.image: ghcr.io/infynex-io/infynex/infynex-api:v1.7.0-rc.2@sha256:9f3c…a1 | infynexAPI.image: ghcr.io/infynex-io/infynex/infynex-api:v1.7.0@sha256:9f3c…a1 |
cilium.image.tag: v1.7.0-rc.2@sha256:1234…ef | cilium.image.tag: v1.7.0@sha256:1234…ef |
# شکل Diff در Promote PR
- image: infynex-api:v1.7.0-rc.2@sha256:9f3c…a1
+ image: infynex-api:v1.7.0@sha256:9f3c…a1
- بخش
@sha256:…در هر دو حالت یکسان است. - فقط بخش Tag (
:v1.7.0-rc.2←:v1.7.0) عوض میشود. - اگر میزبان (Host) رجیستری جدا نگهداری شده باشد، Host و Repository هم با هم بازنویسی میشوند (طبق فایل اولیه).
۶. مرجعهای ایمیج کجا ذخیره میشوند؟
Commit «Prepare release vX.Y.Z» فقط Digest و Tag Image را عوض میکند. فایلهای لمسشده:
| مسیر | محتوا |
|---|---|
packages/core/{installer,platform,testing}/values.yaml | مرجعهای هستهٔ پلتفرم، Installer و آرتیفکت packages |
packages/system/*/values.yaml | اجزای سیستمی (API، داشبورد، kamaji، linstor، kubevirt-csi-node و …) |
packages/apps/kubernetes/images/*.tag | فایل ساده با یک مرجع در هر فایل (مثل kubevirt-csi-driver)؛ از Template با .Files.Get خوانده میشود |
packages/system/dashboard/templates/configmap.yaml | مقدار $tenantText |
فایل اولیه چهار «شکل» ذخیرهسازی را برمیشمرد و یک استثنا برای فایلهای Vendorشده از بالادست (مثل DaemonSet multus):
| شکل | توضیح |
|---|---|
| ۱ | values.yaml: رشتهٔ کامل، یا نقشهٔ جدا (repository/tag/digest)، یا مقدار سراسری Chart، یا آرتیفکت OCI |
| ۲ | images/*.tag: فایل ساده با یک مرجع |
| ۳ | فایلهای اضافه (IMAGE_REF_EXTRA_FILES) مثل system/multus/templates/multus-daemonset-thick.yml |
IMAGE_REF_EXTRA_FILES و توابع image_ref_files() و collect_image_refs() از فایل اولیهٔ شماست؛ من نتوانستم مستند docs/agents/image-refs.md را بخوانم. پیش از انتشار این سند، آنها را با فایل واقعی مخزن تطبیق دهید.چیزهای عجیبنما اما عادی در Diff
- پکیج بدون تغییر: وقتی Cache کامل بوده و Base Imageها جابهجا نشدهاند، Digest عیناً همان قبلی است. «بدون Diff» یعنی «Build نشده» نیست؛ اجرای گام Build را بررسی کنید.
- تغییر ناگهانی Tagهای
ubuntu-container-disk-*: منبع بالادستی یک هدف متحرک است. - تغییر رجیستری: اگر ایمیجی از یک رجیستری به دیگری منتقل شده، بخش رجیستریِ مرجع عوض میشود؛ این یک انتقال عمدی است.
targetVersionرا PR انتشار تغییر نمیدهد: این عدد قبلاً در یک Commit قابلیت بالا رفته؛ PR انتشار فقط Digest ایمیج Migrationها را ثابت میکند.
۷. استثنا: آرتیفکت packages و «ارجاع به خود»
علاوه بر ایمیجهای کانتینر، پلتفرم یک آرتیفکت OCI به نام packages (درخت packages/) منتشر میکند. Digest این آرتیفکت در Promote جدید است، چون درخت بازنویسی شده است:
- Promote درخت را بازنویسی میکند (Tag ←
1.7.0) و زیر یک Tag موقتpromotion-*منتشر میکند. - Commit دوم Digest تغییرناپذیر همین Candidate را در Installer ثابت میکند.
- آرتیفکت نمیتواند Digest خودش را در خودش داشته باشد؛ این «ارجاع به خود» غیرممکن است و هنگام تأیید نادیده گرفته میشود.
| مورد | در Promote |
|---|---|
| Digest ایمیجهای کانتینر | ثابت همان RC |
| Digest آرتیفکت packages | جدید (Candidate)، بعد از Merge با Digest به v1.7.0 Retag میشود |
repo@digest کانتینرها با آرتیفکت RC اصلی برابر است. هر ناهماهنگی، Finalize را قبل از ساخت هر نام پایدار متوقف میکند.۸. اسکریپتها و workflowها
| فایل | نقش | منبع |
|---|---|---|
hack/lib/image-refs.sh | تنها فهرستکنندهٔ فایلهای دارای مرجع ایمیج؛ مشترک بین Promote، Retag و Mirror | مستند انتشار |
hack/promote-rewrite-tags.sh | بازنویسی Tag در درخت packages/ هنگام Promote | فایل اولیه |
hack/promote-retag.sh | Retag آرتیفکت و ایمیجهای RC با Digest در Finalize، بدون Build | مستند انتشار |
hack/verify-promoted-packages.sh | تأیید تطابق Candidate با درخت Mergeشده و Digestهای RC پیش از ساخت Tag پایدار | مستند انتشار |
hack/nightly-mirror.sh | کپی Digest به Digest از رجیستری CI به رجیستری Release | مستند انتشار |
.github/workflows/promote-rc.yaml | Promote: بازنویسی، Candidate، PR | مستند انتشار |
.github/workflows/pull-requests-release.yaml | Finalize: تأیید، Tag، Retag | مستند انتشار |
.github/workflows/nightly.yaml | Mirror و E2E روزانه | مستند انتشار |
hack/build-matrix.sh، hack/overlay-main-images.sh | ماتریس Build و روکش Imageهای main (ابزار Packaging، نه منطق کنترلری) | فایل اولیه |
ردیفهای «فایل اولیه» در این گفتگو راستیآزمایی نشدهاند. فایل docs/alm-versioning-release-guide.md که در فایل اولیه آمده بود هم همینطور؛ در مستند انتشار چنین نامی نیست و از جدول حذف شد.
۹. کنترل در سطح چارت است یا کنترلرهای کلاستر؟
- کنترلرهای کلاستر فقط مصرفکنندهاند: چارت رندر میشود و Deployment/StatefulSet/Pod با
image: repo:tag@sha256:…ساخته میشود. کنترلر هیچچیز را بهvalues.yamlیا فایل چارت بازنمینویسد. - بازنویسی Tag، Retag، Mirror و تأیید Digest همه با اسکریپتهای Shell و workflowهای CI انجام میشوند.
flowchart LR CI["CI scripts and workflows"] -->|rewrite tag| CH["Helm chart files"] CI -->|retag by digest| REG["OCI registry"] CH -->|helm template| MAN["rendered manifests"] MAN -->|deploy| K8S["Kubernetes cluster"] K8S --> CTL["controllers (read only)"] CTL -. never write .-> CH
۱۰. Nightly Mirror
Nightly یک کپی قابلنصب از main روی رجیستری Release است؛ نه Build دوباره و نه Release. Build هر Push روی main قبلاً در رجیستری CI ساخته شده؛ nightly.yaml آن را ارتقا میدهد و نصبپذیریاش را اثبات میکند:
- mirror: Commit دقیقی که packages:main از آن ساخته شده پیدا میشود و تمام ایمیجهای اجزای پلتفرم با Digest کپی میشوند. آرتیفکت packages و Chart نصبکننده با Tag زیر منتشر میشوند:
0.0.0-nightly.<YYYYMMDD>.<run-id>.<attempt>بهعلاوهٔ Tag شناورnightly. - build-disk: دیسک Talos از Imager بالادستی ساخته و منتشر میشود.
- e2e: مجموعهٔ کامل اپلیکیشنها روی نتیجه اجرا میشود.
برای Nightly هیچ GitHub Release و Tag ماژول Go ساخته نمیشود، و retention.yaml نسخههای قدیمی را پاک میکند (۱۴ نسخهٔ اخیر هر بسته میماند).
۱۱. خودتان چطور بررسی کنید؟
با crane میتوانید ثابت بودن Digest را عملاً ببینید:
# Digest مربوط به هر دو Tag باید یکسان باشد
crane digest ghcr.io/infynex-io/infynex/infynex-api:v1.7.0-rc.2
crane digest ghcr.io/infynex-io/infynex/infynex-api:v1.7.0
# مرجع با Digest باید Pull شود (مثلاً هنگام بازبینی Promote PR)
crane manifest ghcr.io/infynex-io/infynex/infynex-api@sha256:9f3c…a1 | head
# فقط Tag عوض شده، Digest نه: Diff شاخهٔ Promote
git diff release-1.7...release-1.7.0 -- 'packages/**/values.yaml' | grep -E '^[+-].*image'
- ☐ در Diff فقط Tag عوض شده و بخش
@sha256:…هر خط یکسان است. - ☐
crane manifest …@digestبرای هر Digest تغییرکرده جواب میدهد. - ☐
Verify release packages candidateدر PR سبز است. - ☐ هیچ Tag
-rc.در درخت Mergeشده باقی نمانده.
۱۲. پرسشهای پرتکرار
| پرسش | پاسخ |
|---|---|
| چرا Stable دوباره Build نمیشود؟ | چون باید همان چیزی منتشر شود که E2E را گذرانده؛ Build دوباره ممکن است بایتهای دیگری بدهد. |
| اگر بعد از ساخت RC چیزی در main عوض شود؟ | در Stable نیست. rc بعدی باید از release-X.Y و با Cherry-pick ساخته شود. |
آیا Tag latest همیشه جابهجا میشود؟ | نه؛ فقط وقتی نسخه جدیدترین Stable منتشرشده باشد. |
| اگر Digest در Finalize با RC نخواند چه میشود؟ | Finalize پیش از ساخت هر Tag پایدار متوقف میشود و هیچ نام پایداری ساخته نمیشود. |
| Promote رهاشده چه میگذارد؟ | فقط یک Tag موقت promotion-*؛ نام پایدار ساخته نمیشود و Retention آن را بعد از ۳۰ روز پاک میکند. |
| Tag را میتوان جابهجا کرد؟ | خیر. Tagهای v* Write-onceاند؛ اصلاح یعنی نسخهٔ Patch بعدی. |
برای جزئیات کامل مراحل Promote و Finalize به «راهنمای انتشار نسخه در پلتفرم Infynex» مراجعه کنید.