محتوای آموزشی وبلاگ نتآرام
کانتینر را با Docker در حالت بدون دسترسی root اجرا کردهاید، اما هنگام انتشار پورت ۸۰ پیام
cannot expose privileged port یا bind: permission denied میبینید. معمولاً برنامه حتی به مرحله دریافت درخواست نمیرسد، چون بخش انتشار پورت روی میزبان اجازه گرفتن آن پورت را ندارد.راهکار این نیست که همه کانتینرها را با دسترسی بیشتر اجرا کنیم. در این راهنما ابتدا خطای مجوز را از پورت اشغالشده جدا میکنیم، سپس یک پورت محلی بالاتر را به کانتینر میدهیم و اگر سایت باید با پورت معمول باز شود، Nginx موجود روی سرور را به آن متصل میکنیم.
مثال برای لینوکس و Docker Compose روی سرور مجازی ایران یا سرور اختصاصی ایران است. فرض میکنیم برنامه داخل کانتینر روی پورت ۸۰ گوش میدهد و یک Nginx روی میزبان قابل تنظیم است. اگر برنامه شما پورت دیگری دارد، همان عدد واقعی را جایگزین کنید. پیش از بازسازی کانتینر یا تغییر Nginx، از تنظیمات نسخه پشتیبان بگیرید و مطمئن شوید داده مهم داخل لایه موقت کانتینر تنها نسخه موجود نیست.
۱. متن خطا و نوع Docker را مشخص کنید
در پوشه پروژه، با همان حسابی که Docker بدون root را اجرا میکند، این دستورها را بزنید:
کد / دستورdocker context show
docker info --format '{{json .SecurityOptions}}'
docker compose ps
در خروجی اطلاعات امنیتی، وجود
rootless یکی از نشانههای اجرای daemon بدون root است. docker context show نشان میدهد CLI به کدام context وصل است؛ اگر context یا متغیر DOCKER_HOST به daemon دیگری اشاره کند، نتیجه متعلق به همان مقصد است. اگر اتصال به socket از ابتدا شکست میخورد، راهنمای رفع دسترسی به Docker socket را جداگانه ببینید.حالا گزارش سرویس را بخوانید. بهجای
کد / دستورweb نام سرویس واقعی داخل Compose را بگذارید:docker compose logs --tail=100 web
خطایی که موقع
docker compose up در ترمینال نمایش داده شده نیز مهم است؛ اگر کانتینر اصلاً ساخته نشده باشد، ممکن است گزارش برنامه خالی باشد. عبارت cannot expose privileged port همراه با permission denied به مجوز گرفتن پورت میزبان اشاره میکند. در مقابل، address already in use یا port is already allocated معمولاً یعنی پورت قبلاً توسط برنامه دیگری گرفته شده است.۲. چه چیزی پورت را گرفته است؟
کد / دستورsudo ss -ltnp
در ستون آدرس محلی، پورتهای ۸۰، ۴۴۳ و پورت جایگزین مورد نظرتان، مثلاً ۸۰۸۰، را ببینید. دستور فقط پورتهای TCP در حال گوشدادن را نمایش میدهد و به کمک sudo معمولاً نام پردازش را هم نشان میدهد. اگر ۸۰ از قبل در اختیار Nginx است، لازم نیست آن را برای کانتینر متوقف کنید؛ میتوانید Nginx را واسطه قرار دهید. اگر ۸۰۸۰ هم اشغال است، یک پورت آزاد دیگر انتخاب کنید و همان عدد را در تمام مثالهای بعدی یکسان بگذارید.
تنظیم مرز پورتهای بدون مجوز را نیز در میزبان ببینید:
کد / دستورsysctl net.ipv4.ip_unprivileged_port_start
روی بسیاری از سیستمها مقدار ۱۰۲۴ است؛ در این حالت پورتهای پایینتر معمولاً به مجوز ویژه نیاز دارند. مقدار میتواند متفاوت باشد و طبق مستندات هسته، به فضای نام شبکه وابسته است. اگر پیام دقیق خطا به پورت دیگری یا مرحله داخل کانتینر اشاره میکند، صرفاً از روی این عدد نتیجه نگیرید.
۳. پورت کانتینر را به یک پورت محلی بالاتر وصل کنید
ابتدا سرویسهای پروژه و تنظیم فعلی را ببینید:
کد / دستورdocker compose config --services
docker compose config
خروجی دستور دوم ممکن است مقدار متغیرها و اطلاعات حساس را نشان دهد؛ آن را بدون حذف اطلاعات محرمانه منتشر نکنید. فایل Compose پروژه را باز کنید و فقط نگاشت پورت سرویس مورد نظر را مطابق پورت واقعی برنامه تغییر دهید. در این نمونه نام سرویس
کد / دستورweb و پورت داخل کانتینر ۸۰ است:services:
web:
ports:
- '127.0.0.1:8080:80'
این قطعه را به سرویس موجود اضافه یا در همان بخش جایگزین نگاشت قبلی کنید؛ سایر تنظیمات سرویس مانند image، volume و environment را حذف نکنید. عدد اول پورت میزبان و عدد دوم پورت داخل کانتینر است. نشانی
127.0.0.1 برای دسترسی محلی میزبان در نظر گرفته شده است، نه انتشار مستقیم روی همه رابطها. در نسخههای قدیمی Docker ممکن است محدودیتهای امنیتی انتشار localhost متفاوت باشد؛ نسخه و رفتار شبکه خود را با مستندات انتشار پورت تطبیق و از شبکه بیرونی نیز آزمایش کنید.اگر داخل کانتینر برنامه روی ۸۰۸۰ گوش میدهد، بخش آخر نیز باید ۸۰۸۰ باشد. اگر سرویس از
network_mode: host استفاده میکند، نگاشت ports این روش معمول را ندارد؛ ابتدا معماری شبکه آن را جداگانه بررسی کنید. این تنظیم برای سرویس HTTP است و برای پروتکلهای دیگر نیاز به راهکار مناسب همان پروتکل دارد.۴. تغییر را اعمال و مسیر داخلی را آزمایش کنید
قبل از اعمال، از فایل Compose و دادههای لازم نسخه قابل بازیابی داشته باشید. این دستور ممکن است کانتینر همان سرویس را متوقف و با تنظیم جدید دوباره بسازد؛ فایلهای ذخیرهشده فقط در لایه نوشتنی کانتینر میتوانند از دست بروند. گزینه
کد / دستور--no-deps از شروع سرویسهای وابسته با این دستور جلوگیری میکند؛ از سالم و آمادهبودن وابستگیها مطمئن شوید:docker compose up -d --no-deps web
docker compose ps
docker compose port web 80
خروجی آخر در مثال باید نگاشت میزبان و پورت انتخابشده را نشان دهد. اگر سرویس شما چند کانتینر یا پورت متفاوت دارد، همان مورد درست را بررسی کنید. سپس از خود میزبان درخواست بدهید:
کد / دستورcurl -I http://127.0.0.1:8080/
یک پاسخ HTTP مانند ۲۰۰ یا تغییر مسیر مورد انتظار برنامه یعنی دستکم اتصال HTTP برقرار شده است؛ اگر برنامه درخواست HEAD را نمیپذیرد، با
curl -i یک GET انجام دهید. Connection refused میتواند به اجرا نبودن سرویس، نگاشت اشتباه یا گوشندادن برنامه روی نشانی مناسب داخل کانتینر مربوط باشد. گزارش همان سرویس و پورت واقعی برنامه را دوباره ببینید. خطای HTTP ۵۰۰ نشان میدهد درخواست به برنامه رسیده، اما خود برنامه خطا داده است.۵. اتصال Nginx به پورت محلی
اگر Nginx میزبان از قبل روی پورت ۸۰ پاسخ میدهد، نباید یک سرویس دوم را بیهدف روی همان پورت راه بیندازید. ابتدا تنظیمات موجود و نام دامنه خود را مشخص کنید:
کد / دستورsudo nginx -T
این خروجی ممکن است حاوی اطلاعات حساس باشد. نمونه زیر یک بلوک HTTP مستقل برای
کد / دستورexample.com است؛ فقط اگر با سایتهای فعلی تداخل ندارد، در فایل مناسب نصب خود اضافه کنید. در یک سایت موجود ممکن است لازم باشد فقط location مربوط را با تنظیم فعلی ادغام کنید. دامنه و پورت را با مقادیر خود جایگزین کنید:server {
listen 80;
server_name example.com;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
proxy_pass درخواست را به سرویس محلی میفرستد و هدرها اطلاعاتی درباره دامنه و مسیر اتصال به برنامه میدهند. برنامه نباید کورکورانه به هدرهای ارسالی از هر منبع اعتماد کند؛ تنظیم پراکسی مورد اعتماد آن را مطابق مستنداتش انجام دهید. این مثال بهتنهایی HTTPS، گواهی یا تنظیم ویژه WebSocket نمیسازد. اگر سایت HTTPS دارد، تنظیم معتبر پورت ۴۴۳ و گواهی موجود را حفظ و محل پراکسی را متناسب با همان سایت اصلاح کنید.پیش از اعمال تنظیم جدید:
کد / دستورsudo nginx -t
اگر آزمون خطا داد، فایل و خط گزارششده را اصلاح کنید و سرویس را reload نکنید. پس از موفقیت آزمون و اطمینان از اینکه همان Nginx سرویس وب شماست:
کد / دستورsudo systemctl reload nginx
نام واحد در برخی نصبها متفاوت است. ابتدا مسیر محلی را با دامنه نمونه خود بیازمایید:
کد / دستورcurl -I -H 'Host: example.com' http://127.0.0.1/
سپس از یک اینترنت بیرون سرور، دامنه واقعی را با HTTP یا HTTPS معمول خود باز کنید. اگر پاسخ محلی درست است اما بیرون باز نمیشود، DNS، فایروال، پورتهای عمومی مجاز و گواهی را بررسی کنید. اگر Nginx خطای ۵۰۲ میدهد، اول
curl به ۸۰۸۰ و گزارش برنامه و Nginx را بخوانید؛ بازکردن پورت ۸۰۸۰ به تمام اینترنت معمولاً لازم نیست.۶. اگر پورت پایین باید مستقیم در اختیار Rootless باشد
Docker راههای دیگری از جمله تنظیم مجوز RootlessKit یا آستانه پورتهای بدون مجوز را توضیح میدهد. اینها تصمیم سطح سیستماند: پایینآوردن آستانه میتواند اجازه گرفتن چند پورت را به حسابهای بدون امتیاز بدهد و تغییر قابلیت فایل نیز روی استفادهکنندگان همان برنامه اثر میگذارد. قبل از انتخاب، کاربران دیگر سرور، محدوده اثر، نسخه Docker و روش برگشت را بررسی کنید. برای یک سایت معمول، نگاشت محلی و پراکسی موجود اغلب کنترل روشنتری دارند.
در پایان پس از آزمون بیرونی، با
docker compose ps و لاگ برنامه مطمئن شوید کانتینر پایدار مانده است. پس از یک راهاندازی مجدد کنترلشده در زمان مناسب، دوباره سرویس و پراکسی را امتحان کنید. اگر بازگشت لازم شد، تنظیم قبلی Compose و Nginx را از نسخه ثبتشده برگردانید، صحت Nginx را با nginx -t بسنجید و سپس تغییر را با همان روش محدود اعمال کنید.اشتراکگذاری:
لینکدینتلگرامایکس
مقاله قبلی
مقاله قبلی وجود ندارد.
مقاله بعدی
دیدگاهها (۰)
دیدگاههای تاییدشده و پاسخهای تیم نتآرام
