صورة لرسو تطبيق صفحة واحدة

تطبيق الصفحة الواحدة (SPA) عبارة عن مجموعة من ملفات JavaScript و HTML الثابتة ، بالإضافة إلى الصور والموارد الأخرى. نظرًا لأنها لا تتغير ديناميكيًا ، فإن نشرها على الإنترنت بسيط للغاية. للقيام بذلك ، هناك عدد كبير من الخدمات الرخيصة وحتى المجانية ، بدءًا من صفحات GitHub البسيطة (وللشخص حتى مع narod.ru) وتنتهي ب CDN مثل Amazon S3. ومع ذلك ، كنت بحاجة إلى شيء آخر.


كنت بحاجة إلى صورة Docker مع SPA بحيث يمكن إطلاقها بسهولة في الإنتاج كجزء من مجموعة Kubernetes وعلى جهاز مطور خلفي ليس لديه فكرة عن ماهية SPA.


لقد حددت متطلبات الصورة التالية لنفسي:


  • سهولة الاستخدام (ولكن ليس التجميع) ؛
  • الحد الأدنى للحجم من حيث القرص وذاكرة الوصول العشوائي ؛
  • ضبط متغيرات البيئة بحيث يمكن استخدام الصورة في بيئات مختلفة ؛
  • توزيع الملفات الأكثر كفاءة.

اليوم سأخبرك كيف:


  • nginx الأمعاء.
  • تجميع بروتلي من المصدر ؛
  • تعليم الملفات الثابتة لفهم متغيرات البيئة ؛
  • وبالطبع كيفية بناء صورة Docker من كل هذا.

الغرض من هذه المقالة هو مشاركة تجربتي وإثارة أعضاء المجتمع ذوي الخبرة للنقد البناء.


بناء صورة لبناء


Docker- , : . Alpine Linux, . - , Alpine , , . .


, 2 . – , . .


.


, SPA-, , node.js. npm yarn. node-gyp, npm-, Brotli Google, .


Dockerfile .
#  
FROM node:12-alpine
LABEL maintainer="Aleksey Maydokin <amaydokin@gmail.com>"
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

, .


: https://hub.docker.com/r/alexxxnf/spa-builder. .


nginx


web-. nginx, .


nginx Docker-, . Dockerfile.


$ 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

Dockerfile, , . HTTPS, . , Brotli, , gzip. , .


Dockerfile . – , – .


Dockerfile
#    Alpine
FROM alpine:3.9
LABEL maintainer="Aleksey Maydokin <amaydokin@gmail.com>"
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;"]

nginx.conf, gzip brotli . , . 404 index.html, 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";
            }
        }
    }
}

: https://hub.docker.com/r/alexxxnf/nginx-spa. 10,5 . nginx 19,7 . .



SPA ? , , RESTful API . SPA . - , . . CI, , , CD .


, . , . nginx, shell-, , , nginx.


Dockerfile ENTRYPOINT. ( Angular):


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

, js- : ${API_URL}.


, SPA . , . - , , .


, , , - . , .



-.


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

-.


All Articles