Aworan Docker fun pinpin Ohun elo Oju-iwe Kanṣoṣo

Ohun elo oju-iwe ẹyọkan (SPA) jẹ eto JavaScript aimi ati awọn faili HTML, ati awọn aworan ati awọn orisun miiran. Nitoripe wọn ko yipada ni agbara, titẹjade wọn lori ayelujara jẹ irọrun pupọ. Nọmba nla ti olowo poku ati paapaa awọn iṣẹ ọfẹ fun eyi, bẹrẹ pẹlu awọn oju-iwe GitHub ti o rọrun (ati fun diẹ ninu paapaa pẹlu narod.ru) ati ipari pẹlu CDN bi Amazon S3. Sibẹsibẹ, Mo nilo nkan miiran.

Mo nilo aworan Docker kan pẹlu SPA ki o le ni irọrun ṣe ifilọlẹ mejeeji ni iṣelọpọ bi apakan ti iṣupọ Kubernetes, ati lori ẹrọ ti olupilẹṣẹ ipari-ipari ti ko ni imọran kini SPA jẹ.

Mo ti pinnu awọn ibeere aworan wọnyi fun ara mi:

  • irọrun ti lilo (ṣugbọn kii ṣe apejọ);
  • Iwọn to kere julọ mejeeji ni awọn ofin ti disk ati Ramu;
  • iṣeto ni nipasẹ awọn oniyipada ayika ki aworan le ṣee lo ni awọn agbegbe oriṣiriṣi;
  • julọ ​​daradara pinpin awọn faili.

Loni Emi yoo sọ fun ọ bi:

  • ikun nginx;
  • kọ brotli lati awọn orisun;
  • kọ awọn faili aimi lati ni oye awọn oniyipada ayika;
  • ati nitorinaa bii o ṣe le ṣajọ aworan Docker lati gbogbo eyi.

Idi ti nkan yii ni lati pin iriri mi ati ru awọn ọmọ ẹgbẹ agbegbe ti o ni iriri si ibawi ti o tọ.

Ilé aworan kan fun apejọ

Lati ṣe aworan Docker ikẹhin kekere ni iwọn, o nilo lati faramọ awọn ofin meji: o kere ju awọn fẹlẹfẹlẹ ati aworan ipilẹ minimalistic. Ọkan ninu awọn aworan ipilẹ ti o kere julọ ni aworan Alpine Linux, nitorinaa iyẹn ni ohun ti Emi yoo yan. Diẹ ninu awọn le jiyan pe Alpine ko dara fun iṣelọpọ, ati pe wọn le jẹ ẹtọ. Ṣugbọn tikalararẹ, Emi ko ni awọn iṣoro eyikeyi pẹlu rẹ ati pe ko si awọn ariyanjiyan si i.

Lati ni awọn ipele diẹ, Emi yoo pejọ aworan ni awọn ipele meji. Ohun akọkọ jẹ apẹrẹ kan; gbogbo awọn ohun elo iranlọwọ ati awọn faili igba diẹ yoo wa ninu rẹ. Ati ni ipele ikẹhin Emi yoo kọ si isalẹ ẹya ikẹhin ti ohun elo nikan.

Jẹ ki a bẹrẹ pẹlu aworan iranlọwọ.

Lati le ṣajọ ohun elo SPA kan, o nigbagbogbo nilo node.js. Emi yoo ya aworan osise, eyiti o tun wa pẹlu npm ati awọn alakoso package owu. Fun ara mi, Emi yoo ṣafikun node-gyp, eyiti o nilo lati kọ diẹ ninu awọn idii npm, ati compressor Brotli lati Google, eyiti yoo wulo fun wa nigbamii.

Dockerfile pẹlu comments.

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

Tẹlẹ nibi Mo n ja fun minimalism, nitorinaa a fi aworan naa papọ nipasẹ ẹgbẹ nla kan.

Aworan ti o pari ni a le rii nibi: https://hub.docker.com/r/alexxxnf/spa-builder. Botilẹjẹpe Mo ṣeduro pe ki o gbẹkẹle awọn aworan eniyan miiran ati gbigba tirẹ.

nginx

O le lo olupin wẹẹbu eyikeyi lati pin kaakiri akoonu aimi. Mo n ṣiṣẹ pẹlu nginx, nitorinaa Emi yoo lo ni bayi.

Nginx ni aworan Docker osise, ṣugbọn o ni ọpọlọpọ awọn modulu fun pinpin aimi ti o rọrun. Awọn wo ni o wa ninu ifijiṣẹ ni o le rii nipasẹ ẹgbẹ pataki kan tabi ni Dockerfile osise.

$ 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

Emi yoo lo Dockerfile gẹgẹbi ipilẹ, ṣugbọn Emi yoo fi silẹ ninu rẹ nikan ohun ti o nilo lati kaakiri akoonu aimi. Ẹya mi kii yoo ni anfani lati ṣiṣẹ lori HTTPS, kii yoo ṣe atilẹyin aṣẹ, ati pupọ diẹ sii. Ṣugbọn ẹya mi yoo ni anfani lati kaakiri awọn faili fisinuirindigbindigbin pẹlu Brotli algorithm, eyiti o jẹ diẹ sii daradara ju gzip. A yoo compress awọn faili lẹẹkan; ko si iwulo lati ṣe eyi lori fo.

Eyi ni Dockerfile ti Mo pari pẹlu. Awọn asọye ni Russian jẹ temi, ni Gẹẹsi - lati atilẹba.

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

Emi yoo ṣe atunṣe lẹsẹkẹsẹ nginx.conf ki gzip ati brotli ti ṣiṣẹ nipasẹ aiyipada. Emi yoo tun pẹlu awọn akọle caching, nitori a kii yoo ni iyipada aimi rara. Ati ifọwọkan ikẹhin yoo jẹ lati tun gbogbo awọn ibeere 404 lọ si index.html, eyi jẹ pataki fun lilọ kiri ni 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";
            }
        }
    }
}

O le ṣe igbasilẹ aworan ti o pari nibi: https://hub.docker.com/r/alexxxnf/nginx-spa. O gba soke 10,5 MB. Nginx atilẹba gba 19,7 MB. Ifẹ ere idaraya mi ni itẹlọrun.

Awọn iṣiro ikọni lati ni oye awọn oniyipada ayika

Kini idi ti awọn eto le nilo ni SPA? Fun apẹẹrẹ, lati le pato iru API RESTful lati lo. Ni deede, awọn eto fun agbegbe ti o fẹ ni a gbe lọ si SPA ni ipele kikọ. Ti o ba nilo lati yi nkan pada, iwọ yoo ni lati tun ohun elo naa ṣe. Nko fe e. Mo fẹ ki ohun elo naa kọ ni ẹẹkan ni ipele CI, ati tunto bi o ṣe pataki ni ipele CD nipa lilo awọn oniyipada ayika.

Nitoribẹẹ, awọn faili aimi funrararẹ ko loye eyikeyi awọn oniyipada ayika. Nitorina, iwọ yoo ni lati lo ẹtan kan. Ni aworan ikẹhin, Emi kii yoo ṣe ifilọlẹ nginx, ṣugbọn iwe afọwọkọ ikarahun pataki kan ti yoo ka awọn oniyipada ayika, kọ wọn si awọn faili aimi, rọpọ wọn, ati lẹhinna gbe iṣakoso lọ si nginx.

Fun idi eyi, Dockerfile n pese paramita ENTRYPOINT. Jẹ ki a fun u ni iwe afọwọkọ atẹle (lilo Angular bi apẹẹrẹ):

docker-iwọle.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 "$@"

Ni ibere fun iwe afọwọkọ lati ṣe iṣẹ rẹ, awọn eto gbọdọ wa ni kikọ sinu awọn faili js ni fọọmu yii: ${API_URL}.

O tọ lati ṣe akiyesi pe ọpọlọpọ awọn SPA ode oni ṣafikun hashes si awọn faili wọn nigba kikọ. Eyi jẹ pataki ki ẹrọ aṣawakiri le kaṣe faili lailewu fun igba pipẹ. Ti faili naa ba yipada, lẹhinna hash rẹ yoo yipada, eyiti yoo fi ipa mu ẹrọ aṣawakiri lati ṣe igbasilẹ faili lẹẹkansi.

Laanu, ni ọna mi, yiyipada iṣeto ni nipasẹ awọn oniyipada ayika ko yorisi iyipada ninu hash faili, eyiti o tumọ si pe kaṣe ẹrọ aṣawakiri gbọdọ jẹ asan ni ọna miiran. Emi ko ni iṣoro yii nitori awọn atunto oriṣiriṣi ti wa ni ransogun ni awọn agbegbe oriṣiriṣi.

Fifi papo ik aworan

Níkẹyìn.

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

Bayi aworan ti o yọrisi le ṣe apejọ ati lo nibikibi.

orisun: www.habr.com