适用于: SQL Server - Linux

本文包含有关使用 StatefulSet 在 Kubernetes 上运行 SQL Server 容器的最佳做法和指南。 建议在 Kubernetes 中为每个 Pod 部署一个 SQL Server 容器(实例)。 这样,你就在 Kubernetes 群集中为每个 Pod 部署了一个 SQL Server 实例。

同样,部署脚本建议通过将 replicas 值设置为 1 来部署一个 SQL Server 实例。 如果输入一个大于 1 的数字作为 replicas 值,将获得许多具有共同相关名称的 SQL Server 实例。 例如,在下面的脚本中,如果将数字 2 分配为 replicas 的值,你就会部署两个 SQL Server Pod,名称分别为 mssql-0 mssql-1

建议每个部署脚本对应一个 SQL Server 的另一个原因是,这样就可以为每个部署的 SQL Server 实例单独更改配置值、版本、跟踪标志和其他设置。

在以下示例中,StatefulSet 工作负载名称应与 .spec.template.metadata.labels 值匹配,本例中为 mssql 。 有关详细信息,请参阅 StatefulSet

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
     - 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 中的可用性组副本)。

选择工作负载类型

选择正确的工作负载部署类型不会影响性能,但 StatefulSet 确实提供了标识粘性要求。

StatefulSet 工作负载

SQL Server 是一个数据库应用程序,因此大多数情况下应部署为 StatefulSet 工作负载类型。 将工作负载部署为 StatefulSet 有助于提供唯一网络标识、持久稳定的存储等功能。 有关这一类型的工作负载的详细信息,请参阅 Kubernetes 文档

在使用与 StatefulSet 工作负载相同的部署 YAML 脚本部署 SQL Server 容器的多个副本时,需要考虑的一个重要参数是 Pod 管理策略,即 .spec.podManagementPolicy

此设置有两个可能的值:

  • OrderedReady:这是默认值,行为如部署和缩放保证中所述。

  • Parallel:这是一种备用策略,用于并行创建和启动 Pod(在本例中为 SQL Server Pod),而无需等待创建其他 Pod。同样,在终止期间,所有 Pod 都会被并行删除。 如果要部署彼此独立的 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
                - 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 Pod 是彼此独立的,所以 Parallel 通常是用于 podManagementPolicy 的值。

    下面的示例是在使用并行策略创建 Pod 后 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 目的。

    通过命名空间隔离

    命名空间提供了一种机制,用于隔离单个 Kubernetes 群集中的资源组。 有关命名空间及其使用场景的详细信息,请参阅命名空间

    从 SQL Server 的角度来看,如果你计划在同时还托管其他资源的 Kubernetes 群集上运行 SQL Server Pod,则应在它们自己的命名空间中运行 SQL Server Pod,以便于管理。 例如,假设有多个部门共享同一 Kubernetes 群集,你需要为销售团队部署一个 SQL Server 实例,为营销团队部署另一个实例。 你将创建名为 salesmarketing 的两个命名空间,如以下示例所示:

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

    命名空间还可用于限制在命名空间中创建的资源和 Pod,使用 限制范围和/或资源配额策略来管理命名空间内的总体资源创建。

    配置 Pod 服务质量

    在单个 Kubernetes 群集上部署多个 Pod 时,必须相应地共享资源,以确保 Kubernetes 群集高效运行。 可以配置 Pod,以便为其分配特定的服务质量 (QoS)。

    Kubernetes 使用 QoS 类做出有关计划和逐出 Pod 的决策。 有关不同 QoS 类的详细信息,请参阅为 Pod 配置服务质量

    从 SQL Server 的角度来看,对于基于生产的工作负载,建议使用 QoS 作为 Guaranteed 来部署 SQL Server Pod。 考虑到 SQL Server Pod 只有一个运行的 SQL Server 容器实例来实现该 Pod 的有保证的 QoS,你需要为容器指定 CPU 和内存请求,它应等于内存和 CPU 限制。 这确保了节点将提供并提交部署期间指定的所需资源,并为 SQL Server Pod 提供可预测的性能。

    下面是在默认命名空间中部署一个 SQL Server 容器的示例部署 YAML,由于未指定资源请求,但根据有保证的服务质量示例中的准则规定了限制,因此我们看到下面创建的 Pod 已将 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
           - 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
         - 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 设置为 BurstableBestEffort

    可突发 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
           - 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 示例,请删除资源请求和资源限制。 你最终会获得最佳 QoS,如创建一个分配有 BestEffort QoS 类的 Pod 中定义的那样。 与前面一样,下面的代码只显示与 Guaranteed 示例的区别,以便定义最佳工作负载。 这些是 SQL Server Pod 最不推荐的选项,因为它们可能是第一批在资源争用的情况下终止的选项。 即使对于测试和 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
                - 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
  • 快速入门:使用 Azure Kubernetes 服务 (AKS) 部署 SQL Server 容器
  • 快速入门:使用 Helm 图表将 SQL Server Linux 容器部署到 Kubernetes
  • 在 AKS 上使用 DH2i 为 SQL Server 容器部署可用性组
  •