From 4cbc77e491e4cdba320dc3e17c5dd1d9376c1328 Mon Sep 17 00:00:00 2001 From: Jiaying Zhang Date: Wed, 19 Jul 2017 17:53:03 -0700 Subject: [PATCH] Device plugin proposal patch by Jiaying --- .../design-proposals/device-plugin-2.png | Bin 0 -> 68427 bytes .../design-proposals/device-plugin.md | 433 ++++++++++++------ 2 files changed, 304 insertions(+), 129 deletions(-) create mode 100644 contributors/design-proposals/device-plugin-2.png diff --git a/contributors/design-proposals/device-plugin-2.png b/contributors/design-proposals/device-plugin-2.png new file mode 100644 index 0000000000000000000000000000000000000000..60892429f4c46b3a59a24713b8f1afd6f7464c06 GIT binary patch literal 68427 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 z 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%0A;$o}xy0X38nnUeEwWKk{!*f%Csog|_ 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 literal 0 HcmV?d00001 diff --git a/contributors/design-proposals/device-plugin.md b/contributors/design-proposals/device-plugin.md index 339070d89..2f73c9324 100644 --- a/contributors/design-proposals/device-plugin.md +++ b/contributors/design-proposals/device-plugin.md @@ -1,99 +1,77 @@ -# Device Manager Proposal +Device Manager Proposal +=============== - 1. [Abstract](#abstract) - 2. [Motivation](#motivation) - 3. [Use Cases](#use-cases) - 4. [Objectives](#objectives) - 5. [Non Objectives](#non-objectives) - 6. [Stories](#stories) - * [Vendor story](#vendor-story) - * [User story](#user-story) - 8. [Device Plugin](#device-plugin) - * [Protocol Overview](#protocol-overview) - * [Protobuf specification](#protobuf-specification) - * [Installation](#installation) - * [API Changes](#api-changes) - * [Versioning](#versioning) + + +- [Motivation](#motivation) +- [Use Cases](#use-cases) +- [Objectives](#objectives) +- [Non Objectives](#non-objectives) +- [Proposed Implementation 1](#proposed-implementation-1) + - [Vendor story](#vendor-story) + - [End User story](#end-user-story) + - [Device Plugin](#device-plugin) + - [Introduction](#introduction) + - [Registration](#registration) + - [Unix Socket](#unix-socket) + - [Protocol Overview](#protocol-overview) + - [Protobuf specification](#protobuf-specification) +- [Proposed Implementation 2](#proposed-implementation-2) + - [Device Plugin Lifecycle](#device-plugin-lifecycle) + - [Protobuf API](#protobuf-api) + - [Failure recovery](#failure-recovery) + - [Roadmap](#roadmap) + - [Open Questions](#open-questions-1) +- [Installation](#installation) +- [Versioning](#versioning) + - [References](#references) + + _Authors:_ * @RenaudWasTaken - Renaud Gaubert <rgaubert@NVIDIA.com> -## Abstract - -This document describes a vendor independant solution to: - * Discovering and representing external devices - * Making these devices available to the container and cleaning them up - afterwards - * Health Check of these devices - -Because devices are vendor dependant and have their own sets of problems -and mechanisms, the solution we describe is a plugin mechanism managed by -Kubelet. - -At their core, device plugins are simple gRPC servers that may run in a -container deployed through the pod mechanism. - -These servers implement the gRPC interface defined later in this design -document and once the device plugin makes itself know to kubelet, kubelet -will interact with the device through three simple functions: - 1. A `Discover` function for the kubelet to Discover the devices and - their properties. - 2. An `Allocate` and `Deallocate` function which are called respectively - before container creation and after container deletion with the - devices to allocate and deallocate. - 3. A `Monitor` function to notify Kubelet whenever a device becomes - unhealthy. - -The goal is for a user to be able to enable vendor devices (e.g: GPUs) through -the simple following steps: - * `kubectl create -f http://vendor.com/device-plugin-daemonset.yaml` - * When launching `kubectl describe nodes`, the devices appear in the node spec - * In the long term users will be able to select them through Resource Class - -We expect the plugins to be deployed across the clusters through DaemonSets. -The targeted devices are GPUs, NICs, FPGAs, InfiniBand, Storage devices, .... - - -## Motivation +# Motivation Kubernetes currently supports discovery of CPU and Memory primarily to a minimal extent. Very few devices are handled natively by Kubelet. It is not a sustainable solution to expect every vendor to add their vendor -specific code inside Kubernetes. This approach does not scale and is not -portable. +specific code inside Kubernetes to make their devices usable. +Instead, we want a solution for vendors to be able to advertise their resources +to Kubelet and monitor them without writing custom Kubernetes code. +We also want to provide a consistent and portable solution for users to +consume hardware devices across k8s clusters. -We want a solution for those vendors to be able to advertise their resources -to kubelet and monitor them. -We also want a way for the user to specify which resource their jobs will use -and what constraints are associated to these resources. +This document describes a vendor independant solution to: + * Discovering and representing external devices + * Making these devices available to the containers using these devices and + cleaning them up afterwards + * Monitoring these devices -In order to solve this problem it is obvious that we need a plugin system in -order to have vendors advertise and monitor their resources on behalf -of Kubelet. +Because devices are vendor dependant and have their own sets of problems +and mechanisms, the solution we describe is a plugin mechanism that may run +in a container deployed through the DaemonSets mechanism. +The targeted devices include GPUs, High-performance NICs, FPGAs, InfiniBand, +Storage devices, and other similar computing resources that require vendor +specific initialization and setup. -Additionally, we introduce the concept of Device to be able to select -resources with constraints in a pod spec. +The goal is for a user to be able to enable vendor devices (e.g: GPUs) through +the following simple steps: + * `kubectl create -f http://vendor.com/device-plugin-daemonset.yaml` + * When launching `kubectl describe nodes`, the devices appear in the node spec + * In the long term users will be able to select them through Resource Class -_GPU Integration Example:_ - * [Enable "kick the tires" support for NVIDIA GPUs in COS](https://github.com/Kubernetes/Kubernetes/pull/45136) - * [Extend experimental support to multiple NVIDIA GPUs](https://github.com/Kubernetes/Kubernetes/pull/42116) +# Use Cases -_Kubernetes Meeting Notes On This:_ - * [Meeting notes](https://docs.google.com/document/d/1Qg42Nmv-QwL4RxicsU2qtZgFKOzANf8fGayw8p3lX6U/edit#) - * [Better Abstraction for Compute Resources in Kubernetes](https://docs.google.com/document/d/1666PPUs4Lz56TqKygcy6mXkNazde-vwA7q4e5H92sUc) - * [Extensible support for hardware devices in Kubernetes (join Kubernetes-dev@googlegroups.com for access)](https://docs.google.com/document/d/1LHeTPx_fWA1PdZkHuALPzYxR0AYXUiiXdo3S0g2VSlo/edit) + * I want to use a particular device type (GPU, InfiniBand, FPGA, etc.) + in my pod. + * I should be able to use that device without writing custom Kubernetes code. + * I want a consistent and portable solution to consume hardware devices + across k8s clusters. -## Use Cases - - * I want to use a particular device type (GPU, InfiniBand, FPGA, etc.) - in my pod. - * I should be able to use that device without writing custom Kubernetes code. - * I want a consistent and portable solution to consume hardware devices - across k8s clusters. - -## Objectives +# Objectives 1. Add support for vendor specific Devices in kubelet: * Through a pluggable mechanism. @@ -103,16 +81,18 @@ _Kubernetes Meeting Notes On This:_ 2. Define a deployment mechanism for this new API. 3. Define a versioning mechanism for this new API. -## Non Objectives -1. Advanced scheduling and resource selection (solved through [#782](https://github.com/Kubernetes/community/pull/782)). +# Non Objectives + +1. Advanced scheduling and resource selection (solved through + [#782](https://github.com/Kubernetes/community/pull/782)). We will only try to give basic selection primitives to the devices 2. Metrics: this should be the job of cadvisor and should probably either be addressed there (cadvisor) or if people feel there is a case to be made for it being addressed in the Device Plugin, in a follow up proposal. -## Stories +# Proposed Implementation 1 -### Vendor story +## Vendor story Kubernetes provides to vendors a mechanism called device plugins to: * advertise devices. @@ -144,7 +124,7 @@ onwn gRPC server. Only then will kubelet start interacting with the vendor's device plugin through the gRPC apis. -### End User story +## End User story When setting up the cluster the admin knows what kind of devices are present on the different machines and therefore can select what devices they want to @@ -182,6 +162,7 @@ He might in the future be in charge of selecting the device. ## Device Plugin ### Introduction + The device plugin is structured in 5 parts: 1. Registration: The device plugin advertises it's presence to Kubelet 2. Discovery: Kubelet calls the device plugin to list it's devices @@ -189,7 +170,7 @@ The device plugin is structured in 5 parts: devices advertised by the device plugin, Kubelet calls the device plugin's `Allocate` and `Deallocate` functions. 4. Cleanup: Kubelet terminates the communication through a "Stop" -4. Heartbeat: The device plugin polls Kubelet to know if it's still alive +5. Heartbeat: The device plugin polls Kubelet to know if it's still alive and if it has to re-issue a Register request ### Registration @@ -247,7 +228,7 @@ The device plugin is also expected to periodically call the `Heartbeat` function exposed by Kubelet and issue a `Registration` request when it either can't reach Kubelet or Kubelet answers with a `KO` response. -![Process](./device-plugin.png) +![Process](device-plugin.png) ### Protobuf specification @@ -343,7 +324,233 @@ message DeviceHealth { } ``` -## Installation +# Proposed Implementation 2 + +The main strategy of this proposed implemenation is that we want to start with +something simple that can show benefits on our immediate use cases, yet the +API design should be extendable to support future requirements. +Here are the main motivations for this alternative proposed implementation: + +* Discovery phase: can we eliminate this gRPC procedure? It seems more + natural for device plugin to send Kubelet the discovered device information + right after device initialization and the registration gRPC procedure. +* The current implementation uses gRPC to communicate between Kubelet and + device plugin. Both Kubelet and device plugin need to start a gRPC server + for two-way communication. This seems a bit complicated. Can we have + device plugin send enough information to Kubelet so that we only need + Kubelet to start gRPC server and device plugin is kept as gRPC client? + The main concern with one-way gRPC communication is that we can NOT + support device specific operations, like reset device, during + allocation/deallocation. Depending on how long we expect device specific + operations to take, we can support this feature later by + having device plugin also provide a gRPC service or Device Plugin + can instruct Kubelet to perform device specific operation hooks. +* Do we need checkpointing in the initial prototype implementation? + Even in alpha release, we may still want to be able to recover from + various failure scenario. Otherwise, it would affect user experience. + Currently, it seems the only information we need to record somewhere + is what device is allocated to what pod/container. There have been + discussions on different ways and places to record this information. + The approach taken by the current implementation pushes this information + to ApiServer by extending NodeStatus interface between Kubelet and ApiServer. + The major concern on this approach is that it introduced an API extension + apart from the current model (Currently Node information recorded at ApiServer + only contains resource capacity information. Resource allocation information + is kept at Node). The second approach is for Kubelet to checkpoint this + information. This seems to align with the current Kubernetes model that + Kubelet is the component to implement allocation/deallocation functionalities. + The information we want to checkpoint, i.e., what device is allocated to what + pod/container, also seems generic enough to be implemented at Kubelet. + It may also allow other use cases outside device plugin, e.g., cpu pin. + The third approach is to implement this in device plugin. This way, + device plugin can also record any state information useful to its own + failure recovery in checkpoints. One concern on this approach is that it + may add more burdens on vendors to implement their device plugin images. + Surprises might happen in production if things were not implemented correctly. + It also seems apart from the current model as today Kubelet is the place + where allocation/deallocation happens for other types of resources. +* Heartbeat: do we need this to make sure connections can be re-established + between kubelet and device plugin? Can we reuse keepalive feature from gRPC? + Or if Kubelet checkpoints device allocation state information, device plugin + may only need to detect Kubelet failure when it needs to update device + information. Or can device plugin send periodic device state updates + (this may be needed anyway if we want to collect device usage stats) + and use that to detect Kubelet failure or device plugin failure? + +## Device Plugin Lifecycle + +![Process](device-plugin-2.png) + +1. User or cluster admin push vendor-specific device plugin DaemonSets. + The DaemonSets YAML config includes mountPaths to the host directories + where device driver, user-space libraries, and tools will be installed. +2. After device plugin container is brought up, it detects the specific + types of HW devices. If such devices exist, it initializes these devices + and sets up the environments to access these devices (e.g., install + device drivers, user-space libraries, and tools). +3. After initialization, device plugin queries HW device states through the + installed device monitoring tools or other device interfaces. Then device + plugin connects to the Kubelet device plugin gRPC server and sends it the + obtained list of HW device information. In the initial prototype, the + device resource exported by a device plugin can be implemented as an + [extended OIR](https://github.com/kubernetes/kubernetes/pull/48922) + with special prefix “extensions.kubernetes.io/”, plus device resource_name + that uniquely identifies a device plugin on a node. + Kubelet can use existing API to add this resource to API server so that the + device resource is available for scheduling. +4. Device plugin runs in a loop to continuously query HW device states. If it + detects any changes, it sends the Kubelet device plugin gRPC server the new + list of HW device information. Kubelet can use this information to update its + device capacity states and if necessary, re-allocate new device to a user + container with unhealthy allocated devices. +5. When Kubelet receives an allocation request for a HW device advertised + by a device plugin (i.e., resource with “extensions.kubernetes.io/” prefix + plus device resource_name), it updates its internal allocation state, + issues certain calls to CRI to bind mount the host directories where + user-space libraries and tools are installed to the device-specific + default directories in user Pod or set up certain environment variables, + and checkpoints the container-to-device allocation information to persistent + storage. +6. When user container accessing the device finishes, Kubelet updates its + internal state to deallocate the device, and updates its checkpoint state + in persistent storage. +7. When device plugin DaemonSets is removed, clean up device state (e.g., uninstall + device driver, remove user-space libraries and tools). This step can be + specified as a preStop + [container lifecycle step](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/). + Note one implication from this approach is that device plugin upgrade process + will be disruptive. It will need more thinkings if we want to + support non-disruptive upgrade process. + +## Protobuf API + +```go + +service PluginResource { + rpc Register(RegisterRequest) returns (RegisterResponse) {} + rpc ReportDeviceInfo(ReportRequest) returns (ReportResponse) {} +} + +message RegisterRequest { + // Version of the API the Device Plugin was built against + string version = 1; + // E.g., "nvidia-gpu". Used to construct OIR: + // “extensions.kubernetes.io/resourcename”. + string resourcename = 2; +} + +message RegisterResponse { + bool success = 1; + // Kubelet fills this field with details if it encounters any errors + // during the registration process, e.g., for version mismatch, what + // is the required version and minimum supported version by kubelet. + string error = 2; +} + +message ReportRequest { + repeated DeviceInfo devices = 1; +} + +message DeviceInfo { + // E.g., "GPU-fef8089b-4820-abfc-e83e-94318197576e". + // Needs to be unique per device plugin. + string Id = 1; + // E.g., UNKNOWN, HEALTHY, UNHEALTHY. + enum State = 2; + // E.g., {"/rootfs/nvidia":"/usr/local/nvidia"} + // Maps from host directory where device library or tools + // are installed to user pod directory where the library or + // tools are expected to be accessed. Kubelet will use this + // information to bind mount host directory to the user pod + // directory during allocation. + map mountpaths = 3; + // E.g., {"LD_PRELOAD":"xxx.so"}. Kubelet will export these + // env variables in user pod during allocation. + map envariables = 4; + // E.g., {"Family":"Pascal"} {"ECC":"True"} + // These fields can be used as node labels for selection + map labels = 4; +} + +message ReportResponse { + bool success = 1; + // Kubelet fills this field if it encounters any errors + // during the report process, e.g., device plugin hasn’t + // registered yet (could happen when Kubelet restarts). + string error = 2; +} +``` + +## Failure recovery + +* Device failure: Device plugin should be able to detect device failure and + report that to Kubelet. Kubelet should then remove the failed device from + available list. If there is any user container using the failed device, + Kubelet may terminate the user container and reschedule it on a good + available device. When a failed device recovers, device plugin will send + Kubelet the updated device state and Kubelet can add the device to the + available device list. +* Kubelet crash: When Kubelet restarts after a crash, it should be able to + recover allocation states from the checkpoints recorded on persistent storage. + The checkpoint records should include allocated device id to pod mapping + information as well as non-allocated device information, so Kubelet can + re-establish precise allocation state. Device plugin should be able to + detect Kubelet failure when it needs to update device informaiton, + and re-registers with the new Kubelet. +* Device plugin crash: A device plugin is deployed through DaemonSets. + If a device plugin process fails, Kubelet will detect that and automatically + restart it. After restart, device plugin will reconnect to Kubelet and + report the current device states. Kubelet can compare the reported device + information with its internal device states, and makes adjustments if + necessary. One thing we need to pay special attention is that device plugin + may fail at any time, e.g., during initialization. When the new device plugin + process starts, it needs to be able to recover from incomplete states. + +## Roadmap + +* Phase 1: device plugin is supported in alpha mode in 1.8 kubernetes release. + Make sure it provides the following functionalities: initialize, discover, + allocate/deallocate, cleanup, basic health check, and can recover from device, + Kubelet or device plugin failures . Make sure the interface is kept simple and + extensible, and the document is clear. With the initial implemented API, + make sure we can use the interface to implement device plugin images for at + least two types of devices: Nvidia GPU and Solarflare NIC. Note the support + for Nvidia GPU will help gpu support to enter beta by providing a general + and extendable api. Test coverage: e2e tests with the developed Nvidia GPU + image and Solarflare image to make sure these devices can be correctly + initialized, allocated, deallocated, and cleaned up. Also should test we + can recover from device failure, Kubelet restarts, and device plugin failure. +* Phase 2: device plugin is supported in beta mode in 1.9 kubernetes release. + At this phase, the primary design and API should be stabilized. We need to + implement authentication mechanism to ensure only trusted device plugin + images can be registered. We can support device specific + allocation/deallocation requests by having device plugin also provide a gRPC + service or Device Plugin can instruct Kubelet to perform device specific + operation hooks during allocation/deallocation procedures. + Hopefully at this time, we may make good progress on supporting more flexible + resource allocation policies in Kubernetes, and with that, we can switch + device plugin from using OIR to using ResourceClass to allow more efficient + HW specific resource allocations, e.g., topology aware resource allocations, + NUMA aware resource allocations etc. +* Phase 3: device plugin is supported in GA mode in 1.10 kubernetes release. + Device plugin should have clear error handling and problem report that + allows easy debuggability and monitoring on its exported devices. + We should have clear documentation on how to develop a device plugin and + how interact with device plugin. The framework needs to be stable and + demonstrate good user experiences through the support on multiple types + of devices, such as GPU, Infiniband, high-performance NIC, and etc. + +## Open Questions + +* The proposal assumes we can omit device specific allocation/deallocation +operations in the alpha release and support this feature in later releases. +If people are concerned that such omission would impact the usability of +alpha release, we will need to come up with a solution that would either +require two-way gRPC communication between Kubelet and Device Plugin or +Device Plugin can instruct Kubelet to perform device specific operation hooks +during allocation/deallocation procedures. + +# Installation The installation process should be straightforward to the user, transparent and similar to other regular Kubernetes actions. @@ -366,6 +573,7 @@ as `kubeadm` they would use the examples that we would provide at: `https://github.com/Kubernetes/Kubernetes/tree/master/examples/device-plugin.yaml` YAML example: + ```yaml apiVersion: extensions/v1beta1 kind: DaemonSet @@ -388,48 +596,6 @@ spec: path: /var/run/Kubernetes ``` -## API Changes -### Device - -When discovering the devices, Kubelet will be in charge of advertising those -resources to the API server. - -We will advertise each device returned by the Device Plugin in a new structure -called `Device`. -It is defined as follows: - -```golang -type Device struct { - Kind string - Vendor string - Name string - Health DeviceHealthStatus - Properties map[string]string -} -``` - -Because the current API (Capacity) can not be extended to support Device, -we will need to create two new attributes in the NodeStatus structure: - * `DevCapacity`: Describing the device capacity of the node - * `DevAvailable`: Describing the available devices - -```golang -type NodeStatus struct { - DevCapacity []Device - DevAvailable []Device -} -``` - -We also introduce the `Allocated` field in the pod's status so that user -can know what devices were assigned to the pod. It could also be useful in -the case of monitoring - -```golang -type ContainerStatus struct { - Devices []Device -} -``` - # Versioning Currently there is only one part (CRI) of Kubernetes which is based on @@ -469,3 +635,12 @@ Negotiation would take place in the registration: contacts the Device Plugin 4. If the Device Plugin supports the version sent by Kubelet it can and should answer the different calls made by Kubelet + +## References + + * [Enable "kick the tires" support for NVIDIA GPUs in COS](https://github.com/Kubernetes/Kubernetes/pull/45136) + * [Extend experimental support to multiple NVIDIA GPUs](https://github.com/Kubernetes/Kubernetes/pull/42116) + * [Kubernetes Meeting notes](https://docs.google.com/document/d/1Qg42Nmv-QwL4RxicsU2qtZgFKOzANf8fGayw8p3lX6U/edit#) + * [Better Abstraction for Compute Resources in Kubernetes](https://docs.google.com/document/d/1666PPUs4Lz56TqKygcy6mXkNazde-vwA7q4e5H92sUc) + * [Extensible support for hardware devices in Kubernetes (join Kubernetes-dev@googlegroups.com for access)](https://docs.google.com/document/d/1LHeTPx_fWA1PdZkHuALPzYxR0AYXUiiXdo3S0g2VSlo/edit) +