Partager via


Déployer des conteneurs Linux SQL Server sur Kubernetes avec StatefulSets

S’applique à : SQL Server - Linux

Cet article contient des bonnes pratiques et des conseils pour l’exécution de conteneurs SQL Server sur Kubernetes avec StatefulSets. Nous vous recommandons de déployer un conteneur (instance) SQL Server pour chaque pod dans Kubernetes. Ainsi, vous disposez d’une instance SQL Server déployée pour chaque pod dans le cluster Kubernetes.

De même, la recommandation du script de déploiement est de déployer une instance SQL Server en définissant la valeur de replicas sur 1. Si vous entrez un nombre supérieur à 1 pour la valeur replicas, vous obtenez ce nombre d’instances SQL avec des noms corrélés. Par exemple, dans le script ci-dessous, si vous avez affecté le nombre 2 comme valeur pour replicas, vous allez déployer deux pods SQL Server, respectivement avec les noms mssql-0 et mssql-1.

Une autre raison pour laquelle nous recommandons un seul SQL Server par script de déploiement est de permettre que les modifications des valeurs de configuration, de l’édition, des indicateurs de trace et d’autres paramètres soient effectuées indépendamment pour chaque instance SQL Server déployée.

Dans l’exemple suivant, le nom de la charge de travail StatefulSet doit correspondre à la valeur de .spec.template.metadata.labels, qui est dans ce cas mssql. Pour plus d’informations, consultez StatefulSets.

Important

La variable d’environnement SA_PASSWORD est dépréciée. Utilisez MSSQL_SA_PASSWORD à la place.

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

Si vous choisissez néanmoins de déployer plusieurs réplicas de l’instance SQL Server en utilisant le même déploiement, vous trouverez ce scénario décrit dans la section suivante. Cependant, il s’agit d’instances SQL Server indépendantes distinctes et non pas de réplicas (contrairement aux réplicas de groupe de disponibilité dans SQL Server).

Choisir le type de charge de travail

Le choix du type de déploiement de charge de travail approprié n’affecte pas les performances, mais StatefulSet répond aux exigences de persistance des identités.

Charges de travail StatefulSet

SQL Server est une application de base de données et elle doit donc principalement être déployée en tant que type de charge de travail StatefulSet. Le déploiement de charges de travail en tant que StatefulSet permet de fournir des fonctionnalités comme des identifiants réseau uniques, un stockage persistant et stable, etc. Pour plus d’informations sur ce type de charge de travail, consultez la documentation Kubernetes.

Lors du déploiement de plusieurs réplicas de conteneurs SQL Server en utilisant le même script YAML de déploiement qu’une charge de travail StatefulSet, un paramètre important à prendre en compte est Stratégies de gestion des pods, c’est-à-dire .spec.podManagementPolicy.

Deux valeurs sont possibles pour ce paramètre :

  • OrderedReady : c’est la valeur par défaut, et le comportement est celui décrit dans les garanties de déploiement et de mise à l’échelle.

  • Parallel : c’est la stratégie alternative qui crée et lance les pods (dans le cas présent, les pods SQL Server) en parallèle, sans attendre que d’autres pods soient créés. De même, tous les pods sont supprimés en parallèle lors de l’arrêt. Vous pouvez utiliser cette option quand vous déployez des instances SQL Server indépendantes les unes des autres, et quand vous n’avez pas l’intention de suivre un ordre pour démarrer ou supprimer les instances 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
    

Comme les pods SQL Server déployés sur Kubernetes sont indépendants les uns des autres, Parallel est la valeur normalement utilisée pour podManagementPolicy.

L’exemple suivant est l’exemple de sortie pour kubectl get all, juste après la création des pods en utilisant une stratégie parallèle :

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

Charges de travail de déploiement

Vous pouvez utiliser le type déploiement pour SQL Server dans les scénarios où vous voulez déployer des conteneurs SQL Server en tant qu’applications de base de données sans état, par exemple quand la persistance des données n’est pas critique. Vous utilisez cela par exemple à des fins de test/assurance qualité ou pour la CI/CD.

Isolation via des espaces de noms

Les espaces de noms fournissent un mécanisme permettant d’isoler des groupes de ressources au sein d’un même cluster Kubernetes. Pour plus d’informations sur les espaces de noms et quand les utiliser, consultez Espaces de noms.

Du point de vue de SQL Server, si vous envisagez d’exécuter des pods SQL Server sur un cluster Kubernetes qui héberge également d’autres ressources, vous devez exécuter les pods SQL Server dans leur propre espace de noms afin de faciliter la gestion et l’administration. Par exemple, supposez que vous avez plusieurs départements partageant le même cluster Kubernetes, et que vous voulez déployer une instance SQL Server pour l’équipe Ventes et une autre pour l’équipe Marketing. Vous allez créer deux espaces de noms appelés sales et marketing, comme illustré dans l’exemple suivant :

kubectl create namespace sales
kubectl create namespace marketing

Pour vérifier que les espaces de noms sont créés, exécutez kubectl get namespaces, et vous verrez une liste similaire à la sortie suivante.

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

Vous pouvez maintenant déployer des conteneurs SQL Server dans chacun de ces espaces de noms en utilisant l’exemple YAML illustré dans l’exemple suivant. Notez que les métadonnées namespace ont été ajoutées au fichier YAML de déploiement, de sorte que tous les conteneurs et services de ce déploiement sont déployés dans l’espace de noms 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

Pour voir les ressources, vous pouvez exécuter la commande kubectl get all en spécifiant l’espace de noms :

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

Les espaces de noms peuvent également être utilisés pour limiter les ressources et les pods créés au sein d’un espace de noms, en utilisant la plage de limites et/ou des stratégies de quota de ressources, afin de gérer la création globale des ressources au sein d’un espace de noms.

Configurer la qualité de service des pods

Quand vous déployez plusieurs pods sur un même cluster Kubernetes, vous devez partager les ressources de façon appropriée pour garantir l’exécution efficace du cluster Kubernetes. Vous pouvez configurer les pods de façon à qu’ils bénéficient d’une qualité de service (QoS) particulière.

Kubernetes utilise des classes de qualité de service pour prendre des décisions concernant la planification et l’éviction des pods. Pour plus d’informations sur les différentes classes de qualité de service, consultez Configurer la qualité de service pour les pods.

Du point de vue SQL Server, nous vous recommandons de déployer les pods SQL Server en utilisant la qualité de service Guaranteed pour les charges de travail en production. Étant donné qu’un pod SQL Server n’a qu’une seule instance de conteneur SQL Server en cours d’exécution pour satisfaire à la garantie de qualité de service pour ce pod, vous devez spécifier que les demandes de processeur et de mémoire pour le conteneur doivent être égales aux limites de mémoire et de processeur. Ceci garantit que les nœuds fournissent et valident les ressources requises spécifiées lors du déploiement et qu’ils offrent des performances prévisibles pour les pods SQL Server.

Voici un exemple de déploiement YAML qui déploie un conteneur SQL Server dans l’espace de noms par défaut. Étant donné que les demandes de ressources n’ont pas été spécifiées, mais que les limites ont été spécifiées conformément aux instructions de l’exemple de qualité de service garantie, nous constatons que la valeur QoS (Qualité de service) du pod créé dans l’exemple suivant est définie sur Guaranteed. Quand vous ne spécifiez pas les demandes de ressources, Kubernetes considère que les limites de ressources sont égales aux demandes de ressources.

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

Vous pouvez exécuter la commande kubectl describe pod mssql-0 pour voir que la qualité de service est définie sur Guaranteed, avec une sortie similaire à l’extrait de code suivant.

...
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
...

Pour les charges de travail hors production, où les performances et la disponibilité ne sont pas une priorité, vous pouvez envisager de définir la qualité de service sur Burstable ou BestEffort.

Exemple de qualité de service burstable

Pour définir un exemple YAML Burstable, vous spécifiez les demandes de ressources et non pas les limites de ressources, ou bien vous spécifiez les limites, qui sont supérieures aux demandes. Le code suivant montre seulement la différence par rapport à l’exemple précédent, afin de définir une charge de travail burstable .

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'

Vous pouvez exécuter la commande kubectl describe pod mssql-0 pour voir que la qualité de service est définie sur Burstable, avec une sortie similaire à l’extrait de code suivant.

...
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
...

Exemple de qualité de service « meilleure possibilité » (best effort)

Pour définir un exemple YAML BestEffort, supprimez les demandes de ressources et les limites de ressources. Vous obtenez alors la qualité de service « meilleure possibilité », comme défini dans Créer un pod qui reçoit une classe de qualité de service « BestEffort ». Comme précédemment, le code suivant montre seulement la différence par rapport à l’exemple Guaranteed, afin de définir une charge de travail « meilleure possibilité » (best effort). Ce sont les options les moins recommandées pour les pods SQL Server, car ils seraient probablement les premiers à être arrêtés en cas de contention des ressources. Même pour les scénarios de test et d’assurance qualité, nous vous recommandons d’utiliser l’option Burstable pour 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

Vous pouvez exécuter la commande kubectl describe pod mssql-0 pour voir que la qualité de service est définie sur BestEffort, avec une sortie similaire à l’extrait de code suivant.

...
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
...