<p>I'm trying to leverage a local volume dynamic provisioner for k8s, Rancher's one, with multiple instances, each with its own storage class so that I can provide multiple types of local volumes based on their performance (e.g. ssd, hdd ,etc).</p>
<p>The underlying infrastructure is not symmetric; some nodes only have ssds, some only hdds, some of them both.</p>
<p>I know that I can hint the scheduler to select the proper nodes by providing node affinity rules for pods.</p>
<p>But, is there a better way to address this problem at the level of provisioner / storage class only ? E.g., make a storage class only available for a subset of the cluster nodes.</p>

<blockquote>
<p>I know that I can hint the scheduler to select the proper nodes by
providing node affinity rules for pods.</p>
</blockquote>
<p>There is no need to define node affinity rules on <code>Pod</code> level when using <a href="https://kubernetes.io/docs/concepts/storage/volumes/#local" rel="noreferrer">local</a> persistent volumes. Node affinity can be specified in <code>PersistentVolume</code> definition.</p>
<blockquote>
<p>But, is there a better way to address this problem at the level of
provisioner / storage class only ? E.g., make a storage class only
available for a subset of the cluster nodes.</p>
</blockquote>
<p>No, it cannot be specified on a <code>StorageClass</code> level. Neither you can make a <code>StorageClass</code> available only for a subset of nodes.</p>
<p>But when it comes to a provisioner, I would say yes, it should be feasible as one of the major storage provisioner tasks is creating matching <code>PersistentVolume</code> objects in response to <code>PersistentVolumeClaim</code> created by the user. You can read about it <a href="https://kubernetes.io/docs/concepts/storage/dynamic-provisioning/" rel="noreferrer">here</a>:</p>
<blockquote>
<p>Dynamic volume provisioning allows storage volumes to be created
on-demand. Without dynamic provisioning, cluster administrators have
to manually make calls to their cloud or storage provider to create
new storage volumes, and then create PersistentVolume objects to
represent them in Kubernetes. The dynamic provisioning feature
eliminates the need for cluster administrators to pre-provision
storage. Instead, it automatically provisions storage when it is
requested by users.</p>
</blockquote>
<p>So looking at the whole volume provision process from the very beginning it looks as follows:</p>
<p>User creates only <code>PersistenVolumeClaim</code> object, where he specifies a <code>StorageClass</code>:</p>
<pre><code>apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  resources:
    requests:
      storage: 10Gi
  storageClassName: local-storage ### 👈
</code></pre>
<p>and it can be used in a <code>Pod</code> definition:</p>
<pre><code>apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: &quot;/var/www/html&quot;
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim ### 👈
</code></pre>
<p>So in practice, in a <code>Pod</code> definition you need only to specify the proper <code>PVC</code>. <strong>No need for defining any node-affinity rules</strong> here.</p>
<p>A <code>Pod</code> references a <code>PVC</code>, <code>PVC</code> then references a <code>StorageClass</code>, <code>StorageClass</code> references the <code>provisioner</code> that should be used:</p>
<pre><code>apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/my-fancy-provisioner ### 👈
volumeBindingMode: WaitForFirstConsumer
</code></pre>
<p>So in the end it is the task of a <code>provisioner</code> to create matching <code>PersistentVolume</code> object. It can look as follows:</p>
<pre><code>apiVersion: v1
kind: PersistentVolume
metadata:
  name: example-pv
spec:
  capacity:
    storage: 10Gi
  volumeMode: Filesystem
  accessModes:
  - ReadWriteOnce
  persistentVolumeReclaimPolicy: Delete
  storageClassName: local-storage
  local:
    path: /var/tmp/test
  nodeAffinity: ### 👈
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - ssd-node ### 👈
</code></pre>
<p>So a <code>Pod</code> which uses <strong>myclaim</strong> <code>PVC</code> -&gt; which references the <strong>local-storage</strong> <code>StorageClass</code> -&gt; which selects a proper storage <code>provisioner</code> will be automatically scheduled on the node selected in <code>PV</code> definition created by this provisioner.</p>
