כיצד ליישם משתני סביבת זמן ריצה באמצעות create-react-app, Docker ו- Nginx

ישנן דרכים רבות להגדיר את יישום ה- React שלך. בואו נשתמש בגישה שמכבדת את מתודולוגיית היישום Twelve-Factor. פירוש הדבר שהוא אוכף תצורה מחודשת במהלך זמן הריצה. לכן לא תידרש בנייה לכל סביבה.

? מה אנחנו רוצים להשיג?

אנו רוצים להיות מסוגלים להריץ את יישום ה- React שלנו כמיכל Docker שנבנה פעם אחת. זה פועל בכל מקום על ידי הגדרתו במהלך זמן הריצה. הפלט צריך להיות מיכל קל וביצועי המשמש את יישום ה- React שלנו כתוכן סטטי, אותו אנו משיגים באמצעות Ngnix Alpine. היישום שלנו אמור לאפשר תצורה בתוך קובץ ה- docker-compose כגון זה:

version: "3.2" services: my-react-app: image: my-react-app ports: - "3000:80" environment: - "API_URL=//production.example.com"

אנו אמורים להיות מסוגלים להגדיר את יישום ה- React שלנו באמצעות -eדגל (משתני סביבה) בעת שימוש Docker runבפקודה.

במבט ראשון, נראה כי גישה זו מביאה יתרון קטן מדי עבור העבודה הנוספת הנדרשת לצורך ההתקנה הראשונית. אך לאחר סיום ההתקנה, תצורות ופריסה ספציפיות לסביבה יהיו קלות יותר לטיפול. אז עבור כל מי שמתמקד בסביבות דינמיות או משתמש במערכות תזמורת, גישה זו היא בהחלט מה שיש לקחת בחשבון.

? הבעיה

קודם כל, צריך להיות ברור שאין דבר כזה משתנה סביבה בתוך סביבת הדפדפן. כל פיתרון בו אנו משתמשים כיום אינו אלא הפשטה מזויפת.

אבל אז אתה יכול לשאול, מה לגבי .envקבצים REACT_APPומשתני סביבה קידומת שמגיעים ישירות מהתיעוד? גם בתוך קוד המקור, אלה משמשים process.envבדיוק כמו שאנחנו משתמשים במשתני סביבה בתוך Node.js.

במציאות, האובייקט processאינו קיים בסביבת הדפדפן, הוא ספציפי לצומת. CRA כברירת מחדל לא מבצע טיוח בצד השרת. זה לא יכול להזרים משתני סביבה במהלך הצגת התוכן (כמו Next.js). במהלך השידור , תהליך Webpack מחליף את כל המופעים process.envבערך מחרוזת שניתן. המשמעות היא שניתן להגדיר אותה רק בזמן הבנייה .

? פִּתָרוֹן

הרגע הספציפי שבו עדיין ניתן להזריק משתני סביבה קורה כאשר אנו מתחילים את המכולה שלנו. אז נוכל לקרוא משתני סביבה מתוך המיכל. אנו יכולים לכתוב אותם לקובץ אשר ניתן להגיש באמצעות Nginx (המשרת גם את אפליקציית React שלנו). הם מיובאים ליישום שלנו באמצעות תג בתוך החלק הראשי של index.html. אז באותו רגע, אנו מריצים סקריפט bash שיוצר קובץ JavaScript עם משתני סביבה שהוקצו כתכונות של windowהאובייקט הגלובלי . מוזרק להיות זמין ברחבי העולם ביישום שלנו בדרך הדפדפן.

? מדריך שלב אחר שלב

נתחיל create-react-appבפרויקט פשוט וניצור .envקובץ עם משתנה הסביבה הראשון שאותו אנו רוצים לחשוף.

# Generate React App create-react-app cra-runtime-environment-variables cd cra-runtime-environment-variables # Create default environment variables that we want to use touch .env echo "API_URL=https//default.dev.api.com" >> .env

אז בוא נכתוב סקריפט bash קטן שיקרא את .envהקובץ ויחלץ משתני סביבה שייכתבו לקובץ. אם תגדיר משתנה סביבה בתוך המיכל, ישתמש בערכו, אחרת הוא יחזור לערך ברירת המחדל מקובץ .env. זה ייצור קובץ JavaScript אשר מציב את ערכי משתני הסביבה כאובייקט המוקצה כמאפיין של windowאובייקט.

#!/bin/bash # Recreate config file rm -rf ./env-config.js touch ./env-config.js # Add assignment echo "window._env_ = {" >> ./env-config.js # Read each line in .env file # Each line represents key=value pairs while read -r line || [[ -n "$line" ]]; do # Split env variables by character `=` if printf '%s\n' "$line" | grep -q -e '='; then varname=$(printf '%s\n' "$line" | sed -e 's/=.*//') varvalue=$(printf '%s\n' "$line" | sed -e 's/^[^=]*=//') fi # Read value of current variable if exists as Environment variable value=$(printf '%s\n' "${!varname}") # Otherwise use value from .env file [[ -z $value ]] && value=${varvalue} # Append configuration property to JS file echo " $varname: \"$value\"," >> ./env-config.js done > ./env-config.js

עלינו להוסיף את השורה הבאה לאלמנט שבתוכו index.htmlמייבא את הקובץ שנוצר על ידי סקריפט ה- bash שלנו.

בואו להציג את המשתנה הסביבתי שלנו בתוך היישום:

API_URL: {window._env_.API_URL}

? התפתחות

במהלך הפיתוח, אם איננו רוצים להשתמש ב- Docker, אנו יכולים להריץ את ה- script bash באמצעות npm scriptrunner על ידי שינוי package.json:

 "scripts": { "dev": "chmod +x ./env.sh && ./env.sh && cp env-config.js ./public/ && react-scripts start", "test": "react-scripts test", "eject": "react-scripts eject", "build": "react-scripts build'" },

ואם אנחנו רצים yarn devאנחנו צריכים לראות פלט כזה:

ישנן שתי דרכים להגדרת תצורה מחדש של משתני סביבה בתוך dev. או שנה את ערך ברירת המחדל בתוך .envהקובץ או עקוף את ברירות המחדל על ידי הפעלת yarn devפקודה עם משתני סביבה מוכנים מראש:

API_URL=//my.new.dev.api.com yarn dev

ולסיום, ערוך .gitignoreכך שלא נכלול תצורות סביבה בקוד המקור:

# Temporary env files /public/env-config.js env-config.js

באשר לסביבת הפיתוח, זהו זה! אנחנו בחצי הדרך לשם. לא עשינו הבדל עצום בשלב זה בהשוואה למה שה- CRA הציע כברירת מחדל עבור סביבת הפיתוח. הפוטנציאל האמיתי של גישה זו זורח בייצור.

? הפקה

כעת אנו הולכים ליצור תצורת Nginx מינימלית כדי שנוכל לבנות תמונה מותאמת המשרתת את היישום המוכן לייצור.

# Create directory for Ngnix configuration mkdir -p conf/conf.d touch conf/conf.d/default.conf conf/conf.d/gzip.conf

קובץ התצורה הראשי אמור להיראות כך:

server { listen 80; location / { root /usr/share/nginx/html; index index.html index.htm; try_files $uri $uri/ /index.html; expires -1; # Set it to different value depending on your standard requirements } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }

זה גם שימושי לאפשר דחיסת gzip כך שהנכסים שלנו יהיו קלים יותר במהלך מעבר לרשת:

gzip on; gzip_http_version 1.0; gzip_comp_level 5; # 1-9 gzip_min_length 256; gzip_proxied any; gzip_vary on; # MIME-types gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/x-component;

כעת, לאחר שתצורת Nginx שלנו מוכנה, סוף סוף נוכל ליצור קבצי Dockerfile ו- docker-compose:

touch Dockerfile docker-compose.yml

בתחילה אנו משתמשים node:alpineבתמונה כדי ליצור מבנה ייצור אופטימלי של היישום שלנו. לאחר מכן, אנו בונים תמונה של זמן ריצה nginx:alpine.

# => Build container FROM node:alpine as builder WORKDIR /app COPY package.json . COPY yarn.lock . RUN yarn COPY . . RUN yarn build # => Run container FROM nginx:1.15.2-alpine # Nginx config RUN rm -rf /etc/nginx/conf.d COPY conf /etc/nginx # Static build COPY --from=builder /app/build /usr/share/nginx/html/ # Default port exposure EXPOSE 80 # Copy .env file and shell script to container WORKDIR /usr/share/nginx/html COPY ./env.sh . COPY .env . # Add bash RUN apk add --no-cache bash # Make our shell script executable RUN chmod +x env.sh # Start Nginx server CMD ["/bin/bash", "-c", "/usr/share/nginx/html/env.sh && nginx -g \"daemon off;\""]

עכשיו המכולה שלנו מוכנה. אנחנו יכולים לעשות את כל הדברים הסטנדרטיים איתו. אנו יכולים לבנות מכולה, להריץ אותה עם תצורות מוטבעות ולדחוף אותה למאגר המסופק על ידי שירותים כגון Dockerhub.

docker build . -t kunokdev/cra-runtime-environment-variables docker run -p 3000:80 -e API_URL=//staging.api.com -t kunokdev/cra-runtime-environment-variables docker push -t kunokdev/cra-runtime-environment-variables

docker runהפקודה לעיל צריכה להוציא יישום כך:

לבסוף, בואו ניצור את קובץ ה- docker-compose שלנו. בדרך כלל יהיו לך קבצי דוקר-קומפוזיציה שונים בהתאם לסביבה ותשתמש -fבדגל כדי לבחור באיזה קובץ להשתמש.

version: "3.2" services: cra-runtime-environment-variables: image: kunokdev/cra-runtime-environment-variables ports: - "5000:80" environment: - "API_URL=production.example.com"

ואם נעשה זאת docker-compose upעלינו לראות פלט כך:

גדול! כעת השגנו את מטרתנו. אנו יכולים להגדיר מחדש את היישום שלנו בסביבות פיתוח וייצור בצורה נוחה מאוד. כעת נוכל לבנות רק פעם אחת ולרוץ לכל מקום!

אם נתקעתם או שיש לכם רעיונות נוספים, גשו לקוד המקור ב- GitHub.

? הצעדים הבאים

היישום הנוכחי של סקריפט הפגז ידפיס את כל המשתנים הכלולים בקובץ .env. לרוב אנחנו לא רוצים לחשוף את כולם. אתה יכול ליישם מסננים עבור משתנים שאינך רוצה לחשוף באמצעות קידומות או טכניקה דומה.

? פתרונות חלופיים

כפי שצוין לעיל, תצורת זמן הבנייה תספק את רוב מקרי השימוש. אתה יכול להסתמך על גישת ברירת המחדל באמצעות קובץ .env לכל סביבה ולבנות מיכל לכל סביבה ולהזריק ערכים באמצעות משתני הסביבה של CRA Webpack.

אתה יכול גם להסתכל על נושא מאגר CRA GitHub זה המכסה בעיה זו. נכון לעכשיו, אמורים להיות עוד פוסטים ונושאים המכסים נושא זה. כל אחד מהם מציע פיתרון דומה כאמור לעיל. עליכם להחליט כיצד אתם מיישמים פרטים ספציפיים. אתה יכול להשתמש ב- Node.js כדי להגיש את היישום שלך, מה שאומר שאתה יכול גם להחליף את סקריפט הקליפות בסקריפט Node.js. שים לב ש- Nginx נוח יותר להגיש תוכן סטטי.

אם יש לך שאלות או שאתה רוצה להציע משוב; אל תהסס לפתוח את הבעיה ב- GitHub. אופציונלי עקוב אחרי לקבלת פוסטים נוספים הקשורים לטכנולוגיות אינטרנט.