Íomhá docker le dáileadh Feidhmchlár Aon Leathanach

Is sraith de chomhaid statacha JavaScript agus HTML, chomh maith le híomhánna agus acmhainní eile é Feidhmchlár aon-leathanach (SPA). Toisc nach n-athraíonn siad go dinimiciúil, tá sé an-éasca iad a fhoilsiú ar líne. Tá líon mór seirbhísí saor agus fiú saor in aisce le haghaidh seo, ag tosú le Leathanaigh GitHub simplí (agus do roinnt fiú le narod.ru) agus ag críochnú le CDN cosúil le Amazon S3. Mar sin féin, bhí rud éigin eile ag teastáil uaim.

Bhí íomhá Docker ag teastáil uaim le SPA ionas go bhféadfaí é a sheoladh go héasca i dtáirgeadh mar chuid de bhraisle Kubernetes, agus ar mheaisín forbróra deireadh cúil nach bhfuil aon smaoineamh aige cad é SPA.

Tá na riachtanais íomhá seo a leanas socraithe agam dom féin:

  • éascaíocht úsáide (ach ní cóimeáil);
  • íosmhéid i dtéarmaí diosca agus RAM;
  • cumraíocht trí athróga timpeallachta ionas gur féidir an íomhá a úsáid i dtimpeallachtaí éagsúla;
  • an dáileadh comhad is éifeachtaí.

Inniu inseoidh mé duit conas:

  • nginx gut;
  • brotli a thógáil ó fhoinsí;
  • comhaid statacha a mhúineadh chun athróga timpeallachta a thuiscint;
  • agus ar ndóigh conas íomhá Docker a chur le chéile as seo go léir.

Is é cuspóir an ailt seo ná mo thaithí a roinnt agus baill den phobal a bhfuil taithí acu a spreagadh le cáineadh cuiditheach.

Tógáil íomhá le haghaidh tionóil

Chun íomhá deiridh an Docker a dhéanamh beag i méid, ní mór duit cloí le dhá riail: íosmhéid sraitheanna agus íomhá bonn íostach. Is é ceann de na híomhánna bonn is lú an íomhá Alpach Linux, mar sin is é sin a roghnóidh mé. D'fhéadfadh roinnt a mhaíomh nach bhfuil an Alpach oiriúnach le haghaidh táirgeadh, agus d'fhéadfadh go mbeadh siad ceart. Ach go pearsanta, ní raibh aon fhadhb agam leis agus níl aon argóintí ina choinne.

Chun níos lú sraitheanna a bheith agam, cuirfidh mé an íomhá le chéile i 2 chéim. Is dréacht é an chéad cheann; fanfaidh gach áis chúnta agus comhad sealadach ann. Agus sa chéim dheireanach ní scríobhfaidh mé ach an leagan deiridh den iarratas.

Let tús leis an íomhá cúnta.

Chun iarratas SPA a thiomsú, bíonn nóde.js de dhíth ort de ghnáth. Tógfaidh mé an íomhá oifigiúil, a thagann leis na bainisteoirí pacáiste npm agus snáth freisin. Ar mo shon féin, cuirfidh mé nód-gyp, a theastaíonn chun roinnt pacáistí npm a thógáil, agus an comhbhrúiteoir Brotli ó Google, a bheidh úsáideach dúinn níos déanaí.

Dockerfile le tuairimí.

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

Cheana féin anseo táim ag troid le haghaidh minimalism, agus mar sin cuireann foireann mhór amháin an íomhá le chéile.

Is féidir an íomhá críochnaithe a fháil anseo: https://hub.docker.com/r/alexxxnf/spa-builder. Cé go molaim gan a bheith ag brath ar íomhánna daoine eile agus do chuid féin a bhailiú.

nginx

Is féidir leat aon fhreastalaí gréasáin a úsáid chun ábhar statach a dháileadh. Tá mé cleachta le bheith ag obair le nginx, mar sin úsáidfidh mé anois é.

Tá íomhá oifigiúil Docker ag Nginx, ach tá an iomarca modúil aige le haghaidh dáileadh statach simplí. Is féidir le foireann speisialta nó sa Dockerfile oifigiúil na cinn atá san áireamh sa seachadadh a fheiceáil.

rith $ docker --rm nginx: 1-alpach 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

Bainfidh mé úsáid as an Dockerfile mar bhunús, ach ní fhágfaidh mé ann ach an méid is gá chun ábhar statach a dháileadh. Ní bheidh mo leagan in ann oibriú thar HTTPS, ní thacóidh sé le húdarú, agus i bhfad níos mó. Ach beidh mo leagan in ann comhaid atá comhbhrúite leis an algartam Brotli a dháileadh, atá beagán níos éifeachtaí ná gzip. Déanfaimid comhaid a chomhbhrú uair amháin; ní gá é seo a dhéanamh ar an eitilt.

Is é seo an Dockerfile dar críoch mé suas leis. Is liomsa tuairimí i Rúisis, i mBéarla - ón mbunleagan.

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

Déanfaidh mé nginx.conf a shocrú láithreach ionas go mbeidh gzip agus brotli cumasaithe de réir réamhshocraithe. Cuirfidh mé ceanntásca taisce san áireamh freisin, mar ní bheidh aon athrú statach againn riamh. Agus is é an teagmháil dheireanach ná na 404 iarratas ar fad a atreorú chuig index.html, tá sé seo riachtanach don loingseoireacht sa 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";
            }
        }
    }
}

Is féidir leat an íomhá críochnaithe a íoslódáil anseo: https://hub.docker.com/r/alexxxnf/nginx-spa. Tógann sé suas 10,5 MB. Ghlac an nginx bunaidh suas 19,7 MB. Tá mo spéis sa spórt sásta.

Múineadh statach chun athróga timpeallachta a thuiscint

Cén fáth a bhféadfadh socruithe a bheith ag teastáil in SPA? Mar shampla, chun a shonrú cé acu RESTful API a úsáid. De ghnáth, aistrítear socruithe don timpeallacht inmhianaithe chuig SPA ag an gcéim tógála. Más gá duit rud éigin a athrú, beidh ort an feidhmchlár a atógáil. Níl mé ag iarraidh é. Ba mhaith liom an t-iarratas a thógáil uair amháin ag an gcéim CI, agus a chumrú oiread agus is gá ag an gcéim CD ag baint úsáide as athróga timpeallachta.

Ar ndóigh, ní thuigeann comhaid statacha iad féin aon athróg timpeallachta. Dá bhrí sin, beidh ort cleas a úsáid. San íomhá deiridh, ní sheolfaidh mé nginx, ach script bhlaosc speisialta a léifidh athróga timpeallachta, iad a scríobh chuig comhaid statacha, iad a chomhbhrú, agus gan ach ansin rialú a aistriú chuig nginx.

Chun na críche sin, soláthraíonn an Dockerfile an paraiméadar ENTRYPOINT. Tabhair dúinn an script seo a leanas dó (ag úsáid Angular mar shampla):

docker-pointe iontrála.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 "$@"

Ionas gur féidir leis an script a cuid oibre a dhéanamh, ní mór na socruithe a scríobh sna comhaid js san fhoirm seo: ${API_URL}.

Is fiú a thabhairt faoi deara go gcuireann an chuid is mó de SPAanna nua-aimseartha hashes ar a gcuid comhad agus iad ag tógáil. Tá sé seo riachtanach ionas gur féidir leis an mbrabhsálaí an comhad a thaisceadh go sábháilte ar feadh i bhfad. Má athraíonn an comhad, ansin athrófar a hash, rud a chuirfidh iallach ar an mbrabhsálaí an comhad a íoslódáil arís.

Ar an drochuair, i mo mhodh, níl athrú ar an hash comhaid mar thoradh ar an gcumraíocht a athrú trí athróga timpeallachta, rud a chiallaíonn go gcaithfear taisce an bhrabhsálaí a neamhbhailiú ar bhealach éigin eile. Níl an fhadhb seo agam mar go bhfuil cumraíochtaí éagsúla imscaradh i dtimpeallachtaí éagsúla.

Ag cur an íomhá deiridh le chéile

Ar deireadh.

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

Anois is féidir an íomhá mar thoradh air a chur le chéile agus a úsáid in áit ar bith.

Foinse: will.com