Foto Docker maka nkesa Ngwa Ibe Otu

Ngwa-otu ibe (SPA) bụ nhazi Javascript na faịlụ HTML, yana onyonyo na akụrụngwa ndị ọzọ. N'ihi na ha anaghị agbanwe agbanwe, ibipụta ha n'ịntanetị dị mfe. Enwere ọnụ ọgụgụ buru ibu nke ọnụ ala na ọbụna ọrụ efu maka nke a, malite na ibe GitHub dị mfe (na ụfọdụ ọbụna na narod.ru) na-ejedebe na CDN dị ka Amazon S3. Agbanyeghị, achọrọ m ihe ọzọ.

Achọrọ m ihe onyonyo Docker nwere SPA ka enwere ike ịmalite ya ngwa ngwa ma na mmepụta dịka akụkụ nke ụyọkọ Kubernetes, yana na igwe nke onye nrụpụta azụ azụ nke na-amaghị ihe SPA bụ.

Ekpebiri m ihe onyonyo a chọrọ maka onwe m:

  • ịdị mfe nke iji (ma ọ bụghị mgbakọ);
  • nke kacha nta ma na usoro nke disk na RAM;
  • nhazi site na mgbanwe gburugburu ebe obibi ka e wee nwee ike iji ihe oyiyi ahụ mee ihe na gburugburu ebe dị iche iche;
  • nkesa faịlụ kacha arụ ọrụ nke ọma.

Taa ka m ga-agwa gị otu:

  • eriri afọ nginx;
  • wuo brotli site na isi mmalite;
  • kuzie faịlụ static ka ọ ghọta mgbanwe gburugburu;
  • na n'ezie ka esi achịkọta ihe oyiyi Docker site na ihe a niile.

Ebumnuche nke edemede a bụ ịkọrọ ahụmahụ m na ịkpasu ndị òtù obodo nwere ahụmahụ na nkatọ na-ewuli elu.

Ịrụ ihe oyiyi maka mgbakọ

Iji mee ka ihe oyiyi Docker ikpeazụ dị ntakịrị na nha, ịkwesịrị ịgbaso iwu abụọ: opekempe nke akwa akwa na ihe oyiyi dị ntakịrị. Otu n'ime onyonyo ntọala kacha nta bụ onyonyo Alpine Linux, yabụ nke ahụ bụ ihe m ga-ahọrọ. Ụfọdụ nwere ike ịrụ ụka na Alpine adịghị mma maka mmepụta, na ha nwere ike bụrụ eziokwu. Ma n'onwe m, enwebeghị m nsogbu ọ bụla na ya, ọ dịghịkwa arụmụka megide ya.

Iji nweta ọkwa dị nta, m ga-achịkọta onyonyo a na nkeji abụọ. Nke mbụ bụ akwụkwọ; ihe inyeaka niile na faịlụ nwa oge ga-anọgide na ya. Na n'ọkwa ikpeazụ, m ga-edetu naanị ụdị ngwa ikpeazụ.

Ka anyị bido na onyonyo inyeaka.

Iji chịkọta ngwa SPA, ị na-achọkarị node.js. Aga m ewere foto gọọmentị, nke na-abịa na npm na ndị njikwa ngwugwu yarn. N'aha nke m, m ga-agbakwunye node-gyp, nke dị mkpa iji wuo ụfọdụ ngwugwu npm, yana Brotli compressor sitere na Google, nke ga-abara anyị uru ma emechaa.

Dockerfile nwere nkọwa.

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

Ugbua ebe a, m na-alụ ọgụ maka minimalism, ya mere, otu nnukwu ìgwè na-ejikọta onyinyo ahụ.

Enwere ike ịhụ onyonyo emechara ebe a: https://hub.docker.com/r/alexxxnf/spa-builder. Ọ bụ ezie na m na-akwado ka ị ghara ịdabere na ihe oyiyi ndị ọzọ na ịnakọta nke gị.

nginx

Ị nwere ike iji sava weebụ ọ bụla iji kesaa ọdịnaya kwụ ọtọ. Eji m nginx arụ ọrụ, yabụ m ga-eji ya ugbu a.

Nginx nwere onyonyo Docker gọọmentị, mana ọ nwere ọtụtụ modul maka nkesa static dị mfe. Kedu ndị agbakwunyere na nnyefe nwere ike ịhụ ndị otu pụrụ iche ma ọ bụ na Dockerfile gọọmentị.

$ docker ọsọ --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

M ga-eji Dockerfile dịka ntọala, mana m ga-ahapụ n'ime ya naanị ihe achọrọ iji kesaa ọdịnaya kwụ ọtọ. Ụdị m agaghị enwe ike ịrụ ọrụ na HTTPS, agaghị akwado ikike, yana ọtụtụ ndị ọzọ. Mana ụdị m ga-enwe ike ikesa faịlụ abịakọrọ na Brotli algọridim, nke dị ntakịrị karịa gzip. Anyị ga-akpakọ faịlụ otu ugboro; ọ dịghị mkpa ime nke a na ofufe.

Nke a bụ Dockerfile m kwụsịrị. Comments na Russian bụ nke m, na Bekee - site na mbụ.

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

Aga m edozi nginx.conf ozugbo ka gzip na brotli wee bụrụ ndabara. M ga-etinyekwa nkụnye eji isi mee caching, n'ihi na anyị agaghị agbanwe agbanwe. Na mmetụ ikpeazụ ga-abụ redirect niile arịrịọ 404 na index.html, nke a dị mkpa maka igodo na 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";
            }
        }
    }
}

Ị nwere ike ibudata foto emechara ebe a: https://hub.docker.com/r/alexxxnf/nginx-spa. Ọ na-ewe 10,5 MB. Nginx izizi welitere 19,7 MB. Mmasị egwuregwu m nwere afọ ojuju.

Ịkụzi statics ịghọta mgbanwe gburugburu ebe obibi

Kedu ihe kpatara enwere ike ịchọ ntọala na SPA? Dịka ọmụmaatụ, iji kọwapụta API RESTful ga-eji. A na-ebufe ntọala maka gburugburu ebe a chọrọ na SPA na ọkwa ụlọ. Ọ bụrụ na ịchọrọ ịgbanwe ihe, ị ga-ewughachi ngwa ahụ. Achọghị m ya. Achọrọ m ka e wuo ngwa ahụ otu ugboro na ọkwa CI, ma hazie ya dịka ọ dị mkpa na ọkwa CD site na iji mgbanwe gburugburu ebe obibi.

N'ezie, faịlụ static n'onwe ha anaghị aghọta mgbanwe gburugburu ebe obibi ọ bụla. Ya mere, ị ga-eji aghụghọ. Na onyonyo ikpeazụ, agaghị m ebido nginx, mana edemede shei pụrụ iche nke ga-agụ mgbanwe gburugburu ebe obibi, dee ha na faịlụ static, mpikota onu, naanị wee nyefee njikwa na nginx.

Maka ebumnuche a, Dockerfile na-enye oke ENTRYPOINT. Ka anyị nye ya edemede a (iji Angular dịka ọmụmaatụ):

docker-nbanye.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 "$@"

Ka script wee rụọ ọrụ ya, a ga-ederịrị ntọala na faịlụ js n'ụdị a: ${API_URL}.

Ọ dị mma ịmara na ọtụtụ SPA ọgbara ọhụrụ na-agbakwunye hashes na faịlụ ha mgbe ha na-ewu ụlọ. Nke a dị mkpa ka ihe nchọgharị ahụ wee nwee ike ịchekwa faịlụ ahụ ogologo oge n'enweghị nsogbu. Ọ bụrụ na faịlụ agbanwee, mgbe ahụ hash ya ga-agbanwe, nke ga-eme ka ihe nchọgharị ahụ budata faịlụ ahụ ọzọ.

N'ụzọ dị mwute, na usoro m, ịgbanwe nhazi site na mgbanwe gburugburu ebe obibi adịghị eduga ná mgbanwe na hash faịlụ, nke pụtara na cache ihe nchọgharị ga-emebirịrị n'ụzọ ọzọ. Enweghị m nsogbu a n'ihi na a na-etinye nhazi dị iche iche na gburugburu dị iche iche.

Na-etinye ọnụ na foto ikpeazụ

N'ikpeazụ.

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

Ugbu a, enwere ike ịchịkọta ihe oyiyi a na-emepụta ma jiri ya mee ihe ọ bụla.

isi: www.habr.com