рд╕рд┐рдВрдЧрд▓ рдкреЗрдЬ рдЕреЕрдкреНрд▓рд┐рдХреЗрд╢рдирдЪреНрдпрд╛ рд╡рд┐рддрд░рдгрд╛рд╕рд╛рдареА рдбреЙрдХрд░ рдЗрдореЗрдЬ

рд╕рд┐рдВрдЧрд▓-рдкреЗрдЬ рдЕреЕрдкреНрд▓рд┐рдХреЗрд╢рди (SPA) рд╣рд╛ рд╕реНрдЯреЕрдЯрд┐рдХ JavaScript рдЖрдгрд┐ HTML рдлрд╛рдЗрд▓реНрд╕, рддрд╕реЗрдЪ рдЗрдореЗрдЬ рдЖрдгрд┐ рдЗрддрд░ рд╕рдВрд╕рд╛рдзрдирд╛рдВрдЪрд╛ рд╕рдВрдЪ рдЖрд╣реЗ. рдХрд╛рд░рдг рддреЗ рдЧрддрд┐рдорд╛рдирдкрдгреЗ рдмрджрд▓рдд рдирд╛рд╣реАрдд, рддреНрдпрд╛рдВрдирд╛ рдСрдирд▓рд╛рдЗрди рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд░рдгреЗ рдЦреВрдк рд╕реЛрдкреЗ рдЖрд╣реЗ. рдпрд╛рд╕рд╛рдареА рдореЛрдареНрдпрд╛ рд╕рдВрдЦреНрдпреЗрдиреЗ рд╕реНрд╡рд╕реНрдд рдЖрдгрд┐ рдЕрдЧрджреА рдореЛрдлрдд рд╕реЗрд╡рд╛ рдЖрд╣реЗрдд, рдЬреНрдпрд╛рдЪреА рд╕реБрд░реБрд╡рд╛рдд рдПрдХрд╛ рд╕рд╛рдзреНрдпрд╛ GitHub рдкреЗрдЬреЗрд╕рдиреЗ рд╣реЛрддреЗ (рдЖрдгрд┐ рдХрд╛рд╣реАрдВрд╕рд╛рдареА narod.ru рджреЗрдЦреАрд▓) рдЖрдгрд┐ Amazon S3 рд╕рд╛рд░рдЦреНрдпрд╛ CDN рдиреЗ рд╕рдорд╛рдкреНрдд рд╣реЛрддреЗ. рддрдерд╛рдкрд┐, рдорд▓рд╛ рдХрд╛рд╣реАрддрд░реА рд╡реЗрдЧрд│реЗ рд╣рд╡реЗ рд╣реЛрддреЗ.

рдорд▓рд╛ SPA рд╕рд╣ рдбреЙрдХрд░ рдЗрдореЗрдЬрдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЖрд╣реЗ рдЬреЗрдгреЗрдХрд░реВрди рддреА Kubernetes рдХреНрд▓рд╕реНрдЯрд░рдЪрд╛ рднрд╛рдЧ рдореНрд╣рдгреВрди рдЙрддреНрдкрд╛рджрдирд╛рдд рдЖрдгрд┐ SPA рдореНрд╣рдгрдЬреЗ рдХрд╛рдп рдпрд╛рдЪреА рдХрд▓реНрдкрдирд╛ рдирд╕рд▓реЗрд▓реНрдпрд╛ рдмреЕрдХ-рдПрдВрдб рдбреЗрд╡реНрд╣рд▓рдкрд░рдЪреНрдпрд╛ рдорд╢реАрдирд╡рд░ рд╕рд╣рдЬрдкрдгреЗ рд▓реЙрдиреНрдЪ рдХреЗрд▓реА рдЬрд╛рдК рд╢рдХрддреЗ.

рдореА рд╕реНрд╡рддрдГрд╕рд╛рдареА рдЦрд╛рд▓реАрд▓ рдкреНрд░рддрд┐рдорд╛ рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХреЗрд▓реНрдпрд╛ рдЖрд╣реЗрдд:

  • рд╡рд╛рдкрд░рдгреА рд╕реЛрдкреА (рдкрд░рдВрддреБ рдЕрд╕реЗрдВрдмреНрд▓реА рдирд╛рд╣реА);
  • рдбрд┐рд╕реНрдХ рдЖрдгрд┐ рд░реЕрдо рджреЛрдиреНрд╣реА рджреГрд╖реНрдЯреАрдиреЗ рдХрд┐рдорд╛рди рдЖрдХрд╛рд░;
  • рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕рджреНрд╡рд╛рд░реЗ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдЬреЗрдгреЗрдХрд░реВрди рдкреНрд░рддрд┐рдорд╛ рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рд╡рд╛рддрд╛рд╡рд░рдгрд╛рдд рд╡рд╛рдкрд░рд▓реА рдЬрд╛рдК рд╢рдХрддреЗ;
  • рдлрд╛рдЗрд▓реНрд╕рдЪреЗ рд╕рд░реНрд╡рд╛рдд рдХрд╛рд░реНрдпрдХреНрд╖рдо рд╡рд┐рддрд░рдг.

рдЖрдЬ рдореА рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╕реЗ рд╕рд╛рдВрдЧреЗрди:

  • рдЖрддрдбреЗ nginx;
  • рд╕реНрддреНрд░реЛрддрд╛рдВрдХрдбреВрди рдмреНрд░реЛрдЯрд▓реА рддрдпрд╛рд░ рдХрд░рд╛;
  • рд╕реНрдЯреЕрдЯрд┐рдХ рдлрд╛рдЗрд▓реНрд╕рдирд╛ рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕ рд╕рдордЬрдгреНрдпрд╛рд╕ рд╢рд┐рдХрд╡рд╛;
  • рдЖрдгрд┐ рдЕрд░реНрдерд╛рддрдЪ рдпрд╛ рд╕рд░реНрд╡рд╛рдВрдордзреВрди рдбреЙрдХрд░ рдкреНрд░рддрд┐рдорд╛ рдХрд╢реА рдПрдХрддреНрд░ рдХрд░рд╛рдпрдЪреА.

рдпрд╛ рд▓реЗрдЦрд╛рдЪрд╛ рдЙрджреНрджреЗрд╢ рдорд╛рдЭрд╛ рдЕрдиреБрднрд╡ рд╢реЗрдЕрд░ рдХрд░рдгреЗ рдЖрдгрд┐ рдЕрдиреБрднрд╡реА рд╕рдореБрджрд╛рдп рд╕рджрд╕реНрдпрд╛рдВрдирд╛ рд░рдЪрдирд╛рддреНрдордХ рдЯреАрдХрд╛ рдХрд░рдгреНрдпрд╛рд╕ рдкреНрд░рд╡реГрддреНрдд рдХрд░рдгреЗ рд╣рд╛ рдЖрд╣реЗ.

рдЕрд╕реЗрдВрдмреНрд▓реАрд╕рд╛рдареА рдкреНрд░рддрд┐рдорд╛ рддрдпрд╛рд░ рдХрд░рдгреЗ

рдЕрдВрддрд┐рдо рдбреЙрдХрд░ рдкреНрд░рддрд┐рдорд╛ рдЖрдХрд╛рд░рд╛рдд рд▓рд╣рд╛рди рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рддреБрдореНрд╣рд╛рд▓рд╛ рджреЛрди рдирд┐рдпрдорд╛рдВрдЪреЗ рдкрд╛рд▓рди рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ: рдХрд┐рдорд╛рди рд╕реНрддрд░ рдЖрдгрд┐ рдХрд┐рдорд╛рди рдЖрдзрд╛рд░рднреВрдд рдкреНрд░рддрд┐рдорд╛. рд╕рд░реНрд╡рд╛рдд рд▓рд╣рд╛рди рдмреЗрд╕ рдкреНрд░рддрд┐рдорд╛рдВрдкреИрдХреА рдПрдХ рдЕрд▓реНрдкрд╛рдЗрди рд▓рд┐рдирдХреНрд╕ рдкреНрд░рддрд┐рдорд╛ рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдореА рддреЗрдЪ рдирд┐рд╡рдбреЗрди. рдХрд╛рд╣реАрдЬрдг рдЕрд╕рд╛ рдпреБрдХреНрддрд┐рд╡рд╛рдж рдХрд░реВ рд╢рдХрддрд╛рдд рдХреА рдЕрд▓реНрдкрд╛рдЗрди рдЙрддреНрдкрд╛рджрдирд╛рд╕рд╛рдареА рдпреЛрдЧреНрдп рдирд╛рд╣реА рдЖрдгрд┐ рддреЗ рдпреЛрдЧреНрдп рдЕрд╕реВ рд╢рдХрддрд╛рдд. рдкрдг рд╡реИрдпрдХреНрддрд┐рдХрд░рд┐рддреНрдпрд╛, рдорд▓рд╛ рддреНрдпрд╛рдЪреНрдпрд╛рд╢реА рдХрдзреАрдЪ рдХрд╛рд╣реА рдЕрдбрдЪрдг рдЖрд▓реА рдирд╛рд╣реА рдЖрдгрд┐ рддреНрдпрд╛рдЪреНрдпрд╛рд╡рд┐рд░реБрджреНрдз рдХреЛрдгрддреЗрд╣реА рд╡рд╛рдж рдирд╛рд╣реАрдд.

рдХрдореА рд╕реНрддрд░ рдареЗрд╡рдгреНрдпрд╛рд╕рд╛рдареА, рдореА рдкреНрд░рддрд┐рдорд╛ 2 рдЯрдкреНрдкреНрдпрд╛рдд рдПрдХрддреНрд░ рдХрд░реЗрди. рдкрд╣рд┐рд▓рд╛ рдорд╕реБрджрд╛ рдЖрд╣реЗ; рд╕рд░реНрд╡ рд╕рд╣рд╛рдпреНрдпрдХ рдЙрдкрдпреБрдХреНрддрддрд╛ рдЖрдгрд┐ рддрд╛рддреНрдкреБрд░рддреНрдпрд╛ рдлрд╛рдЗрд▓реНрд╕ рддреНрдпрд╛рдд рд░рд╛рд╣рддреАрд▓. рдЖрдгрд┐ рдЕрдВрддрд┐рдо рдЯрдкреНрдкреНрдпрд╛рдд рдореА рдлрдХреНрдд рдЕрд░реНрдЬрд╛рдЪреА рдЕрдВрддрд┐рдо рдЖрд╡реГрддреНрддреА рд▓рд┐рд╣реАрди.

рдЪрд▓рд╛ рд╕рд╣рд╛рдпреНрдпрдХ рдкреНрд░рддрд┐рдореЗрд╕рд╣ рдкреНрд░рд╛рд░рдВрдн рдХрд░реВрдпрд╛.

SPA рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╕рдВрдХрд▓рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рд╕рд╣рд╕рд╛ node.js рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдЕрд╕рддреЗ. рдореА рдЕрдзрд┐рдХреГрдд рдкреНрд░рддрд┐рдорд╛ рдШреЗрдИрди, рдЬреА рдПрдирдкреАрдПрдо рдЖрдгрд┐ рдпрд╛рд░реНрди рдкреЕрдХреЗрдЬ рд╡реНрдпрд╡рд╕реНрдерд╛рдкрдХрд╛рдВрд╕рд╣ рджреЗрдЦреАрд▓ рдпреЗрддреЗ. рдорд╛рдЭреНрдпрд╛ рд╕реНрд╡рдд: рдЪреНрдпрд╛ рд╡рддреАрдиреЗ, рдореА рдиреЛрдб-рдЬрд┐рдк рдЬреЛрдбреЗрди, рдЬреЗ рдХрд╛рд╣реА рдПрдирдкреАрдПрдо рдкреЕрдХреЗрдЬреЗрд╕ рддрдпрд╛рд░ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рдЖрдгрд┐ Google рдХрдбреВрди рдмреНрд░реЙрдЯрд▓реА рдХрдВрдкреНрд░реЗрд╕рд░, рдЬреЗ рдЖрдореНрд╣рд╛рд▓рд╛ рдирдВрддрд░ рдЙрдкрдпреБрдХреНрдд рдард░реЗрд▓.

рдЯрд┐рдкреНрдкрдгреНрдпрд╛рдВрд╕рд╣ рдбреЙрдХрд░рдлрд╛рдЗрд▓.

# ╨С╨░╨╖╨╛╨▓╤Л╨╣ ╨╛╨▒╤А╨░╨╖
FROM node:12-alpine
LABEL maintainer="Aleksey Maydokin <[email protected]>"
ENV BROTLI_VERSION 1.0.7
# ╨Я╨░╨║╨╡╤В╤Л, ╨║╨╛╤В╨╛╤А╤Л╨╡ ╨╜╤Г╨╢╨╜╤Л, ╤З╤В╨╛╨▒╤Л ╤Б╨╛╨▒╤А╨░╤В╤М ╨╕╨╖ ╨╕╤Б╤Е╨╛╨┤╨╜╨╕╨║╨╛╨▓ Brotli
RUN apk add --no-cache --virtual .build-deps 
        bash 
        gcc 
        libc-dev 
        make 
        linux-headers 
        cmake 
        curl 
    && mkdir -p /usr/src 
    # ╨Ш╤Б╤Е╨╛╨┤╨╜╨╕╨║╨╕ Brotli ╤Б╨║╨░╤З╨╕╨▓╨░╨╡╨╝ ╨╕╨╖ ╨╛╤Д╨╕╤Ж╨╕╨░╨╗╤М╨╜╨╛╨│╨╛ ╤А╨╡╨┐╨╛╨╖╨╕╤В╨╛╤А╨╕╤П
    && curl -LSs https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz | tar xzf - -C /usr/src 
    && cd /usr/src/brotli-$BROTLI_VERSION 
    # ╨Ъ╨╛╨╝╨┐╨╕╨╗╨╕╤А╤Г╨╡╨╝ Brotli
    && ./configure-cmake --disable-debug && make -j$(getconf _NPROCESSORS_ONLN) && make install 
    # ╨Ф╨╛╨▒╨░╨▓╨╗╤П╨╡╨╝ node-gyp
    && yarn global add node-gyp 
    # ╨г╨▒╨╕╤А╨░╨╡╨╝ ╨╖╨░ ╤Б╨╛╨▒╨╛╨╣ ╨╝╤Г╤Б╨╛╤А
    && apk del .build-deps && yarn cache clean && rm -rf /usr/src

рдЖрдзреАрдЪ рдЗрдереЗ рдореА рдорд┐рдирд┐рдорд▓рд┐рдЭрдорд╕рд╛рдареА рд▓рдврдд рдЖрд╣реЗ, рддреНрдпрд╛рдореБрд│реЗ рдкреНрд░рддрд┐рдорд╛ рдПрдХрд╛ рдореЛрдареНрдпрд╛ рдЯреАрдордиреЗ рдПрдХрддреНрд░ рдареЗрд╡рд▓реА рдЖрд╣реЗ.

рддрдпрд╛рд░ рдХреЗрд▓реЗрд▓реА рдкреНрд░рддрд┐рдорд╛ рдпреЗрдереЗ рдЖрдврд│реВ рд╢рдХрддреЗ: https://hub.docker.com/r/alexxxnf/spa-builder. рдЬрд░реА рдореА рд╢рд┐рдлрд╛рд░рд╕ рдХрд░рддреЛ рдХреА рдЗрддрд░ рд▓реЛрдХрд╛рдВрдЪреНрдпрд╛ рдкреНрд░рддрд┐рдорд╛рдВрд╡рд░ рд╡рд┐рд╕рдВрдмреВрди рд░рд╛рд╣реВ рдирдХрд╛ рдЖрдгрд┐ рдЖрдкрд▓реНрдпрд╛ рд╕реНрд╡рддрдГрдЪреНрдпрд╛ рдкреНрд░рддрд┐рдорд╛ рдЧреЛрд│рд╛ рдХрд░рд╛.

nginx

рд╕реНрдерд┐рд░ рд╕рд╛рдордЧреНрд░реА рд╡рд┐рддрд░реАрдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рддреБрдореНрд╣реА рдХреЛрдгрддрд╛рд╣реА рд╡реЗрдм рд╕рд░реНрд╡реНрд╣рд░ рд╡рд╛рдкрд░реВ рд╢рдХрддрд╛. рдорд▓рд╛ nginx рд╕рд╣ рдХрд╛рдо рдХрд░рдгреНрдпрд╛рдЪреА рд╕рд╡рдп рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдореА рддреЗ рдЖрддрд╛ рд╡рд╛рдкрд░реЗрди.

Nginx рдХрдбреЗ рдЕрдзрд┐рдХреГрдд рдбреЙрдХрд░ рдкреНрд░рддрд┐рдорд╛ рдЖрд╣реЗ, рдкрд░рдВрддреБ рддреНрдпрд╛рдд рд╕рд╛рдзреНрдпрд╛ рд╕реНрдерд┐рд░ рд╡рд┐рддрд░рдгрд╛рд╕рд╛рдареА рдмрд░реЗрдЪ рдореЙрдбреНрдпреВрд▓ рдЖрд╣реЗрдд. рдбрд┐рд▓рд┐рд╡реНрд╣рд░реАрдордзреНрдпреЗ рдХреЛрдгрддреЗ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХреЗрд▓реЗ рдЖрд╣реЗрдд рд╣реЗ рд╡рд┐рд╢реЗрд╖ рдЯреАрдорджреНрд╡рд╛рд░реЗ рдХрд┐рдВрд╡рд╛ рдЕрдзрд┐рдХреГрдд рдбреЙрдХрд░рдлрд╛рдЗрд▓рдордзреНрдпреЗ рдкрд╛рд╣рд┐рд▓реЗ рдЬрд╛рдК рд╢рдХрддреЗ.

$ рдбреЙрдХрд░ рд░рди --rm nginx:1-alpine nginx -V

nginx version: nginx/1.17.9
built by gcc 8.3.0 (Alpine 8.3.0) 
built with OpenSSL 1.1.1d  10 Sep 2019
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --with-perl_modules_path=/usr/lib/perl5/vendor_perl --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-Os -fomit-frame-pointer' --with-ld-opt=-Wl,--as-needed

рдореА рдбреЙрдХрд░рдлрд╛рдЗрд▓рдЪрд╛ рдЖрдзрд╛рд░ рдореНрд╣рдгреВрди рд╡рд╛рдкрд░ рдХрд░реЗрди, рдкрд░рдВрддреБ рд╕реНрдерд┐рд░ рд╕рд╛рдордЧреНрд░реА рд╡рд┐рддрд░реАрдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЬреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рддреЗрдЪ рдореА рддреНрдпрд╛рдд рд╕реЛрдбреЗрди. рдорд╛рдЭреА рдЖрд╡реГрддреНрддреА HTTPS рд╡рд░ рдХрд╛рд░реНрдп рдХрд░реВ рд╢рдХрдгрд╛рд░ рдирд╛рд╣реА, рдЕрдзрд┐рдХреГрддрддреЗрд▓рд╛ рд╕рдорд░реНрдерди рджреЗрдгрд╛рд░ рдирд╛рд╣реА рдЖрдгрд┐ рдмрд░реЗрдЪ рдХрд╛рд╣реА. рдкрд░рдВрддреБ рдорд╛рдЭреА рдЖрд╡реГрддреНрддреА рдмреНрд░реЙрдЯрд▓реА рдЕрд▓реНрдЧреЛрд░рд┐рджрдорд╕рд╣ рд╕рдВрдХреБрдЪрд┐рдд рдХреЗрд▓реЗрд▓реНрдпрд╛ рдлрд╛рдпрд▓реА рд╡рд┐рддрд░рд┐рдд рдХрд░рдгреНрдпрд╛рд╕ рд╕рдХреНрд╖рдо рдЕрд╕реЗрд▓, рдЬреА рдЬреАрдЭрд┐рдкрдкреЗрдХреНрд╖рд╛ рдереЛрдбреА рдЕрдзрд┐рдХ рдХрд╛рд░реНрдпрдХреНрд╖рдо рдЖрд╣реЗ. рдЖрдореНрд╣реА рдлрд╛рдпрд▓реА рдПрдХрджрд╛ рд╕рдВрдХреБрдЪрд┐рдд рдХрд░реВ; рдлреНрд▓рд╛рдпрд╡рд░ рд╣реЗ рдХрд░рдгреНрдпрд╛рдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╛рд╣реА.

рд╣реА рдбреЙрдХрд░рдлрд╛рдИрд▓ рдЖрд╣реЗ рдЬреНрдпрд╛рдЪрд╛ рдореА рд╢реЗрд╡рдЯ рдХреЗрд▓рд╛. рд░рд╢рд┐рдпрдирдордзреАрд▓ рдЯрд┐рдкреНрдкрдгреНрдпрд╛ рдорд╛рдЭреНрдпрд╛ рдЖрд╣реЗрдд, рдЗрдВрдЧреНрд░рдЬреАрдордзреНрдпреЗ - рдореВрд│ рдкрд╛рд╕реВрди.

рдбреЙрдХрд░рдлрд╛рдЗрд▓

# ╨С╨░╨╖╨╛╨▓╤Л╨╣ ╨╛╨▒╤А╨░╨╖ ╤Б╨╜╨╛╨▓╨░ Alpine
FROM alpine:3.9
LABEL maintainer="Aleksey Maydokin <[email protected]>"
ENV NGINX_VERSION 1.16.0
ENV NGX_BROTLI_VERSION 0.1.2
ENV BROTLI_VERSION 1.0.7
RUN set -x 
    && addgroup -S nginx 
    && adduser -D -S -h /var/cache/nginx -s /sbin/nologin -G nginx nginx 
# ╨г╤Б╤В╨░╨╜╨░╨▓╨╗╨╕╨▓╨░╨╡╨╝ ╨┐╨░╨║╨╡╤В╤Л, ╨║╨╛╤В╨╛╤А╤Л╨╡ ╨╜╤Г╨╢╨╜╤Л ╤З╤В╨╛╨▒╤Л ╤Б╨╛╨▒╤А╨░╤В╤М nginx ╨╕ ╨╝╨╛╨┤╤Г╨╗╤М ngx_brotli ╨║ ╨╜╨╡╨╝╤Г
    && apk add --no-cache --virtual .build-deps 
            gcc 
            libc-dev 
            make 
            linux-headers 
            curl 
    && mkdir -p /usr/src 
# ╨б╨║╨░╤З╨╕╨▓╨░╨╡╨╝ ╨╕╤Б╤Е╨╛╨┤╨╜╨╕╨║╨╕
    && curl -LSs https://nginx.org/download/nginx-$NGINX_VERSION.tar.gz | tar xzf - -C /usr/src 
    && curl -LSs https://github.com/eustas/ngx_brotli/archive/v$NGX_BROTLI_VERSION.tar.gz | tar xzf - -C /usr/src 
    && curl -LSs https://github.com/google/brotli/archive/v$BROTLI_VERSION.tar.gz | tar xzf - -C /usr/src 
    && rm -rf /usr/src/ngx_brotli-$NGX_BROTLI_VERSION/deps/brotli/ 
    && ln -s /usr/src/brotli-$BROTLI_VERSION /usr/src/ngx_brotli-$NGX_BROTLI_VERSION/deps/brotli 
    && cd /usr/src/nginx-$NGINX_VERSION 
    && CNF="
            --prefix=/etc/nginx 
            --sbin-path=/usr/sbin/nginx 
            --modules-path=/usr/lib/nginx/modules 
            --conf-path=/etc/nginx/nginx.conf 
            --error-log-path=/var/log/nginx/error.log 
            --http-log-path=/var/log/nginx/access.log 
            --pid-path=/var/run/nginx.pid 
            --lock-path=/var/run/nginx.lock 
            --http-client-body-temp-path=/var/cache/nginx/client_temp 
            --http-proxy-temp-path=/var/cache/nginx/proxy_temp 
            --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp 
            --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp 
            --http-scgi-temp-path=/var/cache/nginx/scgi_temp 
            --user=nginx 
            --group=nginx 
            --without-http_ssi_module 
            --without-http_userid_module 
            --without-http_access_module 
            --without-http_auth_basic_module 
            --without-http_mirror_module 
            --without-http_autoindex_module 
            --without-http_geo_module 
            --without-http_split_clients_module 
            --without-http_referer_module 
            --without-http_rewrite_module 
            --without-http_proxy_module 
            --without-http_fastcgi_module 
            --without-http_uwsgi_module 
            --without-http_scgi_module 
            --without-http_grpc_module 
            --without-http_memcached_module 
            --without-http_limit_conn_module 
            --without-http_limit_req_module 
            --without-http_empty_gif_module 
            --without-http_browser_module 
            --without-http_upstream_hash_module 
            --without-http_upstream_ip_hash_module 
            --without-http_upstream_least_conn_module 
            --without-http_upstream_keepalive_module 
            --without-http_upstream_zone_module 
            --without-http_gzip_module 
            --with-http_gzip_static_module 
            --with-threads 
            --with-compat 
            --with-file-aio 
            --add-dynamic-module=/usr/src/ngx_brotli-$NGX_BROTLI_VERSION 
    " 
# ╨б╨╛╨▒╨╕╤А╨░╨╡╨╝
    && ./configure $CNF 
    && make -j$(getconf _NPROCESSORS_ONLN) 
    && make install 
    && rm -rf /usr/src/ 
# ╨г╨┤╨░╨╗╤П╨╡╨╝ ╨┤╨╕╨╜╨░╨╝╨╕╤З╨╡╤Б╨║╨╕╨╣ brotli ╨╝╨╛╨┤╤Г╨╗╤М, ╨╛╤Б╤В╨░╨▓╨╗╤П╤П ╤В╨╛╨╗╤М╨║╨╛ ╤Б╤В╨░╤В╨╕╤З╨╡╤Б╨║╨╕╨╣
    && rm /usr/lib/nginx/modules/ngx_http_brotli_filter_module.so 
    && sed -i '$ d' /etc/apk/repositories 
# Bring in gettext so we can get `envsubst`, then throw
# the rest away. To do this, we need to install `gettext`
# then move `envsubst` out of the way so `gettext` can
# be deleted completely, then move `envsubst` back.
    && apk add --no-cache --virtual .gettext gettext 
    && mv /usr/bin/envsubst /tmp/ 
    && runDeps="$( 
        scanelf --needed --nobanner /usr/sbin/nginx /usr/lib/nginx/modules/*.so /tmp/envsubst 
            | awk '{ gsub(/,/, "nso:", $2); print "so:" $2 }' 
            | sort -u 
            | xargs -r apk info --installed 
            | sort -u 
    )" 
    && apk add --no-cache $runDeps 
    && apk del .build-deps 
    && apk del .gettext 
    && mv /tmp/envsubst /usr/local/bin/ 
# Bring in tzdata so users could set the timezones through the environment
# variables
    && apk add --no-cache tzdata 
# forward request and error logs to docker log collector
    && ln -sf /dev/stdout /var/log/nginx/access.log 
    && ln -sf /dev/stderr /var/log/nginx/error.log
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
STOPSIGNAL SIGTERM
CMD ["nginx", "-g", "daemon off;"]

рдореА рддрд╛рдмрдбрддреЛрдм nginx.conf рджреБрд░реБрд╕реНрдд рдХрд░реЗрди рдЬреЗрдгреЗрдХрд░реБрди gzip рдЖрдгрд┐ brotli рдореБрд▓рднреВрддрд░рд┐рддреНрдпрд╛ рд╕рдХреНрд╖рдо рдХреЗрд▓реЗ рдЬрд╛рддреАрд▓. рдореА рдХреЕрд╢рд┐рдВрдЧ рд╣реЗрдбрд░ рджреЗрдЦреАрд▓ рд╕рдорд╛рд╡рд┐рд╖реНрдЯ рдХрд░реЗрди, рдХрд╛рд░рдг рдЖрдореНрд╣реА рдХрдзреАрд╣реА рд╕реНрдерд┐рд░ рдмрджрд▓рдгрд╛рд░ рдирд╛рд╣реА. рдЖрдгрд┐ рдЕрдВрддрд┐рдо рд╕реНрдкрд░реНрд╢ рд╕рд░реНрд╡ 404 рд╡рд┐рдирдВрддреНрдпрд╛ index.html рд╡рд░ рдкреБрдирд░реНрдирд┐рд░реНрджреЗрд╢рд┐рдд рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА рдЕрд╕реЗрд▓, рд╣реЗ SPA рдордзреНрдпреЗ рдиреЗрд╡реНрд╣рд┐рдЧреЗрд╢рдирд╕рд╛рдареА рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ.

nginx.conf

user nginx;
worker_processes  1;
error_log /var/log/nginx/error.log warn;
pid       /var/run/nginx.pid;
load_module /usr/lib/nginx/modules/ngx_http_brotli_static_module.so;
events {
    worker_connections 1024;
}
http {
    include      mime.types;
    default_type application/octet-stream;
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;
    sendfile on;
    keepalive_timeout 65;
    gzip_static   on;
    brotli_static on;
    server {
        listen      80;
        server_name localhost;
        charset utf-8;
        location / {
            root html;
            try_files $uri /index.html;
            etag on;
            expires max;
            add_header Cache-Control public;
            location = /index.html {
                expires 0;
                add_header Cache-Control "no-cache, public, must-revalidate, proxy-revalidate";
            }
        }
    }
}

рдЖрдкрдг рддрдпрд╛рд░ рдХреЗрд▓реЗрд▓реА рдкреНрд░рддрд┐рдорд╛ рдпреЗрдереЗ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░реВ рд╢рдХрддрд╛: https://hub.docker.com/r/alexxxnf/nginx-spa. рд╣реЗ 10,5 рдПрдордмреА рдШреЗрддреЗ. рдореВрд│ nginx рдиреЗ 19,7 MB рдШреЗрддрд▓рд╛. рдорд╛рдЭреА рдЦреЗрд│рд╛рдЪреА рдЖрд╡рдб рдкреВрд░реНрдг рдЭрд╛рд▓реА рдЖрд╣реЗ.

рдкрд░реНрдпрд╛рд╡рд░рдгреАрдп рдЪрд▓ рд╕рдордЬреВрди рдШреЗрдгреНрдпрд╛рд╕рд╛рдареА рд╕реНрдЯреЕрдЯрд┐рдХреНрд╕ рд╢рд┐рдХрд╡рдгреЗ

SPA рдордзреНрдпреЗ рд╕реЗрдЯрд┐рдВрдЧреНрдЬрдЪреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдХрд╛ рдЕрд╕реВ рд╢рдХрддреЗ? рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде, рдХреЛрдгрддреЗ RESTful API рд╡рд╛рдкрд░рд╛рдпрдЪреЗ рддреЗ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА. рд╕рд╛рдорд╛рдиреНрдпрддрдГ, рдЗрдЪреНрдЫрд┐рдд рд╡рд╛рддрд╛рд╡рд░рдгрд╛рд╕рд╛рдареА рд╕реЗрдЯрд┐рдВрдЧреНрдЬ рдмрд┐рд▓реНрдб рд╕реНрдЯреЗрдЬрд╡рд░ SPA рдордзреНрдпреЗ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХреЗрд▓реНрдпрд╛ рдЬрд╛рддрд╛рдд. рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рд╣реА рдмрджрд▓рд╛рдпрдЪреЗ рдЕрд╕рд▓реНрдпрд╛рд╕, рддреБрдореНрд╣рд╛рд▓рд╛ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдкреБрдиреНрд╣рд╛ рддрдпрд╛рд░ рдХрд░рд╛рд╡рд╛ рд▓рд╛рдЧреЗрд▓. рдорд▓рд╛ рддреЗ рдирдХреЛрдп. рд╕реАрдЖрдп рд╕реНрдЯреЗрдЬрд╡рд░ рдПрдХрджрд╛рдЪ рдЕреЕрдкреНрд▓рд┐рдХреЗрд╢рди рддрдпрд╛рд░ рдХреЗрд▓реЗ рдЬрд╛рд╡реЗ рдЖрдгрд┐ рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕ рд╡рд╛рдкрд░реВрди рд╕реАрдбреА рд╕реНрдЯреЗрдЬрд╡рд░ рдЖрд╡рд╢реНрдпрдХ рддреЗрд╡рдвреЗ рдХреЙрдиреНрдлрд┐рдЧрд░ рдХреЗрд▓реЗ рдЬрд╛рд╡реЗ рдЕрд╢реА рдорд╛рдЭреА рдЗрдЪреНрдЫрд╛ рдЖрд╣реЗ.

рдЕрд░реНрдерд╛рдд, рд╕реНрдЯреЕрдЯрд┐рдХ рдлрд╛рдЗрд▓реНрд╕ рд╕реНрд╡рддрдГрдЪ рдХреЛрдгрддреЗрд╣реА рдкрд░реНрдпрд╛рд╡рд░рдг рдЪрд▓ рд╕рдордЬрдд рдирд╛рд╣реАрдд. рдореНрд╣рдгреВрди, рдЖрдкрд▓реНрдпрд╛рд▓рд╛ рдПрдХ рдпреБрдХреНрддреА рд╡рд╛рдкрд░рд╛рд╡реА рд▓рд╛рдЧреЗрд▓. рдЕрдВрддрд┐рдо рдкреНрд░рддрд┐рдореЗрдд, рдореА nginx рд▓рд╛рдБрдЪ рдХрд░рдгрд╛рд░ рдирд╛рд╣реА, рдкрд░рдВрддреБ рдПрдХ рд╡рд┐рд╢реЗрд╖ рд╢реЗрд▓ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рдЬреА рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕ рд╡рд╛рдЪреЗрд▓, рддреНрдпрд╛рдВрдирд╛ рд╕реНрдерд┐рд░ рдлрд╛рдЗрд▓реНрд╕рд╡рд░ рд▓рд┐рд╣реЗрд▓, рддреНрдпрд╛рдВрдирд╛ рд╕рдВрдХреБрдЪрд┐рдд рдХрд░реЗрд▓ рдЖрдгрд┐ рддреНрдпрд╛рдирдВрддрд░рдЪ рдирд┐рдпрдВрддреНрд░рдг nginx рд╡рд░ рд╣рд╕реНрддрд╛рдВрддрд░рд┐рдд рдХрд░реЗрд▓.

рдпрд╛ рдЙрджреНрджреЗрд╢рд╛рд╕рд╛рдареА, рдбреЙрдХрд░рдлрд╛рдЗрд▓ ENTRYPOINT рдкреЕрд░рд╛рдореАрдЯрд░ рдкреНрд░рджрд╛рди рдХрд░рддреЗ. рдЪрд▓рд╛ рддреНрдпрд╛рд▓рд╛ рдЦрд╛рд▓реАрд▓ рд╕реНрдХреНрд░рд┐рдкреНрдЯ рджреЗрдК (рдЙрджрд╛рд╣рд░рдгрд╛рд░реНрде рдЕрдБрдЧреБрд▓рд░ рд╡рд╛рдкрд░реВрди):

docker-entrypoint.sh

#!/bin/sh
set -e
FLAG_FILE="/configured"
TARGET_DIR="/etc/nginx/html"
replace_vars () {
  ENV_VARS='$(awk 'BEGIN{for(v in ENVIRON) print "

quot;v}')'
# ╨Т Angular ╨╕╤Й╨╡╨╝ ╨┐╨╗╨╡╨╣╤Б╤Е╨╛╨╗╨┤╨╡╤А╤Л ╨▓ main-╤Д╨░╨╣╨╗╨░╤Е
for f in "$TARGET_DIR"/main*.js; do
# envsubst ╨╖╨░╨╝╨╡╨╜╤П╨╡╤В ╨▓ ╤Д╨░╨╣╨╗╨░╤Е ╨┐╨╗╨╡╨╣╤Б╤Е╨╛╨╗╨┤╨╡╤А╤Л ╨╜╨░ ╨╖╨╜╨░╤З╨╡╨╜╨╕╤П ╨╕╨╖ ╨┐╨╡╤А╨╡╨╝╨╡╨╜╨╜╤Л╤Е ╨╛╨║╤А╤Г╨╢╨╡╨╜╨╕╤П
echo "$(envsubst "$ENV_VARS" < "$f")" > "$f"
done
}
compress () {
for i in $(find "$TARGET_DIR" | grep -E ".css$|.html$|.js$|.svg$|.txt$|.ttf


quot;); do
# ╨Ш╤Б╨┐╨╛╨╗╤М╨╖╤Г╨╡╨╝ ╨╝╨░╨║╤Б╨╕╨╝╨░╨╗╤М╨╜╤Г╤О ╤Б╤В╨╡╨┐╨╡╨╜╤М ╤Б╨╢╨░╤В╨╕╤П
gzip -9kf "$i" && brotli -fZ "$i"
done
}
if [ "$1" = 'nginx' ]; then
# ╨д╨╗╨░╨│ ╨╜╤Г╨╢╨╡╨╜, ╤З╤В╨╛╨▒╤Л ╨▓╤Л╨┐╨╛╨╗╨╜╨╕╤В╤М ╤Б╨║╤А╨╕╨┐╤В ╤В╨╛╨╗╤М╨║╨╛ ╨┐╤А╨╕ ╤Б╨░╨╝╨╛╨╝ ╨┐╨╡╤А╨▓╨╛╨╝ ╨╖╨░╨┐╤Г╤Б╨║╨╡
if [ ! -e "$FLAG_FILE" ]; then
echo "Running init script"
echo "Replacing env vars"
replace_vars
echo "Compressing files"
compress
touch $FLAG_FILE
echo "Done"
fi
fi
exec "$@"

рд╕реНрдХреНрд░рд┐рдкреНрдЯрдиреЗ рддреНрдпрд╛рдЪреЗ рдХрд╛рд░реНрдп рдХрд░рдгреНрдпрд╛рд╕рд╛рдареА, рд╕реЗрдЯрд┐рдВрдЧреНрдЬ рдпрд╛ рдлреЙрд░реНрдордордзреНрдпреЗ js рдлрд╛рдЗрд▓реНрд╕рдордзреНрдпреЗ рд▓рд┐рд╣рд┐рд▓реНрдпрд╛ рдкрд╛рд╣рд┐рдЬреЗрдд: ${API_URL}.

рд╣реЗ рд▓рдХреНрд╖рд╛рдд рдШреЗрдгреНрдпрд╛рд╕рд╛рд░рдЦреЗ рдЖрд╣реЗ рдХреА рдмрд╣реБрддреЗрдХ рдЖрдзреБрдирд┐рдХ рдПрд╕рдкреАрдП рдмрд╛рдВрдзрдХрд╛рдо рдХрд░рддрд╛рдирд╛ рддреНрдпрд╛рдВрдЪреНрдпрд╛ рдлрд╛рдпрд▓реАрдВрдордзреНрдпреЗ рд╣реЕрд╢ рдЬреЛрдбрддрд╛рдд. рд╣реЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ рдЬреЗрдгреЗрдХрд░реВрди рдмреНрд░рд╛рдЙрдЭрд░ рдмрд░реНрдпрд╛рдЪ рдХрд╛рд│рд╛рд╕рд╛рдареА рдлрд╛рдЗрд▓ рд╕реБрд░рдХреНрд╖рд┐рддрдкрдгреЗ рдХреЕрд╢реЗ рдХрд░реВ рд╢рдХреЗрд▓. рдЬрд░ рдлрд╛рдЗрд▓ рдмрджрд▓рд▓реА рддрд░ рддрд┐рдЪрд╛ рд╣реЕрд╢ рдмрджрд▓реЗрд▓, рдЬреНрдпрд╛рдореБрд│реЗ рдмреНрд░рд╛рдЙрдЭрд░рд▓рд╛ рдлрд╛рдЗрд▓ рдкреБрдиреНрд╣рд╛ рдбрд╛рдЙрдирд▓реЛрдб рдХрд░рдгреНрдпрд╛рд╕ рднрд╛рдЧ рдкрд╛рдбрд▓реЗ рдЬрд╛рдИрд▓.

рджреБрд░реНрджреИрд╡рд╛рдиреЗ, рдорд╛рдЭреНрдпрд╛ рдкрджреНрдзрддреАрдордзреНрдпреЗ, рдкрд░реНрдпрд╛рд╡рд░рдг рд╡реНрд╣реЗрд░рд┐рдПрдмрд▓реНрд╕рджреНрд╡рд╛рд░реЗ рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рдмрджрд▓рд▓реНрдпрд╛рдиреЗ рдлрд╛рдИрд▓ рд╣реЕрд╢рдордзреНрдпреЗ рдмрджрд▓ рд╣реЛрдд рдирд╛рд╣реА, рдпрд╛рдЪрд╛ рдЕрд░реНрде рдЕрд╕рд╛ рдХреА рдмреНрд░рд╛рдЙрдЭрд░ рдХреЕрд╢реЗ рдЗрддрд░ рдХреЛрдгрддреНрдпрд╛рд╣реА рдкреНрд░рдХрд╛рд░реЗ рдЕрд╡реИрдз рдХрд░рдгреЗ рдЖрд╡рд╢реНрдпрдХ рдЖрд╣реЗ. рдорд▓рд╛ рд╣реА рд╕рдорд╕реНрдпрд╛ рдирд╛рд╣реА рдХрд╛рд░рдг рднрд┐рдиреНрди рдХреЙрдиреНрдлрд┐рдЧрд░реЗрд╢рди рд╡реЗрдЧрд╡реЗрдЧрд│реНрдпрд╛ рд╡рд╛рддрд╛рд╡рд░рдгрд╛рдд рдЙрдкрдпреЛрдЬрд┐рдд рдЖрд╣реЗрдд.

рдЕрдВрддрд┐рдо рдкреНрд░рддрд┐рдорд╛ рдПрдХрддреНрд░ рдареЗрд╡рдгреЗ

рд╢реЗрд╡рдЯреА.

рдбреЙрдХрд░рдлрд╛рдЗрд▓

# ╨Я╨╡╤А╨▓╤Л╨╣ ╨▒╨░╨╖╨╛╨▓╤Л╨╣ ╨╛╨▒╤А╨░╨╖ ╨┤╨╗╤П ╤Б╨▒╨╛╤А╨║╨╕
FROM alexxxnf/spa-builder as builder
# ╨з╤В╨╛╨▒╤Л ╤Н╤Д╤Д╨║╤В╨╕╨▓╨╜╨╡╨╡ ╨╕╤Б╨┐╨╛╨╗╤М╨╖╨╛╨▓╨░╤В╤М ╨║╤Н╤И Docker-╨░, ╤Б╨╜╨░╤З╨░╨╗╨░ ╤Г╤Б╤В╨░╨╜╨░╨▓╨╗╨╕╨▓╨░╨╡╨╝ ╤В╨╛╨╗╤М╨║╨╛ ╨╖╨░╨▓╨╕╤Б╨╕╨╝╨╛╤Б╤В╨╕
COPY ./package.json ./package-lock.json /app/
RUN cd /app && npm ci --no-audit
# ╨Я╨╛╤В╨╛╨╝ ╤Б╨╛╨▒╨╕╤А╨░╨╡╨╝ ╤Б╨░╨╝╨╛ ╨┐╤А╨╕╨╗╨╛╨╢╨╡╨╜╨╕╨╡
COPY . /app
RUN cd /app && npm run build -- --prod --configuration=docker

# ╨Т╤В╨╛╤А╨╛╨╣ ╨▒╨░╨╖╨╛╨▓╤Л╨╣ ╨╛╨▒╤А╨░╨╖ ╨┤╨╗╤П ╤А╨░╨╖╨┤╨░╤З╨╕
FROM alexxxnf/nginx-spa
# ╨Ч╨░╨▒╨╕╤А╨░╨╡╨╝ ╨╕╨╖ ╨┐╨╡╤А╨▓╨╛╨│╨╛ ╨╛╨▒╤А╨░╨╖╨░ ╤Б╨╜╨░╤З╨░╨╗╨░ ╨║╨╛╨╝╨┐╤А╨╡╤Б╤Б╨╛╤А
COPY --from=builder /usr/local/bin/brotli /usr/local/bin
# ╨Я╨╛╤В╨╛╨╝ ╨┤╨╛╨▒╨░╨▓╨╗╤П╨╡╨╝ ╤З╤Г╨┤╨╛-╤Б╨║╤А╨╕╨┐╤В
COPY ./docker/docker-entrypoint.sh /docker-entrypoint.sh
# ╨Ш ╨▓ ╨║╨╛╨╜╤Ж╨╡ ╨╖╨░╨▒╨╕╤А╨░╨╡╨╝ ╤Б╨░╨╝╨╛ ╨┐╤А╨╕╨╗╨╛╨╢╨╡╨╜╨╕╨╡
COPY --from=builder /app/dist/app /etc/nginx/html/
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["nginx", "-g", "daemon off;"]

рдЖрддрд╛ рдкрд░рд┐рдгрд╛рдореА рдкреНрд░рддрд┐рдорд╛ рдПрдХрддреНрд░ рдХреЗрд▓реА рдЬрд╛рдК рд╢рдХрддреЗ рдЖрдгрд┐ рдХреБрдареЗрд╣реА рд╡рд╛рдкрд░рд▓реА рдЬрд╛рдК рд╢рдХрддреЗ.

рд╕реНрддреНрд░реЛрдд: www.habr.com