تحديث تكوين نقطة النهاية في الوقت الفعلي لـ Amazon Sagemaker AI (تغيير نوع المثيل أو عدد المثيلات أو متغيرات البيئة) يتسبب في فشل عمليات النشر عبر AWS CloudFormation أو AWS Cloud Development Kit (AWS CDK). تتطلب SageMaker AI منك إلغاء تسجيل الهدف القابل للتوسع قبل تحديث نقطة النهاية، ولكن عند إلغاء تسجيله، لا يستطيع CloudFormation تتبع موارد AWS Application Auto Scaling.
في هذه التدوينة، نوضح لك كيفية استخدام construct قابل لإعادة الاستخدام في CDK مع موارد مخصصة في AWS CloudFormation لتنظيم إلغاء تسجيل وتسجيل التوسع التلقائي تلقائيًا أثناء تحديثات نقطة النهاية. يمكنك تحديث تكوين نقطة النهاية ومعلمات التوسع التلقائي في عملية نشر واحدة دون تدخل يدوي.
لماذا تحدث هذه المشكلة
عندما تنشر نقطة نهاية SageMaker AI مع موارد التوسع التلقائي الأصلية في CloudFormation، ثم تغيّر تكوين نقطة النهاية وتعيد النشر، تظهر عدة أوضاع فشل. يعتمد الفشل المحدد على ما تغيّره وكيف تتعامل مع المشكلة.
تغيير تكوين نقطة النهاية مع بقاء التوسع التلقائي مسجلًا
يحاول CloudFormation تحديث نقطة النهاية بينما الهدف القابل للتوسع لا يزال مسجلًا. ترفض SageMaker AI التحديث مع الخطأ التالي:
The variant(s) "[AllTraffic]" must be deregistered as scalable targets with Application Auto Scaling before they can be removed or have their instance type updated.
تحجب SageMaker AI تحديث نقطة النهاية قبل أن يصل CloudFormation إلى موارد التوسع التلقائي.
إلغاء تسجيل التوسع التلقائي يدويًا، ثم تغيير تكوين نقطة النهاية فقط
إذا قمت بإلغاء تسجيل الهدف القابل للتوسع عبر AWS Command Line Interface (AWS CLI) وأعدت النشر مع تغيير تكوين نقطة النهاية فقط، تنجح عملية النشر. ومع ذلك، لا يسجل CloudFormation التوسع التلقائي رغم وجوده في قالب CloudFormation. نظرًا لأن معلمات التوسع التلقائي في قالب CloudFormation لم تتغير، لا يقوم CloudFormation بتسجيل الهدف القابل للتوسع أو إعادة تطبيق سياسة التوسع. تصبح نقطة النهاية تعمل الآن بدون توسع تلقائي، ولا يوجد أي خطأ يشير إلى حدوث خلل.
إلغاء تسجيل التوسع التلقائي يدويًا، ثم تغيير معلمات التوسع التلقائي
إذا قمت بإلغاء تسجيل الهدف القابل للتوسع عبر AWS CLI وأعدت النشر مع تغييرات في معلمات التوسع التلقائي، يحاول CloudFormation تحديث مورد ScalableTarget الذي لم يعد موجودًا:
Resource of type 'AWS::ApplicationAutoScaling::ScalableTarget' with identifier 'endpoint/<name>/variant/AllTraffic|sagemaker:variant:DesiredInstanceCount|sagemaker' was not found.
هذا الفشل ملحوظ لأن التراجع يفشل أيضًا (UPDATE_ROLLBACK_FAILED)، مما يترك المكدس في حالة معطلة تتطلب تدخلًا يدويًا في وحدة تحكم CloudFormation للتعافي.
نظرة عامة على الحل
يمكنك استخدام هذا construct في CDK لاستبدال الموارد الأصلية في CloudFormation AWS::ApplicationAutoScaling::ScalableTarget و AWS::ApplicationAutoScaling::ScalingPolicy بثلاثة موارد مخصصة. تقوم هذه الموارد المخصصة بإلغاء تسجيل وتسجيل التوسع التلقائي حول تحديث نقطة النهاية بترتيب تبعيات صريح، مفروض عبر استدعاءات node.add_dependency في CDK. التسلسل الكامل هو:
- إلغاء التسجيل (مورد مخصص): يزيل الهدف القابل للتوسع قبل بدء تحديث نقطة النهاية. في النشر الأول، لا يوجد هدف قابل للتوسع بعد، لذا يتعامل الـ handler مع الخطأ بسلاسة ويستمر.
- تحديث نقطة النهاية (تديره CDK): يحدّث CDK مورد نقطة النهاية بشكل طبيعي، بالتبديل إلى تكوين نقطة النهاية الجديد.
- تسجيل الهدف القابل للتوسع (مورد مخصص): ينتظر حتى تصل نقطة النهاية إلى الحالة
InService، ثم يسجل الهدف القابل للتوسع. - إرفاق سياسة التوسع (مورد مخصص): ينتظر حتى يصبح الهدف القابل للتوسع قابلاً للاستعلام، ثم يطبق سياسة target tracking.
الشكل 1: تنظيم الموارد المخصصة للتوسع التلقائي لنقطة نهاية SageMaker AI حول تحديث نقطة نهاية يديره CDK.
يستخدم تسجيل الهدف القابل للتوسع وإرفاق سياسة التوسع نمط الإكمال غير المتزامن الخاص بمزوّد CDK. لكل مورد مخصص on_event handler يعيد الاستجابة فورًا و is_complete handler. يعيد منتظِر المزوّد استدعاء الـ is_complete handler على فترات زمنية (كل 30 ثانية) حتى تنجح فحوصاته للجاهزية. الـ الخاص بالمزوّد total_timeout (ساعة واحدة أي 1 ساعة لـ Register Scalable Target، و15 دقيقة لـ Attach Scaling Policy) يحدد مدة الانتظار، لذا فهو غير محدود بمهلة انتهاء أي استدعاء Lambda واحد.
خاصية ConfigHash على كل مورد مخصص تجبر CloudFormation على إعادة استدعاء دوال Lambda كلما تغيّرت قيمة إعدادية. تُعرَّف إعدادات نقطة النهاية ومعاملات التوسع التلقائي معًا لأن ConfigHash وحيدًا يقود تنسيق التوسع التلقائي.
بينما يعرّف هذا الإنشاء (construct) على ثلاثة موارد مخصصة فقط، فإن المكدس المنشور يحتوي على أكثر من ثلاث دوال Lambda. كل مورد مخصص مدعوم بدالة Lambda ضمن CDK cr.Provider، ويقوم إطار العمل الخاص بالمزود بتوليد دوال Lambda المساعدة الخاصة به. يضيف المزود المتزامن (Deregister) دالة إطار عمل واحدة. ويضيف المزود غير المتزامن (Register Scalable Target و Attach Scaling Policy) ثلاث دوال (onEvent, isComplete، و onTimeout) بالإضافة إلى آلة حالة من AWS Step Functions تقود آلية الانتظار (waiter). ونتيجة لذلك، تحتوي الحزمة المنشورة على ثلاثة موارد مخصصة، وآلتي حالة من Step Functions، واثنتي عشرة دالة من Lambda. خمسة من هذه الموارد تحتوي على كود المعالج الخاص بالبناء نفسه (واحدة لـ Deregister، وزوج من on_event و is_complete لكل مورد غير متزامن)، بينما تولّد العناصر السبعة المتبقية بواسطة إطار عمل المزود. أنت لا تكتب أو تصون الدوال المولدة بواسطة إطار العمل، لكنها موجودة في الحزمة ومن المفيد أخذها في الاعتبار عند مراجعة الموارد المنشورة.
استعراض تفصيلي
في هذا الاستعراض التفصيلي، تقوم بنشر نقطة نهاية SageMaker AI مع التوسع التلقائي باستخدام حزمة المثال المقدمة، ثم تتحقق من أن تحديثات التكوين تعمل بشكل صحيح.
يتضمن الاستعراض التفصيلي الخطوات التالية:
- استنساخ المستودع وتثبيت التبعيات.
- تكوين نقطة النهاية وإعدادات التوسع التلقائي.
- النشر والتحقق.
- اختبار التحديث.
الكود المصدري متاح في مستودع GitHub المرتبط.
المتطلبات الأساسية
لهذا الاستعراض التفصيلي، تحتاج إلى ما يلي:
- حساب AWS لديه أذونات لـ Amazon SageMaker AI و AWS Lambda و AWS Identity and Access Management (IAM) و Application Auto Scaling و Amazon CloudWatch.
- AWS CDK v2 مثبت.
- Python 3.12 أو أحدث.
- uv لإدارة تبعيات Python.
- قطعة أثرية لنموذج SageMaker AI في Amazon Simple Storage Service (Amazon S3) وصورة حاوية في Amazon Elastic Container Registry (Amazon ECR)، أو حاوية خوارزمية مدمجة في SageMaker AI.
استنساخ المستودع وتثبيت التبعيات
- استنسخ المستودع:
git clone https://github.com/aws-samples/sample-sagemaker-endpoint-autoscaling.git cd sample-sagemaker-endpoint-autoscaling - ثبت التبعيات:
uv sync
تكوين نقطة النهاية وإعدادات التوسع التلقائي
يتم تعريف إعدادات نقطة النهاية والتوسع التلقائي معًا في config/config.yaml لأن عنصرًا واحدًا من config_hash يقود تنسيق التوسع التلقائي. أي تغيير في أي من القسمين يطلق التسلسل الكامل.
عدّل config/config.yaml بالقيم الخاصة بك:
endpoint:
endpoint_name: "my-model-endpoint"
image: "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-model:latest"
model_data_url: "s3://my-bucket/models/model.tar.gz"
instance_type: "ml.m5.large"
initial_instance_count: 1
environment: {}
autoscaling:
min_capacity: 1
max_capacity: 10
target_value: 100
scale_in_cooldown: 600
scale_out_cooldown: 120
تتطابق معلمات نقطة النهاية مع واجهات SageMaker AI CreateModel و CreateEndpointConfig APIs. وتتطابق معلمات التحجيم التلقائي مع واجهات Application Auto Scaling RegisterScalableTarget و PutScalingPolicy APIs.
النشر والتحقق
- قم بتهيئة بيئة AWS الخاصة بك إذا لم تكن قد فعلت ذلك بالفعل:
uv run cdk bootstrap - انشر الـ stack:
uv run cdk deploy InferenceStack
ينشئ CloudFormation نموذج SageMaker AI وتكوين نقطة النهاية ونقطة النهاية، ثم يشغّل الموارد المخصصة الثلاثة بالتسلسل لتسجيل التحجيم التلقائي.
- تحقق من أن النشر قد سجّل الهدف القابل للتحجيم:
aws application-autoscaling describe-scalable-targets --service-namespace sagemaker --resource-ids endpoint/my-model-endpoint/variant/AllTraffic - تحقق من أن النشر قد طبّق سياسة التحجيم:
aws application-autoscaling describe-scaling-policies --service-namespace sagemaker --resource-id endpoint/my-model-endpoint/variant/AllTraffic
اختبر تحديثًا
غيّر قيمة في config/config.yaml، على سبيل المثال، حدّث نوع المثيل من ml.m5.large إلى ml.m5.xlarge، ثم أعد النشر:
uv run cdk deploy InferenceStack
بعد اكتمال النشر، تحقق من أن التحجيم التلقائي لا يزال مسجلاً وأن لديه القيم المحدثة:
aws application-autoscaling describe-scalable-targets
--service-namespace sagemaker
--resource-ids endpoint/my-model-endpoint/variant/AllTraffic
الترافق (Integration) مع مجموعة تقنية قائمة
استخدم مثيل المكدس النموذجي إذا كنت تبدأ من الصفر أو تريد تنفيذًا مرجعيًا. استخدم البنية (construct) مباشرة إذا كان لديك بالفعل مكدس CDK يحتوي على موارد SageMaker AI.
للتكامل مع حزمة تقنية قائمة، انسخ sagemaker_autoscaling/constructs/ إلى دليل في مشروعك وأضف البنية إلى جانب موارد نقطة النهاية الخاصة بك:
import json
from sagemaker_autoscaling.constructs.sagemaker_autoscaling_construct import (
AutoscalingConfig,
SagemakerAutoscalingConstruct,
SagemakerAutoscalingProps,
)
# Both endpoint and auto scaling values must be in config_hash so that
# a change to either triggers the auto scaling orchestration.
config_hash = json.dumps({
"endpoint": {
"image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-model:latest",
"modelDataUrl": "s3://my-bucket/models/model.tar.gz",
"instanceType": "ml.m5.large",
"initialInstanceCount": 1,
},
"autoscaling": {
"minCapacity": 1,
"maxCapacity": 10,
"targetValue": 100,
"scaleInCooldown": 600,
"scaleOutCooldown": 120,
},
})
SagemakerAutoscalingConstruct(
self, "Autoscaling",
props=SagemakerAutoscalingProps(
endpoint=your_endpoint, # your CfnEndpoint
autoscaling=AutoscalingConfig(
min_capacity=1,
max_capacity=10,
target_value=100,
scale_in_cooldown=600,
scale_out_cooldown=120,
),
config_hash=config_hash,
),
)
كيف يعمل هذا البناء البرمجي
يستعرض هذا القسم التفاصيل الأساسية للتنفيذ الخاصة بـ SagemakerAutoscalingConstruct.
إلغاء التسجيل (Deregister)
يستدعي المورد المخصص Deregister deregister_scalable_target قبل تحديث الـ endpoint. وهو يعالج ObjectNotFoundException الحالة في النشرات الأولى حيث لا يوجد هدف قابل للتوسعة:
def handler(event, context):
resource_id = event["ResourceProperties"]["ResourceId"]
if event["RequestType"] in ("Create", "Update"):
try:
client.deregister_scalable_target(
ServiceNamespace="sagemaker",
ResourceId=resource_id,
ScalableDimension="sagemaker:variant:DesiredInstanceCount",
)
except client.exceptions.ObjectNotFoundException:
pass # No scalable target on first deploy
return {"PhysicalResourceId": f"deregister-{resource_id}"}
يتم إنشاء التبعية في CDK:
props.endpoint.node.add_dependency(deregister)
تسجيل الهدف القابل للتوسعة (Register Scalable Target)
على الرغم من أن CloudFormation يعتبر مورد الـ endpoint مكتملاً بمجرد قبول التحديث، إلا أنه لا يمكن تسجيل الهدف القابل للتوسعة إلا بعد وصول الـ endpoint إلى InService. وبدلاً من إبقاء استدعاء Lambda واحد محجوزاً في انتظار ذلك، يستخدم Register Scalable Target نمط الإكمال غير المتزامن الخاص بالمزوّد. يُعيد معالج on_event الاستجابة فوراً، ويعيد الـ waiter الخاص بالمزوّد استدعاء معالج is_complete كل query_interval حتى يصبح الـ endpoint InService. إن قيمة total_timeout الحد الزمني (1 ساعة) هو الذي يقيّد مدة الانتظار، وليس مهلة أي استدعاء Lambda واحد:
def handler(event, context):
props = event["ResourceProperties"]
resource_id = props["ResourceId"]
endpoint_name = resource_id.split("/")[1]
if event["RequestType"] == "Delete":
return {"IsComplete": True}
status = sm_client.describe_endpoint(EndpointName=endpoint_name)["EndpointStatus"]
if status == "Failed":
raise Exception("Endpoint in Failed state")
if status != "InService":
return {"IsComplete": False}
as_client.register_scalable_target(
ServiceNamespace="sagemaker",
ResourceId=resource_id,
ScalableDimension="sagemaker:variant:DesiredInstanceCount",
MinCapacity=int(props["MinCapacity"]),
MaxCapacity=int(props["MaxCapacity"]),
)
return {"IsComplete": True}
عند حذف المكدس (stack)، يقوم معالج on_event بإلغاء تسجيل الهدف القابل للتوسعة للتنظيف.
إرفاق سياسة التوسعة (Attach Scaling Policy)
توجد فترة قصيرة بعد عودة RegisterScalableTarget لا يكون فيها الهدف قابلاً للاستعلام بعد. يستخدم Attach Scaling Policy نفس نمط الإكمال غير المتزامن: يعيد معالج on_event الاستجابة فوراً، ولا يُبلغ معالج is_complete عن الاكتمال إلا بمجرد أن يصبح الهدف القابل للتوسعة قابلاً للاستعلام، وعندئذ يطبّق السياسة. إن قيمة total_timeout الخاصة بالمزوّد (15 دقيقة) هي التي تحدد مدة الانتظار:
def handler(event, context):
props = event["ResourceProperties"]
resource_id = props["ResourceId"]
if event["RequestType"] == "Delete":
return {"IsComplete": True}
resp = client.describe_scalable_targets(
ServiceNamespace="sagemaker",
ResourceIds=[resource_id],
ScalableDimension="sagemaker:variant:DesiredInstanceCount",
)
if not resp.get("ScalableTargets"):
return {"IsComplete": False}
client.put_scaling_policy(
PolicyName=props["PolicyName"],
ServiceNamespace="sagemaker",
ResourceId=resource_id,
ScalableDimension="sagemaker:variant:DesiredInstanceCount",
PolicyType="TargetTrackingScaling",
TargetTrackingScalingPolicyConfiguration=json.loads(props["ScalingPolicyConfig"]),
)
return {"IsComplete": True}
عند حذف المكدس (stack)، يقوم معالج on_event بحذف سياسة التوسعة للتنظيف.
التنظيف
لتجنّب تكبّد رسوم مستقبلية، احذف الموارد:
uv run cdk destroy InferenceStack
يؤدي هذا إلى إزالة الـ endpoint الخاص بـ SageMaker AI، وإعداد الـ endpoint، والنموذج، ووظائف Lambda، وأدوار IAM، وتنبيهات Amazon CloudWatch التي أنشأتها سياسة التوسعة المستندة إلى تتبع الهدف (target tracking)، كما يقوم بإلغاء تسجيل الهدف القابل للتوسعة.
الخاتمة
في هذه التدوينة، عرضنا لك كيفية استخدام بنية CDK قابلة لإعادة الاستخدام لإلغاء تسجيل التوسع التلقائي وتسجيله تلقائيًا لتنسيق تحديثات نقاط نهاية SageMaker AI. تستخدم البنية ثلاثة موارد مخصصة من CloudFormation مع ترتيب تبعيات صريح وخاصية ConfigHash للحفاظ على التوسع التلقائي سليمًا عبر عمليات النشر.
للبدء، استنسخ مستودع sample-sagemaker-endpoint-autoscaling واتبع تعليمات الإعداد. يتضمن المستودع مثالًا لمكدس يمكنك نشره مباشرة، أو يمكنك دمج البنية في مكدس CDK الحالي لديك.
لمزيد من المعلومات حول Application Auto Scaling لـ SageMaker AI، راجع التوسع التلقائي لنماذج Amazon SageMaker AI. لمزيد من المعلومات حول الموارد المخصصة في CloudFormation، راجع Custom resources في دليل مستخدم AWS CloudFormation.