ฉันสร้างที่เก็บ PyPI ของฉันด้วยการให้สิทธิ์และ S3 บน Nginx

ในบทความนี้ ฉันต้องการแบ่งปันประสบการณ์ของฉันกับ NJS ซึ่งเป็นล่าม JavaScript สำหรับ Nginx ที่พัฒนาโดย Nginx Inc โดยอธิบายความสามารถหลักโดยใช้ตัวอย่างจริง NJS เป็นชุดย่อยของ JavaScript ที่ช่วยให้คุณสามารถขยายฟังก์ชันการทำงานของ Nginx ได้ ถึงคำถาม ทำไมคุณถึงมีล่ามของคุณเอง??? Dmitry Volyntsev ตอบโดยละเอียด กล่าวโดยย่อ: NJS เป็นแบบ nginx-way และ JavaScript มีความก้าวหน้ามากกว่า "ดั้งเดิม" และไม่มี GC ซึ่งแตกต่างจาก Lua

กระโน้น…

ในงานสุดท้ายของฉัน ฉันได้สืบทอด Gitlab ด้วยไปป์ไลน์ CI/CD หลายแบบที่มี docker-compose, dind และความสุขอื่นๆ ซึ่งถูกถ่ายโอนไปยัง kaniko rails รูปภาพที่เคยใช้ใน CI ก่อนหน้านี้ถูกย้ายในรูปแบบดั้งเดิม พวกเขาทำงานได้อย่างถูกต้องจนถึงวันที่ IP gitlab ของเราเปลี่ยนไปและ CI กลายเป็นฟักทอง ปัญหาคือหนึ่งในอิมเมจนักเทียบท่าที่เข้าร่วมใน CI มีคอมไพล์ ซึ่งดึงโมดูล Python ผ่าน ssh สำหรับ ssh คุณต้องมีรหัสส่วนตัวและ... มันอยู่ในรูปภาพพร้อมกับknown_hosts และ CI ใด ๆ ล้มเหลวโดยมีข้อผิดพลาดในการตรวจสอบคีย์เนื่องจาก IP จริงไม่ตรงกันกับ IP ที่ระบุในknown_hosts ภาพใหม่ถูกประกอบอย่างรวดเร็วจาก Dockfiles ที่มีอยู่และเพิ่มตัวเลือก StrictHostKeyChecking no. แต่รสชาติที่ไม่ดียังคงอยู่และมีความปรารถนาที่จะย้าย libs ไปยังที่เก็บ PyPI ส่วนตัว โบนัสเพิ่มเติมหลังจากเปลี่ยนมาใช้ PyPI ส่วนตัวคือไปป์ไลน์ที่ง่ายกว่าและคำอธิบายปกติของ Requirements.txt

สุภาพบุรุษได้เลือกแล้ว!

เราดำเนินการทุกอย่างบนคลาวด์และ Kubernetes และท้ายที่สุดแล้ว เราต้องการรับบริการเล็กๆ ที่เป็นคอนเทนเนอร์ไร้สัญชาติพร้อมพื้นที่จัดเก็บข้อมูลภายนอก เนื่องจากเราใช้ S3 จึงให้ความสำคัญกับมัน และหากเป็นไปได้ด้วยการรับรองความถูกต้องใน gitlab (คุณสามารถเพิ่มได้เองหากจำเป็น)

การค้นหาอย่างรวดเร็วให้ผลลัพธ์หลายประการ: s3pypi, pypicloud และตัวเลือกที่มีการสร้างไฟล์ html สำหรับหัวผักกาด "ด้วยตนเอง" ตัวเลือกสุดท้ายหายไปเอง

s3pypi: นี่คือ cli สำหรับการใช้โฮสติ้ง S3 เราอัปโหลดไฟล์ สร้าง html และอัปโหลดไปยังบัคเก็ตเดียวกัน เหมาะสำหรับใช้ในบ้าน

pypicloud: ดูเหมือนเป็นโปรเจ็กต์ที่น่าสนใจ แต่หลังจากอ่านเอกสารแล้ว ฉันก็ผิดหวัง แม้จะมีเอกสารที่ดีและความสามารถในการขยายให้เหมาะกับความต้องการของคุณ แต่ในความเป็นจริง กลับกลายเป็นว่ามีความซ้ำซ้อนและกำหนดค่าได้ยาก การแก้ไขโค้ดให้เหมาะสมกับงานของคุณตามประมาณการ ณ ขณะนั้น อาจจะใช้เวลาประมาณ 3-5 วัน บริการยังต้องการฐานข้อมูล เราทิ้งมันไว้ในกรณีที่เราไม่พบสิ่งอื่นใด

การค้นหาเชิงลึกมากขึ้นทำให้ได้โมดูลสำหรับ Nginx, ngx_aws_auth ผลการทดสอบของเขาคือ XML ที่แสดงในเบราว์เซอร์ ซึ่งแสดงเนื้อหาของบัคเก็ต S3 การกระทำครั้งสุดท้ายในขณะที่ทำการค้นหาคือหนึ่งปีที่แล้ว พื้นที่เก็บข้อมูลดูเหมือนถูกทิ้งร้าง

โดยเข้าไปอ่านที่มา เป๊ป-503 ฉันรู้ว่า XML สามารถแปลงเป็น HTML ได้ทันทีและมอบให้กับ pip หลังจาก googling เพิ่มเติมเล็กน้อยเกี่ยวกับ Nginx และ S3 ฉันพบตัวอย่างการรับรองความถูกต้องใน S3 ที่เขียนด้วย JS สำหรับ Nginx นั่นคือวิธีที่ฉันได้พบกับ NJS

จากตัวอย่างนี้เป็นพื้นฐาน ชั่วโมงต่อมาฉันเห็น XML เดียวกันกับเมื่อใช้โมดูล ngx_aws_auth ในเบราว์เซอร์ของฉัน แต่ทุกอย่างถูกเขียนด้วย JS แล้ว

ฉันชอบโซลูชัน nginx มาก ประการแรก เอกสารที่ดีและตัวอย่างมากมาย ประการที่สอง เราได้รับข้อดีทั้งหมดของ Nginx สำหรับการทำงานกับไฟล์ (นอกกรอบ) ประการที่สาม ใครก็ตามที่รู้วิธีเขียนการกำหนดค่าสำหรับ Nginx จะสามารถเข้าใจได้ว่าอะไรคืออะไร ความเรียบง่ายก็เป็นข้อดีสำหรับฉันเช่นกัน เมื่อเทียบกับ Python หรือ Go (หากเขียนตั้งแต่เริ่มต้น) ไม่ต้องพูดถึง Nexus

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 สตริงบางประเภทจะถูกสร้างขึ้น ซึ่งมีการลงนาม (HMAC_SHA1) โดยใช้ SECRET_KEY ต่อไปเป็นเส้นเหมือน 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 Sign 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: นี่คือตัวแปรที่สร้างคำขอไปยัง S3 ขึ้นอยู่กับ uri เริ่มต้น หากคุณต้องการรับเนื้อหาของ "รูท" คุณจะต้องสร้างคำขอ uri ที่ระบุตัวคั่น delimiterซึ่งจะส่งคืนรายการองค์ประกอบ XML ของ CommonPrefixes ทั้งหมดที่สอดคล้องกับไดเร็กทอรี (ในกรณีของ PyPI คือรายการแพ็คเกจทั้งหมด) หากคุณต้องการรับรายการเนื้อหาในไดเร็กทอรีเฉพาะ (รายการเวอร์ชันแพ็กเกจทั้งหมด) คำขอ uri จะต้องมีฟิลด์คำนำหน้าที่มีชื่อของไดเร็กทอรี (แพ็กเกจ) ซึ่งจำเป็นต้องลงท้ายด้วยเครื่องหมายทับ / มิฉะนั้น อาจเกิดการขัดแย้งกันได้เมื่อร้องขอเนื้อหาของไดเร็กทอรี เป็นต้น มีไดเร็กทอรี aiohttp-request และ aiohttp-requests และหากคำขอระบุไว้ /?prefix=aiohttp-requestจากนั้นการตอบกลับจะมีเนื้อหาของทั้งสองไดเร็กทอรี หากมีเครื่องหมายทับต่อท้าย /?prefix=aiohttp-request/จากนั้นการตอบกลับจะมีเฉพาะไดเร็กทอรีที่จำเป็นเท่านั้น และหากเราขอไฟล์ ผลลัพธ์ที่ได้ก็ไม่ควรแตกต่างจากไฟล์ต้นฉบับ

บันทึกและรีสตาร์ท 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>&#34;00000000000000000000000000000000-1&#34;</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>&#34;b2d76df4aeb4493c5456366748218093&#34;</ETag>
    <Size>93183</Size>
    <Owner>
      <ID>02d6176db174dc93cb1b899f7c6078f08654445fe8cf1b6ce98d8855f66bdbf4</ID>
      <DisplayName></DisplayName>
    </Owner>
    <StorageClass>STANDARD</StorageClass>
  </Contents>
</ListBucketResult>

จากรายการไฟล์เราจะรับเฉพาะองค์ประกอบเท่านั้น Key.

สิ่งที่เหลืออยู่คือแยกวิเคราะห์ XML ที่เป็นผลลัพธ์แล้วส่งออกเป็น HTML โดยแทนที่ส่วนหัว Content-Type ด้วย text/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="/th/${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

เราทำซ้ำกับ libs ของเรา

# Создаем для тестов новое окружение
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}"

การรับรอง

ใน Gitlab คุณสามารถใช้ JWT เพื่อรับรองความถูกต้อง/อนุญาตบริการภายนอกได้ การใช้คำสั่ง auth_request ใน Nginx เราจะเปลี่ยนเส้นทางข้อมูลการตรวจสอบสิทธิ์ไปยังคำขอย่อยที่มีการเรียกใช้ฟังก์ชันในสคริปต์ สคริปต์จะสร้างคำขอย่อยอีกครั้งไปยัง URL ของ Gitlab และหากข้อมูลการตรวจสอบสิทธิ์ถูกระบุอย่างถูกต้อง Gitlab จะส่งคืนโค้ด 200 และอนุญาตให้อัปโหลด/ดาวน์โหลดแพ็คเกจได้ ทำไมไม่ใช้แบบสอบถามย่อยเดียวแล้วส่งข้อมูลไปที่ Gitlab ทันที เนื่องจากเราจะต้องแก้ไขไฟล์การกำหนดค่า Nginx ทุกครั้งที่เราทำการเปลี่ยนแปลงการอนุญาต และนี่เป็นงานที่ค่อนข้างน่าเบื่อ นอกจากนี้ หาก Kubernetes ใช้นโยบายระบบไฟล์รูทแบบอ่านอย่างเดียว สิ่งนี้จะเพิ่มความซับซ้อนมากยิ่งขึ้นเมื่อแทนที่ nginx.conf ผ่าน configmap และเป็นไปไม่ได้เลยที่จะกำหนดค่า Nginx ผ่าน configmap ในขณะเดียวกันก็ใช้นโยบายที่ห้ามการเชื่อมต่อโวลุ่ม (pvc) และระบบไฟล์รูทแบบอ่านอย่างเดียว (สิ่งนี้ก็เกิดขึ้นเช่นกัน)

เมื่อใช้ตัวกลาง 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 = need('aws-sdk') และไม่จำเป็นต้องเขียน “bike” ด้วยการตรวจสอบสิทธิ์ S3!

มาดูข้อเสียกันดีกว่า

สำหรับฉัน การไม่สามารถนำเข้าโมดูล JS ภายนอกกลายเป็นคุณสมบัติที่ไม่พึงประสงค์ แต่ก็คาดหวังได้ อธิบายไว้ในตัวอย่างข้างต้น need('crypto') is โมดูลบิวด์อิน และต้องการแต่ผลงานเท่านั้น นอกจากนี้ยังไม่มีวิธีนำโค้ดจากสคริปต์มาใช้ซ้ำ และคุณต้องคัดลอกและวางลงในไฟล์อื่น ฉันหวังว่าสักวันหนึ่งฟังก์ชันนี้จะถูกนำไปใช้

ต้องปิดใช้งานการบีบอัดสำหรับโครงการปัจจุบันใน Nginx gzip off;

เนื่องจากไม่มีโมดูล gzip ใน NJS และไม่สามารถเชื่อมต่อได้ ดังนั้นจึงไม่มีวิธีทำงานกับข้อมูลที่บีบอัด จริงอยู่นี่ไม่ใช่ข้อเสียจริงๆสำหรับกรณีนี้ มีข้อความไม่มากและไฟล์ที่ถ่ายโอนได้รับการบีบอัดแล้วและการบีบอัดเพิ่มเติมจะไม่ช่วยอะไรได้มากนัก นอกจากนี้ นี่ไม่ใช่บริการโหลดหรือสำคัญที่คุณต้องกังวลกับการส่งเนื้อหาเร็วขึ้นสองสามมิลลิวินาที

การดีบักสคริปต์ใช้เวลานานและทำได้ผ่านการ "พิมพ์" ใน error.log เท่านั้น ขึ้นอยู่กับข้อมูลระดับการบันทึกที่ตั้งไว้ คำเตือนหรือข้อผิดพลาด คุณสามารถใช้ 3 วิธี r.log, r.warn, r.error ตามลำดับ ฉันพยายามแก้ไขสคริปต์บางตัวใน Chrome (v8) หรือเครื่องมือคอนโซล njs แต่ไม่ใช่ทุกสิ่งที่สามารถตรวจสอบได้ เมื่อทำการดีบักโค้ด หรือที่รู้จักกันในชื่อ Functional Testing ประวัติจะมีลักษณะดังนี้:

docker-compose restart nginx
curl localhost:8080/
docker-compose logs --tail 10 nginx

และอาจมีลำดับเช่นนี้ได้หลายร้อยลำดับ

การเขียนโค้ดโดยใช้แบบสอบถามย่อยและตัวแปรสำหรับสิ่งเหล่านั้นจะกลายเป็นเรื่องยุ่งเหยิง บางครั้งคุณเริ่มวิ่งไปรอบๆ หน้าต่าง IDE ต่างๆ โดยพยายามค้นหาลำดับการทำงานของโค้ดของคุณ ไม่ใช่เรื่องยากแต่บางครั้งก็น่ารำคาญมาก

ไม่มีการรองรับ ES6 อย่างเต็มรูปแบบ

อาจมีข้อบกพร่องอื่น ๆ แต่ฉันไม่พบสิ่งอื่นใด แบ่งปันข้อมูลหากคุณมีประสบการณ์เชิงลบในการใช้ NJS

ข้อสรุป

NJS เป็นล่ามโอเพ่นซอร์สน้ำหนักเบาที่ช่วยให้คุณสามารถใช้สคริปต์ JavaScript ต่างๆ ใน ​​Nginx ในระหว่างการพัฒนา มีการให้ความสนใจอย่างมากต่อประสิทธิภาพ แน่นอนว่ายังขาดหายไปอีกมาก แต่โครงการนี้กำลังได้รับการพัฒนาโดยทีมงานเล็กๆ และพวกเขากำลังเพิ่มคุณสมบัติใหม่และแก้ไขข้อบกพร่องอย่างแข็งขัน ฉันหวังว่าสักวันหนึ่ง NJS จะอนุญาตให้คุณเชื่อมต่อโมดูลภายนอก ซึ่งจะทำให้ฟังก์ชัน Nginx แทบจะไร้ขีดจำกัด แต่มี NGINX Plus และมีแนวโน้มว่าจะไม่มีฟีเจอร์!

พื้นที่เก็บข้อมูลพร้อมรหัสเต็มสำหรับบทความ

njs-pypi พร้อมรองรับ AWS Sign v4

คำอธิบายของคำสั่งของโมดูล ngx_http_js_module

พื้นที่เก็บข้อมูลอย่างเป็นทางการของ NJS и เอกสาร

ตัวอย่างการใช้ NJS จาก Dmitry Volintsev

njs - การเขียนสคริปต์ JavaScript ดั้งเดิมใน nginx / สุนทรพจน์โดย Dmitry Volnyev ที่ Saint HighLoad++ 2019

NJS ในการผลิต / สุนทรพจน์โดย Vasily Soshnikov ที่งาน HighLoad++ 2019

การลงนามและการรับรองความถูกต้องของคำขอ REST ใน AWS

ที่มา: will.com