این آزمون هفت درس بخش «مبانی» را میسنجد: از Nginx چیست؟ تا لاگها. جواب هر سؤال همان لحظه با توضیح نشان داده میشود و نتیجهات در همین مرورگر ذخیره میشود (پیشرفت من).
اپ Python تو روی 127.0.0.1:8000 اجرا میشود و میخواهی کاربران از پورت ۸۰ به آن برسند. Nginx در این حالت چه نقشی دارد؟
proxy_pass درخواست را به اپ میرساند و جواب را برمیگرداند.
چرا worker های Nginx با کاربر www-data اجرا میشوند و نه root؟
master با root پورت را باز میکند و worker ها کمدسترسیاند.
apt install nginx با «Errors were encountered while processing: nginx» تمام شد. اولین کار؟
معمولاً بسته نصب شده و فقط شروع سرویس شکست خورده است.
تنظیمات را عوض کردهای و یک ; جا انداختهای. کدام دستور سایت را از دسترس خارج میکند؟
reload با تنظیم خراب رد میشود و قبلی میماند؛ restart خاموش میکند و بالا نمیآید.
بعد از چند start و restart پشت سر هم، systemctl start nginx پیام «Start request repeated too quickly» میدهد. چه میکنی؟
systemd جلوی شروعهای پیاپی را موقتاً میگیرد.
یک فایل سایت در /etc/nginx/sites-available نوشتهای، nginx -t و reload موفق بودند ولی هیچ تغییری نمیبینی. محتملترین علت؟
Nginx فقط sites-enabled را include میکند.
nginx -t فقط یک [warn] میدهد: server name "/var/www/shop" has suspicious symbols. محتملترین علت؟
هشدارها را جدی بگیر؛ این یکی بدون خطا سایت را خراب میکند.
در http یک add_header گذاشتهای و در یک location یک add_header دیگر. پاسخ آن location کدام هدرها را دارد؟
هدرهای مشترک را در snippet بگذار و هر جا لازم است include کن.
کسی IP سرور را مستقیم در مرورگر زده و پنل وبلاگ را دیده؛ هیچ server block ای default_server ندارد. چرا؟
catch-all: listen 80 default_server; server_name _; return 444;
server_name *.shop.test با کدام جور نمیشود؟
wildcard ابتدایی فقط زیردامنهها را میگیرد.
میخواهی www.shop.test با حفظ مسیر و query string به shop.test برود. کدام؟
$request_uri مسیر و query کامل را نگه میدارد.
هم location /images/ هست و هم location ~* \.png$. درخواست /images/a.png به کدام میرسد؟
برای اینکه /images/ برنده باشد: location ^~ /images/.
دو regex داری که هر دو با /admin/app.js جور میشوند. کدام برنده است؟
regex خاصتر را بالاتر بنویس.
location /files { alias /srv/files/; } چه مشکلی دارد؟
location /files/ { alias /srv/files/; } یا root.
در location /static/ { root /srv/web; } درخواست /static/app.css کدام فایل را میخواند؟
root کل مسیر را به انتها اضافه میکند.
در یک SPA، رفرش روی /dashboard ۴۰۴ میدهد. تنظیم درست location / چیست؟
و برای /assets/ جدا try_files $uri =404; تا داراییهای گمشده HTML برنگردانند.
صفحهی ۴۰۴ سفارشی را در آخر try_files گذاشتهای (try_files $uri /404.html). مشکل؟
try_files ... =404; و error_page 404 /404.html;
root سایت روی پوشهی پروژه (با .git و .env) است. بهترین کار؟
اسکنرهای خودکار هر روز دنبال /.env و /.git/config میگردند.
کدام دستور ده IP پرتکرار را از access.log (قالب combined) درمیآورد؟
uniq -c فقط تکرارهای پشت سر هم را میشمارد؛ sort قبلش لازم است.
میخواهی زمان پاسخ هر درخواست و سهم اپ پشتی را در لاگ ببینی. چه میکنی؟
log_format فقط در context http مجاز است.
لاگ را با mv چرخاندی و Nginx هنوز در فایل .1 مینویسد. راهحل؟
Nginx فایل را با inode باز نگه میدارد؛ USR1 آن را با اسم دوباره باز میکند.
tail -f access.log بعد از چرخش شبانهی logrotate چیزی نشان نمیدهد. چه بزنی؟
tail -F اسم فایل را دنبال میکند، نه inode را.
روی یک سرور (یا ماشین آزمایشی) اینها را با Nginx بساز و با یک اسکریپت آزمایش ثابت کن همه درستاند:
۱. catch-all: هر دامنهی ناشناخته (یا IP خالی) اتصالش بسته شود (444).
۲. docs.test: یک سایت چندصفحهای؛ آدرسهای بدون .html کار کنند (/guide ← guide.html)، صفحهی ۴۰۴ سفارشی با کد ۴۰۴، و فایلهای مخفی (.env) بسته باشند.
۳. app.test: یک SPA؛ هر مسیر صفحه به index.html برسد ولی /assets/ گمشده ۴۰۴ واقعی بدهد؛ و www.app.test با 301 به app.test (با حفظ مسیر) برود.
۴. لاگ: هر سایت access log جدای خودش را داشته باشد.
۵. اسکریپت آزمایش برای هر قانون یک درخواست بفرستد و PASS یا FAIL چاپ کند.
راهنمایی
دامنهها را در /etc/hosts به 127.0.0.1 بفرست. برای catch-all listen 80 default_server; server_name _; return 444; (و سایت پیشفرض Ubuntu را خاموش کن). برای docs.test: try_files $uri $uri.html $uri/ =404; و error_page 404 /404.html; و location ~ /\.(?!well-known) { deny all; }. برای app.test: location /assets/ { try_files $uri =404; } و location / { try_files $uri $uri/ /index.html; }. در اسکریپت، یک تابع check URL کد-مورد-انتظار بنویس که با curl -s -o /dev/null -w '%{http_code}' کد را بگیرد (برای 444، curl کد 000 میدهد).
راهحل
export LXC=/root/challengerm -rf "$LXC" && mkdir -p "$LXC"rm -f /etc/nginx/sites-enabled/*grep -v '\.test$' /etc/hosts > /tmp/hosts.lx; cat /tmp/hosts.lx > /etc/hostsecho '127.0.0.1 docs.test app.test www.app.test' >> /etc/hosts
mkdir -p /var/www/docs.test /var/www/app.test/assetsecho '<h1>Docs home</h1>' > /var/www/docs.test/index.htmlecho '<h1>Guide</h1>' > /var/www/docs.test/guide.htmlecho '<h1>Not found (docs)</h1>' > /var/www/docs.test/404.htmlecho 'SECRET=1' > /var/www/docs.test/.envecho '<!doctype html><div id="app">SPA</div><script src="/assets/app.js"></script>' > /var/www/app.test/index.htmlecho 'console.log("app")' > /var/www/app.test/assets/app.js
cat > /etc/nginx/sites-available/00-catchall <<'EOF'server { listen 80 default_server; server_name _; return 444;}EOFcat > /etc/nginx/sites-available/docs.test <<'EOF'server { listen 80; server_name docs.test; root /var/www/docs.test; index index.html; access_log /var/log/nginx/docs.access.log;
location ~ /\.(?!well-known) { deny all; } location / { try_files $uri $uri.html $uri/ =404; } error_page 404 /404.html;}EOFcat > /etc/nginx/sites-available/app.test <<'EOF'server { listen 80; server_name www.app.test; return 301 http://app.test$request_uri;}server { listen 80; server_name app.test; root /var/www/app.test; index index.html; access_log /var/log/nginx/app.access.log;
location /assets/ { try_files $uri =404; } location / { try_files $uri $uri/ /index.html; }}EOFfor s in 00-catchall docs.test app.test; do ln -s /etc/nginx/sites-available/$s /etc/nginx/sites-enabled/; donenginx -t 2>&1 | tail -n 1 && systemctl reload nginxsleep 1
cat > "$LXC/check.sh" <<'EOF'#!/usr/bin/env bash# check.sh: one request per rule, PASS/FAILpass=0 fail=0check() { local url=$1 want=$2 got got=$(curl -s -o /dev/null -w '%{http_code}' "$url") if [[ $got == "$want" ]]; then echo "PASS $want $url"; pass=$((pass + 1)) else echo "FAIL want $want got $got $url"; fail=$((fail + 1)) fi}check http://127.0.0.1/ 000check http://unknown.test/ 000check http://docs.test/ 200check http://docs.test/guide 200check http://docs.test/nothing 404check http://docs.test/.env 403check http://app.test/settings/profile 200check http://app.test/assets/app.js 200check http://app.test/assets/old.js 404check 'http://www.app.test/a?b=1' 301echo "redirect target: $(curl -s -o /dev/null -w '%{redirect_url}' 'http://www.app.test/a?b=1')"echo "custom 404 body: $(curl -s http://docs.test/nothing)"echo "docs log lines: $(wc -l < /var/log/nginx/docs.access.log), app log lines: $(wc -l < /var/log/nginx/app.access.log)"echo "result: $pass passed, $fail failed"(( fail == 0 ))EOFchmod +x "$LXC/check.sh""$LXC/check.sh"nginx: configuration file /etc/nginx/nginx.conf test is successfulPASS 000 http://127.0.0.1/PASS 000 http://unknown.test/PASS 200 http://docs.test/PASS 200 http://docs.test/guidePASS 404 http://docs.test/nothingPASS 403 http://docs.test/.envPASS 200 http://app.test/settings/profilePASS 200 http://app.test/assets/app.jsPASS 404 http://app.test/assets/old.jsPASS 301 http://www.app.test/a?b=1redirect target: http://app.test/a?b=1custom 404 body: <h1>Not found (docs)</h1>docs log lines: 5, app log lines: 3result: 10 passed, 0 failedunknown.test در /etc/hosts نیست، پس curl اصلاً به سرور نرسید و کد 000 داد؛ برای آزمایش واقعی catch-all، درخواست مستقیم به IP (127.0.0.1) هم هست که به سرور میرسد و با 444 بسته میشود (باز هم 000، چون پاسخی نیامد). همهی قانونها با یک درخواست ثابت شدند و هر سایت لاگ خودش را دارد.