发布于: Oct 30, 2022

利用云平台监管机器学习的过程需要注意到某些 Amazon SageMaker 资源(例如处理、训练、调优以及批量转换实例)具有显著的临时性特征,Amazon SageMaker 会自动启动实例并在作业完成后将其撤销。但是,也有一部分资源(例如构建计算资源或托管端点)拥有持久性特征,需要由用户控制何时停止/终止这部分资源。因此,我们需要明确了解如何识别闲置资源,并通过及时关停进一步实现成本优化。本节将向大家介绍实现此类流程的几种重要自动化方法。

避免闲置 notebook 实例产生非必要成本的一种重要方式,就是使用生命周期配置自动停止闲置实例。通过 Amazon SageMaker 中的生命周期配置选项,您可以在 notebook 实例上安装软件包或示例 notebook,借此实现 notebook 环境定制,包括配置网络与安全性,或者使用 shell 脚本完成其他自定义。由此带来的灵活性,将帮助大家更全面地控制 notebook 环境的设置与运行方式。

Amazon Web Services 维护着一套包含大量 notebook 生命周期配置脚本的公共 repo,这些脚本能够解决 notebook 实例自定义当中的各类常见用例,当然也包括用于停止闲置 notebook 的示例 bash 脚本。

您可以使用生命周期配置设定您的 notebook 实例,使其在经过指定的闲置状态之后(通过参数进行设置)自动停止。Jupyter notebook 的闲置状态定义方式请参阅此 GitHub 问题。要创建能够自动停止闲置实例的新生命周期配置,请参考以下操作步骤:

  1. Amazon SageMaker 控制台上,选择 Lifecycle configurations
  2. 选择 Create a new lifecycle configuration(如果需要创建新的生命周期配置)。
  3. Name 部分,请使用字母
  4. 数字与-字符输入名称,但不支持使用空格。此名称最多可包含 63 个字符,例如Stop-Idle-Instance
  5. 要创建每次创建 notebook 及启动 notebook 时运行的脚本,请选择 Start notebook
  6. Start notebook editor 当中,输入该脚本。
  7. 选择 Create configuration

您可以在 Amazon Web Services 生命周期配置示例repo中获取相应 bash 脚本。该脚本会根据脚本内IDLE_TIME参数所定义,在特定闲置时段内运行一项 cron 作业。您可以结合实际需求更改此时间,并根据需要在 Lifecycle configuration 页面中更改脚本内容。

为了让脚本正常起效,notebook 还应满足以下两个条件:

  • Notebook 实例应能够接入互联网,借此从公共 repo 中获取示例配置 Python 脚本(autostop.py)
  • 指向SageMaker:StopNotebookInstancenotebook实例执行角色权限可停止该 notebook,指向SageMaker:DescribeNotebookInstance的角色权限则可描述该 notebook

如果您不希望在接入互联网的 VPC 中创建 notebook 实例,则需要在 bash 脚本中内联添加 Python 脚本。该脚本同样可通过 GitHub repo 获取。如下所示,在您的 bash 脚本中添加对应内容,并将其应用于生命周期配置:

#!/bin/bash
set -e
# PARAMETERS
IDLE_TIME=3600

echo "Creating the autostop.py"
cat << EOF > autostop.py
##
## [PASTE PYTHON SCRIPT FROM GIT REPO HERE]
##
EOF

echo "Starting the SageMaker autostop script in cron"
(crontab -l 2>/dev/null; echo "*/5 * * * * /usr/bin/python $PWD/autostop.py --time $IDLE_TIME --ignore-connections") | crontab -

以下截屏所示,为如何在 Amazon SageMaker 控制台上选择生命周期配置。

此外,您也可以将该脚本保存在 Amazon S3 上,并通过 VPC 端点接入该脚本。关于更多详细信息,请参阅新增功能——用于 Amazon S3 VPC 端点。

现在,我们已经创建了生命周期配置,可以在创建新的 notebook、或者更新现有 notebook 时,将其分配给按需 notebook 实例。要使用生命周期配置创建 notebook(本文示例中为Stop-Idle-Instance),大家需要在 Additional Configuration 部分下将该脚本分配给目标 notebook。其他操作步骤与创建一个按需 notebook 实例部分完全相同。要将生命周期配置附加至现有 notebook,您首先需要停止该按需 notebook 实例,而后选择 Update settings 对该实例进行变更,而后在 Additonal configuration 部分附加新的生命周期配置。

下面,我们将介绍另外一种能够在特定时段规划 notebook 启动与停止的方法。例如,如果您希望在工作日(周一到周五)的上午 700 启动 notebook(包括特定分组内的 notebook,或者账户中的所有 notebook),并在晚间 900 停止所有 notebook,则可通过 Amazon CloudWatch Events Amazon Lambda 函数这一组合来实现。关于配置 Lambda 函数的更多详细信息,请参阅在 Amazon Lambda 控制台中配置函数。要为该用例建立时间表,大家可以按照以下各节中的说明进行操作。

要使用 Lambda 函数启动 notebook,请完成以下操作步骤:

1. 在 Lambda 控制台上,选择 create a Lambda function 以启动按需 notebook 实例,并在名称中使用特定关键字。在本文示例中,我们的开发团队在所有按需 notebook 实例的名称开头使用了 dev- 前缀。
2. 使用 Python 作为函数的运行时,并将函数命名为 start-dev-notebooks。

您的 Lambda 函数应在其执行 IAM 角色上添加 SageMakerFullAccess 策略。
3.在 Function 代码编辑区,输入以下脚本:

# Code to start InService Notebooks that contain specific keywords in their name
# Change "dev-" in NameContains to your specific use case

import boto3
client = boto3.client('sagemaker')
def lambda_handler(event, context):
    try:
        response_nb_list = client.list_notebook_instances(
            NameContains='dev-',     # Change this to your specific use case
            StatusEquals= 'Stopped'
                )
        for nb in response_nb_list['NotebookInstances']:
            response_nb_stop = client.start_notebook_instance(
                    NotebookInstanceName = nb['NotebookInstanceName'])
        return {"Status": "Success"} 
    except:
        return {"Status": "Failure"}

4.在 Basic Settings 之下,将 Timeout 调整为 15 分钟(最大)。
这一步可以保证该函数在停止及启动多个 notebook 时,拥有最大的允许超时范围。
5.保存您的函数。 

要使用 Lambda 函数停止您的 notebook,请遵循以下操作步骤,使用以下脚本,并将函数命名为 stop-dev-notebooks:

# Code to stop InService Notebooks that contain specific keywords in their name
# Change "dev-" in NameContains to your specific use case

import boto3
client = boto3.client('sagemaker')
def lambda_handler(event, context):
    try:
        response_nb_list = client.list_notebook_instances(
            NameContains='dev-',     # Change this to your specific use case
            StatusEquals= 'InService'
                )
        for nb in response_nb_list['NotebookInstances']:
            response_nb_stop = client.stop_notebook_instance(
                    NotebookInstanceName = nb['NotebookInstanceName'])  
        return {"Status": "Success"}        
    except:
        return {"Status": "Failure"}

现在,您已经创建了必要的函数,接下来需要创建事件以根据特定时间表触发这些函数。

在这里,我们使用 cron 表达式设定调度计划。关于创建自定义 cron 表达式的更多详细信息,请参阅用于建立规则的调度表达式。所有计划内调度事件均使用 UTC 时区,且最低时间精度为 1 分钟。

例如,适用于全年范围内周一到周五早 700cron 表达式写为 0 7 ? * MON-FRI *,而同一天内晚 900 的表达式写为 0 21 ? * MON-FRI *

要创建能够按调度计划停止各实例的事件,大家需要完成以下操作步骤:

  1. CloudWatch 控制台的 Events 之下, 选择  Rules
  2. 选择 Create rule
  3.  Event Source 之下,选择 Schedule,而后选择 Cron expression
  4. 输入您的 cron 表达式(例如 21 ? * MON-FRI *,意为周一至周五晚 900)。
  5. Targets 之下, 选择 Lambda function
  6. 从列表中选择您的函数(在本文示例中,为stop-dev-notebooks)
  7. 选择 Configure details

8. 为您的事件添加名称,例如   Stop-Notebooks-Event,外加相关描述。
 
9. 保持 Enabled
 
10. 选择   Create

大家可以采取相同的操作步骤创建 notebook 启动计划,根据调度要求在特定时间(例如工作日上午 700)启动 notebook,保证员工在上班时各 notebook 已经准备就绪。

大家可以将机器学习模型部署为端点,借此测试模型的实时推理效果。有时候,这些端点可能意外长期处于服务状态,导致账户不断产生相关成本。您可以使用 CloudWatch EventsLambda 函数自动检测这些端点,并及时采取纠正措施(例如将其删除)。例如,您可以检测各端点在过去几个小时内是否始终处于闲置状态(例如在过去 24 小时或其他特定时段内,从未接受调用)。我们在本节中提供的功能脚本可用于检测闲置端点,并使用闲置端点列表将检测结果发布至 Amazon Simple Notification Service (Amazon SNS)主题。作为账户管理员,您可以订阅该主题,保证在检测到此类闲置状况时收到包含闲置端点列表的电子邮件。要创建这样一项调度事件,具体操作步骤如下:

  1. 创建一个 SNS 主题,并使用您的电子邮件或手机号码进行订阅。
  2. 使用以下脚本创建一项 Lambda 函数 

您的 Lambda 函数应将以下策略附加至其 IAM 执行角色当中CloudWatchReadOnlyAccess, AmazonSNSFullAccess AmazonSageMakerReadOnly.

import boto3
from datetime import datetime
from datetime import timedelta

def lambda_handler(event, context):
    
    idle_threshold_hr = 24               # Change this to your threshold in hours
    
    cw = boto3.client('cloudwatch')
    sm = boto3.client('sagemaker')
    sns = boto3.client('sns')
    
    try:
        inservice_endpoints = sm.list_endpoints(
            SortBy='CreationTime',
            SortOrder='Ascending',
            MaxResults=100,
            # NameContains='string',     # for example 'dev-'
            StatusEquals='InService'
        )
        
        idle_endpoints = []
        for ep in inservice_endpoints['Endpoints']:
            
            ep_describe = sm.describe_endpoint(
                    EndpointName=ep['EndpointName']
                )
    
            metric_response = cw.get_metric_statistics(
                Namespace='AWS/SageMaker',
                MetricName='Invocations',
                Dimensions=[
                    {
                        'Name': 'EndpointName',
                        'Value': ep['EndpointName']
                        },
                        {
                         'Name': 'VariantName',
                        'Value': ep_describe['ProductionVariants'][0]['VariantName']                  
                        }
                ],
                StartTime=datetime.utcnow()-timedelta(hours=idle_threshold_hr),
                EndTime=datetime.utcnow(),
                Period=int(idle_threshold_hr*60*60), 
                Statistics=['Sum'],
                Unit='None'
                )
    
            if len(metric_response['Datapoints'])==0:     
                idle_endpoints.append(ep['EndpointName'])
        
        if len(idle_endpoints) > 0:
            response_sns = sns.publish(
                TopicArn='YOUR SNS TOPIC ARN HERE',
                Message="The following endpoints have been idle for over {} hrs. Log on to Amazon SageMaker console to take actions.\n\n{}".format(idle_threshold_hr, '\n'.join(idle_endpoints)),
                Subject='Automated Notification: Idle Endpoints Detected',
                MessageStructure='string'
            )
    
        return {'Status': 'Success'}
    
    except:
        return {'Status': 'Fail'}

您也可以修改这部分代码,以根据资源标签过滤各个端点。关于更多详细信息,请参阅 Amazon Python SDK Boto3 说明文档。

此脚本将发送一封包含检测到的闲置端点列表的电子邮件(或者文本消息,具体取决于您 SNS 主题的配置方式)。接下来,您可以登录至 Amazon SageMaker 控制台并调查对应端点,并在确定其属于闲置端点时将其删除。具体操作步骤如下:

1.在 Amazon SageMaker 控制台的 Inference 之下, 选择 Endpoints

您可以看到当前区域中账户内的所有端点列表。

2. 选择您希望调查的端点,并在 Monitor 之下选择 View invocation metrics

3.在 All metrics 之下,选择 Invocations

您可以在端点上看到相应的调用活动。如果您在指定的时段之内未发现任何调用事件(或活动),则表示该端点处于闲置状态,可以将其删除。

4.在确定要删除的端点后,请返回端点列表,选择要删除的端点,而后在 Actions 菜单下选择

本文向大家介绍了 Amazon SageMaker 的计费标准,根据机器学习项目内各个阶段正确调整 Amazon SageMaker 计算资源大小的最佳实践,以及如何通过自动停止闲置的按需 notebook 实例以避免产生非必要运营成本的具体方法。最后,我们还分享了如何自动检测 Amazon SageMaker 端点以保证不致发生误删情况。

相关文章