Π― сдСлал свой PyPI-Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚Π°Ρ€ΠΈΠΉ с Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠ΅ΠΉ ΠΈ S3. На Nginx

Π’ Π΄Π°Π½Π½ΠΎΠΉ ΡΡ‚Π°Ρ‚ΡŒΠ΅ Ρ…ΠΎΡ‡Ρƒ подСлится ΠΎΠΏΡ‹Ρ‚ΠΎΠΌ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с NJS, ΠΈΠ½Ρ‚Π΅Ρ€ΠΏΡ€Π΅Ρ‚Π°Ρ‚ΠΎΡ€Π° JavaScript для Nginx Ρ€Π°Π·Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅ΠΌΠΎΠ³ΠΎ Π² ΠΊΠΎΠΌΠΏΠ°Π½ΠΈΠΈ Nginx Inc, описав Π½Π° Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠΌ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π΅ Π΅Π³ΠΎ основныС возмоТности. NJS это подмноТСство ЯП JavaScript, ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ΅ позволяСт Ρ€Π°ΡΡˆΠΈΡ€ΠΈΡ‚ΡŒ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΡΡ‚ΡŒ Nginx. На вопрос Π·Π°Ρ‡Π΅ΠΌ свой ΠΈΠ½Ρ‚Π΅Ρ€ΠΏΡ€Π΅Ρ‚Π°Ρ‚ΠΎΡ€??? ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½ΠΎ ΠΎΡ‚Π²Π΅Ρ‚ΠΈΠ» Π”ΠΌΠΈΡ‚Ρ€ΠΈΠΉ Π’ΠΎΠ»Ρ‹Π½Ρ†Π΅Π². Если Π²ΠΊΡ€Π°Ρ‚Ρ†Π΅: NJS это nginx-way, Π° JavaScript Π±ΠΎΠ»Π΅Π΅ прогрСссивный, Β«Ρ€ΠΎΠ΄Π½ΠΎΠΉΒ» ΠΈ Π±Π΅Π· GC Π² ΠΎΡ‚Π»ΠΈΡ‡ΠΈΠΈ ΠΎΡ‚ Lua.

A long time ago…

На ΠΏΡ€ΠΎΡˆΠ»ΠΎΠΉ Ρ€Π°Π±ΠΎΡ‚Π΅ Π² наслСдство ΠΌΠ½Π΅ достался gitlab с Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ количСством Ρ€Π°Π·Π½ΠΎΡˆΠ΅Ρ€ΡΡ‚Ρ‹Ρ… CI/CD-ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ΠΎΠ² с docker-compose, dind ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΠΌΠΈ прСлСстями, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π±Ρ‹Π»ΠΈ ΠΏΠ΅Ρ€Π΅Π²Π΅Π΄Π΅Π½Ρ‹ Π½Π° Ρ€Π΅Π»ΡŒΡΡ‹ kaniko. ΠžΠ±Ρ€Π°Π·Ρ‹ ΠΆΠ΅, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ использовались Ρ€Π°Π½Π΅Π΅ Π² CI, ΠΏΠ΅Ρ€Π΅Π΅Ρ…Π°Π»ΠΈ Π² ΠΏΠ΅Ρ€Π²ΠΎΠ·Π΄Π°Π½Π½ΠΎΠΌ Π²ΠΈΠ΄Π΅. Π Π°Π±ΠΎΡ‚Π°Π»ΠΈ ΠΎΠ½ΠΈ исправно Π΄ΠΎ Ρ‚ΠΎΠ³ΠΎ дня, ΠΏΠΎΠΊΠ° Ρƒ нашСго gitlab Π½Π΅ смСнился IP ΠΈ CI прСвратился Π² Ρ‚Ρ‹ΠΊΠ²Ρƒ. ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ° Π±Ρ‹Π»Π° Π² Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ Π² ΠΎΠ΄Π½ΠΎΠΌ ΠΈΠ· docker-ΠΎΠ±Ρ€Π°Π·ΠΎΠ², ΡƒΡ‡Π°ΡΡ‚Π²ΠΎΠ²Π°Π²ΡˆΠ΅Π³ΠΎ Π² CI, Π±Ρ‹Π» git, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΏΠΎ ssh тянул Python-ΠΌΠΎΠ΄ΡƒΠ»ΠΈ. Для ssh Π½ΡƒΠΆΠ΅Π½ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ ΠΊΠ»ΡŽΡ‡ ΠΈ … ΠΎΠ½ Π±Ρ‹Π» Π² ΠΎΠ±Ρ€Π°Π·Π΅ вмСстС с known_hosts. И любой CI Π·Π°Π²Π΅Ρ€ΡˆΠ°Π»ΡΡ ошибкой ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΈ ΠΊΠ»ΡŽΡ‡Π° ΠΈΠ·-Π·Π° нСсовпадСния Ρ€Π΅Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ IP ΠΈ ΡƒΠΊΠ°Π·Π°Π½Π½ΠΎΠ³ΠΎ Π² known_hosts. Из ΠΈΠΌΠ΅ΡŽΡ‰ΠΈΡ…ΡΡ Dockfile-ΠΎΠ² Π±Ρ‹Π» быстро собран Π½ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π· ΠΈ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½Π° опция StrictHostKeyChecking no. Но нСприятный привкус остался ΠΈ появилось ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ пСрСнСсти Π»ΠΈΠ±Ρ‹ Π² ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ PyPI-Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ. Π”ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ΠΌ бонусом, послС ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Π° Π½Π° ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Ρ‹ΠΉ PyPI, становился Π±ΠΎΠ»Π΅Π΅ простой ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½ ΠΈ Π½ΠΎΡ€ΠΌΠ°Π»ΡŒΠ½ΠΎΠ΅ описаниС requirements.txt

Π’Ρ‹Π±ΠΎΡ€ сдСлан, Господа!

ΠœΡ‹ всё ΠΊΡ€ΡƒΡ‚ΠΈΠΌ Π² ΠΎΠ±Π»Π°ΠΊΠ°Ρ… ΠΈ Kubernetes ΠΈ Π² ΠΈΡ‚ΠΎΠ³Π΅ Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ нСбольшой сСрвис ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ прСдставлял ΠΈΠ· сСбя stateless-ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ с внСшнСм Ρ…Ρ€Π°Π½ΠΈΠ»ΠΈΡ‰Π΅ΠΌ. Ну Π° Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΌΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ 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, смоТСт Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ Ρ‡Ρ‚ΠΎ ΠΊ Ρ‡Π΅ΠΌΡƒ. Π’Π°ΠΊ ΠΆΠ΅ плюсом для мСня являСтся ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΠΈΠ·ΠΌ, ΠΏΠΎ ΡΡ€Π°Π²Π½Π΅Π½ΠΈΡŽ с Python ΠΈΠ»ΠΈ Go (Ссли ΠΏΠΈΡΠ°Ρ‚ΡŒ с нуля), Π½Π΅ говоря ΡƒΠΆ ΠΎΠ± nexus.

TL;DR Π§Π΅Ρ€Π΅Π· 2 дня тСстовая вСрсия PyPi ΡƒΠΆΠ΅ Π±Ρ‹Π»Π° использована Π² CI.

Как это Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚?

Π’ Nginx подгруТаСтся ΠΌΠΎΠ΄ΡƒΠ»ΡŒ ngx_http_js_module, Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ Π² ΠΎΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ docker-ΠΎΠ±Ρ€Π°Π·. Π˜ΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠ΅ΠΌ наш скрипт c ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Ρ‹ js_importΠ² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ Nginx. Π’Ρ‹Π·ΠΎΠ² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ осущСствляСтся Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²ΠΎΠΉ js_content. Для установки ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ²Π° js_set, которая Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚ΠΎΠΌ ΠΏΡ€ΠΈΠ½ΠΈΠΌΠ°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡŽ ΠΎΠΏΠΈΡΠ°Π½Π½ΡƒΡŽ Π² скриптС. А Π²ΠΎΡ‚ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ подзапросы Π² NJS ΠΌΡ‹ ΠΌΠΎΠΆΠ΅ΠΌ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Nginx, Π½ΠΈ ΠΊΠ°ΠΊΠΈΡ… Π’Π°ΠΌ Ρ‚Π°ΠΌ XMLHttpRequest. Для этого Π² ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ Nginx Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ локСйшн. А Π² скриптС Π΄ΠΎΠ»ΠΆΠ΅Π½ Π±Ρ‹Ρ‚ΡŒ описан подзапрос (subrequest) ΠΊ этому Π»ΠΎΠΊΠ΅ΠΉΡˆΠ΅Π½Ρƒ. Π§Ρ‚ΠΎΠ±Ρ‹ ΠΈΠΌΠ΅Ρ‚ΡŒ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΎΠ±Ρ€Π°Ρ‚ΠΈΡ‚ΡŒΡΡ ΠΊ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ ΠΈΠ· ΠΊΠΎΠ½Ρ„ΠΈΠ³Π° 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, ΠΏΠ΅Ρ€Π΅Π²Π΅Π΄Π΅Π½Π° Π² статус deprecated)

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. Если Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ содСрТимоС «корня», Π² Ρ‚Π°ΠΊΠΎΠΌ случаС Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΡΡ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ uri-запрос с ΡƒΠΊΠ°Π·Π°Π½ΠΈΠ΅ΠΌ раздСлитСля delimiter, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π²Π΅Ρ€Π½Π΅Ρ‚ список всСх xml-элСмСнтов CommonPrefixes, Ρ‡Ρ‚ΠΎ соотвСтствуСт дирСкториям (Π² случаС с PyPI, список всСх ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ²). Если Π½ΡƒΠΆΠ½ΠΎ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ список содСрТимого Π² ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎΠΉ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ (список всСх вСрсий ΠΏΠ°ΠΊΠ΅Ρ‚ΠΎΠ²), Ρ‚ΠΎΠ³Π΄Π° uri-запрос Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅Ρ€ΠΆΠ°Ρ‚ΡŒ ΠΏΠΎΠ»Π΅ prefix с ΠΈΠΌΠ΅Π½Π΅ΠΌ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ (ΠΏΠ°ΠΊΠ΅Ρ‚Π°) ΠΎΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎ Π·Π°ΠΊΠ°Π½Ρ‡ΠΈΠ²Π°ΡŽΡ‰ΠΈΠΉΡΡ Π½Π° слэш /. Π’ ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π½ΠΎΠΌ случаС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Ρ‹ ΠΊΠΎΠ»Π»ΠΈΠ·ΠΈΠΈ ΠΏΡ€ΠΈ запросС содСрТимого Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€. Π•ΡΡ‚ΡŒ Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΎΡ€ΠΈΠΈ 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>&#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="/${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, ΠΏΠ΅Ρ€Π΅Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΎΠ½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ Π² подзапрос содСрТащий Π²Ρ‹Π·ΠΎΠ² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΈ Π² скриптС. Π’ скриптС Π±ΡƒΠ΄Π΅Ρ‚ сдСлан Π΅Ρ‰Ρ‘ ΠΎΠ΄ΠΈΠ½ подзапрос Π½Π° url Gitlab-Π° ΠΈ Ссли Π°ΡƒΡ‚Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΠΎΠ½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ ΡƒΠΊΠ°Π·Π°Π½Ρ‹ Π±Ρ‹Π»ΠΈ Π²Π΅Ρ€Π½ΠΎ, Ρ‚ΠΎ Gitlab Π²Π΅Ρ€Π½Π΅Ρ‚ ΠΊΠΎΠ΄ 200 ΠΈ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½Π° Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ°/скачиваниС ΠΏΠ°ΠΊΠ΅Ρ‚Π°. ΠŸΠΎΡ‡Π΅ΠΌΡƒ Π½Π΅ Π²ΠΎΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ ΠΎΠ΄Π½ΠΈΠΌ подзапросом ΠΈ сразу Π½Π΅ ΠΎΡ‚ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Π΄Π°Π½Π½Ρ‹Π΅ Π² Gitlab? ΠŸΠΎΡ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ придСтся Ρ‚ΠΎΠ³Π΄Π° ΠΏΡ€Π°Π²ΠΈΡ‚ΡŒ Ρ„Π°ΠΉΠ» ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ Nginx ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ Ρ€Π°Π·, ΠΊΠ°ΠΊ Ρƒ нас Π±ΡƒΠ΄ΡƒΡ‚ ΠΊΠ°ΠΊΠΈΠ΅-Ρ‚ΠΎ измСнСния Π² Π°Π²Ρ‚ΠΎΡ€ΠΈΠ·Π°Ρ†ΠΈΠΈ, Π° это достаточно ΠΌΡƒΡ‚ΠΎΡ€Π½ΠΎΠ΅ занятиС. Π’Π°ΠΊ ΠΆΠ΅, Ссли Π² Kubernetes ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠ° read-only root filesystem, Ρ‚ΠΎ это Π΅Ρ‰Ρ‘ большС добавляСт слоТностСй ΠΏΡ€ΠΈ ΠΏΠΎΠ΄ΠΌΠ΅Π½Π΅ nginx.conf Ρ‡Π΅Ρ€Π΅Π· configmap. И становится Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½ΠΎ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π° конфигурация Nginx Ρ‡Π΅Ρ€Π΅Π· configmap ΠΏΡ€ΠΈ ΠΎΠ΄Π½ΠΎΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΠΌ использовании ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊ Π·Π°ΠΏΡ€Π΅Ρ‰Π°ΡŽΡ‰ΠΈΡ… ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ Ρ‚ΠΎΠΌΠΎΠ² (pvc) ΠΈ read-only root filesystem (Ρ‚Π°ΠΊΠΎΠ΅ Ρ‚ΠΎΠΆΠ΅ Π±Ρ‹Π²Π°Π΅Ρ‚).

Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΏΡ€ΠΎΠΌΠ΅ΠΆΡƒΡ‚ΠΎΡ‡Π½Ρ‹ΠΌ Π·Π²Π΅Π½ΠΎΠΌ 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’), это build-in-ΠΌΠΎΠ΄ΡƒΠ»ΠΈ ΠΈ require Ρ€Π°Π±ΠΎΡ‚Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ для Π½ΠΈΡ…. Π’Π°ΠΊ ΠΆΠ΅ Π½Π΅Ρ‚ возмоТности ΠΏΠ΅Ρ€Π΅ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΊΠΎΠ΄ ΠΈΠ· скриптов ΠΈ приходится ΠΊΠΎΠΏΠΈ-ΠΏΠ°ΡΡ‚ΠΈΡ‚ΡŒ Π΅Π³ΠΎ ΠΏΠΎ Ρ€Π°Π·Π½Ρ‹ΠΌ Ρ„Π°ΠΉΠ»Π°ΠΌ. НадСюсь, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ³Π΄Π°-Π½ΠΈΠ±ΡƒΠ΄ΡŒ этот Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½.

Π’Π°ΠΊ ΠΆΠ΅ для Ρ‚Π΅ΠΊΡƒΡ‰Π΅Π³ΠΎ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π² Nginx Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ ΠΎΡ‚ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΎ сТатиС gzip off;

ΠŸΠΎΡ‚ΠΎΠΌΡƒ, Ρ‡Ρ‚ΠΎ Π½Π΅Ρ‚ gzip-модуля Π² NJS ΠΈ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π΅Π³ΠΎ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, соотвСствСнно Π½Π΅Ρ‚ возмоТности Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ с сТатыми Π΄Π°Π½Π½Ρ‹ΠΌΠΈ. ΠŸΡ€Π°Π²Π΄Π°, Π½Π΅ особо это ΠΈ минус для Π΄Π°Π½Π½ΠΎΠ³ΠΎ кСйса. ВСкста Π½Π΅ ΠΌΠ½ΠΎΠ³ΠΎ, Π° ΠΏΠ΅Ρ€Π΅Π΄Π°Π²Π°Π΅ΠΌΡ‹Π΅ Ρ„Π°ΠΉΠ»Ρ‹ ΡƒΠΆΠ΅ сТатыС ΠΈ Π΄ΠΎΠΏΠΎΠ»Π½ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ΅ сТатиС ΠΈΠΌ Π½Π΅ особо ΠΏΠΎΠΌΠΎΠΆΠ΅Ρ‚. Π’Π°ΠΊ ΠΆΠ΅ это Π½Π΅ Π½Π° ΡΡ‚ΠΎΠ»ΡŒΠΊΠΎ Π½Π°Π³Ρ€ΡƒΠΆΠ΅Π½Π½Ρ‹ΠΉ ΠΈΠ»ΠΈ ΠΊΡ€ΠΈΡ‚ΠΈΡ‡Π½Ρ‹ΠΉ сСрвис, Ρ‡Ρ‚ΠΎΠ±Ρ‹ Π·Π°ΠΌΠΎΡ€Π°Ρ‡ΠΈΠ²Π°Ρ‚ΡŒΡΡ с ΠΎΡ‚Π΄Π°Ρ‡Π΅ΠΉ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚Π° Π½Π° нСсколько миллисСкунд быстрСС.

ΠžΡ‚Π»Π°Π΄ΠΊΠ° скрипта долгая ΠΈ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½Π° Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Ρ‡Π΅Ρ€Π΅Π· Β«ΠΏΡ€ΠΈΠ½Ρ‚Ρ‹Β» Π² error.log. Π’ зависимости ΠΎΡ‚ выставлСнного уровня логирования info, warn ΠΈΠ»ΠΈ error Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ 3 ΠΌΠ΅Ρ‚ΠΎΠ΄Π° r.log, r.warn, r.error соотвСтствСнно. НСкоторыС скрипты ΠΏΡ‹Ρ‚Π°ΡŽΡΡŒ ΠΎΡ‚Π»Π°ΠΆΠΈΠ²Π°Ρ‚ΡŒ Π² Chrome (v8) ΠΈΠ»ΠΈ консольной Ρ‚ΡƒΠ»Π·Π΅ njs, Π½ΠΎ Π½Π΅ всС Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ Ρ‚Π°ΠΌ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΈΡ‚ΡŒ. ΠŸΡ€ΠΈ ΠΎΡ‚Π»Π°Π΄ΠΊΠ΅ ΠΊΠΎΠ΄Π°, Π°ΠΊΠ° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π»ΡŒΠ½ΠΎΠ΅ тСстированиС, history выглядит ΠΏΡ€ΠΈΠΌΠ΅Ρ€Π½ΠΎ Ρ‚Π°ΠΊ:

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

ΠΈ Ρ‚Π°ΠΊΠΈΡ… ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚Π΅ΠΉ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ сотни.

НаписаниС ΠΊΠΎΠ΄Π° с использованиС подзапросов ΠΈ ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ… для Π½ΠΈΡ…, прСвращаСтся Π² Π·Π°ΠΏΡƒΡ‚Π°Π½Π½Ρ‹ΠΉ ΠΊΠ»ΡƒΠ±ΠΎΠΊ. Иногда Π½Π°Ρ‡ΠΈΠ½Π°Π΅ΡˆΡŒ ΠΌΠ΅Ρ‚Π°Ρ‚ΡŒΡΡ ΠΏΠΎ Ρ€Π°Π·Π½Ρ‹ΠΌ ΠΎΠΊΠ½Π°ΠΌ IDE ΠΏΡ‹Ρ‚Π°ΡΡΡŒ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Ρ‚ΡŒΡΡ Π² ΠΏΠΎΡΠ»Π΅Π΄ΠΎΠ²Π°Ρ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ дСйствий Ρ‚Π²ΠΎΠ΅Π³ΠΎ ΠΊΠΎΠ΄Π°. Π­Ρ‚ΠΎ Π½Π΅ слоТно, Π½ΠΎ ΠΈΠ½ΠΎΠ³Π΄Π° сильно напрягаСт.

НСт ΠΏΠΎΠ»Π½ΠΎΡ†Π΅Π½Π½ΠΎΠΉ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΈ ES6.

ΠœΠΎΠΆΠ΅Ρ‚ Π΅ΡΡ‚ΡŒ ΠΈ Π΅Ρ‰Ρ‘ ΠΊΠ°ΠΊΠΈΠ΅-Ρ‚ΠΎ нСдостатки, Π½ΠΎ Π½ΠΈ с Ρ‡Π΅ΠΌ я Π±ΠΎΠ»Π΅Π΅ Π½Π΅ сталкивался. ΠŸΠΎΠ΄Π΅Π»ΠΈΡ‚Π΅ΡΡŒ ΠΈΠ½Ρ„ΠΎΠΉ Ссли Ρƒ Вас Π΅ΡΡ‚ΡŒ ΠΎΡ‚Ρ€ΠΈΡ†Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ΠΉ ΠΎΠΏΡ‹Ρ‚ эксплуатации NJS.

Π—Π°ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅

NJS — лСгковСсный open-source ΠΈΠ½Ρ‚Π΅Ρ€ΠΏΡ€Π΅Ρ‚Π°Ρ‚ΠΎΡ€, ΠΏΠΎΠ·Π²ΠΎΠ»ΡΡŽΡ‰ΠΈΠΉ Ρ€Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² Nginx Ρ€Π°Π·Π½Ρ‹Π΅ сцСнарии Π½Π° ЯП JavaScript. ΠŸΡ€ΠΈ Π΅Π³ΠΎ Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ΅ Π±Ρ‹Π»ΠΎ ΡƒΠ΄Π΅Π»Π΅Π½ΠΎ большоС Π²Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΡΡ‚ΠΈ. ΠšΠΎΠ½Π΅Ρ‡Π½ΠΎ ΠΌΠ½ΠΎΠ³ΠΎ Ρ‡Π΅Π³ΠΎ Π² Π½Ρ‘ΠΌ Π΅Ρ‰Ρ‘ Π½Π΅ Ρ…Π²Π°Ρ‚Π°Π΅Ρ‚, Π½ΠΎ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ развиваСтся силами нСбольшой ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ΠΈ ΠΎΠ½ΠΈ Π°ΠΊΡ‚ΠΈΠ²Π½ΠΎ Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ Π½ΠΎΠ²Ρ‹Π΅ Ρ„ΠΈΡ‡ΠΈ ΠΈ фиксят Π±Π°Π³ΠΈ. Π― ΠΆΠ΅ надСюсь, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠ³Π΄Π°-Π½ΠΈΠ±ΡƒΠ΄ΡŒ NJS ΠΏΠΎΠ·Π²ΠΎΠ»ΠΈΡ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Ρ‚ΡŒ внСшниС ΠΌΠΎΠ΄ΡƒΠ»ΠΈ, Ρ‡Ρ‚ΠΎ сдСлаСт Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π» Nginx практичСски Π½Π΅ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½Π½Ρ‹ΠΌ. Но Π΅ΡΡ‚ΡŒ NGINX Plus ΠΈ ΠΊΠ°ΠΊΠΈΡ…-Ρ‚ΠΎ Ρ„ΠΈΡ‡ скорСС всСго Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚!

Π Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ с ΠΏΠΎΠ»Π½Ρ‹ΠΌ ΠΊΠΎΠ΄ΠΎΠΌ ΠΊ ΡΡ‚Π°Ρ‚ΡŒΠ΅

njs-pypi с ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΊΠΎΠΉ AWS Sign v4

ОписаниС Π΄ΠΈΡ€Π΅ΠΊΡ‚ΠΈΠ² модуля ngx_http_js_module

ΠžΡ„ΠΈΡ†ΠΈΠ°Π»ΡŒΠ½Ρ‹ΠΉ Ρ€Π΅ΠΏΠΎΠ·ΠΈΡ‚ΠΎΡ€ΠΈΠΉ NJS ΠΈ докумСнтация

ΠŸΡ€ΠΈΠΌΠ΅Ρ€Ρ‹ использования NJS ΠΎΡ‚ Дмитрия Π’ΠΎΠ»Ρ‹Π½Ρ†Π΅Π²Π°

njs — Ρ€ΠΎΠ΄Π½ΠΎΠΉ JavaScript-скриптинг Π² nginx / ВыступлСниС Дмитрия Π’ΠΎΠ»Π½Ρ‹Π΅Π²Π° Π½Π° Saint HighLoad++ 2019

NJS Π² production / ВыступлСниС Василия Бошникова Π½Π° HighLoad++ 2019

Подпись ΠΈ аутСнтификация REST-запросов Π² AWS

Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com