پشت صحنهٔ DNF و RPM؛ از مخزن تا بستهٔ نصبشده
فهرست نوشته
وقتی روی AlmaLinux یا Rocky Linux فرمان dnf install nginx را اجرا میکنیم، چه کسی نسخه را انتخاب میکند؟ وابستگیها از کجا میآیند؟ امضا روی مخزن است یا خود بسته؟ و سیستم از کجا میداند فایلهای نصبشده متعلق به کدام برنامهاند؟
این نوشته مسیر یک بسته را از متادیتای مخزن تا تراکنش نصب دنبال میکند؛ معادل همان بررسی APT و dpkg، این بار برای خانوادهٔ Enterprise Linux. نمونهها برای AlmaLinux ۹ و Rocky Linux ۹ با DNF 4 نوشته شدهاند. فرمانهای آموزشی روی سرور واقعی اجرا نشدهاند؛ برای نسخههای دیگر، نسخهٔ ابزار و راهنمای همان انتشار را بررسی کنید.
۱. تقسیم کار میان DNF و RPM#
| لایه | مسئولیت |
|---|---|
| DNF | خواندن مخازن، انتخاب مجموعهٔ سازگار، دریافت بستهها و مدیریت درخواست کاربر |
| RPM | بررسی و نصب بستهها، اجرای scriptletها و ثبت اطلاعات نصب |
| مخزن | ارائهٔ فایلهای RPM و متادیتای قابلخواندن برای DNF |
.repo configuration --> repository metadata --> dependency solver
|
download RPMs
|
RPM transaction
|
files + local rpmdb
RPM وابستگیهای بسته را میشناسد و میتواند نبودن آنها را گزارش کند؛ اما rpm -Uvh برای یافتن وابستگی گمشده سراغ مخازن اینترنتی نمیرود. DNF این انتخاب و دریافت را انجام میدهد. راهنمای رسمی RHEL نیز مدیریت روزمرهٔ نرمافزار را با DNF توضیح میدهد. مدیریت نرمافزار در RHEL ۹
۲. داخل فایل RPM چه خبر است؟#
یک RPM فقط آرشیو فایل نیست: اطلاعات نام و نسخه، معماری، روابط وابستگی، فهرست فایلها، scriptletها و دادههای مربوط به امضا را هم حمل میکند. فایل SPEC دستور ساخت بسته است؛ خود SPEC را با باینری RPM یکی نگیرید. Source RPM نیز ورودیها و دستور ساخت را برای بازسازی بسته نگه میدارد.
نامی مانند زیر را در نظر بگیرید؛ مثال ساختگی است:
company-agent-2.4-3.el9.x86_64.rpm
2.4 نسخهٔ برنامه و 3.el9 مقدار Release بسته است. معماری noarch برای محتوای مستقل از معماری استفاده میشود. برچسب el9 بهتنهایی گواه سازگاری نیست؛ وابستگیها و آزمون روی مقصد تعیینکنندهاند.
برای یک فایل واقعی، بدون نصب:
rpm -qpi ./company-agent-2.4-3.el9.x86_64.rpm
rpm -qpl ./company-agent-2.4-3.el9.x86_64.rpm
rpm -qp --requires ./company-agent-2.4-3.el9.x86_64.rpm
rpm -qp --scripts ./company-agent-2.4-3.el9.x86_64.rpm
rpmkeys --checksig --verbose ./company-agent-2.4-3.el9.x86_64.rpm
گزینهٔ -p پرسوجو را روی فایل بسته انجام میدهد. در مقابل، rpm -qi bash اطلاعات بستهٔ نصبشده را میخواند. بررسی امضا زمانی معنی اصالت دارد که کلید متناظر از مسیر مورداعتماد آمده باشد. ابزارها و ساختار بسته در راهنمای RPM
۳. DNF مخزن را از کجا میشناسد؟#
تنظیم عمومی در /etc/dnf/dnf.conf و تعریف مخازن معمولاً در فایلهای /etc/yum.repos.d/*.repo است. هر بخش شناسهٔ مستقلی دارد:
[company-tools]
name=Company tools for EL9
baseurl=https://rpm.example.org/el9/$basearch/releases/v1/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-company
sslverify=1
این نمونه برای مخزن داخلی با RPMهای امضاشده و متادیتای امضاشده است؛ به مخازن رسمی کپی نکنید مگر واقعاً امضای متادیتا داشته باشند. $basearch برای میزبان x86 معمولاً x86_64 میشود؛ $releasever نیز متغیر انتشار است و مقدارش را نباید از روی نام فایل ISO حدس زد.
برای مشاهدهٔ تنظیم مؤثر:
cat /etc/os-release
dnf --version
dnf repolist --enabled
dnf repoinfo
در AlmaLinux ۹، BaseOS و AppStream فعالاند و CRB مجموعهای تکمیلی است. مخزن EPEL پروژهٔ جداگانهای دارد. مخازن AlmaLinux و Rocky را صرفاً به دلیل سازگاری خانواده با هم مخلوط نکنید؛ تعریف رسمی هر توزیع را مبنا بگذارید. مخازن AlmaLinux
۴. ساختار مخزن: repodata در کنار بستهها#
چیدمان بستهها انعطافپذیر است؛ این یک نمونه است:
repository/
├── Packages/
│ └── company-agent-2.4-3.el9.x86_64.rpm
└── repodata/
├── repomd.xml
├── repomd.xml.asc
├── <checksum>-primary.xml.gz
├── <checksum>-filelists.xml.gz
└── <checksum>-other.xml.gz
repomd.xml نشانی و هش فایلهای متادیتا را معرفی میکند. primary اطلاعات اصلی بستهها و مسیر دریافتشان را دارد؛ filelists فهرست فایلها را توصیف میکند؛ other میتواند changelogها را حمل کند. قالب فشرده و فایلهای اضافی بسته به ابزار و انتشار متفاوتاند.
متادیتای updateinfo برای advisoryها و اطلاعات اصلاحیهها اهمیت دارد. در مخازن modular، متادیتای module نیز در انتخاب محتوا مؤثر است. ساخت دوبارهٔ فهرست صرفاً از روی فایلهای RPM الزاماً این اطلاعات را بازسازی نمیکند. راهنمای تولید متادیتا با createrepo_c
۵. makecache با upgrade فرق دارد#
اگر از APT آمدهاید، این تفاوت مهم است: dnf update نام دیگر upgrade است و بستههای نصبشده را ارتقا میدهد؛ معادل apt update نیست.
برای تازهکردن متادیتا:
sudo dnf --refresh makecache
برای دیدن بهروزرسانیهای موجود:
dnf check-update
check-update وقتی بهروزرسانی پیدا کند کد خروج 100 میدهد؛ در اسکریپت آن را با شکست عمومی یکی نگیرید. DNF هنگام نیاز نیز کش را طبق سیاست انقضا تازه میکند. وجود کش قدیمی میتواند در تشخیص نسخه یا خطای 404 نقش داشته باشد. مرجع فرمانهای DNF
۶. امضای بسته و امضای متادیتا دو بررسیاند#
در خانوادهٔ RPM، خود فایل بسته میتواند امضای OpenPGP داشته باشد. برای مخزن نمونهٔ بالا دو مسیر داریم:
metadata key --> signature on repomd.xml --> index checksums --> RPM checksum
package key --> signature on downloaded RPM
HTTPS --> authenticated transport to repository server
gpgcheck=1 بررسی امضای بستههای دریافتی از مخزن را فعال میکند. repo_gpgcheck=1 برای بررسی امضای متادیتاست و به فایل امضای متناظر نیاز دارد. فعالبودن یکی به معنی فعالبودن دیگری نیست. DNF کلیدهای بررسی متادیتا را جدا از کلیدهای بستهها و به تفکیک مخزن نگه میدارد. گزینههای امنیتی DNF
HTTPS و امضای RPM پرسشهای متفاوتی را پاسخ میدهند: «به کدام سرور وصل شدم؟» و «چه کلیدی بسته را امضا کرده؟». امضای معتبر نیز کیفیت برنامه یا نبودن کد مخرب نزد ناشر را تضمین نمیکند.
۷. حل وابستگی بر اساس قابلیتها#
| رابطهٔ RPM | کاربرد |
|---|---|
Requires | قابلیت لازم برای نصب و کار بسته |
Provides | قابلیت ارائهشده توسط بسته |
Conflicts | ناسازگاری با بسته یا قابلیت دیگر |
Obsoletes | معرفی جایگزینی بسته در فرایند ارتقا |
Recommends و Supplements | وابستگیهای ضعیف با رفتار وابسته به سیاست DNF |
وابستگی همیشه نام یک بسته نیست؛ ممکن است یک مسیر فایل یا قابلیت کتابخانه باشد. برای نمونه، نیاز به یک shared library را بستهای با Provides متناظر برآورده میکند.
dnf info nginx
dnf list --showduplicates nginx
dnf repoquery --requires --resolve nginx
dnf provides '/usr/bin/curl'
sudo dnf --assumeno install nginx
خروجی آخر برنامهٔ تراکنش را نشان میدهد و پاسخ نصب را منفی میکند؛ آزمون اجرای scriptletها نیست. در EL۹، streamهای فعال AppStream نیز ممکن است نسخههای قابلانتخاب را محدود کنند. همیشه نسخه، معماری و repo مبدأ را در برنامهٔ نصب بخوانید. پیداکردن و نصب نرمافزار در RHEL
۸. نصب فایل محلی و سیاست امضای آن#
sudo dnf --setopt=localpkg_gpgcheck=1 install ./company-agent-2.4-3.el9.x86_64.rpm
DNF میتواند وابستگیها را از مخازن فعال بگیرد. در این مثال بررسی امضای فایل محلی را هم صریحاً فعال کردهایم؛ صرف وجود gpgcheck=1 در یک بخش repo را برای فایل محلی کافی ندانید. کلید مورداعتماد بسته باید از قبل وارد شده باشد. گزینهٔ localpkg_gpgcheck
rpm -Uvh ./package.rpm حل و دانلود وابستگی از مخازن را انجام نمیدهد. رفع خطا با --nodeps علت ناسازگاری را حل نمیکند؛ باید بستهٔ مناسب مقصد و وابستگیهای معتبر را پیدا کرد.
۹. تراکنش نصب و scriptletها#
RPM علاوه بر فایلها، میتواند کد بسته را در مراحل نصب و حذف اجرا کند:
| مرحله | کاربرد نمونه |
|---|---|
%pretrans | آمادهسازی پیش از تغییرات تراکنش |
%pre | اقدام پیش از نصب بسته |
%post | اقدام پس از نصب فایلها |
%preun و %postun | کارهای مربوط به حذف نسخهٔ بسته |
%posttrans | کارهای انتهای تراکنش |
triggerها و file triggerها نیز برای پردازش تغییرات مشترک کاربرد دارند. ارتقا فقط «اجرای post نسخهٔ جدید» نیست؛ نسخهٔ قدیم هم مراحل مربوط به حذف را دارد. بستهها میتوانند هنگام نصب با دسترسی مدیر کد اجرا کنند. scriptletها و triggerها
واژهٔ تراکنش به معنی rollback کامل فایلها، سرویسها و دادههای برنامه نیست. شکست scriptlet ممکن است پس از بخشی از تغییرات رخ دهد؛ پیام دقیق و وضعیت برنامه را بررسی کنید.
۱۰. تنظیمات هنگام ارتقا: rpmnew و rpmsave#
رفتار فایل تنظیمات به علامتگذاری آن در بسته بستگی دارد. در حالت تعارض تغییر محلی با نسخهٔ تازه، %config(noreplace) معمولاً فایل محلی را نگه میدارد و نسخهٔ جدید را با پسوند .rpmnew میگذارد؛ %config میتواند نسخهٔ محلی را به .rpmsave منتقل و نسخهٔ بسته را نصب کند.
هر فایل زیر /etc الزاماً همین رفتار را ندارد. پس از ارتقا، فایلهای تازه را مقایسه و تغییرات لازم را ادغام کنید؛ وجود .rpmnew یعنی تنظیمات جدید الزاماً فعال نشدهاند. سیاست config و noreplace در راهنمای RPM
۱۱. وضعیت نصب را از کش جدا کنیم#
rpm -q bash
rpm -ql bash
rpm -qf /usr/bin/bash
rpm -qc bash
rpm -V bash
rpm --eval '%{_dbpath}'
dnf history
-qf مالک ثبتشدهٔ یک فایل را میپرسد و -V ویژگیهای فعلی فایلها را با اطلاعات بسته مقایسه میکند. تغییر یک فایل تنظیمات ممکن است عمدی باشد؛ هر اختلاف معادل نفوذ نیست. مسیر rpmdb به نسخه و توزیع بستگی دارد؛ با macro بالا آن را ببینید و مسیر ثابتی را فرض نکنید.
کش معمول DNF زیر /var/cache/dnf/ است؛ rpmdb و تاریخچهٔ DNF اطلاعات متفاوتی دارند. پاککردن کش، حذف بستههای نصبشده نیست. برای بررسی خطا نیز لاگهای موجود زیر /var/log/dnf* و پیام تراکنش مفیدند.
۱۲. ارتقا، حذف و بازگشت#
| درخواست | فرمان نمونه |
|---|---|
| تازهکردن متادیتا | dnf --refresh makecache |
| ارتقا در مخازن فعلی | dnf upgrade |
| هماهنگی نسخههای نصب با مخازن فعلی | dnf distro-sync |
| حذف بسته و وابستگیهای غیرلازم طبق سیاست | dnf remove PACKAGE |
| حذف وابستگیهای خودکارِ غیرلازم | dnf autoremove |
| بررسی سازگاری نصب | dnf check |
distro-sync ممکن است downgrade کند؛ ارتقای major سیستمعامل محسوب نمیشود. پیش از حذف یا هماهنگسازی، برنامه را با --assumeno بخوانید. برای حفظ بستهای که مستقلاً لازم است، در DNF 4 از dnf mark install PACKAGE استفاده میشود.
dnf history info ID جزئیات تراکنش را نشان میدهد. history undo به وجود نسخههای قدیمی و امکان حل وابستگیها وابسته است؛ پایگاه دادهٔ برنامه یا تغییر خارجی را برنمیگرداند. برای بازیابی سرویس، backup و طرح بازگشت مستقل لازم است. تاریخچه و تراکنشهای DNF
۱۳. تمرین: یک بسته را بدون نصب دنبال کنید#
افزونهٔ download را ابتدا روی ماشین آزمایش آماده کنید:
sudo dnf install dnf-plugins-core
mkdir -p ~/dnf-lab/nginx
cd ~/dnf-lab/nginx
dnf download nginx
نام دقیق یکی از RPMهای دانلودشده را بهجای PACKAGE.rpm بگذارید:
rpm -qpi ./PACKAGE.rpm
rpm -qpl ./PACKAGE.rpm
rpm -qp --requires ./PACKAGE.rpm
rpm -qp --scripts ./PACKAGE.rpm
rpmkeys --checksig --verbose ./PACKAGE.rpm
اطلاعات را با dnf info nginx مقایسه کنید: نسخه و repo چیست؟ چه نیازهایی قابلیت کتابخانهاند؟ کدام فایل در بستهٔ اصلی است و کدام در زیربسته؟ سپس برنامهٔ نصب را ببینید و مشخص کنید چه وابستگیهایی از قبل روی میزبان بودهاند. این مسیر، تفاوت اطلاعات «موجود در مخزن» با وضعیت «نصبشده روی ماشین» را روشن میکند.