使用 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-0
和 mssql-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 实例,为营销团队部署另一个实例。 你将创建名为 sales
和 marketing
的两个命名空间,如以下示例所示:
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 设置为 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: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
...