ื™ืฆืจืชื™ ืžืื’ืจ PyPI ืžืฉืœื™ ืขื ื”ืจืฉืื” ื•-S3. ืขืœ Nginx

ื‘ืžืืžืจ ื–ื” ื‘ืจืฆื•ื ื™ ืœื—ืœื•ืง ืืช ื”ื ื™ืกื™ื•ืŸ ืฉืœื™ ืขื NJS, ืžืชื•ืจื’ืžืŸ JavaScript ืขื‘ื•ืจ Nginx ืฉืคื•ืชื— ืขืœ ื™ื“ื™ Nginx Inc, ื”ืžืชืืจ ืืช ื”ื™ื›ื•ืœื•ืช ื”ืขื™ืงืจื™ื•ืช ืฉืœื• ื‘ืืžืฆืขื•ืช ื“ื•ื’ืžื” ืืžื™ืชื™ืช. NJS ื”ื™ื ืชืช-ืงื‘ื•ืฆื” ืฉืœ JavaScript ื”ืžืืคืฉืจืช ืœืš ืœื”ืจื—ื™ื‘ ืืช ื”ืคื•ื ืงืฆื™ื•ื ืœื™ื•ืช ืฉืœ Nginx. ืœืฉืืœื” ืœืžื” ืžืชื•ืจื’ืžืŸ ืžืฉืœืš??? ื“ืžื™ื˜ืจื™ ื•ื•ืœื™ื ืกื‘ ืขื ื” ื‘ืคื™ืจื•ื˜. ื‘ืงื™ืฆื•ืจ: NJS ื”ื•ื ื‘ื“ืจืš nginx, ื•-JavaScript ื™ื•ืชืจ ืคืจื•ื’ืจืกื™ื‘ื™, "ืžืงื•ืจื™" ื•ืœืœื GC, ื‘ื ื™ื’ื•ื“ ืœ-Lua.

ืœืคื ื™ ื–ืžืŸ ืจื‘โ€ฆ

ื‘ืขื‘ื•ื“ื” ื”ืื—ืจื•ื ื” ืฉืœื™, ื™ืจืฉืชื™ ืืช gitlab ืขื ืžืกืคืจ ืฆื™ื ื•ืจื•ืช CI/CD ืกืกื’ื•ื ื™ื™ื ืขื docker-compose, dind ื•ืชืขื ื•ื’ื•ืช ืื—ืจื™ื, ืฉื”ื•ืขื‘ืจื• ืœ-kaniko rails. ื”ืชืžื•ื ื•ืช ืฉืฉื™ืžืฉื• ื‘ืขื‘ืจ ื‘-CI ื”ื•ืขื‘ืจื• ื‘ืฆื•ืจืชืŸ ื”ืžืงื•ืจื™ืช. ื”ื ืขื‘ื“ื• ื›ืžื• ืฉืฆืจื™ืš ืขื“ ื”ื™ื•ื ืฉื‘ื• ื”-IP ืฉืœ gitlab ืฉืœื ื• ื”ืฉืชื ื” ื•-CI ื”ืคืš ืœื“ืœืขืช. ื”ื‘ืขื™ื” ื”ื™ื™ืชื” ืฉืœืื—ืช ืžืชืžื•ื ื•ืช ื”-docker ืฉื”ืฉืชืชืคื• ื‘-CI ื”ื™ื™ืชื” git, ืฉืžืฉื›ื” ืžื•ื“ื•ืœื™ื ืฉืœ Python ื“ืจืš ssh. ืขื‘ื•ืจ ssh ืืชื” ืฆืจื™ืš ืžืคืชื— ืคืจื˜ื™ ื•... ื–ื” ื”ื™ื” ื‘ืชืžื•ื ื” ื™ื—ื“ ืขื ื™ื“ื•ืขื™ื_ืžืืจื—ื™ื. ื•ื›ืœ CI ื ื›ืฉืœ ืขื ืฉื’ื™ืืช ืื™ืžื•ืช ืžืคืชื— ืขืงื‘ ืื™ ื”ืชืืžื” ื‘ื™ืŸ ื”-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. ื”ื”ืชื—ื™ื™ื‘ื•ืช ื”ืื—ืจื•ื ื” ื‘ื–ืžืŸ ื”ื—ื™ืคื•ืฉ ื”ื™ื™ืชื” ืœืคื ื™ ืฉื ื”. ื”ืžืื’ืจ ื ืจืื” ื ื˜ื•ืฉ.

ืขืœ ื™ื“ื™ ืžืขื‘ืจ ืœืžืงื•ืจ ื•ืงืจื™ืื” PEP-503 ื”ื‘ื ืชื™ ืฉื ื™ืชืŸ ืœื”ืžื™ืจ XML ืœ-HTML ืชื•ืš ื›ื“ื™ ืชื ื•ืขื” ื•ืœืชืช ืœ-pip. ืœืื—ืจ ืฉื’ื•ื’ืœืชื™ ืขื•ื“ ืงืฆืช ืขืœ Nginx ื•-S3, ื ืชืงืœืชื™ ื‘ื“ื•ื’ืžื” ืฉืœ ืื™ืžื•ืช ื‘-S3 ืฉื ื›ืชื‘ ื‘-JS ืขื‘ื•ืจ Nginx. ื›ืš ืคื’ืฉืชื™ ืืช NJS.

ืื ืœื•ืงื—ื™ื ืืช ื”ื“ื•ื’ืžื” ื”ื–ื• ื›ื‘ืกื™ืก, ืฉืขื” ืœืื—ืจ ืžื›ืŸ ืจืื™ืชื™ ื‘ื“ืคื“ืคืŸ ืฉืœื™ ืืช ืื•ืชื• XML ื›ืžื• ื‘ืขืช ื”ืฉื™ืžื•ืฉ ื‘ืžื•ื“ื•ืœ ngx_aws_auth, ืื‘ืœ ื”ื›ืœ ื›ื‘ืจ ื”ื™ื” ื›ืชื•ื‘ ื‘-JS.

ืžืื•ื“ ืื”ื‘ืชื™ ืืช ืคืชืจื•ืŸ nginx. ืจืืฉื™ืช, ืชื™ืขื•ื“ ื˜ื•ื‘ ื•ื“ื•ื’ืžืื•ืช ืจื‘ื•ืช, ืฉื ื™ืช, ืื ื—ื ื• ืžืงื‘ืœื™ื ืืช ื›ืœ ื”ื˜ื•ื‘ ืฉืœ Nginx ืœืขื‘ื•ื“ื” ืขื ืงื‘ืฆื™ื (ืžื”ืงื•ืคืกื”), ืฉืœื™ืฉื™ืช, ื›ืœ ืžื™ ืฉื™ื•ื“ืข ืœื›ืชื•ื‘ ื”ื’ื“ืจื•ืช ืขื‘ื•ืจ Nginx ื™ื•ื›ืœ ืœื”ื‘ื™ืŸ ืžื” ื–ื” ืžื”. ืžื™ื ื™ืžืœื™ื–ื ื”ื•ื ื’ื ื™ืชืจื•ืŸ ืขื‘ื•ืจื™, ื‘ื”ืฉื•ื•ืื” ืœืคื™ื™ืชื•ืŸ ืื• ื’ื• (ืื ื ื›ืชื‘ ืžืืคืก), ืฉืœื ืœื“ื‘ืจ ืขืœ nexus.

TL;DR ืœืื—ืจ ื™ื•ืžื™ื™ื, ื’ืจืกืช ื”ื‘ื“ื™ืงื” ืฉืœ 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 ื”ืžืฆื™ื™ื ืช ืืช ื”ืžืคืจื™ื“ 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="/iw/${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}"

ืื™ืžื•ืช

ื‘-Gitlab ื ื™ืชืŸ ืœื”ืฉืชืžืฉ ื‘-JWT ืœืื™ืžื•ืช/ื”ืจืฉืื” ืฉืœ ืฉื™ืจื•ืชื™ื ื—ื™ืฆื•ื ื™ื™ื. ื‘ืืžืฆืขื•ืช ื”ื”ื ื—ื™ื” auth_request ื‘-Nginx, ื ื ืชื‘ ืืช ื ืชื•ื ื™ ื”ืื™ืžื•ืช ืœ-subrequest ื”ืžื›ื™ืœื” ืงืจื™ืืช ืคื•ื ืงืฆื™ื” ื‘ืกืงืจื™ืคื˜. ื”ืกืงืจื™ืคื˜ ื™ื‘ืฆืข ื‘ืงืฉืช ืžืฉื ื” ื ื•ืกืคืช ืœื›ืชื•ื‘ืช ื”-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 = require('aws-sdk') ื•ืื™ืŸ ืฆื•ืจืš ืœื›ืชื•ื‘ "ืื•ืคื ื™ื™ื" ืขื ืื™ืžื•ืช S3!

ื‘ื•ืื• ื ืขื‘ื•ืจ ืœื—ืกืจื•ื ื•ืช

ืขื‘ื•ืจื™, ื—ื•ืกืจ ื”ื™ื›ื•ืœืช ืœื™ื™ื‘ื ืžื•ื“ื•ืœื™ JS ื—ื™ืฆื•ื ื™ื™ื ื”ืคืš ืœืชื›ื•ื ื” ืœื ื ืขื™ืžื”, ืืš ืฆืคื•ื™ื”. ื”ืžืชื•ืืจ ื‘ื“ื•ื’ืžื” ืœืขื™ืœ require('crypto') is ืžื•ื“ื•ืœื™ื ืžื•ื‘ื ื™ื ื•ื“ื•ืจืฉื™ื ืจืง ืขื‘ื•ื“ื•ืช ืขื‘ื•ืจื. ืื™ืŸ ื’ื ื“ืจืš ืœืขืฉื•ืช ืฉื™ืžื•ืฉ ื—ื•ื–ืจ ื‘ืงื•ื“ ืžืกืงืจื™ืคื˜ื™ื ื•ื™ืฉ ืœื”ืขืชื™ืง ื•ืœื”ื“ื‘ื™ืง ืื•ืชื• ื‘ืงื‘ืฆื™ื ืฉื•ื ื™ื. ืื ื™ ืžืงื•ื•ื” ืฉื™ื•ื ืื—ื“ ื”ืคื•ื ืงืฆื™ื•ื ืœื™ื•ืช ื”ื–ื• ืชื™ื•ืฉื.

ื™ืฉ ืœื”ืฉื‘ื™ืช ืืช ื”ื“ื—ื™ืกื” ื’ื ืขื‘ื•ืจ ื”ืคืจื•ื™ืงื˜ ื”ื ื•ื›ื—ื™ ื‘-Nginx gzip off;

ืžื›ื™ื•ื•ืŸ ืฉืื™ืŸ ืžื•ื“ื•ืœ gzip ื‘-NJS ื•ืื™ ืืคืฉืจ ืœื—ื‘ืจ ืื•ืชื•; ืœื›ืŸ, ืื™ืŸ ื“ืจืš ืœืขื‘ื•ื“ ืขื ื ืชื•ื ื™ื ื“ื—ื•ืกื™ื. ื ื›ื•ืŸ, ื–ื” ืœื ืžืžืฉ ืžื™ื ื•ืก ืœืžืงืจื” ื”ื–ื”. ืื™ืŸ ื”ืจื‘ื” ื˜ืงืกื˜, ื•ื”ืงื‘ืฆื™ื ื”ืžื•ืขื‘ืจื™ื ื›ื‘ืจ ื“ื—ื•ืกื™ื ื•ื“ื—ื™ืกื” ื ื•ืกืคืช ืœื ืชืขื–ื•ืจ ืœื”ื ื”ืจื‘ื”. ื›ืžื• ื›ืŸ, ื–ื” ืœื ืฉื™ืจื•ืช ื›ืœ ื›ืš ื˜ืขื•ืŸ ืื• ืงืจื™ื˜ื™ ืฉืืชื” ืฆืจื™ืš ืœื”ืชืขืกืง ื‘ืืกืคืงืช ืชื•ื›ืŸ ื›ืžื” ืืœืคื™ื•ืช ืฉื ื™ื™ื” ืžื”ืจ ื™ื•ืชืจ.

ืื™ืชื•ืจ ื‘ืื’ื™ื ื‘ืกืงืจื™ืคื˜ ืœื•ืงื— ื–ืžืŸ ืจื‘ ื•ืžืชืืคืฉืจ ืจืง ื‘ืืžืฆืขื•ืช "ื”ื“ืคืกื•ืช" ื‘- error.log. ื‘ื”ืชืื ืœืคืจื˜ื™ ืจืžืช ื”ืจื™ืฉื•ื ื”ืžื•ื’ื“ืจื™ื, ืื–ื”ืจื” ืื• ืฉื’ื™ืื”, ืืคืฉืจ ืœื”ืฉืชืžืฉ ื‘-3 ืฉื™ื˜ื•ืช r.log, r.warn, r.error ื‘ื”ืชืืžื”. ืื ื™ ืžื ืกื” ืœื ืคื•ืช ื‘ืื’ื™ื ื‘ื›ืžื” ืกืงืจื™ืคื˜ื™ื ื‘ื›ืจื•ื (v8) ืื• ื‘ื›ืœื™ ื”ืงื•ื ืกื•ืœ ืฉืœ njs, ืื‘ืœ ืœื ื”ื›ืœ ื ื™ืชืŸ ืœื‘ื“ื•ืง ืฉื. ื‘ืขืช ืื™ืชื•ืจ ื‘ืื’ื™ื ื‘ืงื•ื“, ื›ืœื•ืžืจ ื‘ื“ื™ืงื•ืช ืคื•ื ืงืฆื™ื•ื ืœื™ื•ืช, ื”ื”ื™ืกื˜ื•ืจื™ื” ื ืจืื™ืช ื‘ืขืจืš ื›ืš:

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 ืฉืœ ื“ืžื™ื˜ืจื™ ื•ื•ืœื™ื ื˜ืกื‘

njs - ืกืงืจื™ืคื˜ื™ื ืžืงื•ืจื™ื™ื ืฉืœ JavaScript ื‘-nginx / ื ืื•ื ืฉืœ ื“ืžื™ื˜ืจื™ ื•ื•ืœื ื™ื™ื‘ ื‘-Saint HighLoad++ 2019

NJS ื‘ื™ื™ืฆื•ืจ / ื ืื•ื ืžืืช ื•ืกื™ืœื™ ืกื•ืฉื ื™ืงื•ื‘ ื‘-HighLoad++ 2019

ื—ืชื™ืžื” ื•ืื™ืžื•ืช ืฉืœ ื‘ืงืฉื•ืช REST ื‘-AWS

ืžืงื•ืจ: www.habr.com