سنگل پيج ايپليڪيشن جي ورڇ لاءِ ڊڪر تصوير

سنگل پيج ايپليڪيشن (SPA) جامد JavaScript ۽ HTML فائلن جو هڪ سيٽ آهي، انهي سان گڏ تصويرون ۽ ٻين وسيلن. ڇاڪاڻ ته اهي متحرڪ طور تي تبديل نه ڪندا آهن، انهن کي آن لائن شايع ڪرڻ تمام آسان آهي. ان لاءِ سستا ۽ اڃا به مفت خدمتون آهن، جن جي شروعات هڪ سادي GitHub صفحن سان ٿئي ٿي (۽ ڪجهه لاءِ ته narod.ru سان به) ۽ ختم ٿئي ٿي CDN جهڙوڪ Amazon S3 سان. بهرحال، مون کي ٻي شيء جي ضرورت هئي.

مون کي SPA سان گڏ Docker تصوير جي ضرورت آهي ته جيئن اهو آساني سان ٻنهي پيداوار ۾ شروع ڪري سگهجي هڪ Kubernetes ڪلستر جي حصي جي طور تي، ۽ هڪ پٺتي پيل ڊولپر جي مشين تي جنهن کي خبر ناهي ته SPA ڇا آهي.

مون پنهنجي لاءِ هيٺ ڏنل تصويري گهرجن کي طئي ڪيو آهي:

  • استعمال ۾ آسان (پر نه اسيمبلي)؛
  • گھٽ ۾ گھٽ ماپ ٻنهي ڊسڪ ۽ رام جي لحاظ کان؛
  • ماحوليات جي متغير ذريعي ترتيب ڏيڻ ته جيئن تصوير مختلف ماحول ۾ استعمال ڪري سگهجي.
  • فائلن جي سڀ کان وڌيڪ موثر ورڇ.

اڄ مان توهان کي ٻڌايان ٿو ته ڪيئن:

  • گٽ nginx؛
  • ذريعن کان بروٿلي ٺاهيو؛
  • جامد فائلن کي سيکاريو ماحول جي متغيرن کي سمجهڻ لاءِ؛
  • ۽ يقيناً هن سڀني مان هڪ ڊاکر تصوير ڪيئن گڏ ڪجي.

هن آرٽيڪل جو مقصد منهنجو تجربو شيئر ڪرڻ ۽ تجربيڪار ڪميونٽي جي ميمبرن کي تعميري تنقيد لاءِ اڀاريو آهي.

اسيمبليءَ لاءِ تصوير ٺاهڻ

حتمي Docker تصوير کي سائيز ۾ ننڍو ڪرڻ لاء، توهان کي ٻن قاعدن تي عمل ڪرڻ جي ضرورت آهي: گهٽ ۾ گهٽ پرت ۽ هڪ ننڍڙي بنيادي تصوير. ھڪڙي ننڍڙي بنيادي تصويرن مان ھڪڙو آھي الپائن لينڪس تصوير، تنھنڪري اھو اھو آھي جيڪو مان چونڊيندس. ڪجھ بحث ڪري سگھي ٿو ته الپائن پيداوار لاء مناسب نه آھي، ۽ اھي صحيح آھن. پر ذاتي طور تي، مون کي هن سان ڪڏهن به ڪو مسئلو ناهي ۽ هن جي خلاف ڪو به دليل نه آهي.

ٿورن تہن لاء، مان تصوير کي 2 مرحلن ۾ گڏ ڪندس. پهريون هڪ مسودو آهي؛ سڀ معاون افاديت ۽ عارضي فائلون ان ۾ رهنديون. ۽ آخري مرحلي ۾ آئون صرف ايپليڪيشن جو حتمي ورزن لکندس.

اچو ته معاون تصوير سان شروع ڪريون.

SPA ايپليڪيشن کي گڏ ڪرڻ لاء، توهان کي عام طور تي ضرورت آهي node.js. مان سرڪاري تصوير وٺندس، جيڪو پڻ اچي ٿو npm ۽ يارن پيڪيج مينيجرز سان. منهنجي طرفان، مان شامل ڪندس node-gyp، جيڪو ڪجهه npm پيڪيجز ٺاهڻ لاءِ گهربل آهي، ۽ گوگل کان Brotli ڪمپريسر، جيڪو بعد ۾ اسان لاءِ مفيد ٿيندو.

تبصرن سان Dockerfile.

# Базовый образ
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

اڳ ۾ ئي هتي آئون minimalism لاء وڙهندي آهيان، تنهنڪري تصوير هڪ وڏي ٽيم پاران گڏ ڪيو ويو آهي.

ختم ٿيل تصوير هتي ملي سگهي ٿي: https://hub.docker.com/r/alexxxnf/spa-builder. جيتوڻيڪ آئون ٻين ماڻهن جي تصويرن تي ڀروسو نه ڪرڻ جي صلاح ڏيان ٿو ۽ پنهنجو پاڻ کي گڏ ڪرڻ.

نگنڪس

توهان جامد مواد ورهائڻ لاءِ ڪنهن به ويب سرور کي استعمال ڪري سگهو ٿا. مان nginx سان ڪم ڪرڻ لاء استعمال ڪيو ويو آهيان، تنهنڪري مان هاڻي ان کي استعمال ڪندس.

نينگڪس وٽ هڪ سرڪاري ڊاکر تصوير آهي، پر ان ۾ سادي جامد ورڇ لاءِ ڪيترائي ماڊل آهن. جن کي ترسيل ۾ شامل ڪيو ويو آهي هڪ خاص ٽيم يا سرڪاري Dockerfile ۾ ڏسي سگهجي ٿو.

$ ڊاڪر رن --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 تي ڪم ڪرڻ جي قابل نه هوندو، اختيار جي حمايت نه ڪندو، ۽ گهڻو ڪجهه. پر منهنجو نسخو برٽلي الگورٿم سان ٺهيل فائلن کي ورهائڻ جي قابل هوندو، جيڪو gzip کان ٿورو وڌيڪ ڪارائتو آهي. اسان فائلن کي هڪ ڀيرو دٻائينداسين؛ فلائي تي ائين ڪرڻ جي ڪا ضرورت ناهي.

هي آهي Dockerfile جنهن سان مون ختم ڪيو. روسي ۾ تبصرا منهنجا آهن، انگريزيء ۾ - اصل کان.

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

مان فوري طور تي 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 MB. اصل nginx ورتو 19,7 MB. منهنجي راندين جي دلچسپي مطمئن آهي.

ماحوليات جي متغيرن کي سمجهڻ لاءِ انگ اکر سيکارڻ

SPA ۾ سيٽنگن جي ضرورت ڇو ٿي سگھي ٿي؟ مثال طور، وضاحت ڪرڻ لاءِ ته ڪهڙو RESTful API استعمال ڪجي. عام طور تي، گهربل ماحول لاء سيٽنگون تعمير اسٽيج تي SPA ڏانهن منتقل ڪيا ويا آهن. جيڪڏهن توهان کي ڪجهه تبديل ڪرڻ جي ضرورت آهي، توهان کي ايپليڪيشن کي ٻيهر ٺاهڻو پوندو. مان اهو نٿو چاهيان. مان چاهيان ٿو ته ايپليڪيشن CI اسٽيج تي هڪ ڀيرو ٺاهي وڃي، ۽ سي ڊي اسٽيج تي ماحوليات جي متغيرن کي استعمال ڪندي جيترو ضروري هجي ترتيب ڏني وڃي.

يقينا، جامد فائلون پاڻ کي ڪنهن به ماحول جي متغير کي نه سمجھندا آهن. تنهن ڪري، توهان کي هڪ چال استعمال ڪرڻو پوندو. آخري تصوير ۾، مان nginx لانچ نه ڪندس، پر هڪ خاص شيل اسڪرپٽ جيڪو ماحول جي متغيرن کي پڙهندو، انهن کي جامد فائلن ۾ لکندو، انهن کي دٻايو، ۽ صرف پوء ڪنٽرول کي نينڪس ڏانهن منتقل ڪيو.

هن مقصد لاء، Dockerfile مهيا ڪري ٿو ENTRYPOINT پيٽرول. اچو ته هن کي هيٺ ڏنل اسڪرپٽ ڏيو (مثال طور Angular استعمال ڪندي):

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}.

اهو نوٽ ڪرڻ جي قابل آهي ته اڪثر جديد SPAs تعمير ڪرڻ وقت انهن جي فائلن ۾ هيش شامل ڪندا آهن. اهو ضروري آهي ته برائوزر محفوظ طور تي فائل کي ڊگهي وقت تائين ڪيش ڪري سگهي ٿو. جيڪڏهن فائل تبديل ٿئي ٿي، ته ان جي هيش تبديل ٿي ويندي، جنهن جي نتيجي ۾ برائوزر کي فائل کي ٻيهر ڊائون لوڊ ڪرڻ تي مجبور ڪندو.

بدقسمتي سان، منهنجي طريقي ۾، ماحول جي متغير ذريعي ترتيبن کي تبديل ڪرڻ سان فائل هيش ۾ تبديلي نه ايندي آهي، جنهن جو مطلب آهي ته برائوزر ڪيش کي ڪنهن ٻئي طريقي سان باطل ڪيو وڃي. مون کي اهو مسئلو ناهي ڇو ته مختلف ترتيبون مختلف ماحول ۾ ترتيب ڏنل آهن.

آخري تصوير گڏ ڪرڻ

آخرڪار.

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

هاڻي نتيجو تصوير گڏ ڪري سگهجي ٿو ۽ ڪٿي به استعمال ڪري سگهجي ٿو.

جو ذريعو: www.habr.com