משימות מתוזמנות ללא שרת ב-AWS: EventBridge, Lambda, ניטור

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

פיתוח ותחזוקה של כל סוגי האתרים:

אתרי מידע או יישומי אינטרנט
אתרי תדמית, דפי נחיתה, אתרי חברה, קטלוגים מקוונים, חידונים, אתרי קידום, בלוגים, מקורות חדשות, פורטלי מידע, פורומים, אגרגטורים
אתרי מסחר אלקטרוני או יישומי אינטרנט
חנויות מקוונות, פורטלי B2B, שווקים, בורסות מקוונות, אתרי קאשבק, בורסות, פלטפורמות דרופשיפינג, מנתחי מוצרים
יישומי אינטרנט לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, פורטלים ארגוניים, מערכות ניהול ייצור, מנתחי מידע
אתרי שירות אלקטרוני או יישומי אינטרנט
פלטפורמות מודעות, בתי ספר מקוונים, בתי קולנוע מקוונים, בוני אתרים, פורטלים לשירותים אלקטרוניים, פלטפורמות אירוח וידאו, פורטלים נושאיים

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

השירותים שאנו מציעים
מציג 1 מתוך 1כל 2062 השירותים
משימות מתוזמנות ללא שרת ב-AWS: EventBridge, Lambda, ניטור
בינוני
מ- 1 יום עד 3 ימים
שאלות נפוצות

משימות מתוזמנות ב-Serverless: היכן נשברות משימות רגילות

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

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

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

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

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

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

מה נותן הפעלה מבוקרת של משימות לפי לוח זמנים

צפיות בהפעלות במקום זמן "צף"

לוח הזמנים ב-EventBridge מופעל לפי ביטוי cron בדיוק של דקה, ולא "כשהשרת יתעורר". עבור משימות שגרתיות — ייצוא, התאמות, תפוצות, חישוב מחדש של אגרגטים — זה מסיר סוג שלם של בעיות: ההפעלה לא נדחקת בגלל אתחול המכונה, הצפת התור או תהליך קודם שנתקע.

אם משימה נמשכת יותר מהחלון, ההפעלה הבאה לא נערמת: מופעים מקבילים נחסמים ברמת הכלל. זמן ההתחלה מפסיק להיות עניין של ניחושים — הוא זהה גם בימי חול וגם בעומס שיא.

בדיקות ידניות עוברות לניטור

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

זמן התגובה לתקלה — דקות, לא יממה

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

מה זה נותן בסך הכל

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

דרכי הפעלה: מתזמן, תור, טריגר ידני

משימות תקופתיות בסביבת serverless מופעלות בשלוש דרכים, והבחירה ביניהן נקבעת לא לפי נוחות אלא לפי אופי העומס והדרישות לחזרתיות. מתזמן (ב-AWS זה EventBridge Scheduler) מפעיל פונקציה לפי ביטוי cron או rate: הוא מתאים למשימות בזמן קבוע — דוחות לילה, סנכרון מזהים, חימום מטמון.

תור משימות (SQS + Lambda) מפעיל עיבוד לפי אירוע: הודעה מופיעה — הפונקציה מתחילה, וכך עד התרוקנות התור. טריגר ידני — הפעלה דרך API או קונסולה לפעולות חד-פעמיות: אינדוקס מחדש, מיגרציה, חישוב מחדש.

אפשרות מתי מתאימה מגבלות
מתזמן הפעלות משימות תקופתיות לפי לוח זמנים: דוחות, סנכרון, בדיקות שגרתיות מרווח מינימלי — דקה אחת; cron ב-UTC; בעת פרץ הפעלות מקבילות נדרש בקרת כפילויות
תור משימות זרם של פעולות זהות, ניסיונות חוזרים, פריקת עומסי שיא אין תלות בזמן; הסדר אינו מובטח ללא FIFO; נדרשת אידמפוטנטיות של המטפל
טריגר ידני פעולות חד-פעמיות, דיבוג, הפעלה מחדש בחירום אין אוטומציה ובקרת לוח זמנים; נדרשים גישה והגנה מפני הפעלה מקרית

מגבלות לוח הזמנים כדאי לקחת בחשבון מראש: ביטויי cron מחושבים ב-UTC, לכן מעבר לשעון קיץ מזיז את ההפעלה, וצעד מינימלי של דקה אחת אינו מתאים למשימות בשניות.

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

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

איך אנחנו מטמיעים משימות מתוזמנות: שלבי העבודה

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

  1. ביקורת משימות. אוספים את כל מה שרץ כעת לפי לוח זמנים: מתזמנים מערכתיים, סקריפטים על שרתים, הרצות ידניות "לפי קריאה". עבור כל אחת מתעדים את התדירות, משך הריצה, התלות בתהליכים אחרים וההשלכות של כשל — אילו משימות קריטיות ואילו ניתן להריץ שוב בלי נזק.
  2. תרשים הרצות. ממיינים את המשימות לפי סוגים: קצרות ותכופות, ארוכות, תלויות במערכות חיצוניות. קובעים היכן נדרשת הרצה לפי לוח שנה, היכן — לפי אירוע, והיכן — שרשרת עם העברת תוצאה. בתרשים הזה מיד נראים החפיפים שבגללם משימות מתחרות על אותם נתונים.
  3. הגדרת לוח זמנים. מעבירים את הכללים לשירות תזמון מנוהל: אזורי זמן, חלונות הרצה, ניסיונות חוזרים בעת כשל, הגבלת עותקים מקבילים של אותה משימה. לוח הזמנים נשמר כקונפיגורציה, ולא כידע של מהנדס אחד.
  4. לוגיקה והרשאות. כל משימה מקבלת תפקיד משלה עם הרשאות מינימליות — רק הנתונים והפעולות שהיא צריכה. סודות וגישות מועברים לאחסון מאובטח, ולא לקוד.
  5. בדיקת עמידות בכשלים. מריצים תרחישים: אי-זמינות של מערכת חיצונית, הרצה חוזרת, חריגה מזמן הריצה, כשל באמצע השרשרת. בודקים שהמשימה לא הולכת לאיבוד, לא משכפלת פעולות ומדווחת כראוי על שגיאה.
  6. ניטור והתראות. מגדירים בקרה על עצם ההרצה, משך הריצה והתוצאה. התראות מגיעות על החמצת הרצה ועל שגיאות חוזרות, ולא על כל דבר קטן — אחרת האות הולך לאיבוד ברעש.
  7. מסירה לתפעול. מוסרים תיעוד: מפת משימות, לוח זמנים, נוהל הפעלה מחדש ידנית וחזרה לאחור. עורכים סקירה עם הצוות שלכם, כדי שהתמיכה לא תהיה תלויה בנו.

תארו אילו משימות יש לכם כעת — נחזור עם הערכה ותוכנית להעברתן לתזמון מנוהל.

מה הלקוח מקבל לידיו לאחר ההטמעה

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

  • תצורות משימות כקוד — הגדרות לוחות זמנים, מטפלים והפרמטרים שלהם נמצאים במאגר, ולא נבנים ידנית בקונסולת האינטרנט. שינוי עובר דרך סקירה, וחזרה לאחור — בהחזרת הגרסה הקודמת כרגיל.
  • סכמות הרצה — טבלה של "משימה → תדירות → אזור זמן → מה קורה בחפיפת הרצות → לאן הולך התוצאה". ניתן לראות אילו משימות תלויות זו בזו והיכן חלון התחזוקה.
  • מדיניות גישה — תפקידים עם ההרשאות המינימליות הנדרשות: מה מותר למשימה מסוימת, מי מהמהנדסים יכול לשנות את לוח הזמנים, כיצד מוענקת ומבוטלת גישה. בנפרד אנו מתעדים שמשימה אינה יכולה לחרוג מגבולות סמכויותיה.
  • טיפול בכשלים — ניסיונות חוזרים עם השהיה גדלה, "תור מת" להרצות חסרות תקווה, התנהגות בעת אי-זמינות של שירות חיצוני. מתואר כמה פעמים מנסים ומתי מוותרים.
  • ניטור והתראות — מדדים של הרצות מוצלחות וכושלות, השהיית התחלה, התראות לערוץ שלכם. מוגדר כך שנודע על בעיה לפני שהעסק מבחין בה.
  • נוהל תפעול — מה לעשות בעת כשל, כיצד להשבית משימה זמנית, כיצד להוסיף חדשה, למי להתקשר. התרחישים מפורטים שלב אחר שלב, בלי הסתמכות על אדם מסוים.
  • הוראות לצוות — מדריכים קצרים לפעולות שגרתיות והעברת ידע: סקירה בשיחת וידאו עם הקלטה, כדי שמהנדס חדש ייכנס למעגל בלעדינו.

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

מקרה בוחן: כיצד דוחות תקופתיים הפסיקו להיכשל

הלקוח — חברת שירותים עם דיווח חודשי לצוותים פנימיים ולשותפים. הדוחות נאספו על ידי סקריפט cron על שרת אחד: הוא סקר את מערכת החיוב, המחסן וה-CRM, ריכז את הנתונים והפיץ את הקבצים.

כל עוד הנפחים היו קטנים, הסכמה עבדה, אך עם גידול מספר המקורות ההרצה החלה להיכשל — פעם בגלל timeout בייצוא, פעם בגלל אתחול השרת בדיוק ברגע היצירה. שיעור ההרצות המוצלחות עמד על כ-70%, והכשל בדרך כלל הבחינו בו כבר הנמענים: הדוח לא הגיע או הגיע חלקי.

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

שגיאות כבר לא "תלויות" בלוגים של השרת — הן מגיעות לתור הודעות שלא נמסרו, ולפיו רואים מיד איזה מקור ובאיזה שלב נכשל.

מה השתנה במספרים

שיעור ההרצות המוצלחות עלה מכ-70% ל-99.6%: הניסיון החוזר והפרדת השלבים הסירו את הסיבה העיקרית לכשלים — נפילה במקור אחד כשהשאר פעילים. זמן ההתאוששות לאחר כשל הצטמצם משעות רבות של ניתוח ידני לדקות: המשימה מופעלת מחדש אוטומטית, ולמהנדס נותר לבדוק את תור ההודעות שלא נמסרו.

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

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

במה שונה הפעלה לפי לוח זמנים מהפעלה לפי אירוע?

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

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

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

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

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

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

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

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

בפועל מערכות כמעט תמיד היברידיות, והשאלה אינה "מה לבחור", אלא "איזה חלק מהעבודה לקשור ללוח השנה, ואיזה — לשינוי בנתונים".

ספקות טכניים נפוצים לגבי משימות מתוזמנות ב-serverless

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

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

המשימה הופעלה פעמיים — זה תקין או תקלה?

ל-serverless scheduled tasks אין הבטחה מוחלטת ל"הרצה אחת בדיוק": בעת משלוח חוזר של האירוע, המטפל עשוי לקבל אותו בפעם השנייה. לכן הלוגיקה נבנית כאידמפוטנטית — הרצה חוזרת לא אמורה לשכפל חיוב, הודעה או רשומה.

בפועל: מפתח אידמפוטנטיות לפי מזהה האירוע, בדיקת "כבר טופל" לפני הפעולה, ותיעוד נפרד של הרצות שהושלמו. אז הרצה חוזרת היא בלתי מזיקה, ולא תקרית.

מה יקרה אם המשימה מתבצעת יותר מהזמן שהוקצב?

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

איך לדעת שהמשימה לא בוצעה?

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

האם אפשר להעביר cron קיים בלי לכתוב מחדש את הלוגיקה?

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

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

נדון במשימה שלכם ונחזור עם תוכנית

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

בשיחה הראשונה נבהיר:

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

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

תארו את המשימה בצורה הנוחה לכם — נחזור עם הערכה ותוכנית.

הכישורים שלנו:

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1393
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1368
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1139
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1152
  • פיתוח אתר עבור SBH Partners
    פיתוח אתר עבור SBH Partners
    1231
  • פיתוח אתר עבור Red Pear
    פיתוח אתר עבור Red Pear
    641