Nginx Reverse Proxy அமைப்பது எப்படி: முழுமையான வழிகாட்டி
Nginx reverse proxy கட்டமைப்பை வரி வரியாக உருவாக்குவது எப்படி என்பதை அறிக. proxy_pass, headers, websockets மற்றும் கோப்பு பதிவேற்றங்கள் தொடர்பான முக்கிய அமைப்புகளை இதில் காணலாம்.
Nginx reverse proxy configuration என்ன செய்கிறது
Nginx reverse proxy என்பது port 80 மற்றும் port 443-க்கு வரும் கோரிக்கைகளை (requests) பெற்று, அவற்றை ஏற்கனவே local port-ல் இயங்கிக்கொண்டிருக்கும் ஒரு application-க்கு அனுப்பி, அதன் பதிலை மீண்டும் browser-க்கு வழங்கும் ஒரு அமைப்பாகும். இதன் கட்டமைப்பு (config) ஒரு ஒற்றை server block-ஐக் கொண்டது, மேலும் இந்த block மிகச் சிறியது. உண்மையான client யார் மற்றும் அவர்கள் எந்த protocol-ஐப் பயன்படுத்தினார்கள் என்பதை உங்கள் application-க்குத் தெரிவிக்கும் ஐந்து அல்லது ஆறு வரிகளில்தான் பெரும்பாலான சிக்கல்கள் உள்ளன.
கீழே கொடுக்கப்பட்டுள்ள அனைத்தும் Ubuntu 24.04-ல், distribution-ல் உள்ள nginx package-ஐப் பயன்படுத்தி புதிதாக உருவாக்கப்பட்டுள்ளன. ஏற்கனவே 127.0.0.1:3000-ல் பதிலளிக்கும் ஒரு application-ஐ அடிப்படையாகக் கொண்டு இது தொடங்குகிறது. நீங்கள் இன்னும் ஒரு proxy-ஐத் தேர்ந்தெடுக்கவில்லை என்றால், Nginx, Caddy மற்றும் Traefik ஒப்பீடு என்ற கட்டுரையை முதலில் படிக்கவும். Nginx-ன் பதில் ஒவ்வொரு வரியாகக் கீழே கொடுக்கப்பட்டுள்ளது.
இந்தக் கட்டமைப்புகளை உங்கள் சொந்த server-ல் இயக்கவும். ஒவ்வொரு மாற்றத்தையும் reload செய்வதற்கு முன் sudo nginx -t மூலம் சோதித்து, அது வெளியிடும் தகவல்களைப் படிக்கவும்.
Ubuntu-வில் Nginx configuration கோப்புகளை எங்கு சேமிக்கிறது
sudo apt update
sudo apt install -y nginx
ls -l /etc/nginx/sites-enabled/முதன்மை கோப்பு /etc/nginx/nginx.conf ஆகும். இது http { } தொகுதிக்குள் உலகளாவிய விருப்பங்களை அமைக்கிறது, பின்னர் /etc/nginx/conf.d/*.conf மற்றும் /etc/nginx/sites-enabled/* ஆகிய இரண்டு கோப்பகங்களை இணைக்கிறது. Ubuntu மற்றும் Debian-ல், நீங்கள் ஒவ்வொரு தளத்திற்கும் /etc/nginx/sites-available/-ல் ஒரு கோப்பை எழுதி, /etc/nginx/sites-enabled/-க்கு ஒரு symlink உருவாக்குவதன் மூலம் அதைச் செயல்படுத்தலாம். Symlink-ஐ நீக்குவது தளத்தை முடக்கும், ஆனால் கோப்பு அப்படியே இருக்கும்.
பின்வரும் இரண்டு directives http சூழலில் மட்டுமே செயல்படும், server தொகுதிக்குள் ஒருபோதும் செயல்படாது: map மற்றும் upstream. இவற்றை /etc/nginx/conf.d/-ன் கீழ் உள்ள தனி கோப்பில் வைக்கவும், ஏனெனில் அந்த கோப்பகம் http மட்டத்தில் சேர்க்கப்பட்டுள்ளது.
இந்த தொகுப்பு default என்ற செயல்படுத்தப்பட்ட தளத்துடன் வருகிறது. இது default_server என்று குறிக்கப்பட்டுள்ளது, அதாவது உங்கள் configuration-ல் எங்கும் server_name-டன் பொருந்தாத எந்தவொரு Host header-ஐக் கொண்ட கோரிக்கைக்கும் இது பதிலளிக்கும். இது செயல்படுத்தப்பட்டிருக்கும் வரை, உங்கள் பெயர்களுடன் பொருந்தாத கோரிக்கைகள் உங்கள் application-க்கு பதிலாக இதற்குச் செல்லும். உங்கள் சொந்த தளம் செயல்படத் தொடங்கியதும் அந்த symlink-ஐ நீக்கிவிடவும்.
sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginxஒரு செயலியை proxy செய்யும் மிகச்சிறிய server block
server {
listen 80;
listen [::]:80;
server_name app.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
}
}இதை /etc/nginx/sites-available/app.example.com எனச் சேமித்து, பின் அதை enable செய்து load செய்யவும்.
sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
curl -sI -H 'Host: app.example.com' http://127.0.0.1/listen 80; என்பது IPv4-ஐயும், listen [::]:80; என்பது IPv6-ஐயும் இணைக்கிறது. இரண்டாவது வரியை நீக்கிவிட்டால், உங்கள் server-க்கு AAAA record-ஐக் காட்டும் DNS (domain name system) lookup-ஐப் பயன்படுத்தும் பார்வையாளர்களுக்கு connection refused என்று வரும், அதே சமயம் IPv4-ல் உள்ள அனைவருக்கும் தளம் சரியாக இயங்கும். உங்களுக்கு வரும் bug report-ல் "எனக்கு வேலை செய்கிறது" என்று குறிப்பிடப்பட்டிருக்கும்.
server_name என்பது browser அனுப்பும் Host header-உடன் ஒப்பிடப்படுகிறது. பல பெயர்களை இடைவெளி விட்டுப் பட்டியலிடலாம். எந்த block-உம் பொருந்தவில்லை என்றால், nginx எந்த block-ஐ default_server ஆகக் கருதுகிறதோ அதையே பயன்படுத்தும், இதனால்தான் ஏற்கனவே இருந்த தளம் நீக்கப்பட்டது.
location / என்பது request path-ன் முன்னொட்டு (prefix) பொருத்தம், / என்பது அனைத்து path-களுக்கும் பொருந்தும். proxy_pass என்பது nginx இணைப்பை ஏற்படுத்தும் முகவரி. செயலியை 127.0.0.1-ல் மட்டும் இணைக்கப்பட்டுள்ளவாறு வைத்திருக்கவும், அப்போதுதான் nginx வழியாக மட்டுமே உள்ளே நுழைய முடியும். செயலி ஒரு container-ல் இயங்கினால், அதை 127.0.0.1:3000:3000 என publish செய்யவும், 3000:3000 என வேண்டாம். ஏனெனில் Docker தனது சொந்த விதிகளை எழுதி ports-ஐ ufw-ஐத் தாண்டி நேரடியாக publish செய்கிறது, எனவே firewall என்ன சொன்னாலும், நேரடியாக publish செய்யப்பட்ட port இணையத்திலிருந்து அணுகக்கூடியதாக இருக்கும்.
curl வரி server-லிருந்தே சரியான Host header-ஐ அனுப்புகிறது, எனவே DNS எங்கும் சுட்டிக்காட்டுவதற்கு முன்பே நீங்கள் இந்த block-ஐச் சோதிக்கலாம்.
கூடுதல் கட்டளைகள் ஏதுமின்றி nginx upstream-க்கு அனுப்பும் தரவுகள்
proxy_pass தானாகவே உங்கள் application-லிருந்து நான்கு விஷயங்களை மறைக்கிறது.
nginx இயல்பாகவே backend-உடன் HTTP/1.0 முறையில் பேசுகிறது மற்றும் Connection: close-ஐ அனுப்புகிறது. இதனால் ஒவ்வொரு கோரிக்கையும் (request) ஒரு புதிய upstream இணைப்பைத் தொடங்குகிறது, மேலும் எந்தவொரு protocol upgrade-ம் சாத்தியமில்லை.
Host header-ன் மதிப்பு proxy_pass-ஆக மாற்றப்படுகிறது, இது 127.0.0.1:3000 ஆகும். Host-ஐக் கொண்டு absolute links-ஐ உருவாக்கும் ஒரு application, சர்வருக்கு வெளியே எவராலும் திறக்க முடியாத இணைப்புகளை உருவாக்குகிறது.
application-க்கு வரும் இணைப்பு nginx-லிருந்து வருவதால், அது client முகவரியாக 127.0.0.1-ஐ மட்டுமே பார்க்கிறது. இதனால் application-க்குள் உள்ள ஒவ்வொரு log வரியும், rate limit-ம் பார்வையாளருக்குப் பதிலாக proxy-யையே பதிவு செய்கின்றன.
உலாவி (browser) HTTPS-ஐப் பயன்படுத்தியதா என்பதை application-ஆல் அறிய முடியாது, ஏனெனில் அது பெறும் இணைப்பு loopback முகவரியில் உள்ள plain HTTP ஆகும்.
இவை அனைத்தையும் சரிசெய்ய நான்கு வரிகள் போதுமானது.
அமைக்க வேண்டிய நான்கு தலைப்புகள் மற்றும் ஒவ்வொன்றும் backend-க்கு எதைக் காட்டுகிறது
location / {
proxy_pass http://127.0.0.1:3000;
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;
}Host என்பது பயனர் உள்ளிட்ட முகவரியைக் கொண்டிருக்கும். $host என்பது கோரிக்கையில் உள்ள பெயர், இதில் port நீக்கப்பட்டு எழுத்துக்கள் சிறியதாக (lowercase) மாற்றப்பட்டிருக்கும். இதை அமைப்பதன் மூலம் உங்கள் application சரியான absolute URL-களை உருவாக்கும்: login-க்கு பிந்தைய redirect அல்லது password reset மின்னஞ்சலில் உள்ள இணைப்பு போன்றவை. இதைத் தவிர்த்தால், அந்த URL-கள் 127.0.0.1:3000-ஐச் சுட்டிக்காட்டும், இதனால் login செய்யும்போது browser இணைப்பை மறுக்கும் முகவரிக்குச் செல்லும். உங்கள் application-க்கு port-ம் தேவைப்பட்டால் (உதாரணமாக 8080-ல் இயங்கினால்), $http_host-ஐப் பயன்படுத்தவும்; இது client அனுப்பிய அதே header-ஐக் கொண்டிருக்கும்.
X-Real-IP ஒரே ஒரு மதிப்பை மட்டுமே கொண்டிருக்கும்: $remote_addr, அதாவது nginx எந்த முகவரியிலிருந்து இணைப்பை ஏற்றுக்கொண்டதோ அந்த முகவரி. Application-கள் தங்கள் சொந்த access logs மற்றும் rate limiting-க்காக இதைப் படிக்கின்றன.
X-Forwarded-For ஒரு பட்டியலைக் கொண்டிருக்கும். $proxy_add_x_forwarded_for என்பது client ஏற்கனவே அந்த header-ல் கொடுத்திருந்த தகவலுடன் $remote_addr-ஐச் சேர்க்கும், எனவே மதிப்பு கமாவால் பிரிக்கப்பட்டு, nginx சேர்த்த பதிவு கடைசியாக இருக்கும். இந்த விவரமே header-ஐ நம்பலாமா என்பதைத் தீர்மானிக்கிறது: ஒரு client தனக்கு விருப்பமான எந்த X-Forwarded-For-ஐயும் அனுப்பலாம், எனவே முதல் பதிப்பைப் படிக்கும் application-க்கு தவறான முகவரி காட்டப்படலாம். Nginx edge server-ஆக இருக்கும்போது, $remote_addr-ஐப் பயன்படுத்தி client-ன் பதிப்பை நீக்கிவிடவும். முன்னால் ஒரு CDN அல்லது மற்றொரு proxy இருக்கும்போது, realip module-லிருந்து set_real_ip_from மற்றும் real_ip_header-ஐப் பயன்படுத்தவும், அப்போது $remote_addr என்பது உண்மையான client முகவரியாக மாறும்.
X-Forwarded-Proto என்பது http அல்லது https-ஐக் கொண்டிருக்கும். Cookies-ஐ Secure என்று குறிக்க வேண்டுமா மற்றும் HTTPS-க்கு redirect செய்ய வேண்டுமா என்பதைத் தீர்மானிக்க frameworks இதைப் படிக்கின்றன. TLS தளத்தில் இதைத் தவிர்த்தால், HTTPS-ஐக் கட்டாயப்படுத்தும் application http-ஐப் பார்க்கும், HTTPS முகவரிக்கு redirect செய்யும், அடுத்த கோரிக்கையை nginx வழியாகப் பெறும், மீண்டும் http-ஐப் பார்க்கும், மீண்டும் redirect செய்யும். Browser முயற்சியைக் கைவிட்டு ERR_TOO_MANY_REDIRECTS-ஐக் காட்டும்.
ஒவ்வொரு location-லும் இந்த நான்கு வரிகளைத் திரும்பத் திரும்ப எழுதுவது அவை மாறுபடுவதற்கு வழிவகுக்கும். அவற்றை ஒரே கோப்பில் வைத்து include செய்யவும்.
# /etc/nginx/snippets/proxy-headers.conf
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;location / {
include snippets/proxy-headers.conf;
proxy_pass http://127.0.0.1:3000;
}இங்கே inheritance-ல் ஒரு சிக்கல் உள்ளது. ஒரு location, அதன் server block-ல் உள்ள proxy_set_header directives-ஐ, அந்த location-ல் எதுவும் வரையறுக்கப்படாத வரை மட்டுமே பெற்றுக்கொள்ளும். location-க்குள் ஒரே ஒரு proxy_set_header-ஐச் சேர்த்தாலும், server மட்டத்தில் வரையறுக்கப்பட்ட அனைத்து header-களும் அந்த location-க்கு நீக்கப்படும். எனவே, அனைத்தையும் ஒரே மட்டத்தில் வைத்திருக்கவும் அல்லது proxy செய்யும் ஒவ்வொரு location-லும் அந்த snippet-ஐ include செய்யவும்.
எனது WebSocket செயலி ஏன் இணைக்கப்பட்டு, பின் துண்டிக்கப்படுகிறது?
இயல்புநிலை அமைப்புகள் upgrade-ஐ அனுமதிப்பதில்லை மற்றும் இயல்புநிலை read timeout, 60 வினாடிகளுக்குப் பிறகு செயலற்ற நிலையில் உள்ள tunnel-ஐ மூடிவிடுகிறது. ஒரு WebSocket, Upgrade: websocket மற்றும் Connection: Upgrade ஆகியவற்றைக் கொண்ட HTTP கோரிக்கையாகத் தொடங்குகிறது. இவை hop-by-hop headers ஆகும்; அதாவது, ஒரு proxy இவற்றை அடுத்த நிலைக்கு அனுப்பாமல், தானே பயன்படுத்திக்கொள்ள வேண்டும். மேலும், HTTP/1.0-ல் upgrade செய்யும் வசதி இல்லை. எனவே, இவை இரண்டையும் கைமுறையாக மீண்டும் சேர்க்க வேண்டும்.
இந்த map, http சூழலில், அதன் சொந்தக் கோப்பில் இருக்க வேண்டும்.
# /etc/nginx/conf.d/websocket.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}அதன்பின் location பகுதி.
location / {
include snippets/proxy-headers.conf;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}இந்த map இரு வகையான traffic-களையும் ஒரே location மூலம் கையாள உதவுகிறது. சாதாரண கோரிக்கையின் போது $http_upgrade காலியாக இருக்கும், எனவே $connection_upgrade என்பது close ஆக மாறும். ஒரு upgrade கோரிக்கையின் போது அது websocket மதிப்பைக் கொண்டிருக்கும், எனவே upstream-க்கு அனுப்பப்படும் header Connection: upgrade ஆக இருக்கும். proxy_set_header Connection "upgrade"; என்பதை நேரடியாகக் குறிப்பிட்டால், அது சாதாரண பக்கக் கோரிக்கைகளிலும் அந்த header-ஐ அனுப்பிவிடும்; சில backends இத்தகைய கோரிக்கைகளுக்கு 400 பிழையைத் தரும்.
proxy_read_timeout என்பதுதான் "செயலி ஏற்றப்படுகிறது, ஆனால் புதுப்பித்தல் நின்றுவிடுகிறது" என்ற புகார்களுக்குக் காரணமாகிறது. இது இயல்பாக 60 வினாடிகளாக இருக்கும். இது இணைப்பின் மொத்த ஆயுட்காலத்தை அல்ல, மாறாக backend-லிருந்து பெறப்படும் இரண்டு தரவுகளுக்கு இடைப்பட்ட நேரத்தையே கணக்கிடுகிறது. 60 வினாடிகளுக்கு எந்தத் தரவும் பரிமாறப்படாத ஒரு WebSocket-ஐ nginx மூடிவிடும்; அப்போது browser console-ல் socket 1006 என்ற குறியீட்டுடன் மூடியதைக் காட்டும். நிமிடத்திற்கு ஒருமுறை heartbeat அனுப்பும் செயலிகள் இதை உணர்வதில்லை. அவ்வாறு செய்யாத செயலிகள் ஒரு நிமிடத்தில் துண்டிக்கப்படும். நேரலை editors மற்றும் dashboards-ல் இது முதலில் வெளிப்படும்; HTTPS-க்கு பின்னால் இயங்கும் n8n instance இதற்கு ஒரு பொதுவான உதாரணம்.
proxy_pass-ல் உள்ள trailing slash ஏன் எனது URL-களை மாற்றுகிறது?
இதற்கான விதி ஒரே வாக்கியம் தான். proxy_pass ஒரு URI-உடன் (uniform resource identifier) முடிந்தால், அது ஒரு வெறும் / ஆக இருந்தாலும், location prefix-உடன் பொருந்தும் request path-ன் பகுதியை nginx நீக்கிவிட்டு, அந்த URI-ஐ அதற்குப் பதிலாக வைக்கும். proxy_pass host மற்றும் port-உடன் நின்றால், request path மாற்றப்படாமல் அப்படியே அனுப்பப்படும்.
location /app/ {
proxy_pass http://127.0.0.1:3000/;
}/app/status-க்கான ஒரு request, backend-க்கு /status ஆகச் சென்றடையும்.
location /app/ {
proxy_pass http://127.0.0.1:3000;
}/app/status-க்கான ஒரு request, backend-க்கு /app/status ஆகச் சென்றடையும்.
உங்களுக்கு எந்த வடிவம் தேவை என்பது அந்த application-ஐப் பொறுத்தது. Base-path அல்லது sub-folder அமைப்பைக் கொண்ட ஒரு application-க்கு இரண்டாவது வடிவம் தேவை, அதில் /app பற்றிய அமைப்பைக் குறிப்பிட வேண்டும். Prefix-களைப் பற்றித் தெரியாத ஒரு application-க்கு முதல் வடிவம் தேவை. முதல் வடிவத்தில் ஒரு சிக்கல் உடனடியாகத் தெரியும்: அந்த application வழங்கும் HTML-ல் /static/main.css போன்ற absolute paths இருக்கும், browser அவற்றை site root-ல் தேடும், எந்த location-உம் பொருந்தாது, அதனால் பக்கம் styling இல்லாமல் காட்சியளிக்கும். Browser network tab-ல் அந்த asset requests 404 என்று வருவதைக் காணலாம். இதற்குத் தீர்வு, அந்த application-ன் சொந்த base-path அமைப்பை மாற்றுவது அல்லது அதே backend-ஐக் குறிக்கும் இரண்டாவது location /static/-ஐப் பயன்படுத்துவது ஆகும்.
ஒரு regex location-ஆல் proxy_pass-ல் URI-ஐக் கொண்டிருக்க முடியாது. sudo nginx -t இந்த configuration-ஐ நிராகரித்து, அதற்கான காரணத்தை இவ்வாறு குறிப்பிடும்: "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block.
ஒவ்வொரு application-க்கும் அதன் சொந்த பெயர், app.example.com, location /-லிருந்து proxy செய்யப்படும்போது, இந்த வகைச் சிக்கல்கள் அனைத்தும் நீங்கிவிடும். DNS records-ஐச் சேர்க்க முடியாத சூழலில் மட்டுமே sub-paths-ஐப் பயன்படுத்துவது பயனுள்ளது.
ஒரே பெயரின் கீழ் ஒன்றுக்கும் மேற்பட்ட backend-களை எவ்வாறு அமைப்பது?
இதற்கு upstream தொகுதியைப் பயன்படுத்த வேண்டும். இது http சூழலுக்கு உரியது என்பதால், அதே கோப்பில் உள்ள server தொகுதிக்கு மேலேயோ அல்லது /etc/nginx/conf.d/-லோ இதை எழுதவும்.
upstream app_backend {
least_conn;
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
keepalive 32;
}அதன்பின், location பகுதியில் அதை இவ்வாறு குறிப்பிட வேண்டும்: proxy_pass http://app_backend;.
இயல்பான முறை round robin ஆகும். least_conn ஒவ்வொரு கோரிக்கையையும் மிகக் குறைந்த active connections கொண்ட backend-க்கு அனுப்பும்; இது சீரற்ற கால அளவு கொண்ட கோரிக்கைகளுக்கு ஏற்றது. ip_hash ஒரு client முகவரியை ஒரு குறிப்பிட்ட backend-உடன் இணைக்கும். உங்கள் application தனது சொந்த நினைவகத்தில் (memory) sessions-ஐ வைத்திருந்தால், உங்களுக்கு ip_hash தேவைப்படும். ஏனெனில், round robin முறையில் கோரிக்கைகள் வெவ்வேறு instances-க்குச் செல்லும்போது, அந்த session-ஐக் கொண்டிராத instance-க்கு கோரிக்கை சென்றால் பயனர் வெளியேற்றப்படுவார் (log out). sessions-ஐ shared storage-க்கு மாற்றுவதே சிறந்த தீர்வாகும்.
max_fails=3 fail_timeout=30s என்பது, 30 வினாடிகளுக்குள் மூன்று முறை தோல்வியடைந்தால், அந்த server-ஐ 30 வினாடிகளுக்குச் செயல்பாட்டிலிருந்து நீக்கிவிடும் என்று பொருள். தொகுதியில் உள்ள அனைத்து server-களும் அந்த நிலையில் இருந்தால், clients-க்கு 502 பிழை கிடைக்கும் மற்றும் error log-ல் no live upstreams while connecting to upstream என்று பதிவாகும்.
keepalive 32 ஒவ்வொரு worker process-க்கும் backend-களுடன் 32 idle connections வரை திறந்து வைத்திருக்கும், இது பெரும்பாலான கோரிக்கைகளில் TCP handshake-ஐத் தவிர்க்கிறது. இது proxy_http_version 1.1 உடன் மட்டுமே வேலை செய்யும் மற்றும் upstream-க்கு Connection: close செல்லக்கூடாது. அதே location-ல் WebSocket map பயன்படுத்தப்பட்டால், empty case-ஐ close என்பதிலிருந்து காலியான string-க்கு மாற்றவும். அப்போதுதான் சாதாரண கோரிக்கைகளில் Connection header இருக்காது மற்றும் pooled connection மீண்டும் பயன்படுத்தப்படும்.
map $http_upgrade $connection_upgrade {
default upgrade;
'' '';
}upstream தொகுதியில் உள்ள பெயர்கள் nginx தொடங்கும்போதே resolve செய்யப்படும். உங்கள் backend ஒரு container ஆக இருந்து, அது restart ஆகும்போது புதிய முகவரியைப் பெற்றால், நீங்கள் reload செய்யும் வரை nginx பழைய முகவரியையே பயன்படுத்தும். Docker network-க்குள், embedded resolver மூலம் lookup-ஐ கோரிக்கை வரும் நேரத்திற்கு (request time) மாற்றலாம்.
resolver 127.0.0.11 valid=10s;
set $backend http://app:3000;
proxy_pass $backend;containers அடிக்கடி தோன்றி மறைவதால் நீங்கள் nginx-ஐத் தொடர்ந்து மாற்ற வேண்டியிருந்தால், container labels-ஐ வாசிக்கும் proxy சிறந்த கருவியாகும். பல Docker Compose apps-க்கு முன்னால் Traefik அதன் routes-களை அந்தந்த containers-லிருந்தே உருவாக்கிக்கொள்ளும்.
ஏன் பதிவேற்றங்கள் 413 Request Entity Too Large பிழையுடன் தோல்வியடைகின்றன?
client_max_body_size இயல்பாக 1 மெகாபைட்டாக அமைக்கப்பட்டுள்ளது. உங்கள் application-க்கு வருவதற்கு முன்பே, பெரிய கோரிக்கைகளை nginx நிராகரித்துவிடும், மேலும் பிழைப் பதிவில் (error log) client intended to send too large body என்று பதிவாகும். server block-ல் அல்லது பதிவேற்றங்கள் நடைபெறும் location-ல் இதன் மதிப்பை அதிகரிக்கவும்.
client_max_body_size 512m;0 என்ற மதிப்பு இந்தச் சோதனையை முழுமையாக முடக்கிவிடும். ஒவ்வொரு application-க்கும் அதன் சொந்த வரம்பு உள்ளது, எனவே இந்த மாற்றத்திற்குப் பிறகும் 413 பிழை ஏற்பட்டால், அது backend-லிருந்து வருகிறது என்று அர்த்தம். அத்தகைய சூழலில், application-ன் சொந்த பதிவேற்ற அமைப்பைச் சரிபார்க்க வேண்டும்.
இயல்பாக, upstream இணைப்பைத் திறப்பதற்கு முன்பே nginx முழு கோரிக்கை உடலையும் (request body) வாசித்துவிடும், பெரிய கோப்புகளை முதலில் வட்டில் உள்ள தற்காலிகக் கோப்பில் எழுதும். இது மெதுவான வாடிக்கையாளர்களிடமிருந்து (slow clients) application-ஐப் பாதுகாக்கிறது, ஏனெனில் backend பதிவேற்றத்தை முழுமையான உள்ளூர் வேகத்தில் பெறுகிறது. மிகப்பொரிய பதிவேற்றங்களுக்கு, நீங்கள் stream முறையைப் பயன்படுத்தலாம்.
proxy_request_buffering off;அப்போது backend கோரிக்கை உடல் வந்து சேரும்போதே அதைப் பெற்றுக்கொள்ளும், மேலும் அதைச் கையாளும் திறன் அதற்கு இருக்க வேண்டும். கோரிக்கை உடல் ஏற்கனவே அனுப்பப்பட்டுவிட்டதால், மற்றொரு upstream-க்கு எதிராக அந்த கோரிக்கையை மீண்டும் முயற்சிக்கும் (retry) திறனை nginx இழந்துவிடும்.
client_body_timeout, இயல்பாக 60 வினாடிகள், முழு பதிவேற்றத்திற்கும் அல்லாமல், கோரிக்கை உடலின் அடுத்தடுத்த வாசிப்புகளுக்கு இடையே பொருந்தும். மெதுவான ஆனால் சீரான பதிவேற்றம் இதைத் தாண்டிச் செயல்படும். ஆனால், பதிவேற்றம் நின்றுவிட்டால் (stalled), அது துண்டிக்கப்படும்.
பதில் இடையகப்படுத்துதல் (Response buffering) மற்றும் நேரலை வெளியீட்டைத் தடுக்கும் அமைப்பு
proxy_buffering இயல்பாகவே செயல்பாட்டில் இருக்கும், இதுவே பொதுவாகத் தேவைப்படும் அமைப்பாகும். உங்கள் application அனுப்பும் பதிலை Nginx மிக வேகமாகப் பெற்று, அதைச் சேமித்து வைத்து, மெதுவான client-க்கு அந்த client-ன் வேகத்திற்கு ஏற்ப வழங்குகிறது. இதனால், மெதுவான download முடியும் வரை காத்திருக்காமல், application worker தனது பணியை விரைவாக முடித்துக்கொள்ள முடியும்.
இது streaming பதில்களைப் பாதிக்கும். Buffer நிரம்பும் வரை, Server-sent events மற்றும் நேரலை log வெளியீடுகள் எதையும் பயனருக்குக் காட்டாது. அந்த குறிப்பிட்ட location-ல் மட்டும் buffering-ஐ அணைக்கவும்.
proxy_buffering off;நீங்கள் application-ஐக் கட்டுப்படுத்துபவர் என்றால், streaming பதில்களுக்கு மட்டும் X-Accel-Buffering: no header-ஐ அனுப்புவது சிறந்த முறையாகும். ஒவ்வொரு பதிலுக்கும் அந்த header-ஐ Nginx வாசித்து, அந்த குறிப்பிட்ட பதிலுக்கு மட்டும் buffering-ஐ நிறுத்திவிடும்; இதனால் மற்ற சாதாரண பக்கங்கள் buffering-ன் நன்மைகளைப் தொடர்ந்து பெறும்.
Error log-ல் upstream sent too big header while reading response header from upstream என்று காட்டினால், response headers ஒரு buffer-க்குள் அடங்கவில்லை என்று பொருள். proxy_buffer_size இயல்பாக ஒரு memory page அளவு, அதாவது platform-ஐப் பொறுத்து 4 அல்லது 8 kilobytes ஆக இருக்கும். நீண்ட cookies அல்லது பெரிய authentication headers இருக்கும்போது இது நிரம்பிவிடும். எனவே, இந்த இரண்டு மதிப்புகளையும் அதிகரிக்கவும்.
proxy_buffer_size 16k;
proxy_buffers 8 16k;இந்த configuration-ல் TLS எங்கே இருக்க வேண்டும்?
Nginx-ல், மேலே உள்ள அனைத்திற்கும் முன்பாக இருக்க வேண்டும். TLS (transport layer security) proxy-ல் முடிவடைகிறது (terminate). Nginx-லிருந்து application-க்கு செல்லும் இணைப்பு, loopback address வழியாக plain HTTP-ஆகவே இருக்கும்; இதனால் நெட்வொர்க்கில் உள்ள மற்றவர்களால் இதை வாசிக்க முடியாது. நான்கு headers-ல் நான்காவதான X-Forwarded-Proto மூலம், பயனர் HTTPS-ஐப் பயன்படுத்தியதை application அறிந்துகொள்கிறது.
Certificate paths-ஐ நீங்களாக எழுத வேண்டாம். DNS record-ஐ server-ஐ நோக்கிச் சுட்டிக்காட்டி, firewall-ஐத் திறந்து, Certbot-ஐ இந்த server block-ஐ மாற்ற அனுமதிக்கவும்: இது ssl_certificate paths-உடன் listen 443 ssl வரியைச் சேர்க்கும், மேலும் port 80-லிருந்து redirect-ஐயும் அமைக்கும். Certbot மூலம் nginx-க்கு Let's Encrypt certificate பெறுதல் என்ற பகுதி, சான்றிதழ் பெறுதல் மற்றும் புதுப்பித்தல் காலஅமைவு (renewal timer) பற்றி விளக்குகிறது.
sudo ufw allow 'Nginx Full'
sudo ufw statusNginx Full என்பது nginx package நிறுவும் ஒரு application profile ஆகும். இது port 80 மற்றும் port 443 ஆகிய இரண்டையும் ஒரே நேரத்தில் திறக்கும். அனைத்து பயனர்களும் HTTPS-க்கு மாற்றப்பட்ட பிறகும், HTTP-01 renewal challenge-க்காக port 80 திறந்தே இருக்க வேண்டும்.
Configuration-ஐ சோதித்து, பின் reload செய்யவும்
sudo nginx -t
sudo systemctl reload nginxnginx -t அனைத்து உள்ளடக்கப்பட்ட (included) கோப்புகளையும் ஆய்வு செய்து, சோதனை வெற்றிகரமாக முடிந்ததா அல்லது பிழை உள்ள கோப்பு மற்றும் வரி எண் என்ன என்பதைத் தெரிவிக்கும். நீங்கள் reload செய்வதற்கு முன் அந்த வெளியீட்டைப் படிக்கவும். தவறான configuration-உடன் reload செய்தால் அது நடைமுறைக்கு வராது: Nginx பழைய configuration-ஐயே தொடர்ந்து இயக்கும், எனவே உங்கள் மாற்றம் எந்த விளைவையும் ஏற்படுத்தாமல் தளம் தொடர்ந்து இயங்கும். systemctl restart சற்று வித்தியாசமாகவும் மோசமாகவும் செயல்படும், ஏனெனில் restart செய்யும்போது இயங்கிக்கொண்டிருக்கும் server முதலில் நிறுத்தப்படும்; எனவே configuration பிழை இருந்தால் Nginx இயங்காத நிலைக்குச் சென்றுவிடும். இயல்பாக reload கட்டளையைப் பயன்படுத்தவும், கட்டாயமான மாற்றங்களுக்கு மட்டும் restart-ஐப் பயன்படுத்தவும்.
sudo tail -f /var/log/nginx/error.log
sudo ss -lntp | grep -E ':(80|443|3000)'ss வரி எந்த process எந்த port-ஐப் பயன்படுத்துகிறது என்பதைக் காட்டும், எனவே proxy_pass குறிப்பிடும் இடத்தில் application உண்மையில் listening நிலையில் உள்ளதா என்பதை நீங்கள் உறுதிப்படுத்திக்கொள்ளலாம்.
நீங்கள் உண்மையில் எதிர்கொள்ளும் தோல்விகள்
502 Bad Gateway, error log-ல் connect() failed (111: Connection refused) while connecting to upstream உடன். proxy_pass-ல் உள்ள முகவரியில் எந்தச் சேவையும் இயங்கவில்லை. அந்த application நிறுத்தப்பட்டிருக்கலாம், வேறு port-க்கு மாற்றப்பட்டிருக்கலாம், அல்லது host-ஆல் அணுக முடியாத container-க்குள் இருக்கும் முகவரியில் இயங்கிக்கொண்டிருக்கலாம்.
no live upstreams while connecting to upstream உடன் 502 பிழை. upstream தொகுதியில் உள்ள அனைத்து server-களும் தற்போது max_fails-ஆல் தோல்வியடைந்ததாகக் குறிக்கப்பட்டுள்ளன. backend-களைச் சரிசெய்யவும். fail_timeout காலாவதியானதும் nginx மீண்டும் முயற்சிக்கும்.
504 Gateway Time-out, upstream timed out (110: Connection timed out) while reading response header from upstream உடன். backend இணைப்பை ஏற்றுக்கொண்டது, ஆனால் proxy_read_timeout வினாடிகளுக்கு எந்தத் தரவையும் அனுப்பவில்லை. மெதுவாகச் செயல்படும் அறிக்கை (report) உருவாக்கத்திற்கு timeout-ஐ அதிகரிப்பது சரியானது, ஆனால் செயலிழந்துள்ள (stuck) application-க்கு இது தவறான அணுகுமுறை.
அனைத்து path-களும் application-லிருந்து 404 பிழையைத் தருகின்றன. trailing slash விதி path-ஐ மாற்றியமைத்துள்ளது. நீங்கள் கோரிய path-ஐயும், application log-ல் பதிவாகியுள்ள path-ஐயும் ஒப்பிட்டுப் பார்க்கவும்.
வேறு ஒரு தளம் பதிலளிக்கிறது. server_name ஆனது Host header-உடன் பொருந்தவில்லை, எனவே கோரிக்கையானது default_server தொகுதிக்குச் சென்றுவிட்டது.
பக்கம் ஏற்றப்படுகிறது, ஆனால் ஒரு நிமிடத்திற்குப் பிறகு இடைமுகம் (interface) உறைந்துவிடுகிறது. இது WebSocket தொடர்பான சிக்கல்: Upgrade கையாளுதல் விடுபட்டிருக்கலாம், அல்லது proxy_read_timeout இன்னும் 60 வினாடிகளாகவே இருக்கலாம்.
FAQ
nginx-ல் proxy_pass சேர்த்த பிறகு ஏன் 502 Bad Gateway பிழை வருகிறது?
proxy_pass-ல் குறிப்பிடப்பட்டுள்ள முகவரியுடன் nginx-ஆல் இணைப்பை ஏற்படுத்த முடியவில்லை. /var/log/nginx/error.log-ல் உள்ள error log-ஐப் பார்த்தால் காரணத்தைத் தெரிந்துகொள்ளலாம்: connect() failed (111: Connection refused) while connecting to upstream என்பது அந்த முகவரியில் எந்தச் சேவையும் இயங்கவில்லை என்பதையும், no live upstreams என்பது upstream தொகுதியில் உள்ள அனைத்து server-களும் தோல்வியடைந்ததாகக் குறிக்கப்பட்டுள்ளன என்பதையும் குறிக்கும். எந்த process அந்த port-ஐப் பயன்படுத்துகிறது மற்றும் அது எந்த முகவரியுடன் பிணைக்கப்பட்டுள்ளது (bound) என்பதை அறிய sudo ss -lntp | grep 3000 கட்டளையை இயக்கவும். ஒரு application container-க்குள் இருக்கும் முகவரியிலோ அல்லது நீங்கள் குறிப்பிட்ட port-ஐத் தவிர வேறு port-லோ இயங்கினால், இந்த பிழை ஏற்படும்.
nginx-க்கு பின்னால் இருக்கும் எனது application ஏன் ஒரு நிமிடத்திற்குப் பிறகு துண்டிக்கப்படுகிறது?
அந்த இணைப்பு ஒரு WebSocket ஆகும், மேலும் proxy_read_timeout அதன் இயல்புநிலை நேரமான 60 வினாடிகளில் உள்ளது. இது backend-லிருந்து வரும் இரண்டு தரவு வாசிப்புகளுக்கு இடைப்பட்ட நேரத்தைக் கணக்கிடுகிறது. நீண்ட நேரம் தரவு பரிமாற்றம் இல்லாத socket-ஐ nginx துண்டித்துவிடும், இதனால் browser console-ல் 1006 என்ற close code காட்டப்படும். proxy_http_version 1.1-ஐ அமைக்கவும், map மூலம் $http_upgrade-ல் Upgrade மற்றும் Connection ஆகியவற்றை அனுப்பவும், மேலும் proxy_read_timeout-ஐ 3600s போன்ற அதிக நேரத்திற்கு மாற்றவும். Upgrade header இல்லையென்றால், upgrade நடைபெறாது; இதனால் application polling முறைக்குத் திரும்பும் அல்லது நேரலைத் தகவல்கள் காட்டப்படாது.
proxy_pass-ல் உள்ள trailing slash முக்கியமா?
ஆம், இது உங்கள் backend-க்குச் செல்லும் path-ஐ மாற்றும். location /app/ மற்றும் proxy_pass http://127.0.0.1:3000/ இருக்கும்போது, /app/status-க்கான கோரிக்கை backend-க்கு /status என்று செல்லும். ஏனெனில், host மற்றும் port-க்கு பிறகு வரும் URI, பொருத்தப்பட்ட location prefix-ஐ நீக்கிவிட்டு அதன் இடத்தைப் பிடிக்கும். அந்த இறுதி slash-ஐ நீக்கினால், அதே கோரிக்கை /app/status என்று செல்லும். Prefix-ஐ நீக்குவது பெரும்பாலும் application-ன் asset இணைப்புகளைச் சிதைக்கும், ஏனெனில் அவை absolute path-ஆக இருப்பதால் site root-ல் 404 பிழையைக் கொடுக்கும். எனவே, base-path வசதி கொண்ட application-களுக்கு, path-ஐ அப்படியே அனுப்பும் முறையே சிறந்தது.
எனது application ஏன் அனைத்து பார்வையாளர்களின் IP முகவரியையும் 127.0.0.1 என்று காட்டுகிறது?
ஏனெனில், application பெறும் இணைப்பு உண்மையில் loopback முகவரியில் உள்ள nginx-லிருந்து வருகிறது. பார்வையாளரின் உண்மையான முகவரி நீங்கள் அமைக்கும் header மூலம் மட்டுமே application-க்குச் சென்றடையும்: ஒரு மதிப்பிற்கு proxy_set_header X-Real-IP $remote_addr;-ஐயும், தொடர் மதிப்புகளுக்கு proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;-ஐயும் பயன்படுத்தவும். அந்த header-களை நம்பும்படி application-ஐ configure செய்ய வேண்டும். ஒரு client தனது சொந்த X-Forwarded-For-ஐ அனுப்ப முடியும் என்பதை நினைவில் கொள்க. எனவே, nginx edge server-ஆக இருக்கும்போது, header-ஐ append செய்வதற்குப் பதிலாக $remote_addr மூலம் அதை overwrite செய்யவும்.
nginx மற்றும் எனது application-க்கு இடையிலான இணைப்பிற்கு TLS தேவையா?
application ஒரே server-ல் இயங்கி 127.0.0.1-ல் பிணைக்கப்பட்டிருந்தால், அந்த traffic கணினியை விட்டு வெளியேறாது என்பதால் TLS தேவையில்லை. TLS-ஐ nginx-லேயே முடித்துவிட்டு (terminate), proxy_pass-ஐ loopback-ல் plain HTTP-ஆக வைத்திருக்கவும். பார்வையாளர் HTTPS-ஐப் பயன்படுத்தினார் என்பதை application அறிந்துகொள்ள X-Forwarded-Proto $scheme-ஐ அனுப்பவும். ஒருவேளை backend வேறொரு server-ல் இருந்தால், அந்த இணைப்பிற்குப் பாதுகாப்பு தேவைப்படும். அதற்கு backend-க்கு HTTPS-ஐப் பயன்படுத்தலாம் அல்லது இரண்டு இயந்திரங்களுக்கு இடையே ஒரு private tunnel-ஐ உருவாக்கலாம்.