Immaġini Docker għad-distribuzzjoni ta 'Applikazzjoni ta' Paġna Unika

Applikazzjoni ta' paġna waħda (SPA) hija sett ta' fajls statiċi JavaScript u HTML, kif ukoll immaġini u riżorsi oħra. Minħabba li ma jinbidlux b'mod dinamiku, il-pubblikazzjoni tagħhom online hija faċli ħafna. Hemm numru kbir ta 'servizzi irħas u anke b'xejn għal dan, li jibdew b'Paġni GitHub sempliċi (u għal xi wħud anke ma' narod.ru) u jispiċċaw b'CDN bħal Amazon S3. Madankollu, kelli bżonn xi ħaġa oħra.

Kelli bżonn immaġni Docker bl-SPA sabiex tkun tista 'tiġi mnedija faċilment kemm fil-produzzjoni bħala parti minn cluster Kubernetes, kif ukoll fuq il-magna ta' żviluppatur back-end li m'għandux idea x'inhu SPA.

Iddeterminajt ir-rekwiżiti tal-immaġni li ġejjin għalija nnifsi:

  • faċilità ta' użu (iżda mhux assemblaġġ);
  • daqs minimu kemm f'termini ta 'disk u RAM;
  • konfigurazzjoni permezz ta' varjabbli ambjentali sabiex l-immaġni tkun tista' tintuża f'ambjenti differenti;
  • l-aktar distribuzzjoni effiċjenti tal-fajls.

Illum ngħidlek kif:

  • imsaren nginx;
  • tibni brotli minn sorsi;
  • jgħallmu fajls statiċi biex jifhmu varjabbli ambjentali;
  • u ovvjament kif tiġbor immaġni Docker minn dan kollu.

L-iskop ta 'dan l-artikolu huwa li naqsam l-esperjenza tiegħi u nipprovoka membri tal-komunità b'esperjenza għal kritika kostruttiva.

Bini ta 'immaġni għall-assemblaġġ

Biex tagħmel l-immaġni Docker finali żgħira fid-daqs, trid taderixxi ma 'żewġ regoli: minimu ta' saffi u immaġni bażi minimalista. Waħda mill-iżgħar immaġini bażi hija l-immaġni tal-Linux Alpine, għalhekk dan hu li nagħżel. Xi wħud jistgħu jargumentaw li l-Alpine mhix adattata għall-produzzjoni, u jista 'jkollhom raġun. Imma personalment, qatt ma kelli problemi miegħu u m'hemm l-ebda argument kontrih.

Biex ikolli inqas saffi, se niġbor l-immaġni fi stadji 2. L-ewwel huwa abbozz; l-utilitajiet awżiljarji u l-fajls temporanji kollha se jibqgħu fih. U fl-istadju finali se nikteb biss il-verżjoni finali tal-applikazzjoni.

Nibdew bl-immaġni awżiljarja.

Sabiex tiġbor applikazzjoni SPA, normalment ikollok bżonn node.js. Se nieħu l-immaġni uffiċjali, li tiġi wkoll mal-maniġers tal-pakketti tal-npm u tal-ħjut. F'ismi, se nżid node-gyp, li huwa meħtieġ biex jinbnew xi pakketti npm, u l-kompressur Brotli minn Google, li se jkun utli għalina aktar tard.

Dockerfile bil-kummenti.

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

Diġà hawn qed niġġieled għall-minimaliżmu, għalhekk l-immaġni titqiegħed flimkien minn tim wieħed kbir.

L-immaġni lesta tista 'tinstab hawn: https://hub.docker.com/r/alexxxnf/spa-builder. Għalkemm nirrakkomanda li ma tiddependix fuq l-immaġini ta 'nies oħra u tiġbor tiegħek.

nginx

Tista' tuża kwalunkwe web server biex tqassam kontenut statiku. Jien imdorri naħdem ma 'nginx, għalhekk ser nużaha issa.

Nginx għandu immaġni Docker uffiċjali, iżda għandu wisq moduli għal distribuzzjoni statika sempliċi. Liema huma inklużi fil-kunsinna jistgħu jidhru minn tim speċjali jew fid-Dockerfile uffiċjali.

$ 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

Se nuża d-Dockerfile bħala bażi, iżda se nħalli fih biss dak li huwa meħtieġ biex jitqassam kontenut statiku. Il-verżjoni tiegħi mhux se tkun kapaċi taħdem fuq HTTPS, mhux se tappoġġja l-awtorizzazzjoni, u ħafna aktar. Iżda l-verżjoni tiegħi se tkun kapaċi tqassam fajls kompressati bl-algoritmu Brotli, li huwa kemmxejn aktar effiċjenti minn gzip. Aħna se nikkompressaw il-fajls darba; m'hemmx għalfejn nagħmlu dan fuq il-fly.

Dan huwa d-Dockerfile li spiċċajt bih. Il-kummenti bir-Russu huma tiegħi, bl-Ingliż - mill-oriġinal.

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

Immedjatament nirranġa nginx.conf sabiex gzip u brotli ikunu attivati ​​awtomatikament. Se ninkludi wkoll il-caching headers, għaliex qatt ma se jkollna l-istatika li tinbidel. U l-mess finali se jkun li terġa 'tidderieġi l-404 talbiet kollha lejn index.html, dan huwa meħtieġ għan-navigazzjoni fl-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";
            }
        }
    }
}

Tista' tniżżel l-immaġni lesta hawn: https://hub.docker.com/r/alexxxnf/nginx-spa. Huwa jieħu 10,5 MB. L-nginx oriġinali ħa 19,7 MB. L-interess sportiv tiegħi huwa sodisfatt.

Tagħlim tal-istatika biex tifhem il-varjabbli tal-ambjent

Għaliex jistgħu jkunu meħtieġa settings fl-SPA? Pereżempju, sabiex tispeċifika liema API RESTful tuża. Tipikament, is-settings għall-ambjent mixtieq jiġu trasferiti għal SPA fl-istadju tal-bini. Jekk għandek bżonn tibdel xi ħaġa, ser ikollok terġa 'tibni l-applikazzjoni. Ma rridx. Irrid li l-applikazzjoni tinbena darba fl-istadju CI, u kkonfigurata kemm meħtieġ fl-istadju tas-CD billi tuża varjabbli ambjentali.

Naturalment, il-fajls statiċi nfushom ma jifhmu l-ebda varjabbli ambjentali. Għalhekk, ser ikollok tuża trick. Fl-immaġini finali, mhux se nniedi nginx, iżda script tal-qoxra speċjali li se jaqra l-varjabbli tal-ambjent, jiktebhom f'fajls statiċi, jikkompressahom, u mbagħad biss jittrasferixxi l-kontroll għal nginx.

Għal dan il-għan, id-Dockerfile jipprovdi l-parametru ENTRYPOINT. Ejja nagħtuh l-iskrittura li ġejja (bl-użu Angular bħala eżempju):

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

Sabiex l-iskript jagħmel xogħolu, is-settings għandhom jinkitbu fil-fajls js f'din il-forma: ${API_URL}.

Ta 'min jinnota li l-biċċa l-kbira tal-SPAs moderni jżidu l-hashes mal-fajls tagħhom meta jibnu. Dan huwa meħtieġ sabiex il-browser ikun jista 'jaħżen il-fajl b'mod sikur għal żmien twil. Jekk il-fajl jinbidel, allura l-hash tiegħu jinbidel, li min-naħa tiegħu se jġiegħel lill-browser jerġa' tniżżel il-fajl.

Sfortunatament, fil-metodu tiegħi, it-tibdil tal-konfigurazzjoni permezz ta 'varjabbli ambjentali ma jwassalx għal bidla fil-hash tal-fajl, li jfisser li l-cache tal-browser għandu jiġi invalidat b'xi mod ieħor. M'għandix din il-problema minħabba li konfigurazzjonijiet differenti huma skjerati f'ambjenti differenti.

Tgħaqqad l-immaġni finali

Fl-aħħarnett.

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

Issa l-immaġni li tirriżulta tista 'tiġi mmuntata u użata kullimkien.

Sors: www.habr.com