StatefulSet を使用して Kubernetes に SQL Server Linux コンテナーをデプロイする

適用対象: SQL Server - Linux

この記事には、StatefulSet を使用して Kubernetes で SQL Server コンテナーを実行するためのベスト プラクティスとガイダンスが含まれます。 Kubernetes のポッドごとに 1 つの SQL Server コンテナー (インスタンス) をデプロイすることをお勧めします。 そうすれば、Kubernetes クラスター内のポッドごとに 1 つの SQL Server インスタンスがデプロイされます。

同様に、デプロイ スクリプトでは、replicas の値を 1 に設定して、1 つの SQL Server インスタンスをデプロイすることをお勧めします。 replicas の値として 1 より大きい数値を入力すると、相関する名前を持つ SQL Server インスタンスがその数だけ作成されます。 たとえば、次のスクリプトで、replicas の値を 2 にすると、それぞれ mssql-0mssql-1 いう名前を持つ 2 つの SQL Server ポッドがデプロイされます。

デプロイ スクリプトごとに 1 つの SQL Server にすることが推奨されるもう 1 つの理由は、構成値、エディション、トレース フラグ、その他の設定を、デプロイされる SQL Server インスタンスごとに個別に変更できるようにするためです。

次の例では、StatefulSet ワークロード名が .spec.template.metadata.labels の値と一致する必要があります (この場合は mssql)。 詳細については、StatefulSets を参照してください。

重要

SA_PASSWORD 環境変数は非推奨です。 代わりに 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

それでも、同じデプロイを使って SQL Server インスタンスの複数のレプリカをデプロイする場合は、次のセクションでそのシナリオについて説明します。 ただし、これらは個別の独立した SQL Server インスタンスであり、レプリカではありません (SQL Server の可用性グループ レプリカとは異なります)。

ワークロードの種類を選択する

適切なワークロードのデプロイの種類を選んでもパフォーマンスには影響しませんが、ID の持続性の要件が StatefulSet によって提供されます。

StatefulSet のワークロード

SQL Server はデータベース アプリケーションであるため、ほとんどの場合、StatefulSet ワークロードの種類としてデプロイする必要があります。 ワークロードを StatefulSet としてデプロイすると、一意のネットワーク識別や、永続的で安定したストレージなどの機能を提供するのに役立ちます。 この種類のワークロードについて詳しくは、Kubernetes のドキュメントをご覧ください。

StatefulSet ワークロードと同じデプロイ YAML スクリプトを使って SQL Server コンテナーの複数のレプリカをデプロイする場合、考慮すべき重要なパラメーターは、ポッド管理ポリシーつまり .spec.podManagementPolicy です。

この設定には、次の 2 つの値を指定できます。

  • OrderedReady: これは既定値であり、動作は「デプロイとスケーリングの保証」で説明されているとおりです。

  • Parallel: これは、他のポッドが作成されるのを待たずに、ポッド (この場合は SQL Server ポッド) を並列で作成して起動する代わりのポリシーです。同様に、終了時にはすべてのポッドが並列で削除されます。 このオプションは、互いに独立している SQL Server インスタンスをデプロイするときや、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
    

Kubernetes にデプロイされる SQL Server ポッドは相互に独立しているため、podManagementPolicy に通常使われる値は Parallel です。

次に示すのは、並列ポリシーを使ってポッドを作成した直後の kubectl get all に対する出力の例です。

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

デプロイのワークロード

たとえばデータの永続化が重要でない場合など、ステートレス データベース アプリケーションとして SQL Server コンテナーをデプロイするシナリオでは、SQL Server のデプロイの種類を使用できます。 そのような例としては、テストと QA や CI/CD などがあります。

名前空間による分離

名前空間は、1 つの Kubernetes クラスター内でリソースのグループを分離するためのメカニズムを提供します。 名前空間とその使用方法について詳しくは、「Namespace (名前空間)」をご覧ください。

SQL Server の観点からは、他のリソースもホストしている Kubernetes クラスターで SQL Server ポッドを実行する予定の場合は、管理しやすくするため、SQL Server ポッドを独自の名前空間で実行する必要があります。 たとえば、同じ Kubernetes クラスターを共有する複数の部署があり、営業チーム用とマーケティング チーム用に個別の SQL Server インスタンスをデプロイするとします。 次の例に示すように、salesmarketing という名前の 2 つの名前空間を作成します。

kubectl create namespace sales
kubectl create namespace marketing

名前空間が作成されていることを確認するには、kubectl get namespaces を実行すると、次の出力のような一覧が表示されます。

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

これで、次の例に示すような YAML を使って、これらの各名前空間に SQL Server コンテナーをデプロイできます。 デプロイ YAML に追加されている namespace メタデータに注意してください。これにより、このデプロイのすべてのコンテナーとサービスは 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

リソースを表示するには、名前空間を指定して kubectl get all コマンドを実行します。

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

範囲制限リソース クォータのポリシーを使って名前空間内に作成されるリソースとポッドを制限することで、名前空間内でのリソースの全体的な作成を管理することもできます。

ポッドのサービスの品質を構成する

1 つの Kubernetes クラスターに複数のポッドをデプロイする場合は、リソースを適切に共有して、Kubernetes クラスターを効率的に実行できるようにする必要があります。 特定のサービスの品質 (QoS) が割り当てられるようにポッドを構成できます。

Kubernetes では、QoS の "クラス" を使って、ポッドのスケジュールと削除に関する決定が行われます。 QoS のさまざまなクラスについて詳しくは、「Pod に Quality of Service を設定する」をご覧ください。

SQL Server の観点からは、運用ベースのワークロードの場合は QoS として Guaranteed を使って SQL Server ポッドをデプロイすることをお勧めします。 SQL Server ポッドで SQL Server コンテナー インスタンスを 1 つだけ実行して、そのポッドで保証された QoS を実現することを考慮すると、メモリと CPU の "制限" と等しくなるように、コンテナーの CPU とメモリの "要求" を指定する必要があります。 これにより、デプロイの間に指定された必要なリソースがノードによって提供およびコミットされ、SQL Server ポッドのパフォーマンスが予測可能になります。

既定の名前空間に 1 つの SQL Server コンテナーをデプロイするデプロイ YAML の例を次に示します。リソース要求は指定されていませんが、制限はサービスの品質の保証に関するページの例のガイドラインに従って指定されているため、次の例で作成されるポッドの QoS は Guaranteed に設定されることがわかります。 リソース要求を指定しないと、Kubernetes はリソースの "制限" がリソースの "要求" と等しいものと見なします。

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

kubectl describe pod mssql-0 コマンドを実行すると、出力は次のスニペットのようになり、QoS は Guaranteed と表示されます。

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

パフォーマンスと可用性の優先度が高くない非運用ワークロードの場合は、QoS を Burstable または BestEffort に設定することを検討できます。

バースト可能 QoS の例

Burstable YAML の例を定義するには、リソースの "制限" を指定しないでリソースの "要求" を指定するか、"要求" より大きい "制限" を指定します。 次のコードでは、バースト可能なワークロードを定義する場合に、前の例と違う部分のみを示してあります。

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'

kubectl describe pod mssql-0 コマンドを実行すると、出力は次のスニペットのようになり、QoS は Burstable と表示されます。

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

ベスト エフォート QoS の例

BestEffort YAML の例を定義するには、リソースの "要求" とリソースの "制限" を削除します。BestEffort の QoS クラスを割り当てた Pod を作成する」で定義されているように、最終的にベスト エフォート QoS が得られます。 前と同様に、次のコードでは、ベスト エフォートのワークロードを定義する場合に、Guaranteed の例と違う部分のみを示してあります。 これらは、リソースが競合した場合に最初に終了される可能性があるため、SQL Server ポッドに対して最も推奨されないオプションです。 テストや QA のシナリオであっても、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

kubectl describe pod mssql-0 コマンドを実行すると、出力は次のスニペットのようになり、QoS は BestEffort と表示されます。

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