سنگل پیج ایپلیکیشن کی تقسیم کے لیے ڈاکر امیج

سنگل پیج ایپلیکیشن (SPA) جامد JavaScript اور HTML فائلوں کے ساتھ ساتھ تصاویر اور دیگر وسائل کا ایک مجموعہ ہے۔ چونکہ وہ متحرک طور پر تبدیل نہیں ہوتے ہیں، ان کو آن لائن شائع کرنا بہت آسان ہے۔ اس کے لیے بڑی تعداد میں سستی اور یہاں تک کہ مفت خدمات ہیں، جن کا آغاز ایک سادہ GitHub Pages سے ہوتا ہے (اور کچھ کے لیے narod.ru کے ساتھ بھی) اور CDN جیسے Amazon S3 پر ختم ہوتا ہے۔ تاہم، مجھے کسی اور چیز کی ضرورت تھی۔

مجھے ایس پی اے کے ساتھ ایک ڈوکر امیج کی ضرورت تھی تاکہ اسے کبرنیٹس کلسٹر کے حصے کے طور پر پروڈکشن میں اور بیک اینڈ ڈویلپر کی مشین پر آسانی سے لانچ کیا جا سکے جسے اس بات کا کوئی اندازہ نہیں ہے کہ SPA کیا ہے۔

میں نے اپنے لیے تصویر کے درج ذیل تقاضوں کا تعین کیا ہے:

  • استعمال میں آسانی (لیکن اسمبلی نہیں)؛
  • ڈسک اور رام دونوں کے لحاظ سے کم از کم سائز؛
  • ماحولیاتی متغیرات کے ذریعے ترتیب تاکہ تصویر کو مختلف ماحول میں استعمال کیا جا سکے۔
  • فائلوں کی سب سے زیادہ موثر تقسیم۔

آج میں آپ کو بتاؤں گا کہ کیسے:

  • گٹ nginx؛
  • ذرائع سے بروٹلی بنانا؛
  • جامد فائلوں کو ماحولیاتی متغیرات کو سمجھنے کے لئے سکھائیں؛
  • اور یقینا اس سب سے ڈوکر امیج کو کیسے اکٹھا کیا جائے۔

اس مضمون کا مقصد اپنے تجربے کو شیئر کرنا اور کمیونٹی کے تجربہ کار افراد کو تعمیری تنقید پر اکسانا ہے۔

اسمبلی کے لیے تصویر بنانا

حتمی ڈوکر امیج کو سائز میں چھوٹا بنانے کے لیے، آپ کو دو اصولوں پر عمل کرنے کی ضرورت ہے: کم از کم تہوں اور ایک کم سے کم بیس امیج۔ سب سے چھوٹی بیس امیجز میں سے ایک الپائن لینکس امیج ہے، اس لیے میں اسی کا انتخاب کروں گا۔ کچھ لوگ بحث کر سکتے ہیں کہ الپائن پیداوار کے لیے موزوں نہیں ہے، اور وہ درست ہو سکتے ہیں۔ لیکن ذاتی طور پر مجھے اس کے ساتھ کبھی کوئی مسئلہ نہیں ہوا اور نہ ہی اس کے خلاف کوئی دلیل ہے۔

کم تہوں کے لیے، میں تصویر کو 2 مراحل میں جمع کروں گا۔ پہلا مسودہ ہے؛ تمام معاون سہولیات اور عارضی فائلیں اس میں رہیں گی۔ اور آخری مرحلے میں میں صرف درخواست کا حتمی ورژن لکھوں گا۔

آئیے معاون تصویر کے ساتھ شروع کرتے ہیں۔

SPA ایپلیکیشن مرتب کرنے کے لیے، آپ کو عام طور پر node.js کی ضرورت ہوتی ہے۔ میں سرکاری تصویر لوں گا، جو npm اور یارن پیکیج مینیجرز کے ساتھ بھی آتا ہے۔ اپنی طرف سے، میں node-gyp، جو کچھ npm پیکجز بنانے کے لیے درکار ہے، اور Google سے Brotli کمپریسر شامل کروں گا، جو بعد میں ہمارے لیے مفید ہوگا۔

تبصرے کے ساتھ ڈاکر فائل۔

# Базовый образ
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 کے ساتھ کام کرنے کا عادی ہوں، لہذا میں اسے اب استعمال کروں گا۔

Nginx میں ایک آفیشل ڈوکر امیج ہے، لیکن اس میں سادہ جامد تقسیم کے لیے بہت زیادہ ماڈیولز ہیں۔ کون سی ڈیلیوری میں شامل ہیں ایک خصوصی ٹیم یا آفیشل ڈاکر فائل میں دیکھ سکتی ہے۔

$ 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

میں Dockerfile کو بنیاد کے طور پر استعمال کروں گا، لیکن میں اس میں صرف وہی چھوڑوں گا جو جامد مواد کو تقسیم کرنے کے لیے درکار ہے۔ میرا ورژن HTTPS پر کام نہیں کر سکے گا، اجازت کی حمایت نہیں کرے گا، اور بہت کچھ۔ لیکن میرا ورژن Brotli الگورتھم کے ساتھ کمپریس شدہ فائلوں کو تقسیم کرنے کے قابل ہو جائے گا، جو gzip سے قدرے زیادہ موثر ہے۔ ہم فائلوں کو ایک بار کمپریس کریں گے؛ فلائی پر ایسا کرنے کی ضرورت نہیں ہے۔

یہ وہ ڈاکر فائل ہے جس کے ساتھ میں نے ختم کیا۔ روسی میں تبصرے میرے ہیں، انگریزی میں - اصل سے۔

ڈاکر فائل

# Базовый образ снова 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 لیا ہے۔ میری کھیلوں کی دلچسپی مطمئن ہے۔

ماحولیاتی متغیرات کو سمجھنے کے لیے statistics سکھانا

SPA میں ترتیبات کی ضرورت کیوں ہو سکتی ہے؟ مثال کے طور پر، یہ بتانے کے لیے کہ کون سا RESTful API استعمال کرنا ہے۔ عام طور پر، مطلوبہ ماحول کے لیے سیٹنگز کو تعمیر کے مرحلے پر SPA میں منتقل کر دیا جاتا ہے۔ اگر آپ کو کچھ تبدیل کرنے کی ضرورت ہے، تو آپ کو ایپلیکیشن کو دوبارہ بنانا ہوگا۔ میں یہ نہیں چاہتا۔ میں چاہتا ہوں کہ ایپلی کیشن کو CI مرحلے پر ایک بار بنایا جائے، اور CD اسٹیج پر ماحولیاتی متغیرات کا استعمال کرتے ہوئے جتنا ضروری ہو ترتیب دیا جائے۔

یقینا، جامد فائلیں خود کسی بھی ماحولیاتی متغیر کو نہیں سمجھتی ہیں۔ لہذا، آپ کو ایک چال استعمال کرنا پڑے گا. حتمی تصویر میں، میں nginx کو لانچ نہیں کروں گا، لیکن ایک خاص شیل اسکرپٹ جو ماحولیاتی متغیرات کو پڑھے گا، انہیں جامد فائلوں میں لکھے گا، انہیں کمپریس کرے گا، اور تب ہی کنٹرول کو nginx میں منتقل کرے گا۔

اس مقصد کے لیے، Dockerfile 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}.

یہ بات قابل غور ہے کہ زیادہ تر جدید SPA تعمیر کرتے وقت اپنی فائلوں میں ہیش شامل کرتے ہیں۔ یہ ضروری ہے تاکہ براؤزر فائل کو طویل عرصے تک محفوظ طریقے سے کیش کر سکے۔ اگر فائل تبدیل ہوتی ہے، تو اس کی ہیش بدل جائے گی، جس کے نتیجے میں براؤزر فائل کو دوبارہ ڈاؤن لوڈ کرنے پر مجبور ہو جائے گا۔

بدقسمتی سے، میرے طریقہ کار میں، ماحولیاتی متغیرات کے ذریعے کنفیگریشن کو تبدیل کرنے سے فائل ہیش میں کوئی تبدیلی نہیں آتی، جس کا مطلب ہے کہ براؤزر کیش کو کسی اور طریقے سے باطل کرنا ضروری ہے۔ مجھے یہ مسئلہ نہیں ہے کیونکہ مختلف کنفیگریشنز مختلف ماحول میں تعینات ہیں۔

حتمی تصویر کو اکٹھا کرنا

آخر میں.

ڈاکر فائل

# Первый базовый образ для сборки
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