Π ΡΠ°Π·ΠΈ ΡΡΠ°ΡΠΈΡ Π±ΠΈΡ
ΠΈΡΠΊΠ°Π» Π΄Π° ΡΠΏΠΎΠ΄Π΅Π»Ρ ΠΎΠΏΠΈΡΠ° ΡΠΈ Ρ NJS, JavaScript ΠΈΠ½ΡΠ΅ΡΠΏΡΠ΅ΡΠ°ΡΠΎΡ Π·Π° Nginx, ΡΠ°Π·ΡΠ°Π±ΠΎΡΠ΅Π½ ΠΎΡ Nginx Inc, ΠΎΠΏΠΈΡΠ²Π°ΠΉΠΊΠΈ ΠΎΡΠ½ΠΎΠ²Π½ΠΈΡΠ΅ ΠΌΡ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ Ρ ΠΏΠΎΠΌΠΎΡΡΠ° Π½Π° ΡΠ΅Π°Π»Π΅Π½ ΠΏΡΠΈΠΌΠ΅Ρ. NJS Π΅ ΠΏΠΎΠ΄ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ Π½Π° JavaScript, ΠΊΠΎΠ΅ΡΠΎ Π²ΠΈ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ²Π° Π΄Π° ΡΠ°Π·ΡΠΈΡΠΈΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΎΠ½Π°Π»Π½ΠΎΡΡΡΠ° Π½Π° Nginx. ΠΠ° Π²ΡΠΏΡΠΎΡΠ°
ΠΡΠ΅Π΄ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ΅ΠΌΠ΅β¦
ΠΠ° ΠΏΠΎΡΠ»Π΅Π΄Π½Π°ΡΠ° ΠΌΠΈ ΡΠ°Π±ΠΎΡΠ° Π½Π°ΡΠ»Π΅Π΄ΠΈΡ
gitlab Ρ ΡΠ΅Π΄ΠΈΡΠ° ΠΏΡΡΡΡΠΈ CI/CD ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅ΡΠΈ Ρ docker-compose, dind ΠΈ Π΄ΡΡΠ³ΠΈ ΠΈΠ·ΠΊΡΡΠ΅Π½ΠΈΡ, ΠΊΠΎΠΈΡΠΎ Π±ΡΡ
Π° ΠΏΡΠ΅Ρ
Π²ΡΡΠ»Π΅Π½ΠΈ ΠΊΡΠΌ kaniko rails. ΠΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡΠ°, ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½ΠΈ ΠΏΡΠ΅Π΄ΠΈ ΡΠΎΠ²Π° Π² CI, Π±ΡΡ
Π° ΠΏΡΠ΅ΠΌΠ΅ΡΡΠ΅Π½ΠΈ Π² ΠΎΡΠΈΠ³ΠΈΠ½Π°Π»Π½Π°ΡΠ° ΠΈΠΌ ΡΠΎΡΠΌΠ°. Π’Π΅ ΡΠ°Π±ΠΎΡΠ΅Ρ
Π° ΠΏΡΠ°Π²ΠΈΠ»Π½ΠΎ Π΄ΠΎ Π΄Π΅Π½Ρ, ΠΊΠΎΠ³Π°ΡΠΎ Π½Π°ΡΠΈΡΡ IP Π°Π΄ΡΠ΅Ρ Π½Π° gitlab ΡΠ΅ ΠΏΡΠΎΠΌΠ΅Π½ΠΈ ΠΈ CI ΡΠ΅ ΠΏΡΠ΅Π²ΡΡΠ½Π° Π² ΡΠΈΠΊΠ²Π°. ΠΡΠΎΠ±Π»Π΅ΠΌΡΡ Π±Π΅ΡΠ΅, ΡΠ΅ Π΅Π΄Π½ΠΎ ΠΎΡ Π΄ΠΎΠΊΠ΅Ρ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΡΡΠ°, ΠΊΠΎΠΈΡΠΎ ΡΡΠ°ΡΡΠ²Π°Ρ
Π° Π² CI, ΠΈΠΌΠ°ΡΠ΅ git, ΠΊΠΎΠΉΡΠΎ ΠΈΠ·ΡΠ΅Π³Π»Ρ Python ΠΌΠΎΠ΄ΡΠ»ΠΈ ΡΡΠ΅Π· ssh. ΠΠ° ssh Π²ΠΈ ΡΡΡΠ±Π²Π° ΡΠ°ΡΡΠ΅Π½ ΠΊΠ»ΡΡ ΠΈ... Π±Π΅ΡΠ΅ Π² ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ΡΠΎ Π·Π°Π΅Π΄Π½ΠΎ Ρ unknown_hosts. Π Π²ΡΠ΅ΠΊΠΈ CI ΡΠ΅ ΠΏΡΠΎΠ²Π°Π»ΠΈ Ρ Π³ΡΠ΅ΡΠΊΠ° ΠΏΡΠΈ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° Π½Π° ΠΊΠ»ΡΡ ΠΏΠΎΡΠ°Π΄ΠΈ Π½Π΅ΡΡΠΎΡΠ²Π΅ΡΡΡΠ²ΠΈΠ΅ ΠΌΠ΅ΠΆΠ΄Ρ ΡΠ΅Π°Π»Π½ΠΈΡ IP ΠΈ ΡΠΎΠ·ΠΈ, ΠΏΠΎΡΠΎΡΠ΅Π½ Π² unknown_hosts. ΠΠΎΠ²ΠΎ ΠΈΠ·ΠΎΠ±ΡΠ°ΠΆΠ΅Π½ΠΈΠ΅ Π±Π΅ΡΠ΅ Π±ΡΡΠ·ΠΎ ΡΠ³Π»ΠΎΠ±Π΅Π½ΠΎ ΠΎΡ ΡΡΡΠ΅ΡΡΠ²ΡΠ²Π°ΡΠΈΡΠ΅ Dockfiles ΠΈ ΠΎΠΏΡΠΈΡΡΠ° Π±Π΅ΡΠ΅ Π΄ΠΎΠ±Π°Π²Π΅Π½Π° StrictHostKeyChecking no
. ΠΠΎ Π»ΠΎΡΠΈΡΡ Π²ΠΊΡΡ ΠΎΡΡΠ°Π½Π° ΠΈ ΠΈΠΌΠ°ΡΠ΅ ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊΠΈΡΠ΅ Π΄Π° ΡΠ΅ ΠΏΡΠ΅ΠΌΠ΅ΡΡΡΡ Π² ΡΠ°ΡΡΠ½ΠΎ PyPI Ρ
ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅. ΠΠΎΠΏΡΠ»Π½ΠΈΡΠ΅Π»Π΅Π½ Π±ΠΎΠ½ΡΡ, ΡΠ»Π΅Π΄ ΠΏΡΠ΅ΠΌΠΈΠ½Π°Π²Π°Π½Π΅ ΠΊΡΠΌ ΡΠ°ΡΡΠ΅Π½ PyPI, Π±Π΅ΡΠ΅ ΠΏΠΎ-ΠΎΠΏΡΠΎΡΡΠ΅Π½ ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ ΠΈ Π½ΠΎΡΠΌΠ°Π»Π½ΠΎ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π½Π° requirements.txt
ΠΠ·Π±ΠΎΡΡΡ Π΅ Π½Π°ΠΏΡΠ°Π²Π΅Π½, Π³ΠΎΡΠΏΠΎΠ΄Π°!
ΠΠ·ΠΏΡΠ»Π½ΡΠ²Π°ΠΌΠ΅ Π²ΡΠΈΡΠΊΠΎ Π² ΠΎΠ±Π»Π°ΡΠΈΡΠ΅ ΠΈ Kubernetes ΠΈ Π² ΠΊΡΠ°ΠΉΠ½Π° ΡΠΌΠ΅ΡΠΊΠ° ΠΈΡΠΊΠ°Ρ ΠΌΠ΅ Π΄Π° ΠΏΠΎΠ»ΡΡΠΈΠΌ ΠΌΠ°Π»ΠΊΠ° ΡΡΠ»ΡΠ³Π°, ΠΊΠΎΡΡΠΎ Π±Π΅ΡΠ΅ ΠΊΠΎΠ½ΡΠ΅ΠΉΠ½Π΅Ρ Π±Π΅Π· ΡΡΡΡΠΎΡΠ½ΠΈΠ΅ Ρ Π²ΡΠ½ΡΠ½ΠΎ Ρ ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅. Π, ΡΡΠΉ ΠΊΠ°ΡΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ S3, Π±Π΅ΡΠ΅ Π΄Π°Π΄Π΅Π½ ΠΏΡΠΈΠΎΡΠΈΡΠ΅Ρ Π½Π° Π½Π΅Π³ΠΎ. Π, Π°ΠΊΠΎ Π΅ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ, Ρ ΡΠ΄ΠΎΡΡΠΎΠ²Π΅ΡΡΠ²Π°Π½Π΅ Π² gitlab (ΠΌΠΎΠΆΠ΅ΡΠ΅ Π΄Π° Π³ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΡΠ΅ ΡΠ°ΠΌΠΈ, Π°ΠΊΠΎ Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ).
ΠΡΡΠ·ΠΎ ΡΡΡΡΠ΅Π½Π΅ Π΄Π°Π΄Π΅ Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΡΠ΅Π·ΡΠ»ΡΠ°ΡΠ°: s3pypi, pypicloud ΠΈ ΠΎΠΏΡΠΈΡ Ρ βΡΡΡΠ½ΠΎβ ΡΡΠ·Π΄Π°Π²Π°Π½Π΅ Π½Π° html ΡΠ°ΠΉΠ»ΠΎΠ²Π΅ Π·Π° ΡΡΠΏΠ°. ΠΠΎΡΠ»Π΅Π΄Π½Π°ΡΠ° ΠΎΠΏΡΠΈΡ ΠΈΠ·ΡΠ΅Π·Π½Π° ΠΎΡ ΡΠ°ΠΌΠΎ ΡΠ΅Π±Π΅ ΡΠΈ.
s3pypi: Π’ΠΎΠ²Π° Π΅ cli Π·Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ Π½Π° S3 Ρ ΠΎΡΡΠΈΠ½Π³. ΠΠ°ΡΠ²Π°ΠΌΠ΅ ΡΠ°ΠΉΠ»ΠΎΠ²Π΅ΡΠ΅, Π³Π΅Π½Π΅ΡΠΈΡΠ°ΠΌΠ΅ html ΠΈ Π³ΠΎ ΠΊΠ°ΡΠ²Π°ΠΌΠ΅ Π² ΡΡΡΠ°ΡΠ° ΠΊΠΎΡΠ°. ΠΠΎΠ΄Ρ ΠΎΠ΄ΡΡ Π·Π° Π΄ΠΎΠΌΠ°ΡΠ½Π° ΡΠΏΠΎΡΡΠ΅Π±Π°.
pypicloud: ΠΠ·Π³Π»Π΅ΠΆΠ΄Π°ΡΠ΅ ΠΊΠ°ΡΠΎ ΠΈΠ½ΡΠ΅ΡΠ΅ΡΠ΅Π½ ΠΏΡΠΎΠ΅ΠΊΡ, Π½ΠΎ ΡΠ»Π΅Π΄ ΠΊΠ°ΡΠΎ ΠΏΡΠΎΡΠ΅ΡΠΎΡ Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡΡΠ° Π±ΡΡ ΡΠ°Π·ΠΎΡΠ°ΡΠΎΠ²Π°Π½. ΠΡΠΏΡΠ΅ΠΊΠΈ Π΄ΠΎΠ±ΡΠ°ΡΠ° Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ ΠΈ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡΠ° Π·Π° ΡΠ°Π·ΡΠΈΡΡΠ²Π°Π½Π΅ ΡΠΏΠΎΡΠ΅Π΄ Π²Π°ΡΠΈΡΠ΅ Π½ΡΠΆΠ΄ΠΈ, Π² Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»Π½ΠΎΡΡ ΡΠ΅ ΠΎΠΊΠ°Π·Π° ΠΈΠ·Π»ΠΈΡΠ΅Π½ ΠΈ ΡΡΡΠ΄Π΅Π½ Π·Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°Π½Π΅. ΠΠΎΡΠΈΠ³ΠΈΡΠ°Π½Π΅ΡΠΎ Π½Π° ΠΊΠΎΠ΄Π°, Π·Π° Π΄Π° ΠΎΡΠ³ΠΎΠ²Π°ΡΡ Π½Π° Π²Π°ΡΠΈΡΠ΅ Π·Π°Π΄Π°ΡΠΈ, ΡΠΏΠΎΡΠ΅Π΄ ΡΠΎΠ³Π°Π²Π°ΡΠ½ΠΈΡΠ΅ ΠΎΡΠ΅Π½ΠΊΠΈ, Π±ΠΈ ΠΎΡΠ½Π΅Π»ΠΎ 3-5 Π΄Π½ΠΈ. Π£ΡΠ»ΡΠ³Π°ΡΠ° ΡΠ΅ Π½ΡΠΆΠ΄Π°Π΅ ΠΈ ΠΎΡ Π±Π°Π·Π° Π΄Π°Π½Π½ΠΈ. ΠΡΡΠ°Π²ΠΈΡ ΠΌΠ΅ Π³ΠΎ, Π² ΡΠ»ΡΡΠ°ΠΉ ΡΠ΅ Π½Π΅ Π½Π°ΠΌΠ΅ΡΠΈΠΌ Π½ΠΈΡΠΎ Π΄ΡΡΠ³ΠΎ.
ΠΠΎ-Π·Π°Π΄ΡΠ»Π±ΠΎΡΠ΅Π½ΠΎ ΡΡΡΡΠ΅Π½Π΅ Π΄Π°Π΄Π΅ ΠΌΠΎΠ΄ΡΠ» Π·Π° Nginx, ngx_aws_auth. Π Π΅Π·ΡΠ»ΡΠ°ΡΡΡ ΠΎΡ Π½Π΅Π³ΠΎΠ²ΠΎΡΠΎ ΡΠ΅ΡΡΠ²Π°Π½Π΅ Π±Π΅ΡΠ΅ XML, ΠΏΠΎΠΊΠ°Π·Π°Π½ Π² Π±ΡΠ°ΡΠ·ΡΡΠ°, ΠΊΠΎΠΉΡΠΎ ΠΏΠΎΠΊΠ°Π·Π²Π°ΡΠ΅ ΡΡΠ΄ΡΡΠΆΠ°Π½ΠΈΠ΅ΡΠΎ Π½Π° ΠΊΠΎΡΠ°ΡΠ° S3. ΠΠΎΡΠ»Π΅Π΄Π½ΠΈΡΡ Π°Π½Π³Π°ΠΆΠΈΠΌΠ΅Π½Ρ ΠΏΠΎ Π²ΡΠ΅ΠΌΠ΅ Π½Π° ΡΡΡΡΠ΅Π½Π΅ΡΠΎ Π±Π΅ΡΠ΅ ΠΏΡΠ΅Π΄ΠΈ Π³ΠΎΠ΄ΠΈΠ½Π°. Π₯ΡΠ°Π½ΠΈΠ»ΠΈΡΠ΅ΡΠΎ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π°ΡΠ΅ ΠΈΠ·ΠΎΡΡΠ°Π²Π΅Π½ΠΎ.
ΠΠ°ΡΠΎ ΠΎΡΠΈΠ΄Π΅ΡΠ΅ Π΄ΠΎ ΠΈΠ·ΡΠΎΡΠ½ΠΈΠΊΠ° ΠΈ ΠΏΡΠΎΡΠ΅ΡΠ΅ΡΠ΅
ΠΠ·Π΅ΠΌΠ°ΠΉΠΊΠΈ ΡΠΎΠ·ΠΈ ΠΏΡΠΈΠΌΠ΅Ρ ΠΊΠ°ΡΠΎ ΠΎΡΠ½ΠΎΠ²Π°, Π΅Π΄ΠΈΠ½ ΡΠ°Ρ ΠΏΠΎ-ΠΊΡΡΠ½ΠΎ Π²ΠΈΠ΄ΡΡ Π² Π±ΡΠ°ΡΠ·ΡΡΠ° ΡΠΈ ΡΡΡΠΈΡ 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
: ΡΠΎΠ²Π° Π΅ ΠΏΡΠΎΠΌΠ΅Π½Π»ΠΈΠ²Π°, ΠΊΠΎΡΡΠΎ Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ ΠΎΡ ΠΏΡΡΠ²ΠΎΠ½Π°ΡΠ°Π»Π½ΠΈΡ uri ΡΠΎΡΠΌΠΈΡΠ° Π·Π°ΡΠ²ΠΊΠ° ΠΊΡΠΌ S3. ΠΠΊΠΎ ΡΡΡΠ±Π²Π° Π΄Π° ΠΏΠΎΠ»ΡΡΠΈΡΠ΅ ΡΡΠ΄ΡΡΠΆΠ°Π½ΠΈΠ΅ΡΠΎ Π½Π° βrootβ, ΡΠΎΠ³Π°Π²Π° ΡΡΡΠ±Π²Π° Π΄Π° ΡΡΠ·Π΄Π°Π΄Π΅ΡΠ΅ uri Π·Π°ΡΠ²ΠΊΠ°, ΠΏΠΎΡΠΎΡΠ²Π°ΡΠ° ΡΠ°Π·Π΄Π΅Π»ΠΈΡΠ΅Π»Ρ delimiter
, ΠΊΠΎΠΉΡΠΎ ΡΠ΅ Π²ΡΡΠ½Π΅ ΡΠΏΠΈΡΡΠΊ Π½Π° Π²ΡΠΈΡΠΊΠΈ CommonPrefixes 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, ΠΊΠ°ΡΠΎ ΠΏΡΡΠ²ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΠ΅ Π·Π°Π³Π»Π°Π²ΠΊΠ°ΡΠ° 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="/bg/${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 ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΠΏΠΎΠ»ΠΈΡΠΈΠΊΠ° Π·Π° ΠΎΡΠ½ΠΎΠ²Π½Π° ΡΠ°ΠΉΠ»ΠΎΠ²Π° ΡΠΈΡΡΠ΅ΠΌΠ° ΡΠ°ΠΌΠΎ Π·Π° ΡΠ΅ΡΠ΅Π½Π΅, ΡΠΎΠ²Π° Π΄ΠΎΠ±Π°Π²Ρ ΠΎΡΠ΅ ΠΏΠΎΠ²Π΅ΡΠ΅ ΡΠ»ΠΎΠΆΠ½ΠΎΡΡ ΠΏΡΠΈ Π·Π°ΠΌΡΠ½Π°ΡΠ° Π½Π° nginx.conf ΡΡΠ΅Π· configmap. Π ΡΡΠ°Π²Π° Π°Π±ΡΠΎΠ»ΡΡΠ½ΠΎ Π½Π΅Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ Π΄Π° ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠ°ΡΠ΅ Nginx ΡΡΠ΅Π· configmap, ΠΊΠ°ΡΠΎ Π΅Π΄Π½ΠΎΠ²ΡΠ΅ΠΌΠ΅Π½Π½ΠΎ Ρ ΡΠΎΠ²Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΡΠ΅ ΠΏΠΎΠ»ΠΈΡΠΈΠΊΠΈ, Π·Π°Π±ΡΠ°Π½ΡΠ²Π°ΡΠΈ ΡΠ²ΡΡΠ·Π²Π°Π½Π΅ΡΠΎ Π½Π° ΡΠΎΠΌΠΎΠ²Π΅ (pvc) ΠΈ root ΡΠ°ΠΉΠ»ΠΎΠ²Π° ΡΠΈΡΡΠ΅ΠΌΠ° ΡΠ°ΠΌΠΎ Π·Π° ΡΠ΅ΡΠ΅Π½Π΅ (ΡΠΎΠ²Π° ΡΡΡΠΎ ΡΠ΅ ΡΠ»ΡΡΠ²Π°).
ΠΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΠΊΠΈ ΠΌΠ΅ΠΆΠ΄ΠΈΠ½Π½ΠΈΡ 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') ΠΈ Π½ΡΠΌΠ° Π½ΡΠΆΠ΄Π° Π΄Π° ΠΏΠΈΡΠ΅ΡΠ΅ βbikeβ ΡΡΡ S3 ΡΠ΄ΠΎΡΡΠΎΠ²Π΅ΡΡΠ²Π°Π½Π΅!
ΠΠ΅ΠΊΠ° Π΄Π° ΠΏΡΠ΅ΠΌΠΈΠ½Π΅ΠΌ ΠΊΡΠΌ ΠΌΠΈΠ½ΡΡΠΈΡΠ΅
ΠΠ° ΠΌΠ΅Π½ Π½Π΅Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΡΠ° Π·Π° ΠΈΠΌΠΏΠΎΡΡΠΈΡΠ°Π½Π΅ Π½Π° Π²ΡΠ½ΡΠ½ΠΈ JS ΠΌΠΎΠ΄ΡΠ»ΠΈ ΡΡΠ°Π½Π° Π½Π΅ΠΏΡΠΈΡΡΠ½Π°, Π½ΠΎ ΠΎΡΠ°ΠΊΠ²Π°Π½Π° ΡΡΠ½ΠΊΡΠΈΡ. ΠΠΏΠΈΡΠ°Π½ΠΎΡΠΎ Π² ΠΏΡΠΈΠΌΠ΅ΡΠ° ΠΏΠΎ-Π³ΠΎΡΠ΅ require('crypto') Π΅
ΠΠΎΠΌΠΏΡΠ΅ΡΠΈΡΡΠ° ΡΡΡΠΎ ΡΡΡΠ±Π²Π° Π΄Π° Π±ΡΠ΄Π΅ Π΄Π΅Π°ΠΊΡΠΈΠ²ΠΈΡΠ°Π½Π° Π·Π° ΡΠ΅ΠΊΡΡΠΈΡ ΠΏΡΠΎΠ΅ΠΊΡ Π² Nginx gzip off;
Π’ΡΠΉ ΠΊΠ°ΡΠΎ Π² NJS Π½ΡΠΌΠ° gzip ΠΌΠΎΠ΄ΡΠ» ΠΈ Π΅ Π½Π΅Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ Π΄Π° ΡΠ΅ ΡΠ²ΡΡΠΆΠ΅, ΡΠ»Π΅Π΄ΠΎΠ²Π°ΡΠ΅Π»Π½ΠΎ Π½ΡΠΌΠ° Π½Π°ΡΠΈΠ½ Π΄Π° ΡΠ΅ ΡΠ°Π±ΠΎΡΠΈ Ρ ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡΠ°Π½ΠΈ Π΄Π°Π½Π½ΠΈ. ΠΡΡΠ½ΠΎ, ΡΠΎΠ²Π° Π²ΡΡΡΠ½ΠΎΡΡ Π½Π΅ Π΅ ΠΌΠΈΠ½ΡΡ Π·Π° ΡΠΎΠ·ΠΈ ΡΠ»ΡΡΠ°ΠΉ. ΠΡΠΌΠ° ΠΌΠ½ΠΎΠ³ΠΎ ΡΠ΅ΠΊΡΡ, Π° ΠΏΡΠ΅Ρ Π²ΡΡΠ»Π΅Π½ΠΈΡΠ΅ ΡΠ°ΠΉΠ»ΠΎΠ²Π΅ ΡΠ° Π²Π΅ΡΠ΅ ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡΠ°Π½ΠΈ ΠΈ Π΄ΠΎΠΏΡΠ»Π½ΠΈΡΠ΅Π»Π½Π°ΡΠ° ΠΊΠΎΠΌΠΏΡΠ΅ΡΠΈΡ Π½ΡΠΌΠ° Π΄Π° ΠΈΠΌ ΠΏΠΎΠΌΠΎΠ³Π½Π΅ ΠΌΠ½ΠΎΠ³ΠΎ. ΠΡΠ²Π΅Π½ ΡΠΎΠ²Π°, ΡΠΎΠ²Π° Π½Π΅ Π΅ ΡΠΎΠ»ΠΊΠΎΠ²Π° Π½Π°ΡΠΎΠ²Π°ΡΠ΅Π½Π° ΠΈΠ»ΠΈ ΠΊΡΠΈΡΠΈΡΠ½Π° ΡΡΠ»ΡΠ³Π°, ΡΠ΅ Π΄Π° ΡΡΡΠ±Π²Π° Π΄Π° ΡΠ΅ Π·Π°Π½ΠΈΠΌΠ°Π²Π°ΡΠ΅ Ρ Π΄ΠΎΡΡΠ°Π²ΡΠ½Π΅ Π½Π° ΡΡΠ΄ΡΡΠΆΠ°Π½ΠΈΠ΅ Π½ΡΠΊΠΎΠ»ΠΊΠΎ ΠΌΠΈΠ»ΠΈΡΠ΅ΠΊΡΠ½Π΄ΠΈ ΠΏΠΎ-Π±ΡΡΠ·ΠΎ.
ΠΡΡΡΡΠ°Π½ΡΠ²Π°Π½Π΅ΡΠΎ Π½Π° Π³ΡΠ΅ΡΠΊΠΈ Π² ΡΠΊΡΠΈΠΏΡΠ° ΠΎΡΠ½Π΅ΠΌΠ° ΠΌΠ½ΠΎΠ³ΠΎ Π²ΡΠ΅ΠΌΠ΅ ΠΈ Π΅ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ ΡΠ°ΠΌΠΎ ΡΡΠ΅Π· βΠΎΡΠΏΠ΅ΡΠ°ΡΡΡΠΈβ Π² error.log. Π Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ ΠΎΡ Π·Π°Π΄Π°Π΄Π΅Π½Π°ΡΠ° ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΡ Π·Π° Π½ΠΈΠ²ΠΎ Π½Π° ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠ°Π½Π΅, ΠΏΡΠ΅Π΄ΡΠΏΡΠ΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ ΠΈΠ»ΠΈ Π³ΡΠ΅ΡΠΊΠ°, Π΅ Π²ΡΠ·ΠΌΠΎΠΆΠ½ΠΎ Π΄Π° ΡΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ ΡΡΠΎΡΠ²Π΅ΡΠ½ΠΎ 3 ΠΌΠ΅ΡΠΎΠ΄Π° r.log, r.warn, r.error. ΠΠΏΠΈΡΠ²Π°ΠΌ ΡΠ΅ Π΄Π° ΠΎΡΡΡΡΠ°Π½Ρ Π³ΡΠ΅ΡΠΊΠΈ Π² Π½ΡΠΊΠΎΠΈ ΡΠΊΡΠΈΠΏΡΠΎΠ²Π΅ Π² Chrome (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 ΠΈ Π½Π°ΠΉ-Π²Π΅ΡΠΎΡΡΠ½ΠΎ Π½ΡΠΌΠ° Π΄Π° ΠΈΠΌΠ° ΡΡΠ½ΠΊΡΠΈΠΈ!
ΠΠ·ΡΠΎΡΠ½ΠΈΠΊ: www.habr.com