Condividi tramite


Distribuire contenitori Linux di SQL Server in Kubernetes con StatefulSets

Si applica a:SQL Server - Linux

Questo articolo contiene procedure consigliate e materiale sussidiario per l'esecuzione di contenitori SQL Server in Kubernetes con StatefulSets. Si consiglia di distribuire un contenitore di SQL Server (istanza) per pod in Kubernetes. In tal modo, si dispone di un'istanza di SQL Server distribuita per ogni pod nel cluster Kubernetes.

Analogamente, il consiglio dello script di distribuzione consiste nel distribuire un'istanza di SQL Server impostando il valore replicas su 1. Se si immette un numero maggiore di 1 come valore replicas, si ottengono altrettante istanze di SQL Server con nomi correlati. Ad esempio, nello script seguente, se è stato assegnato il numero 2 come valore per replicas, si distribuirebbero due pod di SQL Server, rispettivamente con i nomi mssql-0 e mssql-1.

Un altro motivo per cui è consigliabile usare un'istanza di SQL Server per ogni script di distribuzione consiste nell'consentire di apportare modifiche ai valori di configurazione, all'edizione, ai flag di traccia e ad altre impostazioni in modo indipendente per ogni istanza di SQL Server distribuita.

Nell'esempio seguente il nome del carico di lavoro StatefulSet deve corrispondere al valore .spec.template.metadata.labels, che in questo caso è mssql. Per altre informazioni, vedere StatefulSet.

Importante

La variabile di ambiente SA_PASSWORD è deprecata. Utilizzare invece MSSQL_SA_PASSWORD.

apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: mssql # name of the StatefulSet workload, the SQL Server instance name is derived from this. We suggest to keep this name same as the .spec.template.metadata.labels, .spec.selector.matchLabels and .spec.serviceName to avoid confusion.
spec:
 serviceName: "mssql" # serviceName is the name of the service that governs this StatefulSet. This service must exist before the StatefulSet, and is responsible for the network identity of the set.
 replicas: 1 # only one pod, with one SQL Server instance deployed.
 selector:
  matchLabels:
   app: mssql  # this has to be the same as .spec.template.metadata.labels
 template:
  metadata:
   labels:
    app: mssql # this has to be the same as .spec.selector.matchLabels, as documented [here](https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/):
  spec:
   securityContext:
     fsGroup: 10001
   containers:
   - name: mssql # container name within the pod.
     image: mcr.microsoft.com/mssql/server:2019-latest
     ports:
     - containerPort: 1433
       name: tcpsql
     env:
     - name: ACCEPT_EULA
       value: "Y"
     - name: MSSQL_ENABLE_HADR
       value: "1"
     - name: MSSQL_AGENT_ENABLED
       value: "1"
     - name: MSSQL_SA_PASSWORD
       valueFrom:
         secretKeyRef:
          name: mssql
          key: MSSQL_SA_PASSWORD
     volumeMounts:
     - name: mssql
       mountPath: "/var/opt/mssql"
 volumeClaimTemplates:
   - metadata:
      name: mssql
     spec:
      accessModes:
      - ReadWriteOnce
      resources:
       requests:
        storage: 8Gi

Se si sceglie comunque di distribuire più repliche dell'istanza di SQL Server usando la stessa distribuzione, tale scenario viene trattato nella sezione successiva. Tuttavia, si tratta di istanze di SQL Server indipendenti separate e non di repliche (a differenza delle repliche del gruppo di disponibilità in SQL Server).

Scegliere il tipo di carico di lavoro

La scelta del tipo di distribuzione del carico di lavoro corretto non influisce sulle prestazioni, ma statefulSet fornisce requisiti di conformità delle identità.

Carichi di lavoro statefulSet

SQL Server è un'applicazione di database e pertanto deve essere distribuita principalmente come tipo di carico di lavoro StatefulSet. La distribuzione di carichi di lavoro come StatefulSet consente di fornire funzionalità come l'identificazione di rete univoca, l'archiviazione permanente e stabile e altro ancora. Per altre informazioni su questo tipo di carico di lavoro, consultare la documentazione di Kubernetes.

Quando si distribuiscono più repliche di contenitori di SQL Server usando lo stesso script YAML di distribuzione come carico di lavoro StatefulSet, un parametro importante da considerare sono i criteri di gestione dei pod, ovvero .spec.podManagementPolicy.

I due valori possibili per questa impostazione sono:

  • OrderedReady: è il valore predefinito e il comportamento è descritto nelle garanzie di distribuzione e ridimensionamento.

  • Parallelo: si tratta dei criteri alternativi che creano e avviano i pod (in questo caso pod di SQL Server) in parallelo, senza attendere la creazione di altri pod; in modo analogo, tutti i pod vengono eliminati in parallelo durante la terminazione. È possibile usare questa opzione quando si distribuiscono istanze di SQL Server indipendenti l'una dall'altra e quando non si intende seguire un ordine per avviare o eliminare le istanze di SQL Server.

    apiVersion: apps/v1
    kind: StatefulSet
    metadata:
      name: mssql
    spec:
      serviceName: "mssql"
      replicas: 2 # two independent SQL Server instances to be deployed
      podManagementPolicy: Parallel
      selector:
        matchLabels:
          app: mssql
      template:
        metadata:
          labels:
            app: mssql
        spec:
          securityContext:
            fsGroup: 10001
          containers:
            - name: mssql
              image: mcr.microsoft.com/mssql/server:2019-latest
              ports:
                - containerPort: 1433
                  name: tcpsql
              env:
                - name: ACCEPT_EULA
                  value: "Y"
                - name: MSSQL_ENABLE_HADR
                  value: "1"
                - name: MSSQL_AGENT_ENABLED
                  value: "1"
                - name: MSSQL_SA_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: mssql
                      key: MSSQL_SA_PASSWORD
              volumeMounts:
                - name: mssql
                  mountPath: "/var/opt/mssql"
      volumeClaimTemplates:
        - metadata:
            name: mssql
          spec:
            accessModes:
              - ReadWriteOnce
            resources:
              requests:
                storage: 8Gi
    

Poiché i pod di SQL Server distribuiti in Kubernetes sono indipendenti l'uno dall'altro, Parallel è il valore normalmente usato per podManagementPolicy.

L'esempio seguente è l'output di esempio per kubectl get all, subito dopo aver creato i pod usando un criterio parallelo:

NAME          READY   STATUS              RESTARTS   AGE
pod/mssql-0   0/1     ContainerCreating   0          4s
pod/mssql-1   0/1     ContainerCreating   0          4s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   201.0.0.1    <none>        443/TCP   61d

NAME                     READY   AGE
statefulset.apps/mssql   1/1     4s

Carichi di lavoro di distribuzione

È possibile usare il tipo di distribuzione per SQL Server, negli scenari in cui si vogliono distribuire contenitori di SQL Server come applicazioni di database senza stato, ad esempio quando il salvataggio permanente dei dati non è critico. Alcuni esempi sono per scopi di test/QA o CI/CD.

Isolamento tramite spazi dei nomi

Gli spazi dei nomi forniscono un meccanismo per isolare gruppi di risorse all'interno di un singolo cluster Kubernetes. Per altre informazioni sugli spazi dei nomi e su quando usarli, vedere Spazi dei nomi.

Dalla prospettiva di SQL Server, se si prevede di eseguire pod di SQL Server in un cluster Kubernetes che esegue l’hosting anche di altre risorse, è necessario eseguire i pod di SQL Server nel proprio spazio dei nomi, per semplificare la gestione e l'amministrazione. Se si dispone, ad esempio, di più reparti che condividono lo stesso cluster Kubernetes e si vuole distribuire un'istanza di SQL Server per il team di vendita e un'altra per il team marketing. Verranno creati due spazi dei nomi denominati sales e marketing, come viene illustrato nell'esempio seguente:

kubectl create namespace sales
kubectl create namespace marketing

Per accertarsi che gli spazi dei nomi siano stati creati, eseguire kubectl get namespaces e verrà visualizzato un elenco simile all'output seguente.

NAME              STATUS   AGE
default           Active   39d
kube-node-lease   Active   39d
kube-public       Active   39d
kube-system       Active   39d
marketing         Active   7s
sales             Active   26m

È ora possibile distribuire i contenitori di SQL Server in ognuno di questi spazi dei nomi usando l'esempio YAML illustrato nell'esempio seguente. Si notino i metadati namespace aggiunti alla distribuzione YAML, quindi tutti i contenitori e i servizi di questa distribuzione vengono distribuiti nello spazio dei nomi sales.

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
  name: azure-disk
provisioner: kubernetes.io/azure-disk
parameters:
  storageAccountType: Standard_LRS
  kind: Managed
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mssql-sales
  namespace: sales
  labels:
    app: mssql-sales
spec:
  serviceName: "mssql-sales"
  replicas: 1
  selector:
    matchLabels:
      app: mssql-sales
  template:
    metadata:
      labels:
        app: mssql-sales
    spec:
      securityContext:
        fsGroup: 10001
      containers:
        - name: mssql-sales
          image: mcr.microsoft.com/mssql/server:2019-latest
          ports:
            - containerPort: 1433
              name: tcpsql
          env:
            - name: ACCEPT_EULA
              value: "Y"
            - name: MSSQL_ENABLE_HADR
              value: "1"
            - name: MSSQL_AGENT_ENABLED
              value: "1"
            - name: MSSQL_SA_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: mssql
                  key: MSSQL_SA_PASSWORD
          volumeMounts:
            - name: mssql
              mountPath: "/var/opt/mssql"
  volumeClaimTemplates:
    - metadata:
        name: mssql
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 8Gi
---
apiVersion: v1
kind: Service
metadata:
  name: mssql-sales-0
  namespace: sales
spec:
  type: LoadBalancer
  selector:
    statefulset.kubernetes.io/pod-name: mssql-sales-0
  ports:
    - protocol: TCP
      port: 1433
      targetPort: 1433
      name: tcpsql

Per visualizzare le risorse, è possibile eseguire il comando kubectl get all con lo spazio dei nomi specificato per visualizzare queste risorse:

kubectl get all -n sales
NAME                READY   STATUS    RESTARTS   AGE
pod/mssql-sales-0   1/1     Running   0          17m

NAME                    TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
service/mssql-sales-0   LoadBalancer   10.0.251.120   20.23.79.52   1433:32052/TCP   17m

NAME                           READY   AGE
statefulset.apps/mssql-sales   1/1     17m

Gli spazi dei nomi possono essere usati anche per limitare le risorse e i pod creati all'interno di uno spazio dei nomi, usando i criteri di intervallo limite e/o quota delle risorse, per gestire la creazione complessiva delle risorse all'interno di uno spazio dei nomi.

Configurare la qualità del servizio

Quando si distribuiscono più pod in un singolo cluster Kubernetes, è necessario condividere le risorse in modo appropriato per garantire l'esecuzione efficiente del cluster Kubernetes. È possibile configurare i pod affinché vengano assegnati a una particolare qualità del servizio (QoS).

Kubernetes usa le classi QoS per prendere decisioni sulla pianificazione e la rimozione dei pod. Per altre informazioni sulle diverse classi QoS, vedere Configurare la qualità del servizio per i pod.

Dal punto di vista di SQL Server, si consiglia di distribuire i pod di SQL Server usando QoS come Guaranteed per i carichi di lavoro basati sulla produzione. Considerando che un pod di SQL Server ha una sola istanza di contenitore di SQL Server in esecuzione per ottenere QoS garantito per tale pod, è necessario specificare le richieste di CPU e memoria per il contenitore che devono essere uguali ai limiti di memoria e CPU. In questo modo, i nodi forniscono ed eseguono il commit delle risorse necessarie specificate durante la distribuzione e hanno prestazioni prevedibili per i pod di SQL Server.

Di seguito è riportato un file YAML di distribuzione di esempio che distribuisce un contenitore di SQL Server nello spazio dei nomi predefinito e perché le richieste di risorse non sono state specificate, ma i limiti sono stati specificati in base alle linee guida nell'esempio di Qualità del servizio garantita, si nota che il pod creato nell'esempio seguente ha impostato QoS come Guaranteed. Quando non si specificano le richieste di risorse, Kubernetes considera i limiti delle risorse uguali alle richieste di risorse.

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
     name: azure-disk
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: Managed
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: mssql
 labels:
  app: mssql
spec:
 serviceName: "mssql"
 replicas: 1
 selector:
  matchLabels:
   app: mssql
 template:
  metadata:
   labels:
    app: mssql
  spec:
   securityContext:
     fsGroup: 10001
   containers:
   - name: mssql
     command:
       - /bin/bash
       - -c
       - cp /var/opt/config/mssql.conf /var/opt/mssql/mssql.conf && /opt/mssql/bin/sqlservr
     image: mcr.microsoft.com/mssql/server:2019-latest
     resources:
      limits:
       memory: 2Gi
       cpu: '2'
     ports:
     - containerPort: 1433
     env:
     - name: ACCEPT_EULA
       value: "Y"
     - name: MSSQL_ENABLE_HADR
       value: "1"
     - name: MSSQL_SA_PASSWORD
       valueFrom:
         secretKeyRef:
          name: mssql
          key: MSSQL_SA_PASSWORD
     volumeMounts:
     - name: mssql
       mountPath: "/var/opt/mssql"
     - name: userdata
       mountPath: "/var/opt/mssql/userdata"
     - name: userlog
       mountPath: "/var/opt/mssql/userlog"
     - name: tempdb
       mountPath: "/var/opt/mssql/tempdb"
     - name: mssql-config-volume
       mountPath: "/var/opt/config"
   volumes:
     - name: mssql-config-volume
       configMap:
        name: mssql
 volumeClaimTemplates:
   - metadata:
      name: mssql
     spec:
      accessModes:
      - ReadWriteOnce
      resources:
       requests:
        storage: 8Gi
   - metadata:
      name: userdata
     spec:
      accessModes:
      - ReadWriteOnce
      resources:
       requests:
        storage: 8Gi
   - metadata:
      name: userlog
     spec:
      accessModes:
      - ReadWriteOnce
      resources:
       requests:
        storage: 8Gi
   - metadata:
      name: tempdb
     spec:
      accessModes:
      - ReadWriteOnce
      resources:
       requests:
        storage: 8Gi

È possibile eseguire il comando kubectl describe pod mssql-0 per visualizzare il QoS come Guaranteed, con output simile al frammento di codice seguente.

...
QoS Class:                 Guaranteed
Node-Selectors:            <none>
Tolerations:               node.kubernetes.io/memory-pressure:NoSchedule op=Exists
                           node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                           node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
...

Per i carichi di lavoro non di produzione, in cui le prestazioni e la disponibilità non sono prioritarie, è possibile impostare QoS su Burstable o BestEffort.

Esempio di QoS con possibilità di burst

Per definire un esempio YAML Burstable, è necessario specificare le richieste di risorse, non i limiti delle risorse, oppure specificare i limiti, che sono superiori alle richieste. Il codice seguente mostra solo la differenza dell'esempio precedente, per definire un carico di lavoro con possibilità di burst.

apiVersion: apps/v1
kind: StatefulSet
metadata:
 name: mssql
 labels:
  app: mssql
spec:
 serviceName: "mssql"
 replicas: 1
 selector:
  matchLabels:
   app: mssql
 template:
  metadata:
   labels:
    app: mssql
  spec:
   securityContext:
     fsGroup: 10001
   containers:
   - name: mssql
     command:
       - /bin/bash
       - -c
       - cp /var/opt/config/mssql.conf /var/opt/mssql/mssql.conf && /opt/mssql/bin/sqlservr
     image: mcr.microsoft.com/mssql/server:2019-latest
     resources:
      requests:
       memory: 2Gi
       cpu: '2'

È possibile eseguire il comando kubectl describe pod mssql-0 per visualizzare il QoS come Burstable, con output simile al frammento di codice seguente.

...
QoS Class:                 Burstable
Node-Selectors:            <none>
Tolerations:               node.kubernetes.io/memory-pressure:NoSchedule op=Exists
                           node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                           node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
...

Esempio QoS di massimo sforzo

Per definire un esempio YAML BestEffort, rimuovere le richieste di risorse e i limiti delle risorse. Si otterrà il “best effort” QoS, come definito in Creare un pod a cui viene assegnata una classe QoS di BestEffort. Come prima, il codice seguente mostra solo la differenza dell'esempio Guaranteed, per definire un carico di lavoro best effort. Sono le opzioni meno consigliate per i pod di SQL Server, perché probabilmente sono i primi a essere terminati in caso di contesa di risorse. Anche per scenari di test e controllo di qualità, è consigliabile usare l'opzione con possibilità di burst per SQL Server.

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: mssql
  labels:
    app: mssql
spec:
  serviceName: "mssql"
  replicas: 1
  selector:
    matchLabels:
      app: mssql
  template:
    metadata:
      labels:
        app: mssql
    spec:
      securityContext:
        fsGroup: 10001
      containers:
        - name: mssql
          command:
            - /bin/bash
            - -c
            - cp /var/opt/config/mssql.conf /var/opt/mssql/mssql.conf && /opt/mssql/bin/sqlservr
          image: mcr.microsoft.com/mssql/server:2019-latest
          ports:
            - containerPort: 1433

È possibile eseguire il comando kubectl describe pod mssql-0 per visualizzare il QoS come BestEffort, con output simile al frammento di codice seguente.

...
QoS Class:                 BestEffort
Node-Selectors:            <none>
Tolerations:               node.kubernetes.io/memory-pressure:NoSchedule op=Exists
                           node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                           node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
...