使用 StatefulSet 在 Kubernetes 上部署 SQL Server Linux 容器

适用于: 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-0mssql-1

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

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

重要

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

如果你仍然选择使用相同的部署来部署 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: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
    

由于部署在 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: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

若要查看资源,可以使用指定的命名空间运行 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
       - -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

可以运行 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
       - -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'

可以运行 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
            - -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

可以运行 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
...