ಈ ಲೇಖನದಲ್ಲಿ, Nginx Inc ಅಭಿವೃದ್ಧಿಪಡಿಸಿದ Nginx ಗಾಗಿ JavaScript ಇಂಟರ್ಪ್ರಿಟರ್ NJS ನೊಂದಿಗೆ ನನ್ನ ಅನುಭವವನ್ನು ಹಂಚಿಕೊಳ್ಳಲು ನಾನು ಬಯಸುತ್ತೇನೆ, ನೈಜ ಉದಾಹರಣೆಯನ್ನು ಬಳಸಿಕೊಂಡು ಅದರ ಮುಖ್ಯ ಸಾಮರ್ಥ್ಯಗಳನ್ನು ವಿವರಿಸುತ್ತದೆ. NJS ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ನ ಉಪವಿಭಾಗವಾಗಿದ್ದು ಅದು Nginx ನ ಕಾರ್ಯವನ್ನು ವಿಸ್ತರಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಎಂಬ ಪ್ರಶ್ನೆಗೆ
ಬಹು ಸಮಯದ ಹಿಂದೆ…
ನನ್ನ ಕೊನೆಯ ಕೆಲಸದಲ್ಲಿ, ನಾನು ಡಾಕರ್-ಕಂಪೋಸ್, ಡಿಂಡ್ ಮತ್ತು ಇತರ ಡಿಲೈಟ್ಗಳೊಂದಿಗೆ ಹಲವಾರು ಮಾಟ್ಲಿ CI/CD ಪೈಪ್ಲೈನ್ಗಳೊಂದಿಗೆ ಗಿಟ್ಲ್ಯಾಬ್ ಅನ್ನು ಆನುವಂಶಿಕವಾಗಿ ಪಡೆದುಕೊಂಡಿದ್ದೇನೆ, ಅದನ್ನು ಕನಿಕೋ ರೈಲ್ಗಳಿಗೆ ವರ್ಗಾಯಿಸಲಾಯಿತು. CI ನಲ್ಲಿ ಹಿಂದೆ ಬಳಸಿದ ಚಿತ್ರಗಳನ್ನು ಅವುಗಳ ಮೂಲ ರೂಪದಲ್ಲಿ ಸರಿಸಲಾಗಿದೆ. ನಮ್ಮ ಗಿಟ್ಲ್ಯಾಬ್ ಐಪಿ ಬದಲಾದ ಮತ್ತು ಸಿಐ ಕುಂಬಳಕಾಯಿಯಾಗಿ ಬದಲಾಗುವ ದಿನದವರೆಗೂ ಅವರು ಸರಿಯಾಗಿ ಕೆಲಸ ಮಾಡಿದರು. ಸಮಸ್ಯೆಯೆಂದರೆ CI ನಲ್ಲಿ ಭಾಗವಹಿಸಿದ ಡಾಕರ್ ಚಿತ್ರಗಳಲ್ಲೊಂದು git ಅನ್ನು ಹೊಂದಿತ್ತು, ಇದು ssh ಮೂಲಕ ಪೈಥಾನ್ ಮಾಡ್ಯೂಲ್ಗಳನ್ನು ಎಳೆದಿದೆ. ssh ಗಾಗಿ ನಿಮಗೆ ಒಂದು ಖಾಸಗಿ ಕೀ ಬೇಕು ಮತ್ತು... ಇದು known_hosts ಜೊತೆಗೆ ಚಿತ್ರದಲ್ಲಿದೆ. ಮತ್ತು ಯಾವುದೇ CI ನೈಜ IP ಮತ್ತು known_hosts ನಲ್ಲಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ ನಡುವಿನ ಹೊಂದಾಣಿಕೆಯಿಲ್ಲದ ಕಾರಣ ಕೀ ಪರಿಶೀಲನೆ ದೋಷದೊಂದಿಗೆ ವಿಫಲವಾಗಿದೆ. ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಡಾಕ್ಫೈಲ್ಗಳಿಂದ ಹೊಸ ಚಿತ್ರವನ್ನು ತ್ವರಿತವಾಗಿ ಜೋಡಿಸಲಾಗಿದೆ ಮತ್ತು ಆಯ್ಕೆಯನ್ನು ಸೇರಿಸಲಾಗಿದೆ StrictHostKeyChecking no
. ಆದರೆ ಕೆಟ್ಟ ಅಭಿರುಚಿ ಉಳಿದುಕೊಂಡಿತು ಮತ್ತು ಲಿಬ್ಗಳನ್ನು ಖಾಸಗಿ PyPI ರೆಪೊಸಿಟರಿಗೆ ಸ್ಥಳಾಂತರಿಸುವ ಬಯಕೆ ಇತ್ತು. ಖಾಸಗಿ PyPI ಗೆ ಬದಲಾಯಿಸಿದ ನಂತರ ಹೆಚ್ಚುವರಿ ಬೋನಸ್, ಸರಳವಾದ ಪೈಪ್ಲೈನ್ ಮತ್ತು ಅವಶ್ಯಕತೆಗಳ ಸಾಮಾನ್ಯ ವಿವರಣೆಯಾಗಿದೆ.txt
ಆಯ್ಕೆ ಮಾಡಲಾಗಿದೆ, ಮಹನೀಯರೇ!
ನಾವು ಮೋಡಗಳು ಮತ್ತು ಕುಬರ್ನೆಟ್ಸ್ನಲ್ಲಿ ಎಲ್ಲವನ್ನೂ ಓಡಿಸುತ್ತೇವೆ ಮತ್ತು ಕೊನೆಯಲ್ಲಿ ನಾವು ಬಾಹ್ಯ ಸಂಗ್ರಹಣೆಯೊಂದಿಗೆ ಸ್ಥಿತಿಯಿಲ್ಲದ ಕಂಟೇನರ್ ಆಗಿರುವ ಸಣ್ಣ ಸೇವೆಯನ್ನು ಪಡೆಯಲು ಬಯಸುತ್ತೇವೆ. ಸರಿ, ನಾವು S3 ಅನ್ನು ಬಳಸುವುದರಿಂದ, ಅದಕ್ಕೆ ಆದ್ಯತೆ ನೀಡಲಾಯಿತು. ಮತ್ತು, ಸಾಧ್ಯವಾದರೆ, gitlab ನಲ್ಲಿ ದೃಢೀಕರಣದೊಂದಿಗೆ (ಅಗತ್ಯವಿದ್ದರೆ ನೀವೇ ಅದನ್ನು ಸೇರಿಸಬಹುದು).
ತ್ವರಿತ ಹುಡುಕಾಟವು ಹಲವಾರು ಫಲಿತಾಂಶಗಳನ್ನು ನೀಡಿತು: s3pypi, pypicloud ಮತ್ತು ಟರ್ನಿಪ್ಗಳಿಗಾಗಿ html ಫೈಲ್ಗಳ "ಹಸ್ತಚಾಲಿತ" ರಚನೆಯೊಂದಿಗೆ ಒಂದು ಆಯ್ಕೆ. ಕೊನೆಯ ಆಯ್ಕೆಯು ಸ್ವತಃ ಕಣ್ಮರೆಯಾಯಿತು.
s3pypi: ಇದು S3 ಹೋಸ್ಟಿಂಗ್ ಅನ್ನು ಬಳಸುವುದಕ್ಕಾಗಿ ಒಂದು cli ಆಗಿದೆ. ನಾವು ಫೈಲ್ಗಳನ್ನು ಅಪ್ಲೋಡ್ ಮಾಡುತ್ತೇವೆ, html ಅನ್ನು ರಚಿಸುತ್ತೇವೆ ಮತ್ತು ಅದನ್ನು ಅದೇ ಬಕೆಟ್ಗೆ ಅಪ್ಲೋಡ್ ಮಾಡುತ್ತೇವೆ. ಮನೆ ಬಳಕೆಗೆ ಸೂಕ್ತವಾಗಿದೆ.
pypicloud: ಇದು ಆಸಕ್ತಿದಾಯಕ ಯೋಜನೆಯಂತೆ ತೋರುತ್ತಿದೆ, ಆದರೆ ದಸ್ತಾವೇಜನ್ನು ಓದಿದ ನಂತರ ನಾನು ನಿರಾಶೆಗೊಂಡಿದ್ದೇನೆ. ಉತ್ತಮ ದಸ್ತಾವೇಜನ್ನು ಮತ್ತು ನಿಮ್ಮ ಅಗತ್ಯಗಳಿಗೆ ಸರಿಹೊಂದುವಂತೆ ವಿಸ್ತರಿಸುವ ಸಾಮರ್ಥ್ಯದ ಹೊರತಾಗಿಯೂ, ವಾಸ್ತವದಲ್ಲಿ ಇದು ಅನಗತ್ಯ ಮತ್ತು ಕಾನ್ಫಿಗರ್ ಮಾಡಲು ಕಷ್ಟಕರವಾಗಿದೆ. ನಿಮ್ಮ ಕಾರ್ಯಗಳಿಗೆ ಸರಿಹೊಂದುವಂತೆ ಕೋಡ್ ಅನ್ನು ಸರಿಪಡಿಸಲು, ಆ ಸಮಯದಲ್ಲಿ ಅಂದಾಜಿನ ಪ್ರಕಾರ, 3-5 ದಿನಗಳನ್ನು ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ಸೇವೆಗೆ ಡೇಟಾಬೇಸ್ ಕೂಡ ಅಗತ್ಯವಿದೆ. ನಮಗೆ ಬೇರೆ ಏನೂ ಸಿಗದಿದ್ದರೆ ನಾವು ಅದನ್ನು ಬಿಟ್ಟಿದ್ದೇವೆ.
ಹೆಚ್ಚು ಆಳವಾದ ಹುಡುಕಾಟವು Nginx, ngx_aws_auth ಗಾಗಿ ಮಾಡ್ಯೂಲ್ ಅನ್ನು ನೀಡಿತು. ಅವನ ಪರೀಕ್ಷೆಯ ಫಲಿತಾಂಶವು ಬ್ರೌಸರ್ನಲ್ಲಿ XML ಅನ್ನು ಪ್ರದರ್ಶಿಸಿತು, ಅದು S3 ಬಕೆಟ್ನ ವಿಷಯಗಳನ್ನು ತೋರಿಸಿತು. ಹುಡುಕಾಟದ ಸಮಯದಲ್ಲಿ ಕೊನೆಯ ಕಮಿಟ್ ಒಂದು ವರ್ಷದ ಹಿಂದೆ. ಭಂಡಾರ ಕೈಬಿಟ್ಟಂತೆ ಕಾಣುತ್ತದೆ.
ಮೂಲಕ್ಕೆ ಹೋಗಿ ಓದುವ ಮೂಲಕ
ಈ ಉದಾಹರಣೆಯನ್ನು ಆಧಾರವಾಗಿ ತೆಗೆದುಕೊಂಡರೆ, ಒಂದು ಗಂಟೆಯ ನಂತರ ನಾನು ನನ್ನ ಬ್ರೌಸರ್ನಲ್ಲಿ ngx_aws_auth ಮಾಡ್ಯೂಲ್ ಅನ್ನು ಬಳಸುವಾಗ ಅದೇ XML ಅನ್ನು ನೋಡಿದೆ, ಆದರೆ ಎಲ್ಲವನ್ನೂ ಈಗಾಗಲೇ JS ನಲ್ಲಿ ಬರೆಯಲಾಗಿದೆ.
ನಾನು nginx ಪರಿಹಾರವನ್ನು ನಿಜವಾಗಿಯೂ ಇಷ್ಟಪಟ್ಟೆ. ಮೊದಲನೆಯದಾಗಿ, ಉತ್ತಮ ದಸ್ತಾವೇಜನ್ನು ಮತ್ತು ಅನೇಕ ಉದಾಹರಣೆಗಳು, ಎರಡನೆಯದಾಗಿ, ಫೈಲ್ಗಳೊಂದಿಗೆ (ಬಾಕ್ಸ್ನ ಹೊರಗೆ) ಕೆಲಸ ಮಾಡಲು ನಾವು Nginx ನ ಎಲ್ಲಾ ಗುಡಿಗಳನ್ನು ಪಡೆಯುತ್ತೇವೆ, ಮೂರನೆಯದಾಗಿ, Nginx ಗಾಗಿ ಸಂರಚನೆಗಳನ್ನು ಹೇಗೆ ಬರೆಯಬೇಕೆಂದು ತಿಳಿದಿರುವ ಯಾರಾದರೂ ಏನೆಂದು ಲೆಕ್ಕಾಚಾರ ಮಾಡಲು ಸಾಧ್ಯವಾಗುತ್ತದೆ. ಪೈಥಾನ್ ಅಥವಾ ಗೋಗೆ ಹೋಲಿಸಿದರೆ ಮಿನಿಮಲಿಸಂ ಕೂಡ ನನಗೆ ಒಂದು ಪ್ಲಸ್ ಆಗಿದೆ (ಮೊದಲಿನಿಂದ ಬರೆದಿದ್ದರೆ), ನೆಕ್ಸಸ್ ಅನ್ನು ಉಲ್ಲೇಖಿಸಬಾರದು.
TL;DR 2 ದಿನಗಳ ನಂತರ, PyPi ನ ಪರೀಕ್ಷಾ ಆವೃತ್ತಿಯನ್ನು ಈಗಾಗಲೇ CI ನಲ್ಲಿ ಬಳಸಲಾಗಿದೆ.
ಇದು ಹೇಗೆ ಕೆಲಸ ಮಾಡುತ್ತದೆ?
ಮಾಡ್ಯೂಲ್ ಅನ್ನು Nginx ಗೆ ಲೋಡ್ ಮಾಡಲಾಗಿದೆ ngx_http_js_module
, ಅಧಿಕೃತ ಡಾಕರ್ ಚಿತ್ರದಲ್ಲಿ ಸೇರಿಸಲಾಗಿದೆ. ನಿರ್ದೇಶನವನ್ನು ಬಳಸಿಕೊಂಡು ನಾವು ನಮ್ಮ ಸ್ಕ್ರಿಪ್ಟ್ ಅನ್ನು ಆಮದು ಮಾಡಿಕೊಳ್ಳುತ್ತೇವೆ js_import
Nginx ಕಾನ್ಫಿಗರೇಶನ್ಗೆ. ಕಾರ್ಯವನ್ನು ನಿರ್ದೇಶನದ ಮೂಲಕ ಕರೆಯಲಾಗುತ್ತದೆ js_content
. ಅಸ್ಥಿರಗಳನ್ನು ಹೊಂದಿಸಲು ನಿರ್ದೇಶನವನ್ನು ಬಳಸಲಾಗುತ್ತದೆ js_set
, ಇದು ಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ವಿವರಿಸಿದ ಕಾರ್ಯವನ್ನು ಮಾತ್ರ ವಾದವಾಗಿ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ. ಆದರೆ ನಾವು NJS ನಲ್ಲಿ ಸಬ್ಕ್ವೆರಿಗಳನ್ನು Nginx ಬಳಸಿ ಮಾತ್ರ ಕಾರ್ಯಗತಗೊಳಿಸಬಹುದು, ಯಾವುದೇ XMLHttpRequest ಅಲ್ಲ. ಇದನ್ನು ಮಾಡಲು, ಅನುಗುಣವಾದ ಸ್ಥಳವನ್ನು Nginx ಕಾನ್ಫಿಗರೇಶನ್ಗೆ ಸೇರಿಸಬೇಕು. ಮತ್ತು ಸ್ಕ್ರಿಪ್ಟ್ ಈ ಸ್ಥಳಕ್ಕೆ ಉಪ ವಿನಂತಿಯನ್ನು ವಿವರಿಸಬೇಕು. Nginx ಸಂರಚನೆಯಿಂದ ಕಾರ್ಯವನ್ನು ಪ್ರವೇಶಿಸಲು, ಕಾರ್ಯದ ಹೆಸರನ್ನು ಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿಯೇ ರಫ್ತು ಮಾಡಬೇಕು export default
.
nginx.conf
load_module modules/ngx_http_js_module.so;
http {
js_import imported_name from script.js;
server {
listen 8080;
...
location = /sub-query {
internal;
proxy_pass http://upstream;
}
location / {
js_content imported_name.request;
}
}
script.js
function request(r) {
function call_back(resp) {
// handler's code
r.return(resp.status, resp.responseBody);
}
r.subrequest('/sub-query', { method: r.method }, call_back);
}
export default {request}
ಬ್ರೌಸರ್ನಲ್ಲಿ ವಿನಂತಿಸಿದಾಗ http://localhost:8080/
ನಾವು ಪ್ರವೇಶಿಸುತ್ತೇವೆ location /
ಇದರಲ್ಲಿ ನಿರ್ದೇಶನ js_content
ಒಂದು ಕಾರ್ಯವನ್ನು ಕರೆಯುತ್ತದೆ request
ನಮ್ಮ ಲಿಪಿಯಲ್ಲಿ ವಿವರಿಸಲಾಗಿದೆ script.js
. ಪ್ರತಿಯಾಗಿ, ಕಾರ್ಯದಲ್ಲಿ request
ಒಂದು ಉಪಪ್ರಶ್ನೆಯನ್ನು ಮಾಡಲಾಗಿದೆ location = /sub-query
, ವಾದದಿಂದ ಪಡೆದ ವಿಧಾನದೊಂದಿಗೆ (ಪ್ರಸ್ತುತ ಉದಾಹರಣೆಯಲ್ಲಿ GET). (r)
, ಈ ಕಾರ್ಯವನ್ನು ಕರೆಯುವಾಗ ಸೂಚ್ಯವಾಗಿ ರವಾನಿಸಲಾಗಿದೆ. ಉಪ ವಿನಂತಿಯ ಪ್ರತಿಕ್ರಿಯೆಯನ್ನು ಕಾರ್ಯದಲ್ಲಿ ಪ್ರಕ್ರಿಯೆಗೊಳಿಸಲಾಗುತ್ತದೆ call_back
.
S3 ಅನ್ನು ಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ
ಖಾಸಗಿ S3 ಸಂಗ್ರಹಣೆಗೆ ವಿನಂತಿಯನ್ನು ಮಾಡಲು, ನಮಗೆ ಅಗತ್ಯವಿದೆ:
ACCESS_KEY
SECRET_KEY
S3_BUCKET
ಬಳಸಿದ http ವಿಧಾನದಿಂದ, ಪ್ರಸ್ತುತ ದಿನಾಂಕ/ಸಮಯ, S3_NAME ಮತ್ತು URI, ನಿರ್ದಿಷ್ಟ ರೀತಿಯ ಸ್ಟ್ರಿಂಗ್ ಅನ್ನು ರಚಿಸಲಾಗಿದೆ, ಇದನ್ನು SECRET_KEY ಬಳಸಿಕೊಂಡು ಸಹಿ ಮಾಡಲಾಗಿದೆ (HMAC_SHA1). ಮುಂದಿನದು ಒಂದು ಸಾಲು AWS $ACCESS_KEY:$HASH
, ಅಧಿಕಾರ ಶಿರೋಲೇಖದಲ್ಲಿ ಬಳಸಬಹುದು. ಹಿಂದಿನ ಹಂತದಲ್ಲಿ ಸ್ಟ್ರಿಂಗ್ ಅನ್ನು ರಚಿಸಲು ಬಳಸಿದ ಅದೇ ದಿನಾಂಕ/ಸಮಯವನ್ನು ಹೆಡರ್ಗೆ ಸೇರಿಸಬೇಕು X-amz-date
. ಕೋಡ್ನಲ್ಲಿ ಇದು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:
nginx.conf
load_module modules/ngx_http_js_module.so;
http {
js_import s3 from s3.js;
js_set $s3_datetime s3.date_now;
js_set $s3_auth s3.s3_sign;
server {
listen 8080;
...
location ~* /s3-query/(?<s3_path>.*) {
internal;
proxy_set_header X-amz-date $s3_datetime;
proxy_set_header Authorization $s3_auth;
proxy_pass $s3_endpoint/$s3_path;
}
location ~ "^/(?<prefix>[w-]*)[/]?(?<postfix>[w-.]*)$" {
js_content s3.request;
}
}
s3.js
(AWS ಸೈನ್ v2 ದೃಢೀಕರಣದ ಉದಾಹರಣೆ, ಅಸಮ್ಮಿತ ಸ್ಥಿತಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ)
var crypt = require('crypto');
var s3_bucket = process.env.S3_BUCKET;
var s3_access_key = process.env.S3_ACCESS_KEY;
var s3_secret_key = process.env.S3_SECRET_KEY;
var _datetime = new Date().toISOString().replace(/[:-]|.d{3}/g, '');
function date_now() {
return _datetime
}
function s3_sign(r) {
var s2s = r.method + 'nnnn';
s2s += `x-amz-date:${date_now()}n`;
s2s += '/' + s3_bucket;
s2s += r.uri.endsWith('/') ? '/' : r.variables.s3_path;
return `AWS ${s3_access_key}:${crypt.createHmac('sha1', s3_secret_key).update(s2s).digest('base64')}`;
}
function request(r) {
var v = r.variables;
function call_back(resp) {
r.return(resp.status, resp.responseBody);
}
var _subrequest_uri = r.uri;
if (r.uri === '/') {
// root
_subrequest_uri = '/?delimiter=/';
} else if (v.prefix !== '' && v.postfix === '') {
// directory
var slash = v.prefix.endsWith('/') ? '' : '/';
_subrequest_uri = '/?prefix=' + v.prefix + slash;
}
r.subrequest(`/s3-query${_subrequest_uri}`, { method: r.method }, call_back);
}
export default {request, s3_sign, date_now}
ಬಗ್ಗೆ ಸ್ವಲ್ಪ ವಿವರಣೆ _subrequest_uri
: ಇದು ವೇರಿಯಬಲ್ ಆಗಿದ್ದು, ಆರಂಭಿಕ uri ಅನ್ನು ಅವಲಂಬಿಸಿ, S3 ಗೆ ವಿನಂತಿಯನ್ನು ರೂಪಿಸುತ್ತದೆ. ನೀವು "ರೂಟ್" ನ ವಿಷಯಗಳನ್ನು ಪಡೆಯಬೇಕಾದರೆ, ನೀವು ಡಿಲಿಮಿಟರ್ ಅನ್ನು ಸೂಚಿಸುವ ಯುರಿ ವಿನಂತಿಯನ್ನು ರಚಿಸಬೇಕು delimiter
, ಇದು ಡೈರೆಕ್ಟರಿಗಳಿಗೆ ಅನುಗುಣವಾಗಿ ಎಲ್ಲಾ ಕಾಮನ್ಪ್ರಿಫಿಕ್ಸ್ಗಳ xml ಅಂಶಗಳ ಪಟ್ಟಿಯನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ (PyPI ಸಂದರ್ಭದಲ್ಲಿ, ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್ಗಳ ಪಟ್ಟಿ). ನೀವು ನಿರ್ದಿಷ್ಟ ಡೈರೆಕ್ಟರಿಯಲ್ಲಿ ವಿಷಯಗಳ ಪಟ್ಟಿಯನ್ನು ಪಡೆಯಬೇಕಾದರೆ (ಎಲ್ಲಾ ಪ್ಯಾಕೇಜ್ ಆವೃತ್ತಿಗಳ ಪಟ್ಟಿ), ನಂತರ uri ವಿನಂತಿಯು ಡೈರೆಕ್ಟರಿಯ ಹೆಸರಿನೊಂದಿಗೆ ಪೂರ್ವಪ್ರತ್ಯಯ ಕ್ಷೇತ್ರವನ್ನು ಹೊಂದಿರಬೇಕು (ಪ್ಯಾಕೇಜ್) ಅಗತ್ಯವಾಗಿ ಸ್ಲ್ಯಾಷ್ / ನೊಂದಿಗೆ ಕೊನೆಗೊಳ್ಳುತ್ತದೆ. ಇಲ್ಲದಿದ್ದರೆ, ಡೈರೆಕ್ಟರಿಯ ವಿಷಯಗಳನ್ನು ವಿನಂತಿಸುವಾಗ ಘರ್ಷಣೆಗಳು ಸಾಧ್ಯ, ಉದಾಹರಣೆಗೆ. ಡೈರೆಕ್ಟರಿಗಳು aiohttp-request ಮತ್ತು aiohttp-requests ಇವೆ ಮತ್ತು ವಿನಂತಿಯು ನಿರ್ದಿಷ್ಟಪಡಿಸಿದರೆ /?prefix=aiohttp-request
, ನಂತರ ಪ್ರತಿಕ್ರಿಯೆಯು ಎರಡೂ ಡೈರೆಕ್ಟರಿಗಳ ವಿಷಯಗಳನ್ನು ಒಳಗೊಂಡಿರುತ್ತದೆ. ಕೊನೆಯಲ್ಲಿ ಒಂದು ಸ್ಲ್ಯಾಷ್ ಇದ್ದರೆ, /?prefix=aiohttp-request/
, ನಂತರ ಪ್ರತಿಕ್ರಿಯೆಯು ಅಗತ್ಯವಿರುವ ಡೈರೆಕ್ಟರಿಯನ್ನು ಮಾತ್ರ ಹೊಂದಿರುತ್ತದೆ. ಮತ್ತು ನಾವು ಫೈಲ್ ಅನ್ನು ವಿನಂತಿಸಿದರೆ, ಪರಿಣಾಮವಾಗಿ ಬರುವ uri ಮೂಲದಿಂದ ಭಿನ್ನವಾಗಿರಬಾರದು.
Nginx ಅನ್ನು ಉಳಿಸಿ ಮತ್ತು ಮರುಪ್ರಾರಂಭಿಸಿ. ಬ್ರೌಸರ್ನಲ್ಲಿ ನಾವು ನಮ್ಮ Nginx ನ ವಿಳಾಸವನ್ನು ನಮೂದಿಸುತ್ತೇವೆ, ವಿನಂತಿಯ ಫಲಿತಾಂಶವು XML ಆಗಿರುತ್ತದೆ, ಉದಾಹರಣೆಗೆ:
ಡೈರೆಕ್ಟರಿಗಳ ಪಟ್ಟಿ
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name>myback-space</Name>
<Prefix></Prefix>
<Marker></Marker>
<MaxKeys>10000</MaxKeys>
<Delimiter>/</Delimiter>
<IsTruncated>false</IsTruncated>
<CommonPrefixes>
<Prefix>new/</Prefix>
</CommonPrefixes>
<CommonPrefixes>
<Prefix>old/</Prefix>
</CommonPrefixes>
</ListBucketResult>
ಡೈರೆಕ್ಟರಿಗಳ ಪಟ್ಟಿಯಿಂದ ನಿಮಗೆ ಅಂಶಗಳು ಮಾತ್ರ ಬೇಕಾಗುತ್ತದೆ CommonPrefixes
.
ಬ್ರೌಸರ್ನಲ್ಲಿನ ನಮ್ಮ ವಿಳಾಸಕ್ಕೆ ನಮಗೆ ಅಗತ್ಯವಿರುವ ಡೈರೆಕ್ಟರಿಯನ್ನು ಸೇರಿಸುವ ಮೂಲಕ, ನಾವು ಅದರ ವಿಷಯಗಳನ್ನು XML ರೂಪದಲ್ಲಿ ಸ್ವೀಕರಿಸುತ್ತೇವೆ:
ಡೈರೆಕ್ಟರಿಯಲ್ಲಿರುವ ಫೈಲ್ಗಳ ಪಟ್ಟಿ
<?xml version="1.0" encoding="UTF-8"?>
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<Name> myback-space</Name>
<Prefix>old/</Prefix>
<Marker></Marker>
<MaxKeys>10000</MaxKeys>
<Delimiter></Delimiter>
<IsTruncated>false</IsTruncated>
<Contents>
<Key>old/giphy.mp4</Key>
<LastModified>2020-08-21T20:27:46.000Z</LastModified>
<ETag>"00000000000000000000000000000000-1"</ETag>
<Size>1350084</Size>
<Owner>
<ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
<DisplayName></DisplayName>
</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
<Contents>
<Key>old/hsd-k8s.jpg</Key>
<LastModified>2020-08-31T16:40:01.000Z</LastModified>
<ETag>"b2d76df4aeb4493c5456366748218093"</ETag>
<Size>93183</Size>
<Owner>
<ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
<DisplayName></DisplayName>
</Owner>
<StorageClass>STANDARD</StorageClass>
</Contents>
</ListBucketResult>
ಫೈಲ್ಗಳ ಪಟ್ಟಿಯಿಂದ ನಾವು ಅಂಶಗಳನ್ನು ಮಾತ್ರ ತೆಗೆದುಕೊಳ್ಳುತ್ತೇವೆ Key
.
ಫಲಿತಾಂಶದ XML ಅನ್ನು ಪಾರ್ಸ್ ಮಾಡುವುದು ಮತ್ತು ಅದನ್ನು HTML ಆಗಿ ಕಳುಹಿಸುವುದು ಮಾತ್ರ ಉಳಿದಿದೆ, ಮೊದಲು ವಿಷಯ-ರೀತಿಯ ಹೆಡರ್ ಅನ್ನು ಪಠ್ಯ/html ನೊಂದಿಗೆ ಬದಲಾಯಿಸಲಾಗಿದೆ.
function request(r) {
var v = r.variables;
function call_back(resp) {
var body = resp.responseBody;
if (r.method !== 'PUT' && resp.status < 400 && v.postfix === '') {
r.headersOut['Content-Type'] = "text/html; charset=utf-8";
body = toHTML(body);
}
r.return(resp.status, body);
}
var _subrequest_uri = r.uri;
...
}
function toHTML(xml_str) {
var keysMap = {
'CommonPrefixes': 'Prefix',
'Contents': 'Key',
};
var pattern = `<k>(?<v>.*?)</k>`;
var out = [];
for(var group_key in keysMap) {
var reS;
var reGroup = new RegExp(pattern.replace(/k/g, group_key), 'g');
while(reS = reGroup.exec(xml_str)) {
var data = new RegExp(pattern.replace(/k/g, keysMap[group_key]), 'g');
var reValue = data.exec(reS);
var a_text = '';
if (group_key === 'CommonPrefixes') {
a_text = reValue.groups.v.replace(///g, '');
} else {
a_text = reValue.groups.v.split('/').slice(-1);
}
out.push(`<a href="/kn/${reValue.groups.v}">${a_text}</a>`);
}
}
return '<html><body>n' + out.join('</br>n') + 'n</html></body>'
}
PyPI ಅನ್ನು ಪ್ರಯತ್ನಿಸಲಾಗುತ್ತಿದೆ
ಕೆಲಸ ಮಾಡಲು ತಿಳಿದಿರುವ ಪ್ಯಾಕೇಜ್ಗಳಲ್ಲಿ ಎಲ್ಲಿಯೂ ಏನೂ ಒಡೆಯುವುದಿಲ್ಲ ಎಂದು ನಾವು ಪರಿಶೀಲಿಸುತ್ತೇವೆ.
# Создаем для тестов новое окружение
python3 -m venv venv
. ./venv/bin/activate
# Скачиваем рабочие пакеты.
pip download aiohttp
# Загружаем в приватную репу
for wheel in *.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done
rm -f *.whl
# Устанавливаем из приватной репы
pip install aiohttp -i http://localhost:8080
ನಾವು ನಮ್ಮ ಲಿಬ್ಗಳೊಂದಿಗೆ ಪುನರಾವರ್ತಿಸುತ್ತೇವೆ.
# Создаем для тестов новое окружение
python3 -m venv venv
. ./venv/bin/activate
pip install setuptools wheel
python setup.py bdist_wheel
for wheel in dist/*.whl; do curl -T $wheel http://localhost:8080/${wheel%%-*}/$wheel; done
pip install our_pkg --extra-index-url http://localhost:8080
CI ನಲ್ಲಿ, ಪ್ಯಾಕೇಜ್ ಅನ್ನು ರಚಿಸುವುದು ಮತ್ತು ಲೋಡ್ ಮಾಡುವುದು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:
pip install setuptools wheel
python setup.py bdist_wheel
curl -sSfT dist/*.whl -u "gitlab-ci-token:${CI_JOB_TOKEN}" "https://pypi.our-domain.com/${CI_PROJECT_NAME}"
ದೃ ation ೀಕರಣ
Gitlab ನಲ್ಲಿ ಬಾಹ್ಯ ಸೇವೆಗಳ ದೃಢೀಕರಣ/ಅಧಿಕಾರಕ್ಕಾಗಿ JWT ಅನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಿದೆ. Nginx ನಲ್ಲಿ auth_request ನಿರ್ದೇಶನವನ್ನು ಬಳಸಿಕೊಂಡು, ನಾವು ದೃಢೀಕರಣ ಡೇಟಾವನ್ನು ಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ಫಂಕ್ಷನ್ ಕರೆಯನ್ನು ಹೊಂದಿರುವ ಉಪ ವಿನಂತಿಗೆ ಮರುನಿರ್ದೇಶಿಸುತ್ತೇವೆ. ಸ್ಕ್ರಿಪ್ಟ್ Gitlab url ಗೆ ಮತ್ತೊಂದು ಉಪ ವಿನಂತಿಯನ್ನು ಮಾಡುತ್ತದೆ ಮತ್ತು ದೃಢೀಕರಣ ಡೇಟಾವನ್ನು ಸರಿಯಾಗಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ್ದರೆ, Gitlab ಕೋಡ್ 200 ಅನ್ನು ಹಿಂತಿರುಗಿಸುತ್ತದೆ ಮತ್ತು ಪ್ಯಾಕೇಜ್ನ ಅಪ್ಲೋಡ್/ಡೌನ್ಲೋಡ್ ಅನ್ನು ಅನುಮತಿಸಲಾಗುತ್ತದೆ. ಒಂದು ಉಪಪ್ರಶ್ನೆಯನ್ನು ಏಕೆ ಬಳಸಬಾರದು ಮತ್ತು ತಕ್ಷಣವೇ ಡೇಟಾವನ್ನು Gitlab ಗೆ ಕಳುಹಿಸಬಾರದು? ಏಕೆಂದರೆ ನಾವು ಅಧಿಕಾರದಲ್ಲಿ ಯಾವುದೇ ಬದಲಾವಣೆಗಳನ್ನು ಮಾಡಿದಾಗಲೆಲ್ಲಾ ನಾವು Nginx ಕಾನ್ಫಿಗರೇಶನ್ ಫೈಲ್ ಅನ್ನು ಸಂಪಾದಿಸಬೇಕಾಗುತ್ತದೆ ಮತ್ತು ಇದು ತುಂಬಾ ಬೇಸರದ ಕೆಲಸವಾಗಿದೆ. ಅಲ್ಲದೆ, Kubernetes ಓದಲು-ಮಾತ್ರ ರೂಟ್ ಫೈಲ್ಸಿಸ್ಟಮ್ ನೀತಿಯನ್ನು ಬಳಸಿದರೆ, ಕಾನ್ಫಿಗ್ಮ್ಯಾಪ್ ಮೂಲಕ nginx.conf ಅನ್ನು ಬದಲಾಯಿಸುವಾಗ ಇದು ಇನ್ನಷ್ಟು ಸಂಕೀರ್ಣತೆಯನ್ನು ಸೇರಿಸುತ್ತದೆ. ಮತ್ತು ಏಕಕಾಲದಲ್ಲಿ ವಾಲ್ಯೂಮ್ಗಳ ಸಂಪರ್ಕವನ್ನು (pvc) ಮತ್ತು ಓದಲು-ಮಾತ್ರ ರೂಟ್ ಫೈಲ್ಸಿಸ್ಟಮ್ ಅನ್ನು ನಿಷೇಧಿಸುವ ನೀತಿಗಳನ್ನು ಬಳಸುವಾಗ ಕಾನ್ಫಿಗ್ಮ್ಯಾಪ್ ಮೂಲಕ Nginx ಅನ್ನು ಕಾನ್ಫಿಗರ್ ಮಾಡುವುದು ಸಂಪೂರ್ಣವಾಗಿ ಅಸಾಧ್ಯವಾಗುತ್ತದೆ (ಇದು ಸಹ ಸಂಭವಿಸುತ್ತದೆ).
NJS ಮಧ್ಯಂತರವನ್ನು ಬಳಸುವುದರಿಂದ, ಪರಿಸರದ ಅಸ್ಥಿರಗಳನ್ನು ಬಳಸಿಕೊಂಡು nginx ಸಂರಚನೆಯಲ್ಲಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ ನಿಯತಾಂಕಗಳನ್ನು ಬದಲಾಯಿಸಲು ಮತ್ತು ಸ್ಕ್ರಿಪ್ಟ್ನಲ್ಲಿ ಕೆಲವು ಪರಿಶೀಲನೆಗಳನ್ನು ಮಾಡಲು ನಾವು ಅವಕಾಶವನ್ನು ಪಡೆಯುತ್ತೇವೆ (ಉದಾಹರಣೆಗೆ, ತಪ್ಪಾಗಿ ನಿರ್ದಿಷ್ಟಪಡಿಸಿದ URL).
nginx.conf
location = /auth-provider {
internal;
proxy_pass $auth_url;
}
location = /auth {
internal;
proxy_set_header Content-Length "";
proxy_pass_request_body off;
js_content auth.auth;
}
location ~ "^/(?<prefix>[w-]*)[/]?(?<postfix>[w-.]*)$" {
auth_request /auth;
js_content s3.request;
}
s3.js
var env = process.env;
var env_bool = new RegExp(/[Tt]rue|[Yy]es|[Oo]n|[TtYy]|1/);
var auth_disabled = env_bool.test(env.DISABLE_AUTH);
var gitlab_url = env.AUTH_URL;
function url() {
return `${gitlab_url}/jwt/auth?service=container_registry`
}
function auth(r) {
if (auth_disabled) {
r.return(202, '{"auth": "disabled"}');
return null
}
r.subrequest('/auth-provider',
{method: 'GET', body: ''},
function(res) {
r.return(res.status, "");
});
}
export default {auth, url}
ಹೆಚ್ಚಾಗಿ ಪ್ರಶ್ನೆಯು ಹುದುಗುತ್ತಿದೆ: -ಸಿದ್ಧ ಮಾಡ್ಯೂಲ್ಗಳನ್ನು ಏಕೆ ಬಳಸಬಾರದು? ಎಲ್ಲವನ್ನೂ ಈಗಾಗಲೇ ಅಲ್ಲಿ ಮಾಡಲಾಗಿದೆ! ಉದಾಹರಣೆಗೆ, var AWS = ಅವಶ್ಯಕತೆ ('aws-sdk') ಮತ್ತು S3 ದೃಢೀಕರಣದೊಂದಿಗೆ "ಬೈಕ್" ಅನ್ನು ಬರೆಯುವ ಅಗತ್ಯವಿಲ್ಲ!
ಬಾಧಕಗಳಿಗೆ ಹೋಗೋಣ
ನನಗೆ, ಬಾಹ್ಯ JS ಮಾಡ್ಯೂಲ್ಗಳನ್ನು ಆಮದು ಮಾಡಿಕೊಳ್ಳಲು ಅಸಮರ್ಥತೆಯು ಅಹಿತಕರ, ಆದರೆ ನಿರೀಕ್ಷಿತ ವೈಶಿಷ್ಟ್ಯವಾಗಿದೆ. ಮೇಲಿನ ಉದಾಹರಣೆಯಲ್ಲಿ ವಿವರಿಸಿದ ಅವಶ್ಯಕತೆ ('ಕ್ರಿಪ್ಟೋ') ಆಗಿದೆ
Nginx ನಲ್ಲಿ ಪ್ರಸ್ತುತ ಪ್ರಾಜೆಕ್ಟ್ಗಾಗಿ ಸಂಕೋಚನವನ್ನು ಸಹ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಬೇಕು gzip off;
ಏಕೆಂದರೆ NJS ನಲ್ಲಿ ಯಾವುದೇ gzip ಮಾಡ್ಯೂಲ್ ಇಲ್ಲ ಮತ್ತು ಅದನ್ನು ಸಂಪರ್ಕಿಸಲು ಅಸಾಧ್ಯ; ಆದ್ದರಿಂದ, ಸಂಕುಚಿತ ಡೇಟಾದೊಂದಿಗೆ ಕೆಲಸ ಮಾಡಲು ಯಾವುದೇ ಮಾರ್ಗವಿಲ್ಲ. ನಿಜ, ಈ ಪ್ರಕರಣಕ್ಕೆ ಇದು ನಿಜವಾಗಿಯೂ ಮೈನಸ್ ಅಲ್ಲ. ಬಹಳಷ್ಟು ಪಠ್ಯವಿಲ್ಲ, ಮತ್ತು ವರ್ಗಾವಣೆಗೊಂಡ ಫೈಲ್ಗಳನ್ನು ಈಗಾಗಲೇ ಸಂಕುಚಿತಗೊಳಿಸಲಾಗಿದೆ ಮತ್ತು ಹೆಚ್ಚುವರಿ ಸಂಕೋಚನವು ಅವರಿಗೆ ಹೆಚ್ಚು ಸಹಾಯ ಮಾಡುವುದಿಲ್ಲ. ಅಲ್ಲದೆ, ಇದು ಅಂತಹ ಲೋಡ್ ಮಾಡಲಾದ ಅಥವಾ ನಿರ್ಣಾಯಕ ಸೇವೆಯಲ್ಲ, ಕೆಲವು ಮಿಲಿಸೆಕೆಂಡುಗಳಷ್ಟು ವೇಗವಾಗಿ ವಿಷಯವನ್ನು ತಲುಪಿಸಲು ನೀವು ಚಿಂತಿಸಬೇಕಾಗಿದೆ.
ಸ್ಕ್ರಿಪ್ಟ್ ಅನ್ನು ಡೀಬಗ್ ಮಾಡುವುದು ಬಹಳ ಸಮಯ ತೆಗೆದುಕೊಳ್ಳುತ್ತದೆ ಮತ್ತು error.log ನಲ್ಲಿ "ಪ್ರಿಂಟ್" ಮೂಲಕ ಮಾತ್ರ ಸಾಧ್ಯ. ಸೆಟ್ ಲಾಗಿಂಗ್ ಮಟ್ಟದ ಮಾಹಿತಿ, ಎಚ್ಚರಿಕೆ ಅಥವಾ ದೋಷವನ್ನು ಅವಲಂಬಿಸಿ, ಕ್ರಮವಾಗಿ r.log, r.warn, r.error ಎಂಬ 3 ವಿಧಾನಗಳನ್ನು ಬಳಸಲು ಸಾಧ್ಯವಿದೆ. ನಾನು Chrome (v8) ಅಥವಾ njs ಕನ್ಸೋಲ್ ಟೂಲ್ನಲ್ಲಿ ಕೆಲವು ಸ್ಕ್ರಿಪ್ಟ್ಗಳನ್ನು ಡೀಬಗ್ ಮಾಡಲು ಪ್ರಯತ್ನಿಸುತ್ತೇನೆ, ಆದರೆ ಎಲ್ಲವನ್ನೂ ಅಲ್ಲಿ ಪರಿಶೀಲಿಸಲಾಗುವುದಿಲ್ಲ. ಕೋಡ್ ಅನ್ನು ಡೀಬಗ್ ಮಾಡುವಾಗ, ಕ್ರಿಯಾತ್ಮಕ ಪರೀಕ್ಷೆ, ಇತಿಹಾಸವು ಈ ರೀತಿ ಕಾಣುತ್ತದೆ:
docker-compose restart nginx
curl localhost:8080/
docker-compose logs --tail 10 nginx
ಮತ್ತು ಅಂತಹ ನೂರಾರು ಅನುಕ್ರಮಗಳು ಇರಬಹುದು.
ಅವರಿಗೆ ಉಪಪ್ರಶ್ನೆಗಳು ಮತ್ತು ವೇರಿಯೇಬಲ್ಗಳನ್ನು ಬಳಸಿಕೊಂಡು ಕೋಡ್ ಬರೆಯುವುದು ಅವ್ಯವಸ್ಥೆಯ ಸಿಕ್ಕು ಆಗಿ ಬದಲಾಗುತ್ತದೆ. ಕೆಲವೊಮ್ಮೆ ನೀವು ನಿಮ್ಮ ಕೋಡ್ನ ಕ್ರಿಯೆಗಳ ಅನುಕ್ರಮವನ್ನು ಕಂಡುಹಿಡಿಯಲು ಪ್ರಯತ್ನಿಸುತ್ತಿರುವ ವಿವಿಧ IDE ವಿಂಡೋಗಳ ಸುತ್ತಲೂ ನುಗ್ಗಲು ಪ್ರಾರಂಭಿಸುತ್ತೀರಿ. ಇದು ಕಷ್ಟವಲ್ಲ, ಆದರೆ ಕೆಲವೊಮ್ಮೆ ಇದು ತುಂಬಾ ಕಿರಿಕಿರಿ ಉಂಟುಮಾಡುತ್ತದೆ.
ES6 ಗೆ ಸಂಪೂರ್ಣ ಬೆಂಬಲವಿಲ್ಲ.
ಇನ್ನೂ ಕೆಲವು ನ್ಯೂನತೆಗಳಿರಬಹುದು, ಆದರೆ ನಾನು ಬೇರೆ ಯಾವುದನ್ನೂ ಎದುರಿಸಲಿಲ್ಲ. ನೀವು NJS ಬಳಸಿಕೊಂಡು ನಕಾರಾತ್ಮಕ ಅನುಭವವನ್ನು ಹೊಂದಿದ್ದರೆ ಮಾಹಿತಿಯನ್ನು ಹಂಚಿಕೊಳ್ಳಿ.
ತೀರ್ಮಾನಕ್ಕೆ
NJS ಹಗುರವಾದ ಮುಕ್ತ-ಮೂಲ ಇಂಟರ್ಪ್ರಿಟರ್ ಆಗಿದ್ದು ಅದು Nginx ನಲ್ಲಿ ವಿವಿಧ ಜಾವಾಸ್ಕ್ರಿಪ್ಟ್ ಸ್ಕ್ರಿಪ್ಟ್ಗಳನ್ನು ಕಾರ್ಯಗತಗೊಳಿಸಲು ನಿಮಗೆ ಅನುಮತಿಸುತ್ತದೆ. ಅದರ ಅಭಿವೃದ್ಧಿಯ ಸಮಯದಲ್ಲಿ, ಕಾರ್ಯಕ್ಷಮತೆಗೆ ಹೆಚ್ಚಿನ ಗಮನ ನೀಡಲಾಯಿತು. ಸಹಜವಾಗಿ, ಇನ್ನೂ ಬಹಳಷ್ಟು ಕಾಣೆಯಾಗಿದೆ, ಆದರೆ ಯೋಜನೆಯನ್ನು ಸಣ್ಣ ತಂಡವು ಅಭಿವೃದ್ಧಿಪಡಿಸುತ್ತಿದೆ ಮತ್ತು ಅವರು ಸಕ್ರಿಯವಾಗಿ ಹೊಸ ವೈಶಿಷ್ಟ್ಯಗಳನ್ನು ಸೇರಿಸುತ್ತಿದ್ದಾರೆ ಮತ್ತು ದೋಷಗಳನ್ನು ಸರಿಪಡಿಸುತ್ತಿದ್ದಾರೆ. ಒಂದು ದಿನ NJS ನಿಮಗೆ ಬಾಹ್ಯ ಮಾಡ್ಯೂಲ್ಗಳನ್ನು ಸಂಪರ್ಕಿಸಲು ಅನುವು ಮಾಡಿಕೊಡುತ್ತದೆ ಎಂದು ನಾನು ಭಾವಿಸುತ್ತೇನೆ, ಇದು Nginx ಕಾರ್ಯವನ್ನು ಬಹುತೇಕ ಅನಿಯಮಿತಗೊಳಿಸುತ್ತದೆ. ಆದರೆ NGINX ಪ್ಲಸ್ ಇದೆ ಮತ್ತು ಹೆಚ್ಚಾಗಿ ಯಾವುದೇ ವೈಶಿಷ್ಟ್ಯಗಳಿಲ್ಲ!
ಮೂಲ: www.habr.com