Sary Docker ho fitsinjarana ny Application Pejy tokana

Fampiharana pejy tokana (SPA) dia fitambarana rakitra JavaScript sy HTML static, ary koa sary sy loharano hafa. Satria tsy miova izy ireo, dia tena mora ny famoahana azy ireo amin'ny Internet. Betsaka ny serivisy mora sy maimaim-poana ho an'izany, manomboka amin'ny Pejy GitHub tsotra (ary ho an'ny sasany aza narod.ru) ary mifarana amin'ny CDN toa an'i Amazon S3. Nila zavatra hafa anefa aho.

Nila sary Docker miaraka amin'ny SPA aho mba ho mora atomboka amin'ny famokarana ho ampahany amin'ny cluster Kubernetes, ary amin'ny masinin'ny mpamorona back-end izay tsy mahafantatra ny atao hoe SPA.

Nofaritako ho an'ny tenako ireto fepetra takiana manaraka ireto:

  • mora ampiasaina (fa tsy fivoriambe);
  • haben'ny kely indrindra na amin'ny lafin'ny kapila sy ny RAM;
  • fanamafisam-peo amin'ny alΓ lan'ny fari-piainan'ny tontolo iainana mba ahafahan'ny sary ampiasaina amin'ny tontolo samihafa;
  • ny fizarana rakitra mahomby indrindra.

Anio aho dia hilaza aminao ny fomba:

  • gut nginx;
  • manangana brotli avy amin'ny loharano;
  • ampianaro ny rakitra static mba hahafantarana ny fari-piainan'ny tontolo iainana;
  • ary mazava ho azy ny fomba hanangonana sary Docker avy amin'izany rehetra izany.

Ny tanjon'ity lahatsoratra ity dia ny hizara ny traikefako sy ny fihantsiana ireo mpikambana efa za-draharaha amin'ny fiaraha-monina amin'ny tsikera manorina.

Manangana sary ho an'ny fivoriambe

Mba hahatonga ny sary Docker farany ho kely habe, dia mila manaraka fitsipika roa ianao: sosona kely indrindra ary sary fototra minimalista. Iray amin'ireo sary fototra kely indrindra ny sary Alpine Linux, ka izay no hofidiko. Mety hisy hilaza fa tsy mety amin'ny famokarana ny Alpine, ary mety ho marina izany. Fa izaho manokana dia tsy mbola nanana olana taminy ary tsy misy ady hevitra momba azy.

Mba hananan'ny sosona vitsy kokoa dia hanangona ny sary amin'ny dingana 2 aho. Ny voalohany dia drafitra; ny fitaovana fanampiny rehetra sy ny rakitra vonjimaika dia hijanona ao. Ary amin'ny dingana farany dia hosoratako fotsiny ny dikan-teny farany amin'ny fampiharana.

Andeha isika hanomboka amin'ny sary fanampiny.

Mba hanangonana rindranasa SPA dia matetika mila node.js ianao. Haka ny sary ofisialy aho, izay miaraka amin'ireo mpitantana fonosana npm sy yarn. Amin'ny anaran'ny tenako dia hanampy node-gyp aho, izay ilaina hanamboarana fonosana npm sasany, ary ny compressor Brotli avy amin'ny Google, izay mahasoa antsika any aoriana.

Dockerfile misy fanehoan-kevitra.

# Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π·
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

Efa eto aho miady amin'ny minimalism, ka ny sary dia natambatra tamin'ny ekipa lehibe iray.

Ny sary vita dia azo jerena eto: https://hub.docker.com/r/alexxxnf/spa-builder. Na dia manoro hevitra aza aho mba tsy hiantehitra amin'ny sarin'ny hafa ary hanangona ny anao manokana.

nginx

Afaka mampiasa mpizara tranonkala rehetra ianao mba hizara votoaty static. Efa zatra miasa amin'ny nginx aho ka hampiasa azy izao.

Nginx dia manana sary Docker ofisialy, saingy manana mΓ΄dely be loatra ho an'ny fizarana static tsotra. Iza amin'ireo no tafiditra amin'ny fandefasana dia azo jerena amin'ny ekipa manokana na ao amin'ny Dockerfile ofisialy.

$ docker run --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

Hampiasa ny Dockerfile ho fototra aho, fa izay ilaina ihany no avelako hizarana votoaty static. Ny kinovako dia tsy afaka miasa amin'ny HTTPS, tsy hanohana fanomezan-dΓ lana, sy ny maro hafa. Fa ny dikan-ko dia afaka mizara rakitra voaporitra amin'ny algorithm Brotli, izay mahomby kokoa noho ny gzip. Indray mandeha isika dia hanery rakitra; tsy ilaina ny manao izany amin'ny lalitra.

Ity no Dockerfile niafarako. Ny fanehoan-kevitra amin'ny teny Rosiana dia ahy, amin'ny teny anglisy - avy amin'ny tany am-boalohany.

dockerfile

# Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π· снова 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;"]

Hamboariko avy hatrany ny nginx.conf mba hahafahan'ny gzip sy brotli amin'ny alΓ lan'ny default. Hampiditra lohapejy caching koa aho, satria tsy hiova static mihitsy isika. Ary ny fikitihana farany dia ny hamerenana ny fangatahana 404 rehetra mankany amin'ny index.html, ilaina amin'ny fitetezana ao amin'ny SPA izany.

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";
            }
        }
    }
}

Azonao alaina eto ny sary vita: https://hub.docker.com/r/alexxxnf/nginx-spa. Mahatratra 10,5 MB izany. Ny nginx tany am-boalohany dia naka 19,7 MB. Afa-po ny fahalianako ara-panatanjahantena.

Fampianarana statika mba hahatakarana ny fari-piainan'ny tontolo iainana

Nahoana no ilaina ny fanovana ao amin'ny SPA? Ohatra, mba hamaritana izay API RESTful ampiasaina. Amin'ny ankapobeny, afindra any amin'ny SPA ny fanovana ho an'ny tontolo irina amin'ny dingana fananganana. Raha mila manova zavatra ianao dia tsy maintsy manangana indray ny fampiharana. Tsy tiako izany. Tiako haorina indray mandeha eo amin'ny sehatra CI ny fampiharana, ary amboarina araka izay ilaina amin'ny sehatra CD amin'ny fampiasana ny fari-piainan'ny tontolo iainana.

Mazava ho azy fa ny rakitra static ny tenany dia tsy mahatakatra ny fari-piainan'ny tontolo iainana. Noho izany dia tsy maintsy mampiasa fitaka ianao. Amin'ny sary farany dia tsy hanomboka ny nginx aho, fa script shell manokana izay hamaky ny fari-piainan'ny tontolo iainana, soraty amin'ny rakitra static, manindry azy ireo, ary avy eo mamindra ny fanaraha-maso amin'ny nginx.

Ho an'ity tanjona ity, ny Dockerfile dia manome ny parameter ENTRYPOINT. Andeha homentsika azy ity script manaraka ity (mampiasa Angular ho ohatra):

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 "$@"

Mba hahavitan'ny script ny asany dia tsy maintsy soratana ao amin'ny rakitra js amin'ity endrika ity ny fika: ${API_URL}.

Tsara ny manamarika fa ny ankamaroan'ny SPA maoderina dia manampy hashes amin'ny rakitra rehefa manorina. Ilaina izany mba ahafahan'ny navigateur afaka mitahiry ny rakitra mandritra ny fotoana maharitra. Raha miova ny rakitra dia hiova ny hash-ny, izay hanery ny navigateur hisintona indray ilay rakitra.

Indrisy anefa, amin'ny fombako, ny fanovana ny fanovana amin'ny alΓ lan'ny fari-piainan'ny tontolo iainana dia tsy mitondra fiovana amin'ny hash rakitra, izay midika fa ny cache navigateur dia tsy maintsy ho foana amin'ny fomba hafa. Tsy manana an'io olana io aho satria ny fanamafisana samihafa dia apetraka amin'ny tontolo samihafa.

Manambatra ny sary farany

Farany.

dockerfile

# ΠŸΠ΅Ρ€Π²Ρ‹ΠΉ Π±Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±Ρ€Π°Π· для сборки
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;"]

Ankehitriny dia azo angonina sy ampiasaina na aiza na aiza ny sary azo.

Source: www.habr.com