במדריך הזה מוסבר איך לתכנן ולפרוס משימות גמישות ב-Cloud Run עם GPU, שפועלות עד 12 שעות.
למרות ש-Cloud Run תומך בזמנים קצובים לתפוגה של משימות מורחבות, הוא לא מציע הסכם רמת שירות (SLA) להפעלה ללא הפרעות. משימות שמופעלות באמצעות GPU לא עוברות מיגרציה פעילה, ולכן תחזוקה שבועית שגרתית תסיים אותן באמצע ההרצה. לכן, כל משימה ארוכה צפויה להיקטע לפחות פעם אחת. כדי לוודא שהמשימה תסתיים בהצלחה בלי לבזבז משאבי מחשוב, כדאי לתכנן את האפליקציה כך שתבצע נקודות ביקורת ותמשיך, במקום להסתמך על הפעלה אחת ללא הפרעה.
במדריך הזה מוסבר איך להטמיע נקודות ביקורת, לטפל באותות של פלטפורמת SIGTERM ולהגדיר את מספר הניסיונות החוזרים המקסימלי.
מטרות
במדריך הזה תלמדו:
- מכינים אפליקציית Python שמטמיעה נקודות בדיקה ומטפלת באותות
SIGTERM. - יוצרים קטגוריה של Cloud Storage כדי לשמור נקודות ביקורת.
- מפתחים ומפעילים משימה ב-Cloud Run עם GPU, זמן קצוב לתפוגה של 12 שעות ומדיניות מוגדרת לניסיונות חוזרים.
- מריצים את המשימה ומבינים איך לעקוב אחרי אירועי הפקעה ביומנים.
עלויות
במסמך הזה משתמשים ברכיבים הבאים של Google Cloud, והשימוש בהם כרוך בתשלום:
בעזרת מחשבון העלויות אתם יכולים להעריך את העלויות בהתאם לשימוש הצפוי.
לפני שמתחילים
-
התקינו את ה-CLI של Google Cloud.
-
הגדירו שה-CLI של gcloud ישתמש בזהות המאוחדת שלכם.
-
כדי לאתחל את ה-CLI של gcloud, הריצו את הפקודה הבאה:
gcloud init -
יוצרים או בוחרים פרויקט ב- Google Cloud .
התפקידים שנדרשים כדי לבחור או ליצור פרויקט
- בחירת פרויקט: כדי לבחור פרויקט לא נדרש תפקיד IAM ספציפי – אתם יכולים לבחור כל פרויקט שקיבלתם בו תפקיד.
-
יצירת פרויקט: כדי ליצור פרויקט, צריך את התפקיד "יצירת פרויקטים" (
roles/resourcemanager.projectCreator), שכולל את ההרשאהresourcemanager.projects.create. איך מקצים תפקידים?
-
יוצרים פרויקט ב- Google Cloud :
gcloud projects create PROJECT_ID
מחליפים את
PROJECT_IDבשם של הפרויקט ב- Google Cloud שאתם יוצרים. -
בוחרים את הפרויקט שיצרתם ב- Google Cloud :
gcloud config set project PROJECT_ID
מחליפים את
PROJECT_IDבשם הפרויקט ב- Google Cloud .
מפעילים את ממשקי ה-API של Cloud Run, Artifact Registry, Cloud Build ו-Cloud Storage, אם הם עדיין לא מופעלים:
התפקידים שנדרשים כדי להפעיל ממשקי API
כדי להפעיל ממשקי API, צריך את ההרשאה
serviceusage.services.enable. אם יצרתם את הפרויקט, סביר להניח שכבר יש לכם את ההרשאה הזו דרך התפקיד "בעלים" (roles/owner). אחרת, תוכלו לקבל את ההרשאה הזו דרך התפקיד "אדמין בממשק 'שימוש בשירות'" (roles/serviceusage.serviceUsageAdmin). כך מקצים תפקידים.gcloud services enable run.googleapis.com
cloudbuild.googleapis.com artifactregistry.googleapis.com storage.googleapis.com - מגדירים את האזור כמשתנה סביבה:
export REGION=REGION
- יוצרים חשבון שירות למשימה:
gcloud iam service-accounts create gpu-job-sa
- מוודאים שיש לכם [מכסה מספיקה ל-GPU](/run/docs/configuring/jobs/gpu#request-quota) (למשל Nvidia L4) באזור שבחרתם.
הכנת האפליקציה
כדי להפוך משימה ארוכה לעמידה, האפליקציה צריכה:
שומרים את ההתקדמות מדי פעם (נקודות בקרה) במיקום קבוע, כמו קטגוריה של Cloud Storage.
יש לכם בערך 10 שניות אחרי
SIGTERMלפני שהמשימה תסתיים. זה לא מספיק זמן כדי לכתוב נקודת ביקורת של מודל גדול. התייחסו ל-handler שלSIGTERMכאל ניקוי של מצב קטן בלבד, והסתמכו על נקודות ביקורת תקופתיות כהגנה העיקרית שלכם. בחרו את מרווח הזמן בין נקודות הבדיקה בהתאם לכמות העבודה שאתם יכולים להרשות לעצמכם לאבד, וזכרו שככל שיש יותר נקודות בדיקה, כך אתם משקיעים פחות זמן בעבודה מועילה. המטרה היא להגיע לאיזון שמתאים לתרחיש לדוגמה שלכם.שחזור המצב מנקודת השמירה האחרונה בזמן ההפעלה.
להאזין לאות
SIGTERM, ש-Cloud Run שולח 10 שניות לפני תהליך התחזוקה, ולנסות לשמור נקודת ביקורת אחרונה לפני היציאה.
הדוגמה הבאה ממחישה איך להטמיע את התכונות האלה.
יצירת קוד האפליקציה
יוצרים ספרייה לפרויקט:
mkdir gc-run-gpu-resilience cd gc-run-gpu-resilienceיוצרים קובץ בשם
task.pyומדביקים בו את הסקריפט הבא כדי לדמות תהליך אימון או עיבוד ברצף (batch processing) שתומך בנקודות בקרה ובטיפול באותות:import os import sys import time import signal # The checkpoint directory will be mounted to this path CHECKPOINT_DIR = "/checkpoints" # Cloud Run sets these variables automatically JOB_NAME = os.environ.get("CLOUD_RUN_JOB", "local-job") TASK_INDEX = os.environ.get("CLOUD_RUN_TASK_INDEX", "0") CHECKPOINT_FILE = os.path.join( CHECKPOINT_DIR, f"{JOB_NAME}-task-{TASK_INDEX}-checkpoint.txt") TOTAL_STEPS = 100 current_step = 0 is_exiting = False def save_checkpoint(step): print(f"Saving checkpoint at step {step} to {CHECKPOINT_FILE}...") try: # Write to a temp file and rename to ensure atomicity temp_file = CHECKPOINT_FILE + ".tmp" with open(temp_file, "w") as f: f.write(str(step)) os.rename(temp_file, CHECKPOINT_FILE) print("Checkpoint saved successfully.") except Exception as e: print(f"Failed to save checkpoint: {e}") def load_checkpoint(): print(f"Looking for a checkpoint at {CHECKPOINT_FILE}...") global current_step if os.path.exists(CHECKPOINT_FILE): try: with open(CHECKPOINT_FILE, "r") as f: current_step = int(f.read().strip()) print(f"Resuming from checkpointed step {current_step}.") except Exception as e: print(f"Failed to load checkpoint: {e}. Starting from scratch.") else: print("No checkpoint found. Starting from scratch.") def sigterm_handler(signum, frame): global is_exiting print("Received SIGTERM signal from platform. Initiating graceful shutdown...") is_exiting = True # Register the signal handler for SIGTERM signal.signal(signal.SIGTERM, sigterm_handler) # Load existing checkpoint if any to resume work load_checkpoint() # Simulate work loop while current_step < TOTAL_STEPS and not is_exiting: print(f"Processing step {current_step}/{TOTAL_STEPS}...") # Simulate heavy GPU computation time.sleep(1) current_step += 1 # Periodically save checkpoint (e.g., every 5 steps) if current_step % 5 == 0: save_checkpoint(current_step) if is_exiting: # Save final state before exiting due to preemption # Best-effort only: with ~10s before SIGKILL this will not complete for # large state. Periodic checkpointing (above) is what actually protects # you. save_checkpoint(current_step) # Note that save_checkpoint already writes-then-renames for atomicity, # which is good, but the reason it matters here is that a truncated # checkpoint from an interrupted final save must not clobber the last # good one. print("Graceful shutdown complete. Exiting with error code to trigger retry.") # Exit with a non-zero code to signal to Cloud Run that the task # was interrupted and needs to be retried from the checkpoint. sys.exit(143) # Note that this statement deliberately consumes of the configured retry # attempts. print("Task completed successfully.") sys.exit(0)יוצרים
Dockerfileכדי להכיל את האפליקציה:
בדוגמה הזו אפשר לראות הטמעה מפורטת ומציאותית יותר.
יצירת קטגוריה של Cloud Storage לנקודות בקרה
יוצרים קטגוריה של Cloud Storage כדי לאחסן את קובצי נקודות הבדיקה, ונותנים לחשבון השירות של המשימה הרשאת גישה לקטגוריה.
יוצרים את הקטגוריה:
gcloud storage buckets create gs://gpu-checkpoints-PROJECT_ID \ --location REGIONמקצים את התפקיד "אדמין של אובייקטים באחסון" לחשבון השירות של המשימה:
gcloud storage buckets add-iam-policy-binding gs://gpu-checkpoints-PROJECT_ID \ --member="serviceAccount:gpu-job-sa@PROJECT_ID." \ --role="roles/storage.objectAdmin"
פריסת המשימה
פורסים את המשימה ב-Cloud Run:
gcloud beta run jobs deploy gpu-resilient-job \
--source=. \
--region REGION \
--gpu 1 \
--gpu-type nvidia-l4 \
--no-gpu-zonal-redundancy \
--task-timeout 168h \
--max-retries 3 \
--service-account gpu-job-sa@PROJECT_ID. \
--add-volume=name=checkpoint-volume,type=cloud-storage,bucket=gpu-checkpoints-PROJECT_ID \
--add-volume-mount=volume=checkpoint-volume,mount-path=/checkpoints
שימו לב לדגלים הקריטיים הבאים:
-
--task-timeout 168h: מאפשר למשימה לפעול עד 12 שעות. -
--max-retries 3: למשימות GPU עם זמן קצוב לתפוגה של יותר משעה, מומלץ להגדיר את ברירת המחדל של 3 ניסיונות חוזרים. כדי למנוע כשלים שקטים במהלך תחזוקת הפלטפורמה, מומלץ לא להגדיר 0 ניסיונות חוזרים. לסיום, מומלץ להקצות בתקציב לפחות ניסיון חוזר אחד לכשל שקשור לתחזוקה, וגם ניסיונות חוזרים נוספים לכשלים באפליקציה במהלך תקופת הרצה של 12 שעות.
--add-volumeו---add-volume-mount: טעינת הקטגוריה של Cloud Storage אל/checkpointsכדי שהאפליקציה תוכל לשמור את המצב בין ניסיונות חוזרים של המשימה.
הפעלה ומעקב אחרי המשימה
מריצים את המשימה:
gcloud run jobs execute gpu-resilient-job --region REGIONהפקודה הזו מחזירה הודעה שדומה לזו:
Execution [gpu-resilient-job-abcde] has been successfully started. View details about this execution by running `gcloud run jobs executions describe gpu-resilient-job-abcde`. See logs for this execution at: https://console.cloud.google.com/logs/viewer?project=YOUR_PROJECT_ID&resource=cloud_run_job/job_name/my-job/location/my-region/execution_id/gpu-resilient-job-abcdeהמחרוזת
gpu-resilient-job-abcdeהיא המזהה הייחודי שנוצר אוטומטית לניסיון ההרצה הספציפי הזה. אתם יכולים להשתמש בו כדי לעקוב אחרי ההפעלה הספציפית הזו או לבטל אותה. הפלט כולל גם קישור ישיר לדף של Logs Explorer במסוף Google Cloud , שבו אפשר לראות את הפלט או השגיאות מהמשימות של ה-job.עוקבים אחרי היומנים. אם מתרחש אירוע תחזוקה, Cloud Run מתעד אזהרה ביומן. יופיעו האפשרויות הבאות:
- יומני האפליקציה:
Received SIGTERM signal from platform. Initiating graceful shutdown...ואחריוSaving checkpoint.... - יומן מערכת שמציין הפקעה:
Container instance preemption initiated by the platform due to underlying hardware maintenance.עם הסיבהINSTANCE_PREEMPTED. - Cloud Run מפעיל מחדש את המשימה באופן אוטומטי.
- יומני האפליקציה אחרי הפעלה מחדש:
Resuming from checkpointed step X..
- יומני האפליקציה:
הסרת המשאבים
כדי להימנע מחיובים נוספים בחשבון Google Cloud , מחקו את כל המשאבים שפרסתם במדריך למתחילים הזה.
מחיקת המאגר
ב-Cloud Run החיוב הוא רק על הזמן שבו המשימה פועלת. עם זאת, יכול להיות שעדיין תחויבו על אחסון קובץ האימג' של הקונטיינר ב-Artifact Registry. כדי למחוק מאגרים של Artifact Registry, פועלים לפי השלבים שמפורטים במאמר מחיקת מאגרים במסמכי התיעוד של Artifact Registry.
מחיקת המשרה
במשימות ב-Cloud Run, העלויות נצברות רק כשמשימה מתבצעת. כדי למחוק את המשימה ב-Cloud Run, מבצעים את אחת מהפעולות הבאות:
המסוף
כדי למחוק משימה:
נכנסים ל-Cloud Run במסוף Google Cloud :
ברשימת המשימות, מאתרים את המשימה שרוצים למחוק ולוחצים על תיבת הסימון שלה כדי לבחור אותה.
לוחצים על Delete. הפעולה הזו מסיימת את כל הרצות המשימות שמתבצעות ואת כל המכונות של הקונטיינרים שפועלות.
gcloud
כדי למחוק משימה, מריצים את הפקודה הבאה:
gcloud run jobs delete JOB_NAME
מחליפים את JOB_NAME בשם המשימה.
מחיקת פרויקט הבדיקה
מחיקת הפרויקט ב- Google Cloud מפסיקה את החיוב על כל המשאבים באותו פרויקט. כדי לשחרר את כל המשאבים של Google Cloud בפרויקט:
כדי למחוק פרויקט ב- Google Cloud :
gcloud projects delete PROJECT_ID
מוחקים את הקטגוריה של Cloud Storage:
gcloud storage buckets delete gs://gpu-checkpoints-PROJECT_ID