如何在 SQL Server Linux 容器中使用分布式事务

适用于:SQL Server - Linux

本文介绍如何设置用于分布式事务的 SQL Server Linux 容器,包括特殊要求和方案。

SQL Server 容器映像可以使用支持分布式事务所需的 Microsoft 分布式事务处理协调器 (MSDTC)。 若要理解 MSDTC 的通信要求,请参阅如何在 Linux 上配置 Microsoft 分布式事务处理协调器 (MSDTC)

注意

默认情况下,SQL Server 2017 (14.x) 在根容器中运行,而 SQL Server 2019 (15.x) 及更高版本容器以非根用户身份运行。

配置

若要启用 SQL Server 容器中的 MSDTC 事务,必须设置两个新的环境变量:

  • MSSQL_RPC_PORT:RPC 端点映射程序服务绑定和侦听的 TCP 端口。
  • MSSQL_DTC_TCP_PORT:配置 MSDTC 服务侦听的端口。

请求并运行

以下示例演示如何使用这些环境变量来请求和运行为 MSDTC 配置的单个 SQL Server 2017 容器。 通过此操作,该容器可以与任何主机上的任一应用程序通信。

重要

SA_PASSWORD 环境变量已弃用。 请改用 MSSQL_SA_PASSWORD

docker run \
   -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=<password>' \
   -e 'MSSQL_RPC_PORT=135' -e 'MSSQL_DTC_TCP_PORT=51000' \
   -p 51433:1433 -p 135:135 -p 51000:51000  \
   -d mcr.microsoft.com/mssql/server:2017-latest
docker run `
   -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=<password>" `
   -e "MSSQL_RPC_PORT=135" -e "MSSQL_DTC_TCP_PORT=51000" `
   -p 51433:1433 -p 135:135 -p 51000:51000  `
   -d mcr.microsoft.com/mssql/server:2017-latest

以下示例演示如何使用这些环境变量来拉取和运行为 MSDTC 配置的单个 SQL Server 2019 (15.x) 容器。 通过此操作,该容器可以与任何主机上的任一应用程序通信。

重要

SA_PASSWORD 环境变量已弃用。 请改用 MSSQL_SA_PASSWORD

docker run \
   -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=<password>' \
   -e 'MSSQL_RPC_PORT=135' -e 'MSSQL_DTC_TCP_PORT=51000' \
   -p 51433:1433 -p 135:135 -p 51000:51000  \
   -d mcr.microsoft.com/mssql/server:2019-GA-ubuntu-20.04
docker run `
   -e "ACCEPT_EULA=Y" -e "MSSQL_SA_PASSWORD=<password>" `
   -e "MSSQL_RPC_PORT=135" -e "MSSQL_DTC_TCP_PORT=51000" `
   -p 51433:1433 -p 135:135 -p 51000:51000  `
   -d mcr.microsoft.com/mssql/server:2019-GA-ubuntu-20.04

注意

密码应遵循 SQL Server 默认密码策略。 默认情况下,密码必须为至少八个字符且包含以下四种字符中的三种:大写字母、小写字母、十进制数字、符号。 密码可最长为 128 个字符。 使用的密码应尽可能长,尽可能复杂。

在此命令中,RPC 端点映射程序服务已绑定到 135 端口,并且 MSDTC 服务已绑定到该容器的虚拟网络中的 51000 端口 。 SQL Server TDS 通信发生在 1433 端口,也是在容器的虚拟网络中。 这些端口从外部暴露给主机,即 TDS 端口 51433、RPC 端点映射程序端口 135 和 MSDTC 端口 51000。

RPC 端点映射程序和 MSDTC 端口在主机和容器上不必相同。 因此,若 RPC 端点映射程序端口在容器上配置为 135,它可能会映射到 13501 端口或主机服务器上的任何其他可用端口。

配置防火墙

为了与主机通信和通过主机通信,还必须在主机服务器上为容器配置防火墙。 为 SQL Server 容器公开用于外部通信的所有端口打开防火墙。 前一示例中,这些端口是 135 端口、51433 端口和 51000 端口。 这些端口是主机本身的端口,不是容器中映射到的端口。 因此,如果将容器的 RPC 端点映射程序端口 51000 映射到主机的端口 51001,则应在防火墙中打开端口 51001(而非 51000),以便与主机通信。

以下示例演示如何在 Ubuntu 上创建这些规则。

sudo ufw allow from any to any port 51433 proto tcp
sudo ufw allow from any to any port 51000 proto tcp
sudo ufw allow from any to any port 135 proto tcp

以下示例演示如何在 Red Hat Enterprise Linux (RHEL) 上完成此操作:

sudo firewall-cmd --zone=public --add-port=51433/tcp --permanent
sudo firewall-cmd --zone=public --add-port=51000/tcp --permanent
sudo firewall-cmd --zone=public --add-port=135/tcp --permanent
sudo firewall-cmd --reload

在主机上配置端口路由

在前一示例中,由于单个 SQL Server 容器将 RPC 135 端口映射到主机上的 135 端口,所以主机的分布式事务现在不应进行进一步的配置。 由于 SQL Server 在这些容器中使用提升的权限运行,因此可直接在作为根运行的容器中使用 135 端口。 对于容器外的 SQL Server 或非根容器,必须在容器中使用不同的临时端口(如 13500),然后必须将打算用于 135 端口的流量路由到该端口。 还需要在容器内配置从容器端口 135 到临时端口的端口路由规则。

但是,如果决定将容器的 135 端口映射到主机上的其他端口(如 13500 端口),则必须在主机上配置端口路由。 这样,SQL Server 容器便可参与主机和其他外部服务器的分布式事务。

有关路由端口的详细信息,请参阅配置端口路由

Kubernetes 上具有 MSDTC 的 SQL Server 容器

如果要在 Kubernetes 平台上部署 SQL Server 容器,请参阅以下示例 YAML 部署清单。 在此示例中,Kubernetes 平台是一项 Azure Kubernetes 服务 (AKS)。

方案 1:MSDTC 客户端连接到 Kubernetes 容器中的 SQL Server

下图显示了 MSDTC 客户端连接到在 Kubernetes 上的 Linux 容器内运行的 SQL Server 上的 MSDTC 的过程。

显示 MSDTC 客户端连接到在 Linux 容器内运行的 SQL Server 上的 MSDTC 的过程的关系图。

  1. MSDTC 客户端与 Kubernetes 主机上的端口 135 建立连接。
  2. 该连接转发到容器上的端口 135。
  3. 容器将连接转发到 RPC 端点映射程序,本例中该映射程序位于端口 13500 上。
  4. 端点映射程序告诉 MSDTC 客户端 MSDTC 在容器中运行的是哪个端口(本例中为端口 51000)。
  5. MSDTC 客户端通过连接到端口 51000 上的主机直接连接到 MSDTC,该连接会转发到容器内的 SQL Server。

方案 2:SQL Server 连接到 Kubernetes 容器中的 SQL Server

下图显示了一个 SQL Server Linux 容器连接到 Kubernetes 上的第二个 SQL Server Linux 容器上的 MSDTC 的过程。

显示一个 SQL Server Linux 容器连接到第二个 SQL Server Linux 容器上的 MSDTC 的过程的关系图。

  1. 第一个 SQL Server 实例连接到第二个 SQL Server 实例的 Kubernetes 主机上的端口 135。
  2. 该连接转发到第二个实例的容器上的端口 135。
  3. 容器将连接转发到 RPC 端点映射程序,本例中该映射程序位于端口 13500 上。
  4. 端点映射程序告诉第一个 SQL Server 实例,MSDTC 在第二个容器中运行的是哪个端口(本例中为端口 51000)。
  5. 第一个 SQL Server 实例通过连接到端口 51000 上的第二个主机直接连接到第二个实例上的 MSDTC,该连接将转发到容器内的 SQL Server。

在 Kubernetes 平台上使用配置的 MSDTC 部署 SQL Server 容器

在运行示例部署 YAML 脚本之前,使用以下示例命令创建存储 sa 密码所需的机密:

kubectl create secret generic mssql --from-literal=MSSQL_SA_PASSWORD="<password>"

注意

密码应遵循 SQL Server 默认密码策略。 默认情况下,密码必须为至少八个字符且包含以下四种字符中的三种:大写字母、小写字母、十进制数字、符号。 密码可最长为 128 个字符。 使用的密码应尽可能长,尽可能复杂。

你会注意到清单文件中的以下几点:

  1. 在群集中,我们创建以下对象:StorageClass、两个部署为 statefulset 部署的 SQL Server Pod,以及两项负载均衡器服务,用于连接到相应的 SQL Server 实例。

  2. 你还会注意到,负载均衡器服务是使用静态 IP 地址部署的,可以在 Azure Kubernetes 服务上配置。 请参阅将静态公共 IP 地址和 DNS 标签用于 Azure Kubernetes 服务 (AKS) 负载均衡器。 创建具有静态 IP 地址的负载均衡器服务可确保在删除并重新创建负载均衡器服务时,外部 IP 地址不会更改。

  3. 在下面的脚本中,可以看到端口 13500 用于 MSSQL_RPC_PORT 环境变量,端口 51000 用于 MSSQL_DTC_TCP_PORT 环境变量,这两者都是 MSDTC 所需的。

  4. 端口路由(即路由端口 135 到 13500)是在负载均衡器脚本中通过适当配置 porttargetPort 配置的,如下例所示:

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: 2
 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
     - containerPort: 13500
       name: dtcport
     - containerPort: 51000
       name: dtctcpport
     env:
     - name: ACCEPT_EULA
       value: "Y"
     - name: MSSQL_ENABLE_HADR
       value: "1"
     - name: MSSQL_AGENT_ENABLED
       value: "1"
     - name: MSSQL_RPC_PORT
       value: "13500"
     - name: MSSQL_DTC_TCP_PORT
       value: "51000"
     - 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-0
spec:
  type: LoadBalancer
  loadBalancerIP: 10.88.213.209
  selector:
    statefulset.kubernetes.io/pod-name: mssql-0
  ports:
  - protocol: TCP
    port: 1433
    targetPort: 1433
    name: tcpsql
  - protocol: TCP
    port: 51000
    targetPort: 51000
    name: dtctcpport
  - protocol: TCP
    port: 135
    targetPort: 13500
    name: nonrootport
---
apiVersion: v1
kind: Service
metadata:
  name: mssql-1
spec:
  type: LoadBalancer
  loadBalancerIP: 10.72.137.129
  selector:
    statefulset.kubernetes.io/pod-name: mssql-1
  ports:
  - protocol: TCP
    port: 1433
    targetPort: 1433
    name: tcpsql
  - protocol: TCP
    port: 51000
    targetPort: 51000
    name: dtctcpport
  - protocol: TCP
    port: 135
    targetPort: 13500
    name: nonrootport

假设在默认命名空间中创建了资源,当你在以前部署后运行 kubectl get all 命令以查看创建的所有资源时,应会看到以下示例中显示的输出。

NAME          READY   STATUS    RESTARTS   AGE
pod/mssql-0   1/1     Running   0          4d22h
pod/mssql-1   1/1     Running   0          4d22h

NAME                 TYPE           CLUSTER-IP    EXTERNAL-IP     PORT(S)                                        AGE
service/kubernetes   ClusterIP      10.0.0.1      <none>          443/TCP                                        6d6h
service/mssql-0      LoadBalancer   10.0.18.186   10.88.213.209   1433:31875/TCP,51000:31219/TCP,135:30044/TCP   2d6h
service/mssql-1      LoadBalancer   10.0.16.180   10.72.137.129   1433:30353/TCP,51000:32734/TCP,135:31239/TCP   2d6h

NAME                     READY   AGE
statefulset.apps/mssql   2/2     5d1h

可以使用 SQL Server Management Studio (SSMS) 等工具连接到前面两个 SQL Server 实例之一,并运行示例 DTC 事务。 在本示例中,将连接到 mssql-1 (10.72.137.129) 并创建到 mssql-0 (10.88.213.209) 的链接服务器,以运行分布式事务,如下例所示。

USE [master];
GO

EXECUTE master.dbo.sp_addlinkedserver
    @server = N'10.88.213.209',
    @srvproduct = N'SQL Server';
GO

EXECUTE master.dbo.sp_addlinkedsrvlogin
    @rmtsrvname = N'10.88.213.209',
    @rmtuser = 'sa',
    @rmtpassword = '<password>',
    @useself = N'False';
GO

注意

密码应遵循 SQL Server 默认密码策略。 默认情况下,密码必须为至少八个字符且包含以下四种字符中的三种:大写字母、小写字母、十进制数字、符号。 密码可最长为 128 个字符。 使用的密码应尽可能长,尽可能复杂。

现在可以启动分布式事务,此代码示例会显示 sys.sysprocesses 实例中的 mssql-0

SET XACT_ABORT ON;

BEGIN DISTRIBUTED TRANSACTION;

SELECT *
FROM [10.88.213.209].master.dbo.sysprocesses;

COMMIT TRANSACTION;
GO