Programu ya ukurasa mmoja (SPA) ni seti ya faili tuli za JavaScript na HTML, pamoja na picha na rasilimali zingine. Kwa sababu hazibadiliki kiutendaji, kuzichapisha mtandaoni ni rahisi sana. Kuna idadi kubwa ya huduma za bei nafuu na hata za bure kwa hili, kuanzia na Kurasa rahisi za GitHub (na kwa baadhi hata na narod.ru) na kuishia na CDN kama Amazon S3. Hata hivyo, nilihitaji kitu kingine.
Nilihitaji picha ya Docker iliyo na SPA ili iweze kuzinduliwa kwa urahisi katika uzalishaji kama sehemu ya nguzo ya Kubernetes, na kwenye mashine ya msanidi programu wa nyuma ambaye hajui SPA ni nini.
Nimejiwekea mahitaji yafuatayo ya picha:
- urahisi wa matumizi (lakini sio mkusanyiko);
- ukubwa wa chini wote kwa suala la disk na RAM;
- usanidi kupitia vigezo vya mazingira ili picha iweze kutumika katika mazingira tofauti;
- usambazaji bora zaidi wa faili.
Leo nitakuambia jinsi:
- utumbo nginx;
- jenga brotli kutoka kwa vyanzo;
- kufundisha faili tuli kuelewa vigezo vya mazingira;
- na kwa kweli jinsi ya kukusanya picha ya Docker kutoka kwa haya yote.
Madhumuni ya makala haya ni kushiriki uzoefu wangu na kuwachochea wanajamii wenye uzoefu wakosoaji wenye kujenga.
Kuunda picha kwa mkusanyiko
Ili kufanya picha ya mwisho ya Docker ndogo kwa ukubwa, unahitaji kuzingatia sheria mbili: kiwango cha chini cha tabaka na picha ya msingi ya minimalistic. Mojawapo ya picha ndogo zaidi za msingi ni picha ya Alpine Linux, kwa hivyo hiyo ndiyo nitakayochagua. Wengine wanaweza kusema kuwa Alpine haifai kwa uzalishaji, na wanaweza kuwa sahihi. Lakini binafsi, sijawahi kuwa na matatizo yoyote naye na hakuna mabishano dhidi yake.
Ili kuwa na tabaka chache, nitakusanya picha katika hatua 2. Ya kwanza ni rasimu; huduma zote za usaidizi na faili za muda zitabaki ndani yake. Na katika hatua ya mwisho nitaandika tu toleo la mwisho la programu.
Wacha tuanze na picha ya msaidizi.
Ili kuunda programu ya SPA, kwa kawaida unahitaji node.js. Nitachukua picha rasmi, ambayo pia inakuja na wasimamizi wa vifurushi vya npm na uzi. Kwa niaba yangu mwenyewe, nitaongeza node-gyp, ambayo inahitajika kuunda vifurushi kadhaa vya npm, na compressor ya Brotli kutoka Google, ambayo itakuwa muhimu kwetu baadaye.
Dockerfile na maoni.
# ΠΠ°Π·ΠΎΠ²ΡΠΉ ΠΎΠ±ΡΠ°Π·
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
Tayari hapa ninapigania minimalism, hivyo picha imewekwa pamoja na timu moja kubwa.
Picha iliyokamilishwa inaweza kupatikana hapa:
nginx
Unaweza kutumia seva yoyote ya wavuti kusambaza yaliyomo tuli. Nimezoea kufanya kazi na nginx, kwa hivyo nitatumia sasa.
Nginx ina picha rasmi ya Docker, lakini ina moduli nyingi sana za usambazaji rahisi wa tuli. Ambayo ni pamoja na katika utoaji inaweza kuonekana na timu maalum au katika Dockerfile rasmi.
$ 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
Nitatumia Dockerfile kama msingi, lakini nitaacha ndani yake tu kile kinachohitajika kusambaza yaliyomo tuli. Toleo langu halitaweza kufanya kazi kupitia HTTPS, halitaauni uidhinishaji, na mengine mengi. Lakini toleo langu litaweza kusambaza faili zilizobanwa na algoriti ya Brotli, ambayo ni bora kidogo kuliko gzip. Tutabana faili mara moja; hakuna haja ya kufanya hivyo kwa kuruka.
Hii ndio Dockerfile ambayo niliishia nayo. Maoni katika Kirusi ni yangu, kwa Kiingereza - kutoka kwa asili.
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;"]
Nitarekebisha nginx.conf mara moja ili gzip na brotli ziwezeshwe kwa chaguo-msingi. Pia nitajumuisha vichwa vya kache, kwa sababu hatutawahi kubadilisha tuli. Na mguso wa mwisho utakuwa kuelekeza upya maombi yote 404 kwa index.html, hii ni muhimu kwa urambazaji katika 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";
}
}
}
}
Unaweza kupakua picha iliyokamilishwa hapa:
Kufundisha statics kuelewa vigezo vya mazingira
Kwa nini mipangilio inaweza kuhitajika katika SPA? Kwa mfano, ili kubainisha ni API gani ya RESTful ya kutumia. Kawaida, mipangilio ya mazingira unayotaka huhamishiwa kwa SPA katika hatua ya ujenzi. Ikiwa unahitaji kubadilisha kitu, itabidi ujenge upya programu. sitaki. Ninataka programu ijengwe mara moja katika hatua ya CI, na kusanidiwa kadri inavyohitajika katika hatua ya CD kwa kutumia anuwai za mazingira.
Kwa kweli, faili tuli zenyewe hazielewi anuwai za mazingira. Kwa hivyo, utalazimika kutumia hila. Katika picha ya mwisho, sitazindua nginx, lakini hati maalum ya shell ambayo itasoma vigezo vya mazingira, kuandika kwa faili za tuli, kuzikandamiza, na kisha tu kuhamisha udhibiti kwa nginx.
Kwa kusudi hili, Dockerfile hutoa parameta ya ENTRYPOINT. Wacha tumpe hati ifuatayo (kwa kutumia Angular kama mfano):
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 "
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 "$@"
Ili hati ifanye kazi yake, mipangilio lazima iandikwe katika faili za js katika fomu hii:${API_URL}
.Inafaa kumbuka kuwa SPA nyingi za kisasa huongeza heshi kwenye faili zao wakati wa kujenga. Hii ni muhimu ili kivinjari kiweze kuhifadhi faili kwa muda mrefu. Ikiwa faili itabadilika, basi hashi yake itabadilika, ambayo kwa upande italazimisha kivinjari kupakua faili tena.
Kwa bahati mbaya, kwa njia yangu, kubadilisha usanidi kwa njia ya vigezo vya mazingira haiongoi mabadiliko katika hash ya faili, ambayo ina maana kwamba cache ya kivinjari lazima iwe batili kwa njia nyingine. Sina shida hii kwa sababu usanidi tofauti hutumwa katika mazingira tofauti.
Kuweka pamoja picha ya mwisho
Hatimaye.
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;"]
Sasa picha inayotokana inaweza kukusanyika na kutumika popote.
Chanzo: mapenzi.com