የነጠላ ገጽ መተግበሪያን ለማሰራጨት Docker ምስል

ነጠላ-ገጽ አፕሊኬሽን (SPA) የማይንቀሳቀስ ጃቫስክሪፕት እና ኤችቲኤምኤል ፋይሎች፣ እንዲሁም ምስሎች እና ሌሎች ግብአቶች ስብስብ ነው። በተለዋዋጭነት ስለማይለወጡ በመስመር ላይ ማተም በጣም ቀላል ነው። ለዚህም በቀላል GitHub ገጾች (እና ለአንዳንዶች በ narod.ru) በመጀመር እና እንደ Amazon S3 በ CDN በመጨረስ ለዚህ ብዙ ቁጥር ያላቸው ርካሽ እና ነጻ አገልግሎቶች አሉ። ይሁን እንጂ ሌላ ነገር ያስፈልገኝ ነበር.

እንደ የኩበርኔትስ ክላስተር አካል ሆኖ ሁለቱንም በምርት ውስጥ በቀላሉ እንዲጀምር እና SPA ምን እንደሆነ በማያውቅ የኋላ-መጨረሻ ገንቢ ማሽን ላይ የዶከር ምስል ከ SPA ጋር ያስፈልገኝ ነበር።

ለራሴ የሚከተሉትን የምስል መስፈርቶች ወስኛለሁ፡

  • የአጠቃቀም ቀላልነት (ግን መሰብሰብ አይደለም);
  • ዝቅተኛ መጠን በሁለቱም በዲስክ እና በ RAM;
  • ምስሉ በተለያዩ አካባቢዎች ጥቅም ላይ እንዲውል በአካባቢ ተለዋዋጮች በኩል ማዋቀር;
  • በጣም ውጤታማው የፋይል ስርጭት።

ዛሬ እንዴት እነግራችኋለሁ፡-

  • አንጀት nginx;
  • brotli ከምንጮች መገንባት;
  • የአካባቢ ተለዋዋጮችን ለመረዳት የማይንቀሳቀሱ ፋይሎችን ማስተማር;
  • እና በእርግጥ ከዚህ ሁሉ የዶከር ምስል እንዴት እንደሚሰበስብ.

የዚህ ጽሁፍ አላማ የኔን ልምድ ለማካፈል እና ልምድ ያላቸውን የማህበረሰብ አባላት ወደ ገንቢ ትችት ለመቀስቀስ ነው።

ለስብሰባ ምስል መገንባት

የመጨረሻውን የዶከር ምስል ትንሽ መጠን ለማድረግ, ሁለት ደንቦችን ማክበር አለብዎት: ቢያንስ የንብርብሮች እና ዝቅተኛ የመሠረት ምስል. ከትንንሾቹ የመሠረት ምስሎች አንዱ የአልፓይን ሊኑክስ ምስል ነው፣ ስለዚህ እኔ የምመርጠው ያንን ነው። አንዳንዶች አልፓይን ለማምረት ተስማሚ አይደለም ብለው ይከራከሩ ይሆናል, እና ትክክል ሊሆኑ ይችላሉ. ግን በግሌ ከእሱ ጋር ምንም አይነት ችግር አጋጥሞኝ አያውቅም እና በእሱ ላይ ምንም ክርክሮች የሉም.

ያነሱ ንብርብሮች እንዲኖሩኝ, ምስሉን በ 2 ደረጃዎች እሰበስባለሁ. የመጀመሪያው ረቂቅ ነው, ሁሉም ረዳት መገልገያዎች እና ጊዜያዊ ፋይሎች በእሱ ውስጥ ይቀራሉ. እና በመጨረሻው ደረጃ የመተግበሪያውን የመጨረሻ ስሪት ብቻ እጽፋለሁ.

በረዳት ምስል እንጀምር.

የ SPA መተግበሪያን ለማጠናቀር አብዛኛው ጊዜ node.js ያስፈልግዎታል። ኦፊሴላዊውን ምስል እወስዳለሁ ፣ እሱም ከ npm እና yarn ጥቅል አስተዳዳሪዎች ጋርም ይመጣል። በራሴ ምትክ አንዳንድ npm ጥቅሎችን ለመገንባት የሚያስፈልገውን ኖድ-ጂፕ እና ብሮትሊ መጭመቂያ ከ 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 ኦፊሴላዊ Docker ምስል አለው፣ ግን ለቀላል የማይንቀሳቀስ ስርጭት በጣም ብዙ ሞጁሎች አሉት። በማቅረቡ ውስጥ የትኞቹ እንደሚካተቱ በልዩ ቡድን ወይም በይፋዊው ዶከርፋይል ውስጥ ሊታዩ ይችላሉ።

$ docker ሩጫ --rm nginx:1-አልፓይን 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

Dockerfile ን እንደ መሰረት አድርጌ እጠቀማለሁ፣ ግን በውስጡ የማይንቀሳቀስ ይዘትን ለማሰራጨት የሚያስፈልገውን ብቻ እተወዋለሁ። የእኔ ስሪት በኤችቲቲፒኤስ ላይ መስራት አይችልም፣ ፍቃድን አይደግፍም እና ብዙ ተጨማሪ። ነገር ግን የእኔ ስሪት በ Brotli ስልተ ቀመር የተጨመቁ ፋይሎችን ማሰራጨት ይችላል ፣ ይህም ከ gzip ትንሽ የበለጠ ቀልጣፋ ነው። ፋይሎችን አንድ ጊዜ እንጨምቃለን, በበረራ ላይ ይህን ማድረግ አያስፈልግም.

ይህ እኔ ያበቃሁት ዶከርፋይል ነው። በሩሲያኛ አስተያየቶች የእኔ ናቸው, በእንግሊዝኛ - ከመጀመሪያው.

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

gzip እና brotli በነባሪ እንዲነቁ nginx.confን ወዲያውኑ አስተካክላለሁ። እኔ ደግሞ መሸጎጫ ራስጌዎችን እጨምራለሁ፣ ምክንያቱም መቼም የማይለወጥ ለውጥ ስለማንገኝ። እና የመጨረሻው ንክኪ ሁሉንም 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 ሜባ ወስዷል። የእኔ የስፖርት ፍላጎት ረክቷል.

የአካባቢ ተለዋዋጮችን ለመረዳት ስታቲስቲክስን ማስተማር

በ SPA ውስጥ ቅንብሮች ለምን ያስፈልጉ ይሆናል? ለምሳሌ፣ የትኛውን RESTful ኤፒአይ መጠቀም እንዳለበት ለመግለጽ። በተለምዶ, ለተፈለገው አካባቢ ቅንጅቶች በግንባታ ደረጃ ወደ SPA ይተላለፋሉ. የሆነ ነገር መለወጥ ከፈለጉ ማመልከቻውን እንደገና መገንባት ይኖርብዎታል. አልፈልግም። አፕሊኬሽኑ አንድ ጊዜ በCI ደረጃ እንዲገነባ እና በሲዲ ደረጃ የአካባቢ ተለዋዋጮችን በመጠቀም እንደ አስፈላጊነቱ እንዲዋቀር እፈልጋለሁ።

እርግጥ ነው፣ የማይንቀሳቀሱ ፋይሎች እራሳቸው ማንኛውንም የአካባቢ ተለዋዋጮች አይረዱም። ስለዚህ, ብልሃትን መጠቀም አለብዎት. በመጨረሻው ምስል ላይ nginxን አልጀምርም ፣ ግን ልዩ የሼል ስክሪፕት የአካባቢ ተለዋዋጮችን የሚያነብ ፣ ወደ ስታቲስቲክስ ፋይሎች የሚጽፋቸው ፣ ያጭቃቸው እና ከዚያ ብቻ መቆጣጠሪያን ወደ nginx ያስተላልፋል።

ለዚሁ ዓላማ, Dockerfile የ ENTRYPOINT መለኪያን ያቀርባል. የሚከተለውን ስክሪፕት እንስጠው (አንግላርን እንደ ምሳሌ በመጠቀም)፡-

docker-የመግቢያ ነጥብ.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}.

አብዛኛዎቹ ዘመናዊ SPAዎች በሚገነቡበት ጊዜ በፋይሎቻቸው ላይ hashes እንደሚጨምሩ ልብ ሊባል የሚገባው ጉዳይ ነው። አሳሹ ፋይሉን ደህንነቱ በተጠበቀ ሁኔታ ለረጅም ጊዜ መሸጎጥ እንዲችል ይህ አስፈላጊ ነው። ፋይሉ ከተቀየረ, ከዚያም ሃሽ ይለወጣል, ይህም በተራው አሳሹ ፋይሉን እንደገና እንዲያወርድ ያስገድደዋል.

እንደ አለመታደል ሆኖ በእኔ ዘዴ ውቅሩን በአካባቢ ተለዋዋጮች መለወጥ በፋይል hash ላይ ለውጥ አያመጣም ፣ ይህ ማለት የአሳሹ መሸጎጫ በሌላ መንገድ መበላሸት አለበት ማለት ነው። ይህ ችግር የለብኝም ምክንያቱም የተለያዩ አወቃቀሮች በተለያዩ አካባቢዎች ስለሚሰማሩ።

የመጨረሻውን ምስል አንድ ላይ በማጣመር

በመጨረሻም.

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

አሁን የተገኘው ምስል በማንኛውም ቦታ ተሰብስቦ ጥቅም ላይ ሊውል ይችላል.

ምንጭ: hab.com