使用 AWSInferentia 在 Amazon EKS 上部署 ML 推理工作负载
本主题介绍如何创建一个包含运行 Amazon EC2 Inf1
注意
Neuron 设备逻辑 ID 必须是连续的。如果在 inf1.6xlarge
或 inf1.24xlarge
实例类型(具有多个 Neuron 设备)上调度请求多个 Neuron 设备的 Pod,则当 Kubernetes 调度程序选择不连续的设备 ID 时,该 Pod 将无法启动。有关更多信息,请参阅 GitHub 上的设备逻辑 ID 必须是连续的
先决条件
-
在计算机上安装
eksctl
。如果未安装,请参阅eksctl
文档中的安装。 -
在计算机上安装
kubectl
。有关更多信息,请参阅 设置 kubectl 和 eksctl。 -
(可选)在计算机上安装
python3
。如果未安装,请参阅 Python 下载以查看安装说明。
创建集群
-
创建包含 Inf1 Amazon EC2 实例节点的 Amazon EKS 集群。您可以将
inf1.2xlarge
替换为任何 Inf1 实例类型。 eksctl
实用程序检测到您在使用Inf1
实例类型启动节点组,并将使用一个 Amazon EKS 优化版加速型 Amazon Linux AMI 来启动您的节点。注意
您不能将服务账户的 IAM 角色与 TensorFlow Serving 结合使用。
eksctl create cluster \ --name inferentia \ --region region-code \ --nodegroup-name ng-inf1 \ --node-type inf1.2xlarge \ --nodes 2 \ --nodes-min 1 \ --nodes-max 4 \ --ssh-access \ --ssh-public-key your-key \ --with-oidc
注意
请记住下一个输出行的值。在后面的(可选)步骤中将使用该值。
[9] adding identity "arn:aws-cn:iam::111122223333:role/eksctl-inferentia-nodegroup-ng-in-NodeInstanceRole-FI7HIYS3BS09" to auth ConfigMap
在启动包含
Inf1
实例的节点组时,eksctl
将自动安装 Amazon Neuron Kubernetes 设备插件。此插件将 Neuron 设备作为系统资源传播到 Kubernetes 调度程序,以供容器请求。除了默认的 Amazon EKS 节点 IAM policy 之外,还添加了 Amazon S3 只读访问策略,以便下一个步骤中所述的示例应用程序能够从 Amazon S3 加载经过训练的模型。 -
确保所有 Pods 已正常启动。
kubectl get pods -n kube-system
缩减的输出:
NAME READY STATUS RESTARTS AGE [...] neuron-device-plugin-daemonset-6djhp 1/1 Running 0 5m neuron-device-plugin-daemonset-hwjsj 1/1 Running 0 5m
(可选)部署 TensorFlow Serving 应用程序映像
经过训练的模型必须先编译为 Inferentia 目标,才能部署在 Inferentia 实例上。要继续,您将需要一个在 Amazon S3 中保存的经 Neuron 优化的 TensorFlow
示例部署清单管理一个预构建的推理服务容器,用于由 Amazon Deep Learning Containers 提供的 TensorFlow。容器内部是 Amazon Neuron 运行时间和 TensorFlow 服务应用程序。在 GitHub 上的可用镜像
可以通过更改部署 yaml 中的 aws.amazon.com/neuron
资源,来调整分配给您的服务应用程序的 Neuron 设备的数量。请注意,TensorFlow Serving 和 Neuron 运行时间之间通过 GRPC 进行通信,这需要将 IPC_LOCK
功能传递给容器。
-
将
AmazonS3ReadOnlyAccess
IAM 策略添加到已在创建集群的第 1 步中创建的节点实例角色。必须执行此操作,示例应用程序才能从 Amazon S3 加载经过训练的模型。aws iam attach-role-policy \ --policy-arn arn:aws-cn:iam::aws:policy/AmazonS3ReadOnlyAccess \ --role-name eksctl-inferentia-nodegroup-ng-in-NodeInstanceRole-FI7HIYS3BS09
-
使用以下内容创建名为
rn50_deployment.yaml
的文件。更新区域代码和模型路径以匹配您需要的设置。当客户端向 TensorFlow 服务器发出请求时,模型名称用于标识目的。此示例使用模型名称匹配一个示例 ResNet50 客户端脚本,后面的步骤中将使用该脚本发送预测请求。aws ecr list-images --repository-name neuron-rtd --registry-id 790709498068 --region us-west-2
kind: Deployment apiVersion: apps/v1 metadata: name: eks-neuron-test labels: app: eks-neuron-test role: master spec: replicas: 2 selector: matchLabels: app: eks-neuron-test role: master template: metadata: labels: app: eks-neuron-test role: master spec: containers: - name: eks-neuron-test image: 763104351884.dkr.ecr.us-east-1.amazonaws.com/tensorflow-inference-neuron:1.15.4-neuron-py37-ubuntu18.04 command: - /usr/local/bin/entrypoint.sh args: - --port=8500 - --rest_api_port=9000 - --model_name=resnet50_neuron - --model_base_path=s3://your-bucket-of-models/resnet50_neuron/ ports: - containerPort: 8500 - containerPort: 9000 imagePullPolicy: IfNotPresent env: - name: AWS_REGION value: "us-east-1" - name: S3_USE_HTTPS value: "1" - name: S3_VERIFY_SSL value: "0" - name: S3_ENDPOINT value: s3.us-east-1.amazonaws.com - name: AWS_LOG_LEVEL value: "3" resources: limits: cpu: 4 memory: 4Gi aws.amazon.com/neuron: 1 requests: cpu: "1" memory: 1Gi securityContext: capabilities: add: - IPC_LOCK
-
部署模型。
kubectl apply -f rn50_deployment.yaml
-
使用以下内容创建名为
rn50_service.yaml
的文件。这将打开 HTTP 和 gRPC 端口以接受预测请求。kind: Service apiVersion: v1 metadata: name: eks-neuron-test labels: app: eks-neuron-test spec: type: ClusterIP ports: - name: http-tf-serving port: 8500 targetPort: 8500 - name: grpc-tf-serving port: 9000 targetPort: 9000 selector: app: eks-neuron-test role: master
-
为 TensorFlow 模型 Serving 应用程序创建 Kubernetes 服务。
kubectl apply -f rn50_service.yaml
(可选)根据 TensorFlow Serving 服务进行预测
-
要在本地进行测试,请将 gRPC 端口转发到
eks-neuron-test
服务。kubectl port-forward service/eks-neuron-test 8500:8500 &
-
创建一个名为
tensorflow-model-server-infer.py
的 Python 脚本,其中包含以下内容。该脚本通过 GRPC(这是一个服务框架)运行推理过程。import numpy as np import grpc import tensorflow as tf from tensorflow.keras.preprocessing import image from tensorflow.keras.applications.resnet50 import preprocess_input from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2_grpc from tensorflow.keras.applications.resnet50 import decode_predictions if name == 'main': channel = grpc.insecure_channel('localhost:8500') stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) img_file = tf.keras.utils.get_file( "./kitten_small.jpg", "https://raw.githubusercontent.com/awslabs/mxnet-model-server/master/docs/images/kitten_small.jpg") img = image.load_img(img_file, target_size=(224, 224)) img_array = preprocess_input(image.img_to_array(img)[None, ...]) request = predict_pb2.PredictRequest() request.model_spec.name = 'resnet50_inf1' request.inputs['input'].CopyFrom( tf.make_tensor_proto(img_array, shape=img_array.shape)) result = stub.Predict(request) prediction = tf.make_ndarray(result.outputs['output']) print(decode_predictions(prediction))
-
运行脚本以将预测数据提交给服务。
python3 tensorflow-model-server-infer.py
示例输出如下。
[[(u'n02123045', u'tabby', 0.68817204), (u'n02127052', u'lynx', 0.12701613), (u'n02123159', u'tiger_cat', 0.08736559), (u'n02124075', u'Egyptian_cat', 0.063844085), (u'n02128757', u'snow_leopard', 0.009240591)]]