1. סקירה כללית
שיעור ה-Lab הזה להדגים תכונות ויכולות שנועדו לייעל את תהליך הפיתוח עבור מהנדסי תוכנה שתפקידם לפתח אפליקציות Python בסביבה בקונטיינרים. פיתוח אופייני של קונטיינרים מחייב את המשתמשים להבין את הפרטים של קונטיינרים ואת תהליך הפיתוח של הקונטיינרים. בנוסף, בדרך כלל מפתחים צריכים לפרוץ את התהליך ולצאת מסביבת הפיתוח המשולבת (IDE) שלהם כדי לבדוק את האפליקציות שלהם ולנפות באגים בסביבות מרוחקות. בעזרת הכלים והטכנולוגיות שמוזכרים במדריך הזה, מפתחים יכולים לעבוד ביעילות עם אפליקציות בקונטיינרים בלי לצאת מסביבת הפיתוח המשולבת (IDE).
מה תלמדו
בשיעור ה-Lab הזה תלמדו שיטות לפיתוח עם קונטיינרים ב-GCP, כולל:
- יצירת אפליקציה חדשה של Python לתחילת פעולה
- תסביר לי על תהליך הפיתוח
- פיתוח שירות מנוחה פשוט מסוג CRUD
2. הגדרה ודרישות
הגדרת סביבה בקצב אישי
- נכנסים למסוף Google Cloud ויוצרים פרויקט חדש או עושים שימוש חוזר בפרויקט קיים. אם אין לכם עדיין חשבון Gmail או חשבון Google Workspace, עליכם ליצור חשבון.
- Project name הוא השם המוצג של המשתתפים בפרויקט. זו מחרוזת תווים שלא נעשה בה שימוש ב-Google APIs, ואפשר לעדכן אותה בכל שלב.
- Project ID חייב להיות ייחודי בכל הפרויקטים ב-Google Cloud ואי אפשר לשנות אותו (אי אפשר לשנות אותו אחרי שמגדירים אותו). מסוף Cloud יוצר מחרוזת ייחודית באופן אוטומטי; בדרך כלל לא מעניין אותך מה זה. ברוב ה-Codelabs תצטרכו להפנות אל מזהה הפרויקט (ובדרך כלל הוא מזוהה כ-
PROJECT_ID
), כך שאם הוא לא מוצא חן בעיניכם, תוכלו ליצור פרויקט אקראי אחר או לנסות בעצמכם ולבדוק אם הוא זמין. ואז המכשיר 'קפוא' לאחר יצירת הפרויקט. - יש ערך שלישי, Project Number, שחלק מממשקי ה-API משתמשים בו. מידע נוסף על כל שלושת הערכים האלה זמין במסמכי התיעוד.
- בשלב הבא צריך להפעיל את החיוב במסוף Cloud כדי להשתמש במשאבים או בממשקי API של Cloud. מעבר ב-Codelab הזה לא אמור לעלות הרבה, אם בכלל. כדי להשבית את המשאבים ולא לצבור חיובים מעבר למדריך הזה, פועלים לפי ההנחיות למחיקת המשאבים. בסוף ה-Codelab. משתמשים חדשים ב-Google Cloud זכאים להצטרף לתוכנית תקופת ניסיון בחינם בשווי 1,200 ש"ח.
הפעלת Cloudshell Editor
שיעור ה-Lab הזה תוכנן ונבדק לשימוש עם Google Cloud Shell Editor. כדי לגשת לכלי העריכה:
- ניגשים לפרויקט Google בכתובת https://console.cloud.google.com.
- בפינה השמאלית העליונה, לוחצים על סמל העורך של Cloud Shell
- חלונית חדשה תיפתח בחלק התחתון של החלון
- לוחצים על הלחצן 'פתיחת העורך'.
- העורך ייפתח בצד שמאל עם כלי חקירה משמאל ועורך באזור המרכזי.
- חלונית טרמינל אמורה להיות זמינה גם בחלק התחתון של המסך
- אם הטרמינל לא פתוח, משתמשים בשילוב המקשים של 'Ctrl+' כדי לפתוח חלון טרמינל חדש
הגדרת סביבה
ב-Cloud Shell, מגדירים את מזהה הפרויקט ואת מספר הפרויקט. שומרים אותם כמשתנים מסוג PROJECT_ID
ו-PROJECT_ID
.
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
--format='value(projectNumber)')
קבלת קוד המקור
- קוד המקור של שיעור ה-Lab הזה נמצא בסדנה למפתחים של קונטיינרים ב-GoogleCloudPlatform ב-GitHub. משכפלים באמצעות הפקודה שלמטה ומשנים את שם הספרייה.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git &&
cd container-developer-workshop/labs/python
mkdir music-service && cd music-service
cloudshell workspace .
אם הטרמינל לא פתוח, משתמשים בשילוב המקשים של 'Ctrl+' כדי לפתוח חלון טרמינל חדש
הקצאת התשתית שבה נעשה שימוש בשיעור ה-Lab הזה
בשיעור ה-Lab הזה תפרסו קוד ל-GKE ותגשו לנתונים שמאוחסנים במסד הנתונים של Spanner. סקריפט ההגדרה שבהמשך מכין את התשתית הזו עבורכם. תהליך ההקצאה יימשך יותר מ-10 דקות. אפשר להמשיך בשלבים הבאים בזמן תהליך ההגדרה.
../setup.sh
3. יצירת אפליקציה חדשה של Python לתחילת פעולה
- יוצרים קובץ בשם
requirements.txt
ומעתיקים אליו את התוכן הבא
Flask
gunicorn
google-cloud-spanner
ptvsd==4.3.2
- יוצרים קובץ בשם
app.py
ומדביקים בו את הקוד הבא
import os
from flask import Flask, request, jsonify
from google.cloud import spanner
app = Flask(__name__)
@app.route("/")
def hello_world():
message="Hello, World!"
return message
if __name__ == '__main__':
server_port = os.environ.get('PORT', '8080')
app.run(debug=False, port=server_port, host='0.0.0.0')
- יוצרים קובץ בשם Dockerfile ומדביקים בו את הקוד הבא
FROM python:3.8
ARG FLASK_DEBUG=0
ENV FLASK_DEBUG=$FLASK_DEBUG
ENV FLASK_APP=app.py
WORKDIR /app
COPY requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
COPY . .
ENTRYPOINT ["python3", "-m", "flask", "run", "--port=8080", "--host=0.0.0.0"]
הערה: בעזרת FLASK_DEBUG=1 אפשר לטעון מחדש באופן אוטומטי שינויי קוד באפליקציית בקבוקון Python. קובץ ה-Docker הזה מאפשר להעביר את הערך הזה כארגומנט build.
יצירת מניפסטים
בטרמינל, מריצים את הפקודה הבאה כדי ליצור ברירת מחדל של skaffold.yaml ו-Deploy.yaml
- אתחול Skaffold באמצעות הפקודה הבאה
skaffold init --generate-manifests
כשמוצגת בקשה לעשות זאת, משתמשים בחיצים כדי להזיז את הסמן ובמקש הרווח כדי לבחור את האפשרויות.
בוחרים אפשרות:
8080
לשקעy
כדי לשמור את ההגדרה
עדכון ההגדרות של Skaffold
- שינוי שם האפליקציה שמוגדר כברירת מחדל
- פתיחה של
skaffold.yaml
- בחירת שם התמונה שמוגדר כרגע בתור
dockerfile-image
- לוחצים לחיצה ימנית ובוחרים באפשרות 'שינוי כל המופעים'
- יש להקליד את השם החדש בתור
python-app
- ממשיכים לערוך את קטע ה-build כדי
- הוספה של
docker.buildArgs
כדי לעבור אתFLASK_DEBUG=1
- צריך לסנכרן את ההגדרות כדי לטעון את השינויים בקובצי
*.py
מסביבת פיתוח משולבת (IDE) למאגר התגים שפועל
אחרי העריכות, קטע ה-build בקובץ skaffold.yaml
יהיה:
build:
artifacts:
- image: python-app
docker:
buildArgs:
FLASK_DEBUG: 1
dockerfile: Dockerfile
sync:
infer:
- '**/*.py'
שינוי קובץ התצורה של Kubernetes
- שינוי שם ברירת המחדל
- פתיחת קובץ אחד (
deployment.yaml
) - בחירת שם התמונה שמוגדר כרגע בתור
dockerfile-image
- לוחצים לחיצה ימנית ובוחרים באפשרות 'שינוי כל המופעים'
- יש להקליד את השם החדש בתור
python-app
4. להדרכה על תהליך הפיתוח
אחרי שמוסיפים את הלוגיקה העסקית, אפשר לפרוס את האפליקציה ולבדוק אותה. בקטע הבא נדגיש את השימוש בפלאגין של Cloud Code. בין השאר, הפלאגין הזה משתלב עם skaffold כדי לפשט את תהליך הפיתוח. כשפורסים ב-GKE התהליך הזה מתרחש מאחורי הקלעים ומסיר את הפרטים מהתהליך למפתחים.
פריסה ב-Kubernetes
- בחלונית שבחלק התחתון של Cloud Shell Editor, בוחרים באפשרות Cloud Code כוללת
- בחלונית שמופיעה בחלק העליון, בוחרים באפשרות הפעלה ב-Kubernetes. אם מוצגת הנחיה, בוחרים באפשרות 'כן' כדי להשתמש בהקשר הנוכחי של Kubernetes.
הפקודה הזו מפעילה build של קוד המקור ולאחר מכן מריצה את הבדיקות. ה-build והבדיקות יימשכו כמה דקות. הבדיקות האלה כוללות בדיקות יחידה ושלב אימות שבודק את הכללים שהוגדרו לסביבת הפריסה. שלב האימות הזה כבר מוגדר, והוא מבטיח שתקבלו אזהרה לגבי בעיות בפריסה גם כשאתם עובדים בסביבת הפיתוח.
- בפעם הראשונה שמריצים את הפקודה, תופיע בחלק העליון של המסך הנחיה לשאול אם אתם רוצים את ההקשר הנוכחי של ה-kubernetes, צריך לבחור באפשרות 'כן'. לקבל את ההקשר הנוכחי ולהשתמש בו.
- בשלב הבא תוצג הודעה שמבקשת באיזה מרשם להשתמש. צריך להקיש על Enter כדי לאשר את ערך ברירת המחדל שצוין
- צריך לבחור בכרטיסייה 'פלט' בחלונית התחתונה כדי לראות את ההתקדמות וההתראות
- בוחרים באפשרות Kubernetes: Run/Debug - Detailed (Kubernetes: הרצה/ניפוי באגים - מפורט). בתפריט הנפתח של הערוץ משמאל כדי להציג פרטים נוספים ויומנים בסטרימינג בשידור חי ממאגרי התגים
בסיום ה-build והבדיקות, תופיע בכרטיסייה 'פלט' ההודעה: Attached debugger to container "python-app-8476f4bbc-h6dsl" successfully.
, וכתובת ה-URL http://localhost:8080 מופיעה.
- בטרמינל של Cloud Code, מעבירים את העכבר מעל כתובת ה-URL הראשונה בפלט (http://localhost:8080) ואז בטיפ הכלי שמופיע, בוחרים באפשרות Open Web Preview.
- תיפתח כרטיסייה חדשה בדפדפן שבה תוצג ההודעה
Hello, World!
טעינה מחדש מתוך הזיכרון (hot Reload)
- פתיחת הקובץ
app.py
- שינוי של הודעת הפתיחה ל
Hello from Python
שימו לב שבחלון Output
, בתצוגה Kubernetes: Run/Debug
, המעקב מסנכרן את הקבצים המעודכנים עם הקונטיינר ב-Kubernetes.
Update initiated Build started for artifact python-app Build completed for artifact python-app Deploy started Deploy completed Status check started Resource pod/python-app-6f646ffcbb-tn7qd status updated to In Progress Resource deployment/python-app status updated to In Progress Resource deployment/python-app status completed successfully Status check succeeded ...
- אם עוברים לתצוגה
Kubernetes: Run/Debug - Detailed
, רואים שהיא מזהה שינויים בקבצים, ואז בונה ופורס את האפליקציה מחדש
files modified: [app.py]
Syncing 1 files for gcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Copying files:map[app.py:[/app/app.py]]togcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Watching for changes...
[python-app] * Detected change in '/app/app.py', reloading
[python-app] * Restarting with stat
[python-app] * Debugger is active!
[python-app] * Debugger PIN: 744-729-662
- מרעננים את הדפדפן כדי לראות את התוצאות המעודכנות.
ניפוי באגים
- עוברים לתצוגת ניפוי הבאגים ומפסיקים את השרשור הנוכחי
.
- לוחצים על
Cloud Code
בתפריט התחתון ובוחרים באפשרותDebug on Kubernetes
כדי להריץ את האפליקציה במצבdebug
.
- בתצוגה
Kubernetes Run/Debug - Detailed
של החלוןOutput
, שימו לב ש-skaffold יפרוס את האפליקציה הזו במצב ניפוי באגים.
- בפעם הראשונה שתפעילו את הקובץ, תוצג שאלה איפה המקור נמצא בתוך הקונטיינר. הערך הזה קשור לספריות בקובץ Docker.
צריך להקיש על Enter כדי לאשר את ברירת המחדל
תהליך הפיתוח והפריסה של האפליקציה נמשך כמה דקות.
- בסיום התהליך. המערכת תצרף כלי לניפוי באגים.
Port forwarding pod/python-app-8bd64cf8b-cskfl in namespace default, remote port 5678 -> http://127.0.0.1:5678
- הצבע של שורת הסטטוס התחתונה משתנה מכחול לכתום, כדי לציין שהיא במצב ניפוי באגים.
- בתצוגה
Kubernetes Run/Debug
, ניתן לראות שהופעל מאגר תגים שניתן לניפוי באגים
**************URLs***************** Forwarded URL from service python-app: http://localhost:8080 Debuggable container started pod/python-app-8bd64cf8b-cskfl:python-app (default) Update succeeded ***********************************
שימוש בנקודות עצירה (breakpoint)
- פתיחת הקובץ
app.py
- יש למצוא את ההצהרה שבה כתוב
return message
- כדי להוסיף נקודת עצירה לשורה הזו, לוחצים על הרווח הריק שמימין למספר השורה. יוצג אינדיקטור אדום כדי לציין שנקודת העצירה הוגדרה
- טוענים מחדש את הדפדפן ומציינים שהכלי לניפוי באגים עוצר את התהליך בנקודת העצירה (breakpoint) ומאפשר לכם לחקור את המשתנים והמצב של האפליקציה שרצה מרחוק ב-GKE
- לוחצים למטה לקטע 'משתנים'
- לוחצים על 'מקומיים' כדי להציג את המשתנה
"message"
. - לוחצים לחיצה כפולה על שם המשתנה 'message'. בחלון הקופץ, משנים את הערך למשהו אחר, כמו
"Greetings from Python"
- לוחצים על הלחצן 'המשך' בלוח הבקרה של ניפוי הבאגים
.
- בודקים את התשובה בדפדפן, שמציגה עכשיו את הערך המעודכן שהזנתם.
- איך מפסיקים את 'ניפוי הבאגים' במצב 'הפסקה', לוחצים שוב על לחצן העצירה
ומסירים את נקודת העצירה בלחיצה חוזרת על נקודת העצירה.
5. פיתוח שירות CRUD מסוג מנוחה פשוט
בשלב הזה האפליקציה מוגדרת במלואה לפיתוח בקונטיינרים, והסברתם על תהליך הפיתוח הבסיסי באמצעות Cloud Code. בחלקים הבאים תתרגלו את מה שלמדתם על ידי הוספת נקודות קצה של שירותי מנוחה שמתחברות למסד נתונים מנוהל ב-Google Cloud.
יצירת קוד לשירות השאר
הקוד שבהמשך יוצר שירות מנוחה פשוט שמשתמש ב-Spanner בתור מסד הנתונים שמגבה את האפליקציה. כדי ליצור את האפליקציה, מעתיקים את הקוד הבא לאפליקציה.
- כדי ליצור את האפליקציה הראשית, צריך להחליף את
app.py
בתוכן הבא
import os
from flask import Flask, request, jsonify
from google.cloud import spanner
app = Flask(__name__)
instance_id = "music-catalog"
database_id = "musicians"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
@app.route('/singer', methods=['POST'])
def create():
try:
request_json = request.get_json()
singer_id = request_json['singer_id']
first_name = request_json['first_name']
last_name = request_json['last_name']
def insert_singers(transaction):
row_ct = transaction.execute_update(
f"INSERT Singers (SingerId, FirstName, LastName) VALUES" \
f"({singer_id}, '{first_name}', '{last_name}')"
)
print("{} record(s) inserted.".format(row_ct))
database.run_in_transaction(insert_singers)
return {"Success": True}, 200
except Exception as e:
return e
@app.route('/singer', methods=['GET'])
def get_singer():
try:
singer_id = request.args.get('singer_id')
def get_singer():
first_name = ''
last_name = ''
with database.snapshot() as snapshot:
results = snapshot.execute_sql(
f"SELECT SingerId, FirstName, LastName FROM Singers " \
f"where SingerId = {singer_id}",
)
for row in results:
first_name = row[1]
last_name = row[2]
return (first_name,last_name )
first_name, last_name = get_singer()
return {"first_name": first_name, "last_name": last_name }, 200
except Exception as e:
return e
@app.route('/singer', methods=['PUT'])
def update_singer_first_name():
try:
singer_id = request.args.get('singer_id')
request_json = request.get_json()
first_name = request_json['first_name']
def update_singer(transaction):
row_ct = transaction.execute_update(
f"UPDATE Singers SET FirstName = '{first_name}' WHERE SingerId = {singer_id}"
)
print("{} record(s) updated.".format(row_ct))
database.run_in_transaction(update_singer)
return {"Success": True}, 200
except Exception as e:
return e
@app.route('/singer', methods=['DELETE'])
def delete_singer():
try:
singer_id = request.args.get('singer')
def delete_singer(transaction):
row_ct = transaction.execute_update(
f"DELETE FROM Singers WHERE SingerId = {singer_id}"
)
print("{} record(s) deleted.".format(row_ct))
database.run_in_transaction(delete_singer)
return {"Success": True}, 200
except Exception as e:
return e
port = int(os.environ.get('PORT', 8080))
if __name__ == '__main__':
app.run(threaded=True, host='0.0.0.0', port=port)
הוספת הגדרות של מסד נתונים
כדי להתחבר ל-Spanner באופן מאובטח, צריך להגדיר את האפליקציה כך שתשתמש ב-Workload Identities. כך האפליקציה יכולה לתפקד כחשבון שירות משלה ולקבל הרשאות נפרדות לגשת למסד הנתונים.
- יש לעדכן את
deployment.yaml
. מוסיפים את הקוד הבא בסוף הקובץ (חשוב לוודא שכניסות הטאבים מופיעות בדוגמה שלמטה)
serviceAccountName: python-ksa
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: "true"
פריסה ואימות של אפליקציה
- בחלונית שבחלק התחתון של Cloud Shell Editor, בוחרים באפשרות
Cloud Code
ואז בוחרים באפשרותDebug on Kubernetes
בחלק העליון של המסך. - בסיום ה-build והבדיקות, בכרטיסייה 'פלט' מופיע הכיתוב:
Resource deployment/python-app status completed successfully
, וכתובת ה-URL מופיעה: 'כתובת URL שהועברה משירות python-app: http://localhost:8080' - צריך להוסיף כמה ערכים.
מ-cloudshell Terminal, מריצים את הפקודה הבאה
curl -X POST http://localhost:8080/singer -H 'Content-Type: application/json' -d '{"first_name":"Cat","last_name":"Meow", "singer_id": 6}'
- כדי לבדוק את בקשת ה-GET מריצים את הפקודה הבאה בטרמינל
curl -X GET http://localhost:8080/singer?singer_id=6
- Test Delete: עכשיו אפשר לנסות למחוק רשומה על ידי הרצת הפקודה הבאה. אם צריך, משנים את הערך של מזהה הפריט.
curl -X DELETE http://localhost:8080/singer?singer_id=6
This throws an error message
500 Internal Server Error
זיהוי ותיקון הבעיה
- במצב ניפוי באגים ומוצאים את הבעיה. ריכזנו בשבילכם כמה טיפים:
- אנחנו יודעים שמשהו לא תקין ב-DELETE כי היא לא מחזירה את התוצאה הרצויה. לכן צריך להגדיר את נקודת העצירה (breakpoint) ב-
app.py
בשיטהdelete_singer
. - מריצים שלב אחרי שלב ועוקבים אחרי המשתנים בכל שלב כדי לתעד את הערכים של המשתנים המקומיים בחלון השמאלי.
- כדי להבחין בערכים ספציפיים כמו
singer_id
ו-request.args
, צריך להוסיף את המשתנים האלה לחלון הצפייה.
- חשוב לשים לב שהערך שהוקצה ל-
singer_id
הואNone
. כדי לפתור את הבעיה, משנים את הקוד.
קטע הקוד הקבוע ייראה כך.
@app.route('/delete-singer', methods=['DELETE', 'GET']) def delete_singer(): try: singer_id = request.args.get('singer_id')
- לאחר הפעלת האפליקציה מחדש, בודקים שוב על ידי ניסיון המחיקה.
- כדי להפסיק את פעילות ניפוי הבאגים, לוחצים על הריבוע האדום בסרגל הכלים של ניפוי הבאגים
6. הסרת המשאבים
מעולה! בשיעור ה-Lab הזה יצרתם מאפס אפליקציית Python חדשה והגדרתם אותה לפעול ביעילות עם קונטיינרים. לאחר מכן פרסתם את האפליקציה וביצעתם ניפוי באגים באשכול GKE מרוחק, בהתאם לתהליך הפיתוח של האפליקציה בסטאק אפליקציות מסורתי.
כדי לפנות מקום אחרי שמשלימים את שיעור ה-Lab:
- מחיקת הקבצים ששימשו בשיעור ה-Lab
cd ~ && rm -rf container-developer-workshop
- מחיקת הפרויקט כדי להסיר את כל התשתית והמשאבים הקשורים