אופטימיזציה של אפליקציות Python ל-Cloud Run

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

רבות מהשיטות המומלצות והאופטימיזציות באפליקציות נפוצות מבוססות-אינטרנט של Python מתמקדות ב:

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

אופטימיזציה של קובץ אימג' של קונטיינר

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

  • צמצום הקבצים שנטענים בהפעלה
  • אופטימיזציה של שרת WSGI

צמצום הקבצים שנטענים בהפעלה

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

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

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

  • ייבא רק את תת-המודולים הנדרשים מכל תלויות כבדות, או ייבא מודולים כאשר נדרש בקוד שלך, במקום לטעון אותם בעת הפעלת היישום.

אופטימיזציה של שרת WSGI

שפת Python קובעת תקן לאופן שבו אפליקציות יכולות ליצור אינטראקציה עם שרתי אינטרנט באמצעות הטמעה של תקן WSGI‏, PEP-3333. אחד משרתי WSGI הנפוצים יותר הוא gunicorn, שמשמש בחלק גדול מהתיעוד לדוגמה.

אופטימיזציה של gunicorn

הוסף את ה-CMD הבא ל-Dockerfile כדי לייעל את הקריאה של gunicorn:

CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 --timeout 0 main:app

אם אתם שוקלים לשנות הגדרות אלו, התאימו את מספר העובדים והשרשורים לכל יישום בנפרד. לדוגמה, כדאי לנסות להשתמש במספר עובדים ששווה למספר הליבות הזמינות, ולוודא שיש שיפור בביצועים, ואז לשנות את מספר השרשורים. הגדרת יותר מדי workers או הליכים עלולה להיות בעלת השפעה שלילית, כגון השהיית הפעלה במצב התחלתי (cold start) ארוכה יותר, צריכת זיכרון גדולה יותר, בקשות קטנות יותר לשנייה וכו'.

כברירת מחדל, gunicorn יוצר תהליכי עבודה ומאזין ליציאה שצוינה בזמן ההפעלה, עוד לפני שהוא מעריך את קוד האפליקציה. במקרה כזה, כדאי להגדיר בדיקות הפעלה בהתאמה אישית לשירות, כי בדיקת ההפעלה שמוגדרת כברירת מחדל ב-Cloud Run מסמנת באופן מיידי את מופע הקונטיינר כפעיל ברגע שהוא מתחיל להאזין ב-$PORT.

אם ברצונך לשנות התנהגות זו, תוכל להפעיל את gunicorn עם ההגדרה --preload כדי להעריך את קוד האפליקציה שלך לפני ההאזנה. זה יכול לעזור ל:

  • זיהוי באגים חמורים בזמן הריצה בזמן הפריסה
  • שמירת משאבי הזיכרון

עליך לשקול מה האפליקציה שלך טוענת מראש לפני הוספתה.

שרתי WSGI אחרים

אתם לא חייבים להשתמש ב-gunicorn כדי להריץ Python בקונטיינרים. ניתן להשתמש בכל שרת אינטרנט מסוג WSGI או ASGI, כל עוד המכולה מאזינה ליציאת HTTP $PORT, בהתאם לחוזה זמן הריצה של המכולה.

בין החלופות הנפוצות אפשר למצוא את uwsgi, את uvicorn ואת waitress.

לדוגמה, אם יש קובץ בשם main.py שמכיל את האובייקט app, הפעלת הפקודות הבאות תתחיל שרת WSGI:

# uwsgi: pip install pyuwsgi
uwsgi --http :$PORT -s /tmp/app.sock --manage-script-name --mount /app=main:app

# uvicorn: pip install uvicorn
uvicorn --port $PORT --host 0.0.0.0 main:app

# waitress: pip install waitress
waitress-serve --port $PORT main:app

אפשר להוסיף אותם כCMD exec שורה ב-Dockerfile או כweb: רשומה ב-Procfile כשמשתמשים ב-buildpacks של Google Cloud.

אופטימיזציה של יישומים

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

הפחתת מספר השרשורים

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

אם רוצים לתמוך בפעילויות ברקע בשירות Cloud Run, צריך להגדיר את שירות Cloud Run לחיוב לפי מופע כדי להריץ פעילויות ברקע מחוץ לבקשות ועדיין לקבל גישה למעבד.

הפחתת משימות ההפעלה

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

Cloud Run שולח בקשת משתמש אמיתית להפעלת מופע הפעלה במצב התחלתי (cold start). יכול להיות שיהיו עיכובים ארוכים למשתמשים שהבקשה שלהם הוקצתה למופע שהופעל לאחרונה.

שיפור האבטחה באמצעות תמונות בסיס דקות

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

אם אתם בוחרים לא להתקין Python ממקור בתוך הקונטיינרים, אתם יכולים להשתמש בתמונת בסיס רשמית של Python מ-Docker Hub. התמונות האלה מבוססות על מערכת ההפעלה Debian.

אם אתם משתמשים בתמונה python מ-Docker Hub, כדאי להשתמש בגרסה slim. התמונות האלה קטנות יותר כי הן לא כוללות מספר חבילות שישמשו ליצירת גלגלים, שאולי לא תצטרכו לעשות עבור האפליקציה שלכם. תמונת python מגיעה עם מהדר GNU C, מעבד מקדים וכלי ליבה.

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

DOCKER_IMAGE=python # or python:slim
docker run --rm ${DOCKER_IMAGE} dpkg-query -Wf '${Installed-Size}\t${Package}\t${Description}\n' | sort -n | tail -n10 | column -t -s $'\t'

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

אפשר להוסיף חבילות ספציפיות בחזרה על ידי הוספת שורה RUN apt install ל-Dockerfile. מידע נוסף זמין במאמר בנושא שימוש בחבילות מערכת ב-Cloud Run.

יש גם אפשרויות לקונטיינרים שלא מבוססים על Debian. python:alpine האפשרות הזו עשויה להקטין משמעותית את נפח הקונטיינר, אבל יכול להיות שחבילות Python רבות לא יכללו קבצים מסוג wheel שעברו קומפילציה מראש ותומכים במערכות מבוססות Alpine. התמיכה משתפרת (ראו PEP-656), אבל היא עדיין משתנה. אפשרות נוספת היא להשתמש ב-distroless base image, שלא מכיל מנהלי חבילות, מעטפות או תוכניות אחרות.

השתמש במשתנה סביבתי PYTHONUNBUFFERED לצורך רישום

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

המאמרים הבאים

טיפים נוספים