Hoton Docker don rarraba Aikace-aikacen Shafi guda ɗaya

Aikace-aikacen shafi guda ɗaya (SPA) saitin JavaScript ne da fayilolin HTML, da hotuna da sauran albarkatu. Domin ba sa canzawa sosai, buga su akan layi abu ne mai sauƙi. Akwai adadi mai yawa na arha har ma da sabis na kyauta don wannan, farawa tare da sauƙi GitHub Shafukan (kuma ga wasu har ma da narod.ru) kuma suna ƙarewa tare da CDN kamar Amazon S3. Duk da haka, ina buƙatar wani abu dabam.

Ina buƙatar hoton Docker tare da SPA don a iya ƙaddamar da shi cikin sauƙi duka biyu a cikin samarwa a matsayin ɓangare na gungu na Kubernetes, kuma akan injin mai haɓakawa na baya wanda ba shi da masaniyar menene SPA.

Na ƙaddara waɗannan buƙatun hoto don kaina:

  • sauƙin amfani (amma ba taro ba);
  • mafi ƙarancin girman duka dangane da faifai da RAM;
  • daidaitawa ta hanyar canjin yanayi ta yadda za a iya amfani da hoton a wurare daban-daban;
  • mafi inganci rarraba fayiloli.

A yau zan gaya muku yadda:

  • nginx;
  • gina brotli daga tushe;
  • koyar da fayiloli a tsaye don fahimtar masu canjin yanayi;
  • kuma ba shakka yadda ake hada hoton Docker daga duk wannan.

Manufar wannan labarin ita ce in ba da gogewa ta da kuma tunzura ƙwararrun ƴan al'umma zuwa ga suka mai ma'ana.

Gina hoto don taro

Don sanya hoton Docker na ƙarshe ƙarami a cikin girman, kuna buƙatar bin dokoki biyu: ƙaramin yadudduka da hoton tushe kaɗan. Ɗaya daga cikin ƙananan hotuna na tushe shine hoton Alpine Linux, don haka abin da zan zaɓa ke nan. Wasu na iya jayayya cewa Alpine bai dace da samarwa ba, kuma suna iya zama daidai. Amma ni ni kaina ban taba samun matsala da shi ba kuma babu gardama a kansa.

Don samun ƙarancin yadudduka, zan haɗa hoton a matakai 2. Na farko shi ne daftarin aiki; duk kayan aikin taimako da fayilolin wucin gadi za su kasance a ciki. Kuma a mataki na ƙarshe kawai zan rubuta sigar ƙarshe ta aikace-aikacen.

Bari mu fara da hoton taimako.

Domin hada aikace-aikacen SPA, yawanci kuna buƙatar node.js. Zan ɗauki hoton hukuma, wanda kuma ya zo tare da npm da masu sarrafa kunshin yarn. A madadina, zan ƙara node-gyp, wanda ake buƙata don gina wasu fakitin npm, da kuma Brotli compressor daga Google, wanda zai zama da amfani a gare mu daga baya.

Dockerfile tare da sharhi.

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

Tuni a nan na yi yaƙi don minimalism, don haka hoton yana haɗuwa da babban ƙungiya ɗaya.

Ana iya samun hoton da aka gama a nan: https://hub.docker.com/r/alexxxnf/spa-builder. Ko da yake ina ba da shawarar kada ku dogara ga hotunan wasu da tattara naku.

nginx

Kuna iya amfani da kowane sabar gidan yanar gizo don rarraba abun ciki a tsaye. Na saba yin aiki tare da nginx, don haka zan yi amfani da shi yanzu.

Nginx yana da hoton Docker na hukuma, amma yana da kayayyaki da yawa don rarraba a tsaye. Waɗanne ne aka haɗa a cikin bayarwa za a iya gani ta ƙungiya ta musamman ko a cikin Dockerfile na hukuma.

$ docker gudu --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

Zan yi amfani da Dockerfile a matsayin tushe, amma zan bar a cikinsa kawai abin da ake buƙata don rarraba abun ciki na tsaye. Siga nawa ba zai iya yin aiki akan HTTPS ba, ba zai goyi bayan izini ba, da ƙari mai yawa. Amma sigar nawa zai iya rarraba fayilolin da aka matsa tare da Brotli algorithm, wanda ya fi inganci fiye da gzip. Za mu matsa fayiloli sau ɗaya; babu buƙatar yin wannan akan tashi.

Wannan shine Dockerfile na gama dashi. Sharhi a cikin Rashanci nawa ne, a cikin Ingilishi - daga asali.

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

Nan da nan zan gyara nginx.conf don kunna gzip da brotli ta tsohuwa. Zan kuma haɗa da caching headers, saboda ba za mu taɓa canzawa a tsaye ba. Kuma taɓawa ta ƙarshe ita ce ta tura duk buƙatun 404 zuwa index.html, wannan yana da mahimmanci don kewayawa a cikin 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";
            }
        }
    }
}

Kuna iya sauke hoton da aka gama anan: https://hub.docker.com/r/alexxxnf/nginx-spa. Yana ɗaukar 10,5 MB. Asalin nginx ya ɗauki 19,7 MB. Sha'awar wasanni ta ta gamsu.

Koyar da ƙididdiga don fahimtar canjin yanayi

Me yasa za'a iya buƙatar saiti a cikin SPA? Misali, don tantance wace RESTful API zata yi amfani da ita. Yawanci, saituna don yanayin da ake so ana canjawa wuri zuwa SPA a matakin ginin. Idan kana buƙatar canza wani abu, dole ne ka sake gina aikace-aikacen. Ba na son shi. Ina son a gina aikace-aikacen sau ɗaya a matakin CI, kuma a daidaita shi gwargwadon buƙata a matakin CD ta amfani da masu canjin yanayi.

Tabbas, fayilolin tsaye da kansu ba sa fahimtar kowane canjin yanayi. Don haka, dole ne ku yi amfani da dabara. A cikin hoton ƙarshe, ba zan ƙaddamar da nginx ba, amma rubutun harsashi na musamman wanda zai karanta sauye-sauyen yanayi, rubuta su zuwa fayilolin tsaye, matsa su, sannan kawai canja wurin sarrafawa zuwa nginx.

Don wannan dalili, Dockerfile yana samar da ma'aunin ENTRYPOINT. Bari mu ba shi rubutun mai zuwa (ta yin amfani da Angular a matsayin misali):

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

Domin rubutun ya yi aikinsa, dole ne a rubuta saitunan a cikin fayilolin js a cikin wannan tsari: ${API_URL}.

Yana da kyau a lura cewa yawancin SPA na zamani suna ƙara hashes zuwa fayilolin su lokacin gini. Wannan ya zama dole don mai bincike ya iya adana fayil ɗin a amince na dogon lokaci. Idan fayil ɗin ya canza, to hash ɗinsa zai canza, wanda hakan zai tilasta mai binciken ya sake sauke fayil ɗin.

Abin baƙin ciki, a cikin hanyata, canza tsarin ta hanyar masu canjin yanayi ba ya haifar da canji a cikin hash fayil, wanda ke nufin cewa cache na burauzar dole ne ya ɓace ta wata hanya. Ba ni da wannan matsalar saboda ana tura saiti daban-daban a wurare daban-daban.

Haɗa hoton ƙarshe

Daga karshe.

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

Yanzu hoton da aka samu za a iya haɗawa kuma a yi amfani da shi a ko'ina.

source: www.habr.com