fa
Feedback
ToCode

ToCode

رفتن به کانال در Telegram

טיפים קצרים למתכנתים מאת ינון פרק

نمایش بیشتر
1 419
مشترکین
اطلاعاتی وجود ندارد24 ساعت
اطلاعاتی وجود ندارد7 روز
+130 روز
آرشیو پست ها
ToCode
1 419
ומהמסוף שמריץ את ה logger ניכנס לתיקיית הלוגים כדי לראות את התוצאות:
$ cat /var/log/syslog-ng/*/* 
Jul  8 06:27:35 32-setup-syslogng_client1_1 client-demo[16]: {"message":"Hello World","level":"info"}
Jul  8 06:27:45 32-setup-syslogng_client2_1 client-demo[16]: {"message":"Hello World","level":"info"}
## הוספת Log Destination בואו נעדכן את שרת הלוגים כדי שיכתוב קובץ לוג נוסף הפעם אחד שמרכז את כל הלוגים הנכנסים מכל המקורות - לא משנה מאיזה שרת ומאיזו תוכנית. אני יודע, קל מאוד בעזרת כלי יוניקס לראות במכה אחת את כל הלוגים ממספר קבצים, אבל חישבו על זה יותר בתור דוגמה לאיך מוסיפים יעד לוג נוסף. עדכנו את קובץ הגדרות הלוג syslog-ng.conf והוסיפו את הבלוג הבא:
destination alllogs {
  file("/var/log/syslog-ng/all.log"
      create-dirs(yes));
};
לאחר מכן בתוך בלוק log הוסיפו את ה destination החדש:
log {
  source(s_net);
  destination(logfiles);
  destination(alllogs);
};
נפעיל מחדש וניכנס לשרת הלוגים. שם נוכל למצוא בתיקיית /var/log/syslog-ng את הקובץ החדש all.log הפעם עם כל הודעות הלוג משני הלקוחות שלנו. (שימו לב שהקובץ יופיע רק אחרי שהפעלתם את הלקוחות עצמם וכתבתם הודעות ללוג). ## סיכום העבודה עם syslog-ng איפשרה לנו להרים בקלות סרביס של שמירת לוגים שמקבל לוגים מכל הסרביסים האחרים במערכת ושומר אותם במקום אחד. אני בחרתי לשמור את הלוג לקבצים אבל במערכות גדולות יותר הרבה פעמים הלוג יישמר לבסיס נתונים שם אפשר יהיה להריץ עליו חיפושים או להקפיץ התראות בזמן אמת. בשביל לאחסן את הלוגים בענן נצטרך בסך הכל לפתוח חשבון בשרת לוגים בענן ולהוסיף עוד destination להגדרות ה syslog שיצרנו. לדוגמה כדי לחבר את הלוגים למערכת papertrail אני מוסיף בלוק שנראה בערך ככה:
destination d_papertrail {
 udp("logsN.papertrailapp.com" port(XXXXX));
};

ToCode
1 419
# מדריך: הקמת שרת לוגים עם syslog-ng שרת לוגים הוא בסך הכל סרביס שכולם פונים אליו עם הודעות הלוג שלהם והוא כותב את המידע לאיזשהו יעד. ברירת מחדל פשוטה היא לכתוב את כל הלוגים לקבצים וזה יתאים לנו בשביל התחלה, אבל הרבה פעמים נרצה לנתב את הלוגים החוצה משרת הלוגים גם לבסיס נתונים או אפילו לשירות איחסון לוגים בענן. תוכנת syslog-ng היא מערכת ניהול לוגים פופולרית שמאפשרת גם שמירה מקומית של הלוגים וגם יש לה אינטגרציה מצוינת עם שירותי איחסון לוגים בענן כך שיהיה קל לעבור לשם כשנצטרך. ## יצירת אימג' לסרביס הלוגים בשביל לכתוב סרביס לוגים נפעיל בתוך קונטיינר תוכנית בשם syslog-ng שמקבלת לוגים מהסרביסים השונים ושומרת אותם ליעד שנבחר. כך נראה קובץ קונפיגורציה פשוט של syslog-ng:
@version: 3.19

source s_net {
  tcp( ip("0.0.0.0"));
  udp( ip("0.0.0.0"));
  syslog( ip("0.0.0.0"));
 };

destination logfiles {
  file("/var/log/syslog-ng/$HOST/$PROGRAM.log"
      create-dirs(yes));
};

log {
  source(s_net);
  destination(logfiles);
};

אחרי תיאור הגירסה אנחנו מגדירים "מקורות", כלומר מאיפה לוגים נשלחים אלינו, ו"יעדים", כלומר לאן הלוג ייכתב. בלוק log מחבר בין מקור ליעד והוא שאחראי על הקסם. כרגע היעד הוא קבצים. המשתנים $HOST ו $PROGRAM בתוך שם קובץ היעד יתורגמו לשם השרת ששלח את ההודעה ושם התוכנית שרצה שם. נשמור את הקונפיגורציה בקובץ בשם syslog-ng.conf ואת הקובץ נשמור בתיקיה בשם logserver. באותה תיקיה אני מוסיף גם קובץ Dockerfile עם התוכן הבא:
FROM debian:buster

RUN apt-get update && apt-get upgrade -y && apt-get install -y syslog-ng
COPY syslog-ng.conf /etc/syslog-ng/

EXPOSE 514/udp 514/tcp 601/tcp 6514/tcp

ENTRYPOINT ["/usr/sbin/syslog-ng", "-F", "-f", "/etc/syslog-ng/syslog-ng.conf", "--no-caps"]
הקובץ מתקין את syslog-ng על אימג' של דביאן ומפעיל אותו עם קובץ הקונפיגורציה שיצרנו. ## יצירת אפליקציית Node.JS שכותבת לסרביס הלוגים בואו נראה שזה עובד ובשביל זה נייצר אפליקציית Node.JS שתכתוב ללוג. בתיקיה חדשה בשם client צרו קובץ בשם main.js עם התוכן הבא:
const winston = require('winston');
require('winston-syslog').Syslog;

const logger = winston.createLogger({
  levels: winston.config.syslog.levels,
  transports: [
    new winston.transports.Syslog({
      host: process.env.SYSLOG_NG_HOST,
      app_name: 'client-demo',
    }),
  ]
});

console.log('--- Writing To Syslog');

logger.info("Hello World");
logger.end();
פקודת console.log תכתוב את הפלט לקונסול כך שנראה את זה במסוף של דוקר, ופקודת logger.info תשתמש בספריית winston-syslog כדי לשלוח את ההודעה לשרת לוג מרוחק. שימו לב שכתובת השרת מגיעה לתוכנית דרך משתנה סביבה ולכן אנחנו מצפים למצוא את הערך למשתנה זה בקובץ ה docker-compose הראשי. באותה תיקיה צרו גם קובץ package.json עם התוכן הבא:
{
  "name": "client",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "winston": "^3.3.3",
    "winston-syslog": "^2.4.4"
  }
}
## הפעלה מתוך Docker Compose נבדוק את הכל כדי לוודא שאפשר לכתוב ולקרוא לוגים מכמה סרביסים. בתיקיה הראשית כתבו קובץ docker-compose.yml עם התוכן הבא:
version: '3.9'

services:
  logger:
    build: ./logserver
    volumes:
      - logs:/var/log/syslog-ng/

  client1:
    build: ./client
    command: ["sleep", "infinity"]
    environment:
      SYSLOG_NG_HOST: logger

  client2:
    build: ./client
    command: ["sleep", "infinity"]
    environment:
      SYSLOG_NG_HOST: logger


volumes:
  logs:
והפעילו עם:
$ docker compose up
עכשיו יש לנו שתי מכונות client שלא עושות כלום, אבל כוללות את התוכנית שלנו שיודעת לכתוב ללוג, ומכונת server אחת שמקשיבה להודעות לוג נכנסות. נפתח שלושה חלונות חדשים ונתחבר לכל אחת מהמכונות:
# First Terminal
$ docker compose exec logger /bin/bash

# Second Terminal
$ docker compose exec client1 /bin/bash

# Third Terminal
$ docker compose exec client2 /bin/bash
מהמסופים שמריצים את client1 ו client2 נריץ את הפקודה:
$ node main.js

ToCode
1 419
# אילוצים מזויפים ״בשביל להגיע ל Zero Downtime Deployments אנחנו צריכים לשדרג לקוברנטיס.״ ״בשביל לפתור את בעיות הביצועים אנחנו חייבים לעבור מאנגולר לריאקט.״ ״בשביל שנוכל לבנות פיצ'רים חדשים יותר מהר אנחנו חייבים לעשות Rewrite לכל המערכת.״ ״בשביל לפתור את בעיית האבטחה אנחנו חייבים לשדרג את התשתית לגירסה החדשה ביותר ולשבור המון קוד בתהליך.״ ״בשביל שאוכל להתחיל ללמוד את הטכנולוגיה ההיא שנשמעת לי מעניינת אני צריך שהבוס ייתן לי לעבוד על פרויקט שכתוב בה.״ אנחנו יוצרים לעצמנו אילוצים כל הזמן, בדרך כלל כי האילוץ נותן לנו תירוץ לעשות את הדבר הקשה. אבל שווה לזכור שמותר לוותר על האילוץ אם בפועל הוא מונע מכם לעשות את הדבר הקל. במילים אחרות, אם בגלל שאין זמן עכשיו לשדרג לקוברנטיס אתם מוותרים על Zero Downtime Deployment, הבעיה לא קשורה לקוברנטיס.

ToCode
1 419
הקלטה מהוובינר שהתקיים בחמישי עלתה לאתר https://www.tocode.co.il/past_workshops/113

ToCode
1 419
# טיפ דוקר: שימוש ב Multistage Builds בבניית Docker Image יהיו לנו מצבים שאנחנו צריכים אימג' בסיס מסוים כדי לבצע עבודת "הכנה", אבל אחרי שההכנה הסתיימה אפשר לקחת את התוצרים ולהגיש אותם מאימג' אחר. דוגמה פשוטה היא אפליקציית next.js שיוצרת אתר HTML סטטי. אני צריך את node.js וכל מה שקשור אליו בשביל לבנות את האתר הסטטי, אבל אחרי שבניתי אותו אני יכול להגיש את התוצאה מאימג' של nginx. בדוקר יש מנגנון שנקרא multi stage builds שנועד לתת מענה למצבים כאלה ולאפשר בניה מאימג' אחד ואריזה באימג' אחר. בואו נראה איך זה עובד. ## אפליקציית next רגילה באימג' של node נתחיל עם כתיבת Dockerfile לאפליקציית next רגילה באימג' של node.js. בשביל לבנות את האפליקציה אני צריך להריץ:
$ npm run build
$ npx next export
בהנחה שיש לי בתיקיה תת-תיקיה בשם next-demo-app, בואו נבנה את ה Dockerfile הבא:
FROM node:17
WORKDIR /app

COPY next-demo-app .

RUN npm install
RUN npm run build
RUN npx next export

CMD ["npm", "run", "start"]
אחרי זה אני בונה את האימג' עם:
$ docker build . -t multistage-demo
אני יכול להריץ את האימג' לראות שהכל עובד:
$ docker run -p 8080:3000 --rm multistage-demo
אבל מה שבאמת מעניין זה לראות את הגודל של האימג':
$ docker images | grep multistage-demo

multistage-demo                      latest    e67684b5f7d1   2 minutes ago   1.24GB
כן ראיתם נכון - זה 1.24 ג'יגה. ## מעבר ל Multistage Builds אבל אז אני נזכר שאני לא באמת צריך להגיש את האפליקציה משרת של next - כי הפעלתי export וכל האפליקציה שלי זמינה בתור קבצי html סטטיים. לכן מספיק לי להגיש אותה משרת ווב רגיל ופשוט, למשל nginx. נו, אם רק הייתי יכול להעביר את כל הקבצים שבניתי לאימג' nginx ... ופה נכנס הרעיון של multistage builds. נעדכן את ה Dockerfile לקוד הבא:
FROM node:17 AS builder
WORKDIR /app

COPY next-demo-app .

RUN npm install
RUN npm run build
RUN npx next export

FROM nginx:1.21-alpine
COPY --from=builder /app/out /usr/share/nginx/html/
הוספת שורת FROM נוספת ל Dockerfile מאפשרת לשים בצד את כל מה שיצרנו, ולהתחיל לבנות את האימג' מחדש מתוך אימג' בסיס חדש. בפקודות COPY אני יכול להוסיף --from כדי להעתיק תוצרי בניה מאימג'ים ששמתי בצד. לכן ה Dockerfile המעודכן לוקח את תיקיית out שמכילה את קבצי ה html הסטטיים (אלה שנוצרו ב export), ומעתיק אותה לתיקיה ממנה nginx יגיש את הקבצים הסטטיים שלו. אפשר לבנות מחדש ולהריץ כמעט עם אותן פקודות (צריך לשנות את מיפוי הפורטים כי nginx מאזין על פורט 80) ואתם תראו שה Dockerfile עדיין עובד, אבל יותר מעניין להסתכל על גודל האימג' עכשיו:
$ docker image ls| grep multistage

multistage-demo                      latest    76259b691276   About a minute ago   23.8MB
וכן ראיתם נכון - גודל האימג' הוא עכשיו 23 מגה בלבד, למרות שהוא עושה בדיוק את אותו דבר שעשה האימג' של הג'יגה. הסוד הוא פשוט להשתמש בדיוק במה שאנחנו צריכים.

ToCode
1 419
1. אנחנו יוצרים אימג'ים על מכונת הפיתוח שלנו כשאנחנו רוצים לסגור קוד מסוים ולהכין אותו למשלוח. סגירת תוכנה שכתבתי באימג' מאפשרת לי להעביר את התוכנה הזאת בצורה אמינה לכל מכונה שמריצה את Docker Engine. 2. אנחנו מריצים קונטיינרים מתוך אימג'ים כשאנחנו רוצים להשתמש בהם. אז אם אני צריך עכשיו בסיס נתונים אני מריץ קונטיינר של PostgreSQL; אם אני צריך לנסות משהו ב Elixir אני יכול להריץ קונטיינר של אליקסיר; ואם אני רוצה לכתוב מערכת ב Rails אני יכול להריץ קונטיינר של ריילס. כל עוד יש לי את האימג' של משהו, אני יכול ברגע להריץ ממנו קונטיינר או מספר קונטיינרים. החל מ 2015, דוקר וחברות מובילות נוספות בתעשיה, מתחזקים מבנה קוד פתוח שנקרא OCI (קיצור ל Open Container Initiative) שאחראי על יצירת סטנדרט פתוח לפורמטים של קונטיינרים ואימג'ים. למידע נוסף על דוקר ואיך המנגנון הזה של קונטיינרים עובד מתחת לפני השטח שווה לקרוא את המאמר בקישור: https://accenture.github.io/blog/2021/03/10/docker-behind-the-scenes.html

ToCode
1 419
# מושגים בסיסיים ב Docker: קונטיינרים ואימג'ים דוקר פותח בחברת איחסון ענן בשם dotCloud (כיום כבר שינתה את שמה ל Docker Inc) בשנת 2013. באותה תקופה דוטקלאוד הציעה מוצר שיודע לקחת קוד מהמחשב שלכם ולהעלות אותו להרצה בענן שלהם - והבעיה המרכזית שהיתה להם היתה איך להעביר את הקוד ממכונה אחת לשניה בלי לשבור אותו. עד לפיתוח של דוקר בשביל להעביר קוד ממקום למקום היית צריך לשכפל את "הדברים החשובים בסביבת הריצה" למכונה החדשה. אז אם יש לי תוכנית פייתון שצריכה לרוץ על שרת, אני צריך להתקין על השרת את אותה גירסת פייתון שעליה עבדתי בפיתוח, את אותן ספריות פייתון שהתוכנית שלי צריכה (עם pip install), והחלק הקשה - את החלקים ממערכת ההפעלה שרלוונטיים לתוכנית שלי. אם התוכנית שלי משתמשת ב Shared Library מסוימת אז אני צריך לוודא שהספריה תותקן גם על השרת. אם התוכנית שלי משתמשת בתוכנית חיצונית מסוימת, אותה תוכנית צריכה להיות מותקנת גם על השרת. הרבה פעמים אי תאימויות קטנות בין הכלים או הספריות יוצרים באגים שקשה לזהות אותם, ולכן המעבר לפרודקשן דרש תמיד תשומת לב מיוחדת לפרטים. דוקר היה אמור להיות עבור תעשיית התוכנה מה שקונטיינרים היו לתעשיית הספנות: טכנולוגיה שכל התעשייה תתיישר סביבה, שתאפשר להעביר קוד ממכונה אחת למכונה שניה בצורה אמינה ובלי להתעסק בפרטים. החלום היה שמתכנתת תבנה קופסה, תתן את הקופסה הזאת לצוות Devops ואותו צוות Devops יוכל להפעיל את הקוד שבקופסה על כל מכונה בעולם. הדרך של דוקר להגשים את החלום הזה היתה שימוש בפיצ'רים של Linux Kernel שמאפשרים להריץ מעין מערכת הפעלה בתוך מערכת הפעלה. סוג של קופסה סגורה בתוך המחשב שיכולה, בתוך העולם שלה, לתת ליישומים סביבת ריצה מלאה שכוללת את כל הספריות אפילו הבסיסיות ביותר של מערכת ההפעלה. דוקר, כטכנולוגיה, מאפשר בניה של קופסאות כאלה והפעלה שלהן בכל מקום. וזה מביא אותנו לשני המושגים שרציתי לדבר עליהם בפוסט זה: קונטיינרים ואימג'ים. הקונטיינר הוא אותה קופסה סגורה שרצה על מכונה מסוימת. קונטיינר הוא סביבת ריצה שהיישום או היישומים שרצים בתוכה רואים את עצמם כאילו הם רצים בתוך מערכת הפעלה מסוימת, עם ספריות מסוימות וכלי עבודה מסוימים. לדוגמה קונטיינר של תוכנית פייתון יכלול גם את קוד התוכנית, גם את פייתון עצמה, גם את כל הספריות שהתוכנית צריכה, וגם את כל הספריות של מערכת ההפעלה והקבצים ממערכת ההפעלה שהתוכנית משתמשת בהם. למעשה זה כאילו לקחנו את כל המחשב מהתוכנית למטה עד מערכת ההפעלה, ארזנו את הכל יחד והרצנו את זה על מכונה אחרת. על מחשב פיזי אחד אפשר להריץ כמה קונטיינרים שאנחנו רוצים, בדיוק כמו שמריצים תוכניות רגילות. קונטיינר מזכיר מכונה וירטואלית, בגלל שבתוכו רצה מערכת הפעלה מלאה עם כל הכלים והספריות שלה - אבל זו אשליה. הקונטיינר בדוקר הוא תוכנית רגילה, שרצה בתוך מערכת ההפעלה של המחשב המארח. מסיבה זאת הביצועים של קונטיינרים הם מאוד טובים ובתור מתכנתים קל לנו להריץ קונטיינר נפרד לכל אפליקציה שאנחנו עובדים איתה. ואם קונטיינר הוא תוכנית שכרגע רצה על המכונה בתוך קופסה נפרדת, אז Container Image הוא התבנית שממנה אנחנו מייצרים קונטיינרים. ה Image הוא הדבר שאפשר בקלות לשלוח ממכונה למכונה. היחס בין קונטיינרים לאימג'ים מזכיר את היחס בין קובץ תוכנית לתהליך על המחשב - האימג' הוא הקובץ, וכשמפעילים אותו מקבלים קונטיינר. המנוע של דוקר (Docker Engine) הוא רכיב תוכנה שיודע לבנות קונטיינר מתוך אימג'. לכן על כל מכונה שמריצה Docker Engine אני יכול לקחת כל אימג' שתואם לו וליצור ממנו קונטיינר. אימג' בדוקר מורכב מקבצים, וכל קובץ מייצג "שכבה" של נתונים. כשהמנוע צריך להריץ קונטיינר מהאימג', הוא ממזג את כל השכבות למערכת קבצים אחת שכוללת את כל הקבצים מכל השכבות. כתיבה לקובץ באימג' פשוט יוצרת שכבה חדשה. מבנה השכבות של האימג' מאפשר מאוד בקלות ליצור אימג' מתוך אימג', וגם מהווה את הבסיס לבניית הקונטיינרים. בשביל "להריץ" אימג', כלומר בשביל לבנות קונטיינר מתוך אימג', המנוע של דוקר לוקח את האימג' ומוסיף עליו שכבה חדשה של מערכת קבצים אינטרקטיבית. הוספת השכבה החדשה היא תהליך כמעט מיידי ולכן הרצה של קונטיינר מתוך אימג' היא מאוד מהירה. הצד המפתיע בשיטת העבודה הזאת הוא שקונטיינרים הם ישויות זמניות. אם הקונטיינר כותב קובץ למערכת הקבצים שלו, ברגע שהקונטיינר ייסגר הקובץ ייעלם יחד איתו. העבודה שלנו עם דוקר כוללת שני מנגנונים שכל אחד מהם מתרחש בזמן שונה:

ToCode
1 419
# תוכנית וובינרים לחודשים הקרובים קוראים וותיקים כבר יודעים שבכל יום חמישי הראשון של החודש אני מעביר בזום וובינר על נושא טכנולוגי מעניין. הבוקר סגרתי את התוכנית לשלושת החודשים הקרובים והנה מה שצפוי לנו: ## אפריל - היכרות עם Solid.JS לפני כמה ימים כתבתי פה על סוליד, שהיתה בשבילי אהבה ממבט ראשון. האג'נדה של סוליד מזכירה לי את הדברים שאהבתי בריאקט - והיא לתת למפתחים כלי עבודה טובים ולא להגיד לך יותר מדי איך צריך לקודד. סוליד היא פריימוורק ריאקטיבית בדומה ל Vue, אבל יש בה הרבה פחות תחביר חדש. מפתחי ריאקט יצליחו להיות יעילים בסוליד תוך זמן מאוד קצר. בוובינר נראה מה מיוחד ב Solid.JS לעומת ריאקט (ולעומת Vue), וכמובן נבנה פרויקט ודוגמאות קוד כדי להמחיש כל אחת מהנקודות. בפרט אני מתכנן: 1. להציג סטארטר לתוכנית Solid.JS 2. להבין את ההבדל בין הריאקטיביות של סוליד ל Virtual DOM של ריאקט. 3. לבנות קומפוננטות ולחבר ביניהן. לפרטים נוספים והרשמה לוובינר זה מוזמנים לבקר בדף הוובינר בקישור https://www.tocode.co.il/workshops/116. ## מאי - תכנות מבוזר עם Rabbit MQ יום חמישי הראשון של מאי הוא יום העצמאות ולכן הזזתי את הוובינר לשבוע השני של החודש. לגבי הנושא - שלושת תורי ההודעות שיצא לי לעבוד איתם הם ZeroMQ ו Kafka. מתוכם קפקא הוא כנראה המפורסם והמתוחכם מכולם, זירו עדיין מצליח לבלבל אותי ורביט נשאר הבחירה שלי בפרויקטים בהם יש לי בחירה. בוובינר ארצה להראות מספר תבניות לתכנות מבוזר באמצעות תור הודעות, ותור ההודעות להדגמת המושגים יהיה רביט. רשמתי בתוכנית שאני רוצה לדבר על שלושת התבניות: 1. תהליך מרכזי שמוציא משימות לתהליך רקע. 2. קבוצה של סרביסים שכל אחד מהם צריך להגיב אחרת לאירוע. 3. טיפול באירוע בתוך מספר סרביסים לפי סדר קבוע. אם אתם מכירים Node.JS ועושים את הצעדים הראשונים (או השניים) בעולם התכנות המבוזר, וובינר זה אמור להיות מאוד מעניין עבורכם. לפרטים נוספים והרשמה בקרו בדף הוובינר בקישור https://www.tocode.co.il/workshops/115. ## יוני - ניהול משתמשים ב Auth0 ובחודש יוני רציתי להמשיך עם הנושא של תכנות מבוזר והפעם לעבור על נושא של הזדהות וניהול משתמשים. הפרוטוקולים OAuth ו Open ID Connect הם הדרך המקובלת לנהל משתמשים במערכת מבוזרת. הם קצת קשים ללמידה כי יש בהם המון מושגים וטיפול במקרים ותהליכי עבודה מגוונים. בוובינר אסביר את התהליכים המרכזיים ש OAuth מטפל בהם, אראה ספציפית איך עובד Single Sign On, איך מנהלים הרשאות ואיך צריכים להתנהג קוד צד לקוח וקוד צד שרת במערכת מבוזרת הכוללת הגדרת משתמשים. את כל ההדגמות אני אראה עם שירות Auth0 שהוא שירות ניהול משתמשים ידידותי למפתחים. קוד צד שרת יוצג ב Node.JS וקוד צד לקוח ב React. אם אתם מכירים את הטכנולוגיות ועובדים על מערכת מבוזרת (או רוצים לעבוד על אחת) שווה להצטרף. פרטים והרשמה בקישור https://www.tocode.co.il/workshops/114. הקלטות של וובינרים עולות לאתר כמה ימים אחרי האירוע לדף הקלטות מוובינרים ודוגמאות הקוד זמינות בגיטהאב בקישור: https://github.com/tocodeil/webinar-live-demos מקווה לראותכם, ינון

ToCode
1 419
בוקר טוב עוד עשר דקות (בשעה 10:00) אתחיל וובינר על העלאת יישום Node.JS לקלאסטר קוברנטיס לפרודקשן מי שמעוניין מוזמנים להצטרף בקישור: https://us06web.zoom.us/j/86168203144?pwd=b1YvdnVQWnVDd25Vc016WDFURTN1UT09

ToCode
1 419
# מתי לבנות לבד בעידן ה npm והפיתרונות האינסופיים הנטיה של מתכנתים רבים היא להגיד "מישהו כבר בטח בנה את זה" פחות או יותר על כל בעיה שנתקלים בה, במטרה לחסוך זמן ולבנות על הצלחה של אחרים. אבל מה קורה כשהניסיון להתבסס על עבודה של אחרים דווקא מבזבז לנו זמן? ומתי אנחנו עוצרים להתלבט אם כדאי לבנות לבד? יש שתי סיבות מרכזיות לשימוש בפיתרון קיים- 1. שימוש בקוד קיים הופך את המערכת שלי לסטנדרטית יותר וכך קל יותר לגייס מתכנתים נוספים ולהם קל יותר להיכנס מהר לעניינים. אחד מקלפי המכירה המרכזיים של ריילס כשהיא רק יצאה היה הבלאגן ששרר בעולם ה PHP באותה תקופה, והטענה שאצלנו בריילס כל הפרויקטים מגיעים באותו מבנה תיקיות ומתכנתים יוכלו בקלות לעבור בין פרויקטים. היום ברור לגמרי שיותר קל לי לגייס מתכנתת לפרויקט ריאקט מאשר מתכנתת לפרויקט שישתמש ב Framework שאני המצאתי. המגויסת הטריה יודעת שעם טכנולוגיה שהיא מכירה היא תוכל להתחיל להיות פרודוקטיבית הרבה יותר מהר, ושעבודה בריאקט תתן לה ניסיון בעל ערך לקראת העבודה הבאה. 2. שימוש בקוד קיים מאפשר לי לחסוך זמן פיתוח באמצעות בניה על עבודה של אחרים. דוגמה טובה היא מה שהיה עולם פיתוח הווב בימים של jQuery, כשהיינו צריכים להתאים אתרים למספר דפדפנים והיו אינסוף טריקים שהיית צריך לכתוב כדי שדברים יעבדו טוב בכל מקום. ספריה כמו jQuery נתנה ממשק אחיד שעובד בכל מקום, וכך חסכה למכתנתים עבודה. אבל גם לבניית פיתרון לבד יש יתרונות, לדוגמה: 1. מתכנתים שבונים פיתרון לבד מבינים טוב יותר את הבעיות, ובעתיד הידע הזה יעזור לנו לבחור פיתרון מוכן טוב יותר או להתאים פיתרון מדף לצרכים שלנו. 2. פיתרון שבנינו לבד יהיה מדויק וחסכוני יותר בהשוואה לפיתרון מדף. הפיתרון שלנו צריך לפתור רק את הבעיה שלנו, בעוד שפיתרון מדף פותר עוד המון בעיות שאולי לא רלוונטיות אלינו. 3. פיתרון שבנינו לבד מושפע רק מהפוליטיקה הפנימית שלנו, ולא מאינטרסים של אחרים. 4. בדרך כלל יהיה לי יותר קל לתקן באגים או להוסיף פיצ'רים לפיתרון שאני כתבתי, מאשר להתאים התנהגות של פיתרון חיצוני שאין לי שליטה על הקוד שלו. בסוף השיקול האם לבנות לבד או לקחת פיתרון מוכן צריך להיות שיקול מקצועי ותלוי מצב. אם אתם מוצאים את עצמכם תמיד בוחרים בגישה אחת או בשניה, שווה לנסות לגוון כדי לראות איך זה מרגיש, ולאט לאט למצוא את נקודת האיזון שתתאים למוצר שלכם.

ToCode
1 419
בגלל שסוליד מחשב מחדש רק את הקטעים בתוך ה Virtual DOM שהשתנו, הוא היה חייב לבנות מנגנון משלו עבור לולאות ותנאים. בתרגום ל JavaScript הלולאה נראית כך:
insert(_el$2, createComponent(For, {
  get each() {
    return range(n);
  },

  children: (item, index) => createComponent(Text, {})
}), null);
לא בדקתי ביצועים ולא חיפשתי בעיות. אני לא יודע עדיין איך זה עובד מבחינת מפתחות ולמה הוא לא מבקש שאני אגדיר key לכל פריט. זה משהו שיהיה מעניין לבדוק. אפשר לשחק עם הדוגמה השניה ב Playground שלהם בקישור https://bit.ly/36IOdlE ## משיכת מידע מ API בדוגמה האחרונה אני רוצה למשוך מידע מ API באמצעות fetch. אני אגדיר קומפוננטה של פוקימון שתיקח פרמטר id ותמשוך את השם של הפוקימון משרת pokeapi.co. כל פעם שה id משתנה נרצה למשוך מחדש את המידע ולהציג את השם המעודכן. זה הקוד:
import { render } from "solid-js/web";
import { createSignal, For, splitProps, createEffect } from "solid-js";

function Pokemon(props) {
  const [local, others] = splitProps(props, ["id"]);
  const [name, setName] = createSignal("");

  createEffect(async () => {
    let isActive = true;
    const url = `https://pokeapi.co/api/v2/pokemon/${local.id}`;
    console.log(`Fetching url ${url}`)
    setName('');
    const res = await fetch(url);    
    const data = await res.json();

    if (isActive) {
      setName(data.name);
    }

    onCleanup(() => { isActive = false });
  });

  return (
    <p>Pokemon {local.id} is named {name}</p>
  )
}

function App() {
  const [id, setId] = createSignal(1);

  return (
    <>
    <input type="number" value={id()} onInput={(e) => setId(e.target.value)} /> 
    <Pokemon id={id()} />
    </>
  );
}

render(() => <App />, document.getElementById("app"));
הרבה דברים מעניינים כאן: 1. הפונקציה createEffect היא המקבילה בסוליד ל useEffect, אבל היא לא צריכה לקבל מערך של דברים שמשפיעים על האפקט. באופן אוטומטי היא מסתכלת על קריאות ל getters בתוך הפונקציה ומושפעת רק משינויים במשתנים אלה. 2. במקום שפונקציית אפקט תצטרך להחזיר פונקציית ביטול לאפקט, יש לנו פונקציה בשם onCleanup שמקבלת את פונקציית הניקוי. 3. פיענוח ה Properties דורש טיפול מיוחד:
const [local, others] = splitProps(props, ["id"]);
בגלל שהכל ריאקטיבי, אני לא יכול להשתמש ב Destructuring רגיל כי זה יגרום להעתקת האוביקט ולאיבוד הריאקטיביות. לכן סוליד היו צריכים לתת לי פונקציה מיוחדת שלוקחת ערכים מ props והופכת אותם לשדות ריאקטיביים של האוביקט local. וכן אני צריך להגיד איזה שדות אני רוצה, והמשתנה others מאפשר להעביר props הלאה לקומפוננטות ילדים שאני יוצר. יכולים לשחק עם הקומפוננטה האחרונה ב Playground שלהם בקישור https://bit.ly/3toBdJC. סך הכל יש משהו יפה בגישה הריאקטיבית של סוליד ורוב הקוד שכתבתי בו פשוט עבד. אהבתי את הגמישות שאפשר ליצור משתני State גם מחוץ לקומפוננטות ובסך הכל אתר התיעוד שלהם וה Playground עובדים ממש בסדר. למידע נוסף ואינסוף משאבים עליו ממליץ לחטט ברשימה הזאת: https://github.com/one-aalam/awesome-solid-js.

ToCode
1 419
# צעדים ראשונים עם סוליד סוליד היא פריימוורק ריאקטיבי לפיתוח צד לקוח שלקח השראה מאוד גדולה מריאקט מבחינת התחביר, אבל הטמיע את הריאקטיביות בצורה טובה יותר מאשר ריאקט. אפשר לחשוב עליו כמו הכלאה בין ריאקט למובאקס, רק עם הרבה פחות מושגים שצריך להכיר. ומאחר ואני חושב שהדרך הכי טובה לדבר על פריימוורק היא לראות קוד שכתוב בו - הכנתי לכם 3 תוכניות דוגמה קטנות עם סוליד שיעזרו לנו להבין מה מיוחד בו. ## מונה לחיצות תוכנית ראשונה שנראה בסוליד היא גם התוכנית הראשונה שמופיעה בטמפלייט באתר ה playground שלהם - וזה מונה לחיצות:
import { render } from "solid-js/web";
import { createSignal } from "solid-js";

function Counter() {
  const [count, setCount] = createSignal(0);
  const increment = () => setCount(count() + 1);

  return (
    <button type="button" onClick={increment}>
      {count()}
    </button>
  );
}

render(() => <Counter />, document.getElementById("app"));
אתם יכולים לפתוח אותו ב Playground בקישור https://bit.ly/3tlqP5r. עכשיו לקוד: 1. הפונקציה createSignal מחליפה את useState. היא מחזירה מערך של "ערך" ו"פונקציית עדכון". עד פה אין הפתעות. 2. הבדל גדול ראשון מריאקט שאנחנו כבר רואים הוא שה"ערך" הוא בעצם פונקציה. בצורה כזאת אנחנו לא צריכים את החתימה הכפולה של ה setter ותמיד משתמשים בפונקציית ה getter כדי לקבל את הערך העדכני. זאת השורה:
const increment = () => setCount(count() + 1);
מעניין גם לראות איך סוליד מתרגם את קוד הקומפוננטה לקוד JavaScript רגיל. בניגוד ל React שפשוט הופך כל אלמנט jsx לקריאה לפונקציה createElement, סוליד מפצל את הקוד. זה התוכן שמופיע בטאב output של מגרש המשחקים:
import { template, render, createComponent, delegateEvents, insert } from 'solid-js/web';
import { createSignal } from 'solid-js';

const _tmpl$ = template(`<button type="button"></button>`, 2);

function Counter() {
  const [count, setCount] = createSignal(0);

  const increment = () => setCount(count() + 1);

  return (() => {
    const _el$ = _tmpl$.cloneNode(true);

    _el$.$$click = increment;

    insert(_el$, count);

    return _el$;
  })();
}

render(() => createComponent(Counter, {}), document.getElementById("app"));

delegateEvents(["click"]);
אנחנו רואים שסוליד הפריד בין החלק "הקבוע" ב DOM - זה מה שעובר לפונקציה template שלהם, לבין החלק המשתנה שזה מה שעובר לפונקציה insert. הפיצול הזה הוא שמאפשר לסוליד לעדכן רק את החלקים ב DOM שהשתנו בלי שיצטרך להשוות בין עצי DOM וירטואליים. ## חמש תיבות טקסט מסונכרנות הדוגמה השניה של סוליד כבר מתחילה להפתיע. הקוד הבא מציג 5 תיבות טקסט מסונכרנות, כלומר שינוי הטקסט באחת התיבות יעדכן את כל ה-5:
import { render } from "solid-js/web";
import { createSignal, For } from "solid-js";
const range = (n: number) => (new Array(n)).fill(null).map((_, i) => i);

const [text, setText] = createSignal("");

function Text() {
  function handleInput(e: InputEvent) {
    const input = e.target as HTMLInputElement;
    setText(input.value)
  }

  return (
    <input type="text" value={text()} onInput={handleInput} />
  );
}

function App() {
  const n = 5;

  return (
    <div>
    <p>n={n}</p>
    <For each={range(n)}>
      {(item, index) => <Text />}
    </For>
    </div>
  )
}

render(() => <App />, document.getElementById("app"));
הפתעה ראשונה שאנחנו רואים בקוד היא שהוצאתי את createSignal החוצה מהקומפוננטות והפכתי את התוצאה שלו למשתנה גלובאלי. את זה אי אפשר היה לעשות בריאקט - כי useState של ריאקט חייב להיכתב בתוך קומפוננטה. הדבר הכי קרוב בריאקט למה שכתבתי כאן יהיה קונטקסט, אבל רוב מתכנתי ריאקט פשוט יגדירו את ה State בקומפוננטה App ויעבירו אותו בתור Property לילדים. חדי העין ביניכם יכולים לראות גם ששם האירוע של השינוי השתנה מ change ל input כדי ליישר קו עם קוד DOM סטנדרטי:
<input type="text" value={text()} onInput={handleInput} />
ואולי השינוי הכי גדול מריאקט זה הלולאה:
<For each={range(n)}>
  {(item, index) => <Text />}
</For>

ToCode
1 419
# טיפ דוקר - ניקוי volumes שאינם בשימוש את הפקודה המופלאה הבאה גיליתי רק לא מזמן, והיא כבר הצליחה לפנות לי 30 ג'יגה בדיסק בלי להתאמץ. היא נקראת:
$ docker volume prune
בעבודה עם דוקר ו docker compose נוצרים כל הזמן Volumes, אבל אותם Volumes לא נמחקים בעבודה השוטפת (אלא אם כן מחקתם אותם בצורה יזומה). במיוחד מעצבנים ה Anonymous Volumes, כלומר Volumes עם שם אקראי שנוצרים מהוראת Volume ב Dockerfile של אימג'ים מסוימים, ואתם אפילו לא יודעים שיצרתם אותם. הפקודה prune מוחקת במכה אחת את כל ה Volumes שאינם בשימוש, כלומר Volumes שאף קונטיינר לא משתמש בהם. הנה הפלט שהופיע אצלי באחת ההפעלות:
WARNING! This will remove all local volumes not used by at least one container.
Are you sure you want to continue? [y/N] y
Deleted Volumes:
af3aae34cdad2d27ce238052ad76066f5007a994afd8e43e630ed1e065e1adc2
1bd5b35c4b045efcedab000946b4b0cc47a31c6ab9797aa7c15abe68a187dd24
215e9ed82a1248713a37856d91bef06322a0aeba29983bb3bda73c6d3474975f
81800e9125a43c536733d540776576c459857fa161aad9a022089ce9f29f4b39
b86e5619f32e50c1750d06463f93f1222b0a1cb654961b03eb05f693602036d5
21053b79f98090cef2ed0d2e370aab1c87902712554fe106fcdf7776ad49e432
36da000251fd03a84d7d7aad5d1387b88067993d4db37f93be101ee6b39a8fa0
7f46b0540d958befaaa9eba7f546f58ac345d43aca76d78c82fd6fa0f2310a9d
0503e2f1595109e47fe34520f50d0cae62bf61829b110329b1736dead696a36a
38487902f058dd998a018d694c6a0415df30f1764caccd06aaefed7bcdc7f4f9
9262a4d3d727b2ed8ec6d1eb53f9d12666492a31b7b156bdbf25c2cc4f9e72ba
85acc9837d945b8da346e0a3005a41f25f11bbe6dba3254acf0b050ea760f99b
f84ef10a22b6c39e4b500fff3dd2ab41a47d9c3c16521c72f51accf3ee9ae733
25b3e355acae3df348d705e90b932b2152e42c74a2e522352244b7a461a7854b
c887268872c39fff371c05f59989d35e0ed2662c784fde35c65a31622ef3cb90
3a69d7d9f5e96c566f39aedc8b3fc6c07d8bea3beebd47c3c624cc5e4dae593b
491272fb1d91144b92ad990edb92cf8d15a7ebfbf26d7b8d4bbc2c3a077c2166
b75c33d193925f9cbd059e863eb9e44e0612fd5614716eac7de4cbc5847dfbd8

Total reclaimed space: 2.519GB
בגלל שאני משתדל להיות מסודר ולמחוק Volumes אחרי שאני יוצר אותם, אפשר לראות שרוב ה Volumes ש prune מחק היו בכלל אנונימיים. זה לא הפריע להם לתפוס 2.5 ג'יגה של מקום על הלפטופ שלי - מקום שעכשיו אני יכול לשמור בו דברים חשובים יותר כמו תמונות של חתולים.

ToCode - آمار و تحلیل کانال تلگرام @tocodeil