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

适用于:Linux 上的 SQL Server

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

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

Note

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

配置

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

  • MSSQL_RPC_PORT:RPC端点映射器服务绑定并监听的TCP端口。
  • MSSQL_DTC_TCP_PORTMSDTC服务配置监听的端口。

拉取并运行

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

Important

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) 容器。 这使得它可以与任何主机上的任何应用程序通信。

Important

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 端口。 这些端口是主机本身的端口,不是容器中映射到的端口。 因此,如果容器的MSDTC端口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)是在负载均衡器脚本中通过适当配置 port 和 targetPort 配置的,如下例所示:

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 个字符。 使用的密码应尽可能长,尽可能复杂。

现在你可以开始分布式交易了。 以下代码示例查询了实例上的mssql-0sys.sysprocesses兼容性视图:

SET XACT_ABORT ON;

BEGIN DISTRIBUTED TRANSACTION;

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

COMMIT TRANSACTION;
GO