Udostępnij za pośrednictwem


Wdrażanie kontenerów systemu Linux programu SQL Server na platformie Kubernetes przy użyciu elementów StatefulSets

Dotyczy:programu SQL Server — Linux

Ten artykuł zawiera najlepsze rozwiązania i wskazówki dotyczące uruchamiania kontenerów programu SQL Server na platformie Kubernetes z zestawami StatefulSets. Zalecamy wdrożenie jednego kontenera SQL Server na pod na platformie Kubernetes. W związku z tym w klastrze Kubernetes wdrożono jedno wystąpienie SQL Server w każdym zasobniku.

Podobnie zaleca się wdrożenie jednego wystąpienia programu SQL Server przez ustawienie wartości replicas na 1. Jeśli wprowadzisz liczbę większą niż 1 jako wartość replicas, uzyskasz tyle wystąpień programu SQL Server ze skorelowanymi nazwami. Na przykład w poniższym skrypcie, jeśli przypiszesz liczbę 2 jako wartość dla replicas, wdrożysz dwa zasobniki SQL Server, z nazwami mssql-0 i mssql-1 odpowiednio.

Innym powodem, dla którego zalecamy użycie jednego skryptu wdrażania programu SQL Server, jest umożliwienie niezależnie wprowadzania zmian w wartościach konfiguracji, edycji, flag śledzenia i innych ustawień dla każdego wdrożonego wystąpienia programu SQL Server.

W poniższym przykładzie nazwa obciążenia StatefulSet powinna być zgodna z wartością .spec.template.metadata.labels, która w tym przypadku jest mssql. Aby uzyskać więcej informacji, zobacz StatefulSets.

Ważny

Zmienna środowiskowa SA_PASSWORD jest przestarzała. Zamiast tego użyj 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:2022-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

Jeśli nadal zdecydujesz się wdrożyć więcej niż jedną replikę wystąpienia programu SQL Server przy użyciu tego samego wdrożenia, ten scenariusz zostanie omówiony w następnej sekcji. Są to jednak oddzielne niezależne wystąpienia programu SQL Server, a nie repliki (w przeciwieństwie do replik grup dostępności w programie SQL Server).

Wybieranie typu obciążenia

Wybór odpowiedniego typu wdrożenia obciążenia nie wpływa na wydajność, ale StatefulSet zapewnia wymagania dotyczące zachowania spójności tożsamości.

Obciążenia StatefulSet

SQL Server to aplikacja bazy danych, dlatego w większości powinna być wdrażana jako typ obciążenia StatefulSet. Wdrażanie obciążeń jako StatefulSet pomaga udostępniać funkcje takie jak unikatowe identyfikatory sieciowe, trwałe i stabilne przechowywanie oraz inne. Aby uzyskać więcej informacji na temat tego typu obciążenia, zapoznaj się z dokumentacją Kubernetes.

Podczas wdrażania więcej niż jednej repliki kontenerów programu SQL Server przy użyciu tego samego skryptu YAML wdrożenia co obciążenie StatefulSet, ważnym parametrem do rozważenia jest zasad zarządzania zasobnikami, czyli .spec.podManagementPolicy.

Dla tego ustawienia są dostępne dwie wartości:

  • OrderedReady: jest to wartość domyślna, a zachowanie jest tak jak opisano w wdrażaniu i gwarancjach skalowania.

  • Parallel: jest to alternatywna zasada, która tworzy i uruchamia zasobniki (w tym przypadku zasobniki programu SQL Server) równolegle, bez oczekiwania na utworzenie innych zasobników Podobnie wszystkie zasobniki są usuwane równolegle podczas kończenia. Tej opcji można użyć podczas wdrażania wystąpień programu SQL Server niezależnych od siebie, a jeśli nie zamierzasz wykonywać instrukcji w celu uruchomienia lub usunięcia wystąpień programu 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:2022-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
    

Ponieważ zasobniki programu SQL Server wdrożone na platformie Kubernetes są niezależne od siebie, Parallel jest wartością używaną zwykle dla podManagementPolicy.

Poniższy przykład to przykładowe dane wyjściowe dla kubectl get all, tuż po utworzeniu zasobników przy użyciu zasad równoległych:

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

Obciążenia wdrożenia

Można użyć typu wdrożenia dla SQL Servera w scenariuszach, kiedy chcesz wdrożyć kontenery SQL Servera jako bezstanowe aplikacje bazodanowe, na przykład gdy trwałość danych nie jest krytyczna. Niektóre z takich przykładów są przeznaczone do celów testowych, kontroli jakości lub do procesów ciągłej integracji i ciągłego wdrażania.

Izolacja za pośrednictwem przestrzeni nazw

Przestrzeń nazw zapewnia mechanizm izolowania grup zasobów w obrębie jednego klastra Kubernetes. Aby uzyskać więcej informacji o przestrzeniach nazw i korzystaniu z nich, zobacz Przestrzenie nazw.

Z perspektywy programu SQL Server, jeśli planujesz uruchamiać zasobniki programu SQL Server w klastrze Kubernetes, który hostuje również inne zasoby, należy uruchomić zasobniki programu SQL Server we własnej przestrzeni nazw, aby ułatwić zarządzanie i administrowanie. Rozważmy na przykład sytuację, w której masz wiele działów korzystających z tego samego klastra Kubernetes i chcesz wdrożyć instancję SQL Server dla zespołu sprzedaży oraz kolejną dla zespołu marketingu. Utworzysz dwie przestrzenie nazw o nazwie sales i marketing, jak pokazano w poniższym przykładzie:

kubectl create namespace sales
kubectl create namespace marketing

Aby sprawdzić, czy przestrzenie nazw zostały utworzone, uruchom polecenie kubectl get namespacesi zobaczysz listę podobną do poniższego wyniku.

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

Teraz możesz wdrożyć kontenery programu SQL Server w każdej z tych przestrzeni nazw przy użyciu przykładowego kodu YAML pokazanego w poniższym przykładzie. Zwróć uwagę na metadane namespace dodane do wdrożenia YAML, więc wszystkie kontenery i usługi tego wdrożenia są wdrażane w przestrzeni nazw 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:2022-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

Aby wyświetlić zasoby, możesz uruchomić polecenie kubectl get all z określoną przestrzenią nazw, aby zobaczyć te zasoby.

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

Przestrzenie nazw mogą również służyć do ograniczania zasobów i zasobników utworzonych w przestrzeni nazw przy użyciu zakresu limitu i/lub limitu przydziału zasobów zasad, aby zarządzać ogólnym tworzeniem zasobów w przestrzeni nazw.

Konfigurowanie jakości usługi podu

Podczas wdrażania wielu zasobników w jednym klastrze Kubernetes należy odpowiednio udostępniać zasoby, aby zapewnić efektywne działanie klastra Kubernetes. Możesz skonfigurować zasobniki tak, aby były przypisane do określonej jakości usług (QoS).

Platforma Kubernetes używa klas QoS do podejmowania decyzji dotyczących planowania i usuwania zasobników. Aby uzyskać więcej informacji na temat różnych klas QoS, zobacz Configure Quality of Service for Pods.

Z perspektywy programu SQL Server zalecamy wdrożenie zasobników programu SQL Server przy użyciu funkcji QoS jako Guaranteed dla obciążeń opartych na środowisku produkcyjnym. Biorąc pod uwagę, że zasobnik programu SQL Server ma uruchomione tylko jedno wystąpienie kontenera programu SQL Server, aby osiągnąć gwarantowaną jakość usług (QoS) dla tego zasobnika, należy określić procesora CPU i pamięci dla kontenera, które powinno być równe limitom pamięci i procesora CPU. Dzięki temu węzły zapewniają i alokują wymagane zasoby określone podczas wdrażania oraz zapewniają przewidywalną wydajność dla podów programu SQL Server.

Oto przykładowe wdrożenie YAML, które wdraża jeden kontener SQL Server w domyślnej przestrzeni nazw. Ponieważ żądania zasobów nie zostały określone, lecz limity zostały określone zgodnie z wytycznymi w przykładzie Gwarantowana jakość usługi, widzimy, że pod utworzony w poniższym przykładzie ma ustawiony QoS jako Guaranteed. Jeśli nie określisz żądań zasobów, platforma Kubernetes uwzględnia limity zasobów równe żądaniom zasobów.

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:2022-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

Możesz uruchomić polecenie kubectl describe pod mssql-0, aby wyświetlić QoS jako Guaranteed, z danymi wyjściowymi podobnymi do poniższego fragmentu kodu.

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

W przypadku obciążeń nieprodukcyjnych, w których wydajność i dostępność nie są wysokim priorytetem, można rozważyć ustawienie QoS na Burstable lub BestEffort.

Próbka QoS z możliwością burstingu

Aby zdefiniować przykład Burstable YAML, należy określić żądania zasobów , a nie limity zasobów , lub określić limity zasobów , które są wyższe niż żądania . Poniższy kod wyświetla tylko różnicę z poprzedniego przykładu, aby zdefiniować elastyczne obciążenie .

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:2022-latest
     resources:
      requests:
       memory: 2Gi
       cpu: '2'

Możesz uruchomić polecenie kubectl describe pod mssql-0, aby wyświetlić QoS jako Burstable, z danymi wyjściowymi podobnymi do poniższego fragmentu kodu.

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

Przykład QoS w trybie best effort

Aby przygotować przykład BestEffort YAML, usuń żądania zasobów i limity zasobów . W końcu uzyskasz klasę QoS BestEffort, zgodnie z definicją w Utworzyć Pod, który otrzymuje przypisaną klasę QoS BestEffort. Tak jak wcześniej poniższy kod wyświetla tylko różnicę z przykładu Guaranteed, aby zdefiniować najlepsze wysiłki obciążenia. Są to opcje najmniej zalecane dla podów SQL Server, ponieważ prawdopodobnie będą pierwszymi do zakończenia w przypadku konkurencji o zasoby. Nawet w przypadku scenariuszy testowych i kontroli jakości zalecamy użycie opcji Z możliwością rozszerzenia dla programu 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:2022-latest
          ports:
            - containerPort: 1433

Możesz uruchomić polecenie kubectl describe pod mssql-0, aby wyświetlić QoS jako BestEffort, z danymi wyjściowymi podobnymi do poniższego fragmentu kodu.

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