تطوير InnerLoop باستخدام Python

1. نظرة عامة

يوضح هذا التمرين المعملي الميزات والقدرات المصممة لتبسيط سير عمل التطوير لمهندسي البرمجيات المكلفين بتطوير تطبيقات بايثون في بيئة مدمجة. ويتطلّب تطوير الحاوية النموذجي من المستخدم فهم تفاصيل الحاويات وعملية إنشاء الحاوية. بالإضافة إلى ذلك، على المطوّرين إيقاف مسار العمل والخروج من بيئة التطوير المتكاملة (IDE) لاختبار تطبيقاتهم وتصحيح الأخطاء فيها في البيئات البعيدة. باستخدام الأدوات والتقنيات المذكورة في هذا البرنامج التعليمي، يستطيع المطورون العمل بفعالية مع التطبيقات المحوّلة بدون مغادرة بيئة التطوير المتكاملة (IDE).

ما سوف تتعلمه

ستتعلم في هذا التمرين المعملي طُرق التطوير باستخدام حاويات في Google Cloud Platform، بما في ذلك:

  • إنشاء تطبيق Python للمبتدئين جديد
  • التعرّف على عملية التطوير
  • تطوير خدمة استراحة CRUD بسيطة

2. الإعداد والمتطلبات

إعداد بيئة ذاتية

  1. سجِّل الدخول إلى Google Cloud Console وأنشئ مشروعًا جديدًا أو أعِد استخدام مشروع حالي. إذا لم يكن لديك حساب على Gmail أو Google Workspace، عليك إنشاء حساب.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • اسم المشروع هو الاسم المعروض للمشاركين في هذا المشروع. وهي عبارة عن سلسلة أحرف لا تستخدمها Google APIs، ويمكنك تحديثها في أي وقت.
  • يجب أن يكون رقم تعريف المشروع فريدًا في جميع مشاريع Google Cloud وغير قابل للتغيير (لا يمكن تغييره بعد ضبطه). تنشئ Cloud Console سلسلة فريدة تلقائيًا. فعادةً لا تهتم بما هو. في معظم الدروس التطبيقية حول الترميز، يجب الرجوع إلى رقم تعريف المشروع (والذي يتم تحديده عادةً على أنّه PROJECT_ID). لذلك، إذا لم يعجبك، يمكنك إنشاء رقم تعريف عشوائي آخر أو يمكنك تجربة رقم تعريف المشروع الخاص بك ومعرفة ما إذا كان متاحًا. بعد ذلك تكون الحالة "مجمّدة". بعد إنشاء المشروع.
  • هناك قيمة ثالثة، وهي رقم المشروع الذي تستخدمه بعض واجهات برمجة التطبيقات. اطّلِع على مزيد من المعلومات حول هذه القيم الثلاث في المستندات.
  1. بعد ذلك، عليك تفعيل الفوترة في Cloud Console لاستخدام الموارد/واجهات برمجة التطبيقات في Cloud. إنّ تنفيذ هذا الدرس التطبيقي حول الترميز لن يكون مكلفًا أو مكلفًا على الإطلاق. لإيقاف تشغيل الموارد حتى لا تتحمل الفوترة بعد أكثر من هذا البرنامج التعليمي، اتبع أي عملية "تنظيف". التعليمات الموجودة في نهاية الدرس التطبيقي حول الترميز. يكون مستخدمو Google Cloud الجدد مؤهَّلون للانضمام إلى برنامج فترة تجريبية مجانية بقيمة 300 دولار أمريكي.

بدء محرِّر Cloudshell

تم تصميم هذا الدرس التطبيقي واختباره للاستخدام مع Google Cloud Shell Editor. للوصول إلى المحرر،

  1. انتقِل إلى مشروع Google على https://console.cloud.google.com.
  2. في أعلى يسار الشاشة، انقر على رمز محرِّر Cloud Shell

8560cc8d45e8c112.png

  1. سيتم فتح جزء جديد في أسفل النافذة.
  2. انقر على الزر Open Editor (فتح المحرر).

9e504cb98a6a8005.png

  1. سيتم فتح المحرِّر مع ظهور مستكشف على اليمين ومحرِّر في المنطقة الوسطى
  2. ستظهر أيضًا لوحة طرفية في أسفل الشاشة.
  3. إذا لم تكن الوحدة الطرفية مفتوحة، يمكنك استخدام مجموعة المفاتيح "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)')

الحصول على رمز المصدر

  1. يمكنك العثور على رمز المصدر لهذا الدرس في ورشة عمل خاصة بمطوّري الحاويات على 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+ " لفتح نافذة طرفية جديدة.

توفير البنية الأساسية المستخدمة في هذا التمرين المعملي

سوف تنشر في هذا التمرين المعملي الرموز البرمجية في GKE وستصل إلى البيانات المخزنة في قاعدة بيانات Spanner. يعمل النص البرمجي للإعداد أدناه على إعداد هذه البنية الأساسية من أجلك. ستستغرق عملية توفير المتطلبات اللازمة أكثر من 10 دقائق. يمكنك مواصلة تنفيذ الخطوات القليلة التالية أثناء معالجة عملية الإعداد.

../setup.sh

3- إنشاء تطبيق Python للمبتدئين جديد

  1. أنشِئ ملفًا باسم "requirements.txt" وانسخ المحتوى التالي إليه.
Flask
gunicorn
google-cloud-spanner
ptvsd==4.3.2
  1. أنشئ ملفًا باسم "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')

  1. أنشئ ملفًا باسم 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 flask. يسمح لك ملف Docker هذا بتمرير هذه القيمة كوسيطة إنشاء.

إنشاء ملفات بيانات

في الوحدة الطرفية، نفِّذ الأمر التالي لإنشاء skaffold.yaml وpublish.yaml افتراضي.

  1. قم بتهيئة Skaffold باستخدام الأمر التالي
skaffold init --generate-manifests

استخدِم الأسهم لتحريك المؤشر عندما يُطلب منك ذلك واستخدِم مفتاح المسافة لتحديد الخيارات.

اختيار:

  • 8080 للمنفذ
  • y لحفظ الإعدادات

تعديل إعدادات Skaffold

  • تغيير اسم التطبيق التلقائي
  • فتح "skaffold.yaml"
  • اختَر اسم الصورة المحدَّد حاليًا على أنّه dockerfile-image.
  • انقر بزر الماوس الأيمن واختر "تغيير جميع مواضع الورود"
  • اكتب الاسم الجديد كـ python-app
  • عدِّل قسم الإصدار أيضًا من أجل
  • إضافة docker.buildArgs لتخطّي FLASK_DEBUG=1
  • مزامنة الإعدادات لتحميل أي تغييرات على ملفات *.py من بيئة تطوير البرامج (IDE) إلى الحاوية قيد التشغيل

بعد إجراء التعديلات، سيظهر قسم الإصدار في ملف skaffold.yaml على النحو التالي:

build:
 artifacts:
 - image: python-app
   docker:
     buildArgs:
       FLASK_DEBUG: 1
     dockerfile: Dockerfile
   sync:
     infer:
     - '**/*.py'

تعديل ملف إعداد Kubernetes

  1. تغيير الاسم التلقائي
  • فتح ملف deployment.yaml
  • اختَر اسم الصورة المحدَّد حاليًا على أنّه dockerfile-image.
  • انقر بزر الماوس الأيمن واختر "تغيير جميع مواضع الورود"
  • اكتب الاسم الجديد كـ python-app

4. التعرّف على عملية التطوير

وبعد إضافة منطق الأنشطة التجارية، يمكنك الآن نشر تطبيقك واختباره. سيوضح القسم التالي استخدام المكوّن الإضافي لتطبيق Cloud Code. إلى جانب أمور أخرى، يتكامل هذا المكون الإضافي مع skaffold لتبسيط عملية التطوير. عند النشر على GKE باتّباع الخطوات التالية، ستنشئ خدمة Cloud Code وSkaffold صورة الحاوية تلقائيًا وترسلها إلى Container Registry ثم تنشر تطبيقك على GKE. يحدث هذا وراء الكواليس في استخلاص التفاصيل بعيدًا عن تدفق المطور.

النشر على Kubernetes

  1. في الجزء السفلي من "محرِّر Cloud Shell"، اختَر Cloud Code ⁠

fdc797a769040839.png

  1. في اللوحة التي تظهر في أعلى الصفحة، اختَر التشغيل على Kubernetes. إذا طُلب منك ذلك، اختَر "نعم" لاستخدام سياق Kubernetes الحالي.

cfce0d11ef307087.png

يبدأ هذا الأمر إصدارًا من رمز المصدر، ثم يُجري الاختبارات. سيستغرق تنفيذ عملية الإنشاء والاختبار بضع دقائق. تتضمن هذه الاختبارات اختبارات الوحدة وخطوة التحقق التي تتحقق من القواعد التي تم ضبطها لبيئة النشر. سبق أن تم ضبط خطوة التحقّق هذه وتضمن تلقّي تحذير بشأن مشاكل النشر حتى إذا كنت لا تزال تعمل في بيئة التطوير.

  1. عند تشغيل الأمر للمرة الأولى، سيظهر موجه في أعلى الشاشة يسألك عما إذا كنت تريد سياق kubernetes الحالي، حدد "نعم" قبول السياق الحالي واستخدامه.
  2. بعد ذلك، سيتم عرض طلب يسأل عن قاعدة بيانات المسجّلين في الحاوية المطلوب استخدامها. اضغط على مفتاح Enter لقبول القيمة التلقائية المقدَّمة.
  3. اختَر علامة التبويب "الإخراج" في الجزء السفلي لعرض مستوى التقدّم والإشعارات.

f95b620569ba96c5.png

  1. اختَر "Kubernetes: تشغيل/تصحيح الأخطاء - مفصّل" في القائمة المنسدلة للقناة على اليسار لعرض تفاصيل إضافية وسجلات البث المباشر من الحاويات.

94acdcdda6d2108.png

عند الانتهاء من عمليات الإنشاء والاختبارات، تعرض علامة التبويب "الإخراج": Attached debugger to container "python-app-8476f4bbc-h6dsl" successfully.، وعنوان URL http://localhost:8080 مدرج.

  1. في الوحدة الطرفية لرمز السحابة الإلكترونية، مرِّر مؤشر الماوس فوق عنوان URL الأول في الناتج (http://localhost:8080)، ثم في تلميح الأداة التي تظهر، اختَر "فتح معاينة الويب".
  2. سيتم فتح علامة تبويب جديدة في المتصفح وستعرض الرسالة Hello, World!

إعادة التحميل السريع

  1. فتح ملف app.py
  2. تغيير رسالة الترحيب إلى 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
...
  1. في حال التبديل إلى طريقة العرض "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
  1. يُرجى إعادة تحميل صفحة المتصفّح للاطّلاع على النتائج المعدّلة.

تصحيح الأخطاء

  1. الانتقال إلى عرض تصحيح الأخطاء وإيقاف سلسلة المحادثات الحالية 647213126d7a4c7b.png
  2. انقر على Cloud Code في القائمة في أسفل الشاشة واختَر Debug on Kubernetes لتشغيل التطبيق في وضع "debug".
  • في عرض Kubernetes Run/Debug - Detailed لنافذة Output، لاحظ أن skaffold سينشر هذا التطبيق في وضع تصحيح الأخطاء.
  1. في المرة الأولى التي يتم فيها تشغيل هذا الطلب، سيتم طرح سؤال عن مكان المصدر داخل الحاوية. ترتبط هذه القيمة بالأدلة في ملف Docker.

اضغط على Enter لقبول الخيار التلقائي

583436647752e410.png

ستستغرق عملية إنشاء التطبيق ونشره بضع دقائق.

  1. عند اكتمال العملية. ستلاحظ إرفاق برنامج تصحيح الأخطاء.
Port forwarding pod/python-app-8bd64cf8b-cskfl in namespace default, remote port 5678 -> http://127.0.0.1:5678
  1. يغيِّر شريط الحالة السفلي لونه من الأزرق إلى البرتقالي للإشارة إلى أنّه في وضع تصحيح الأخطاء.
  2. في طريقة العرض "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
***********************************

استخدام نقاط الإيقاف

  1. فتح ملف app.py
  2. حدِّد العبارة التي نصّها return message.
  3. أضف نقطة توقف إلى هذا السطر بالنقر على المساحة الفارغة على يمين رقم السطر. سيظهر مؤشر أحمر للإشارة إلى ضبط نقطة الإيقاف.
  4. أعد تحميل المتصفح ولاحظ أن برنامج تصحيح الأخطاء يوقف العملية عند نقطة التوقف ويسمح لك بالتحقيق في متغيرات التطبيق وحالته التي تعمل عن بُعد في GKE.
  5. انقر للأسفل في قسم VARIABLES.
  6. انقر على "السكان المحلية" وستجد المتغيّر "message".
  7. انقر مرّتين على اسم المتغيّر "message" وفي النافذة المنبثقة، غيِّر القيمة إلى قيمة مختلفة، مثل "Greetings from Python".
  8. انقر على الزر "متابعة" في لوحة تحكم تصحيح الأخطاء 607c33934f8d6b39.png.
  9. يمكنك مراجعة الردّ في متصفّحك الذي يعرض الآن القيمة المعدّلة التي أدخلتها للتو.
  10. إيقاف "تصحيح الأخطاء" بالضغط على زر الإيقاف 647213126d7a4c7b.png وإزالة نقطة الإيقاف من خلال النقر على نقطة الإيقاف مرة أخرى.

5- تطوير خدمة CRUD استراحة بسيطة

في هذه المرحلة، تم ضبط تطبيقك بالكامل للتطوير المجمّع، وقد اطّلعت على خطوات سير عمل التطوير الأساسي باستخدام Cloud Code. تتدرب في الأقسام التالية على ما تعلمته عن طريق إضافة نقاط نهاية خدمة غير مكتملة متصلة بقاعدة بيانات مُدارة في Google Cloud.

ترميز خدمة الباقي

ينشئ الكود أدناه خدمة راحة بسيطة تستخدم Spanner كقاعدة بيانات تدعم التطبيق. أنشئ التطبيق عن طريق نسخ الرمز التالي إلى تطبيقك.

  1. إنشاء التطبيق الرئيسي عن طريق استبدال 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 بأمان، عليك ضبط التطبيق على استخدام هويات تحميل العمل. ويتيح ذلك لتطبيقك العمل كحساب خدمة خاص به والحصول على أذونات فردية عند الوصول إلى قاعدة البيانات.

  1. يتوفّر تحديث لجهاز deployment.yaml أضف التعليمة البرمجية التالية في نهاية الملف (تأكد من إبقاء المسافات البادئة لعلامة التبويب في المثال أدناه)
      serviceAccountName: python-ksa
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true" 

نشر التطبيق والتحقّق من صحته

  1. في الجزء السفلي من "محرِّر Cloud Shell"، اختَر Cloud Code ثم اختَر Debug on Kubernetes في أعلى الشاشة.
  2. عند الانتهاء من عملية الإنشاء والاختبارات، تعرض علامة التبويب "الإخراج": Resource deployment/python-app status completed successfully، ويظهر عنوان URL كما يلي: "عنوان URL تمت إعادة توجيهه من خدمة python-app: http://localhost:8080"
  3. أضف بعض الإدخالات.

من cloudshell Terminal، شغِّل الأمر أدناه

curl -X POST http://localhost:8080/singer -H 'Content-Type: application/json' -d '{"first_name":"Cat","last_name":"Meow", "singer_id": 6}'
  1. اختبار رمز GET من خلال تنفيذ الأمر أدناه في الوحدة الطرفية
curl -X GET http://localhost:8080/singer?singer_id=6
  1. اختبار الحذف: حاول الآن حذف إدخال عن طريق تشغيل الأمر التالي. غيِّر قيمة معرّف السلعة إذا لزم الأمر.
curl -X DELETE http://localhost:8080/singer?singer_id=6
    This throws an error message
500 Internal Server Error

تحديد المشكلة وحلّها

  1. وضع تصحيح الأخطاء والعثور على المشكلة. إليك بعض النصائح:
  • نعلم أنّ هناك خطأ في أمر DELETE لأنّه لا يؤدي إلى عرض النتيجة المطلوبة. لذلك، يمكنك ضبط نقطة الإيقاف في app.py بالطريقة delete_singer.
  • يمكنك تنفيذ عملية التنفيذ خطوة بخطوة ومراقبة المتغيّرات في كل خطوة لملاحظة قيم المتغيّرات المحلية في النافذة اليمنى.
  • للاطّلاع على قيم معيّنة مثل singer_id وrequest.args في عملية إضافة هذه المتغيّرات إلى نافذة المشاهدة.
  1. لاحِظ أنّ القيمة المخصّصة للدالة singer_id هي None. غيِّر الرمز لحلّ المشكلة.

سيبدو مقتطف الرمز الثابت على النحو التالي.

@app.route('/delete-singer', methods=['DELETE', 'GET'])
def delete_singer():
    try:
        singer_id = request.args.get('singer_id')
  1. بعد إعادة تشغيل التطبيق، اختبره مرة أخرى من خلال محاولة الحذف.
  2. أوقِف جلسة تصحيح الأخطاء بالنقر على المربّع الأحمر في شريط أدوات تصحيح الأخطاء 647213126d7a4c7b.png.

6- تنظيف

تهانينا! لقد أنشأت في هذا التمرين المعملي تطبيقًا جديدًا بلغة بايثون من البداية وأعددته للعمل بفعالية مع الحاويات. ثم نشرت تطبيقك وصحّحته على مجموعة GKE بعيدة وفقًا لمسار المطوّرين نفسه المتوفّر في حِزم التطبيقات التقليدية.

لتنظيف البيانات بعد الانتهاء من التمرين المعملي:

  1. حذف الملفات المستخدمة في التمرين المعملي
cd ~ && rm -rf container-developer-workshop
  1. حذف المشروع لإزالة جميع البنية التحتية والموارد ذات الصلة