Docker image for distribution of Single Page Application

Singula pagina Applicationis (SPA) est JavaScript et HTML imaginum static, necnon imagines et aliae facultates. Quia dynamically non mutant, eos online libellorum facillima est. Magna vis vilis et etiam gratuita officia ad hoc sunt, incipiens a Paginae GitHub simplici (et aliquot etiam cum narod.ru) et desinentia cum CDN sicut Amazon S3. Sed aliud mihi opus est.

Mihi opus erat cum SPA imaginem Docker ut facile deduci posset tam in productione quam in parte botri Kubernetti, et in machina elit extremi extremitatis, qui quid sit SPA nesciens.

Mihi sequentia statui requisita statui;

  • otium of use (but not conventus);
  • minimae quantitatis tam in terminis orbis quam in RAM;
  • conformatio per variabiles ambitus variabiles ut imago in diversis ambitibus adhiberi possit;
  • efficacissima distributio imaginum.

Hodie quomodo adnuntiem tibi:

  • Viscera nginx;
  • e fontibus constructum brotli;
  • doceat static lima intelligere variabiles ambitus;
  • et sane quomodo ex his omnibus conveniret Docker imaginem.

Propositum huius articuli experientiam meam communicare est et peritos communitatis sodales ad reprehensionem aedificandam provocare.

Aedificare imaginem pro ecclesia

Ut imaginem finalem Docker parvam magnitudine constituat, duobus regulis adhaerere debes: minimum laminis et imaginem basi minimalisticam. Una basium minimarum imaginum est imago Linux Alpini, ut illud quod ego eligam. Possent aliqui disputare Alpinos non esse idoneos ad productionem, et recte esse. Sed personaliter, nihil cum eo habui nec argumenta contra eum sunt.

Ut pauciores habeant ordines, imaginem in gradibus 2 congregem. Prima potio est: omnes utilitates auxiliares et fasciculi temporarii in eo manebunt. Et in ultimo gradu solum ultimam applicationis versionem scribere.

Incipiamus ab imagine auxiliaria.

Ad applicationem SPA ordinandi, node.js solere indigere. Officialis imaginem ducam, quae etiam cum npm et tincto actoribus venit. Me mehercule nodi-gypi addam, quod opus est quibusdam fasciculis npm fabricandis, et compressore Brotli a Google, quod postea nobis utile erit.

Dockerfile cum comment.

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

Iam hic pro minimis pugno, ergo imago una biga componitur.

Imago perfecti hic invenitur: https://hub.docker.com/r/alexxxnf/spa-builder. Quanquam tibi commendo non fretus aliorum imaginibus tuisque colligendis.

nginx

Quovis ministro telae uti potes ut contentus static distribuas. Ego cum nginx laborat, ita nunc utar.

Nginx officialem Docker imaginem habet, sed nimium multos modulos habet pro distributione static simplici. Quae in traditione comprehenduntur, ex speciali manipulo vel in officiali Dockerfile videri potest.

$ docker run --rm nginx:1-alpinae 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 pro fundamento utar, sed in ea tantum relinquemus quod opus est ad contentum statarium distribuendum. Mea versio in HTTPS laborare non poterit, licentia non sustinebit, et multo magis. Sed mea versio limas compressas cum Brotli algorithmo distribuere poterit, quae paulo efficientior est quam gzip. Documenta semel comprimemus, id in musca facere non est.

Haec Dockerfile cum finita sum. Commentaria in Russica mea sunt, Anglico - ex autographo.

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

Statim figam nginx.conf ut gzip et brotli defalta valeant. Caching quoque heading comprehendam, quia numquam static mutandum habebimus. Tactus ultimus erit omnes 404 petitiones ad indicem redigendas.html, hoc ad navigationem in SPA necessarium est.

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

Imaginem perfectam hic deponere potes: https://hub.docker.com/r/alexxxnf/nginx-spa. 10,5 suscipit MB. Originale nginx suscepit 19,7 MB. Ludicra mea cura satiatur.

Doctrinam elit intelligere variabiles

Cur occasus in SPA requirantur? Exempli gratia, ad designandum quod restful API utor. Typice, occasus ambitus desideratae SPA in scaena aedificandi transferuntur. Si quid mutare debes, reficere debebis applicationes. Nolo eam. Applicationem semel in CI scaena aedificari volo, et quantum necessarium est in CD scaena utens variabilium ambituum configurari.

Scilicet, static lima se non intelligere aliquas variabiles ambitus. Ergo fraude uti debebis. In ultima imagine, nginx non immittam, sed singularem scripturam testam, quae variabiles ambitus leget, eas scribe ad static lima, comprimendam, et demum potestatem ad nginx transferendum.

Ad hanc rem, Dockerfile parametrum praebet. Scriptum hoc ei demus (angularis per exemplum);

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

Ut scriptor officium suum expleat, unctiones in scriniis js scribi debent hac forma: ${API_URL}.

Notatu dignum est recentissimas SPAS hashes ad tabellas suas aedificandas addere. Hoc necessarium est ut navigatrum tuto diu tabulam recondere possit. Si tabella mutatur, Nullam eius mutabit, quae iterum navigatrum coget tabellam iterum destruere.

Infeliciter, in methodo mea, figuram mutans per variabiles ambitus variabiles non ducit ad mutationem in tabella Nullam, quae significat navigatrum cella alio modo infirmari debere. Hoc problema non habeo, quia variae figurae in diversis ambitibus explicantur.

Collatis finalem imaginem

Denique.

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

Nunc imago inde colligi potest et alicubi adhiberi.

Source: www.habr.com