Использование распределенных транзакций с SQL Server в контейнерах Linux

Применимо к:SQL Server в Linux

В этой статье показано, как настроить контейнеры SQL Server в Linux для распределенных транзакций, включая специальные требования и сценарии.

Образы контейнеров SQL Server могут использовать координатор распределенных транзакций Майкрософт (MSDTC), необходимый для распределенных транзакций. Сведения о требованиях к связи для MSDTC см. в статье Настройка координатора распределенных транзакций Майкрософт (MSDTC) в Linux.

Note

SQL Server 2017 (14.x) выполняется в корневых контейнерах по умолчанию, в то время как SQL Server 2019 (15.x) и более поздние контейнеры выполняются как не корневой пользователь.

Конфигурация

Чтобы активировать транзакции MSDTC в контейнерах SQL Server, необходимо установить две переменные среды:

  • MSSQL_RPC_PORT: TCP-порт, к которому привязывается служба RPC Endpoint Mapper и который она прослушивает.
  • MSSQL_DTC_TCP_PORT: порт, который служба MSDTC настроена прослушивать.

Извлечение и запуск

В приведенном ниже примере показано, как использовать эти переменные среды для извлечения и запуска одиночного контейнера SQL Server 2017, настроенного для MSDTC. Это позволяет ему взаимодействовать с любым приложением на любом хосте.

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

В следующем примере показано, как использовать эти переменные среды для извлечения и запуска одного контейнера SQL Server 2019 (15.x), настроенного для MSDTC. Это позволяет ему взаимодействовать с любым приложением на любом хосте.

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

Caution

Пароль должен соответствовать политике паролей по умолчанию SQL Server. По умолчанию пароль должен быть не короче восьми символов и содержать три вида символов из следующих: прописные буквы, строчные буквы, десятичные цифры, специальные символы. Пароли могут иметь длину до 128 символов. Рекомендуется использовать максимально длинные и сложные пароли.

В этой команде служба сопоставления конечных точек RPC привязана к порту 135, а служба MSDTC привязана к порту 51000 в виртуальной сети контейнера. Коммуникации TDS в SQL Server проходят через порт 1433 также в виртуальной сети контейнера. Эти порты внешне доступны хосту как TDS-порт 51433, порт RPC endpoint mapper порт 135 и MSDTC порт 51000.

Порты RPC Endpoint Mapper и MSDTC не обязательно должны быть одинаковыми на хосте и контейнере. Таким образом, хотя порт RPC Endpoint Mapper был настроен на 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 на узле, распределенные транзакции с этим узлом теперь должны выполняться без дальнейшей настройки. Можно использовать порт 135 напрямую в контейнерах, работающих от имени пользователя root, потому что SQL Server в таких контейнерах работает с повышенными привилегиями. Для SQL Server за пределами контейнера или для некорневых контейнеров необходимо использовать другой эфемерный порт (например, 13500) в контейнере, а трафик, предназначенный для порта 135, должен затем направляться на этот порт. Кроме того, необходимо настроить правила маршрутизации портов в контейнере с порта контейнера 135 на временный порт.

Однако если вы решили связать порт 135 контейнера с другим портом на узле, например 13500, необходимо настроить маршрутизацию портов на узле. Это позволяет контейнеру SQL Server участвовать в распределенных транзакциях с узлом и другими внешними серверами.

Дополнительные сведения о маршрутизации портов см. в разделе Настройка маршрутизации портов.

Контейнеры SQL Server с MSDTC в Kubernetes

Если вы развертываете контейнеры SQL Server на платформе Kubernetes, ознакомьтесь со следующим примером манифеста развертывания YAML. В этом примере платформа Kubernetes — это служба Azure Kubernetes (AKS).

Сценарий 1. Подключение клиента MSDTC к SQL Server в контейнере Kubernetes

На следующей схеме показан процесс, когда клиент MSDTC подключается к MSDTC на SQL Server, работающем внутри контейнера Linux в Kubernetes.

Схема, показывающая процесс при подключении клиента MSDTC к MSDTC на SQL Server, работающем внутри контейнера Linux.

  1. Клиент MSDTC выполняет подключение к порту 135 на узле Kubernetes.
  2. Подключение перенаправлено на порт 135 в контейнере.
  3. Контейнер перенаправляет подключение к маршрутизатору конечных точек RPC, который в этом примере находится на порту 13500.
  4. Средство сопоставления конечных точек сообщает клиенту MSDTC, какой порт MSDTC выполняется внутри контейнера (порт 51000 в этом примере).
  5. Клиент MSDTC подключается непосредственно к MSDTC путем подключения к узлу через порт 51000, который пересылается в SQL Server внутри контейнера.

Сценарий 2. Подключение SQL Server к SQL Server в контейнере Kubernetes

Следующая схема показывает процесс, когда один контейнер Linux SQL Server подключается к MSDTC на втором контейнере SQL Server Linux на Kubernetes.

Схема, показывающая процесс при подключении одного контейнера SQL Server Linux к MSDTC во втором контейнере SQL Server Linux.

  1. Первый экземпляр SQL Server делает подключение к порту 135 на узле Kubernetes второго экземпляра SQL Server.
  2. Подключение перенаправлено в порт 135 в контейнере второго экземпляра.
  3. Контейнер перенаправляет подключение к маршрутизатору конечных точек RPC, который в этом примере находится на порту 13500.
  4. Сопоставитель конечных точек сообщает первому экземпляру SQL Server, на каком порту (порт 51000) запущен MSDTC во втором контейнере.
  5. Первый экземпляр SQL Server подключается непосредственно к MSDTC во втором экземпляре, подключаясь ко второму узлу через порт 51000, который пересылается в SQL Server внутри контейнера.

Развертывание контейнеров SQL Server с настроенным MSDTC на платформе Kubernetes

Перед запуском примера скрипта YAML развертывания создайте необходимый секрет для хранения sa пароля, выполнив следующую команду:

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

Caution

Пароль должен соответствовать политике паролей по умолчанию SQL Server. По умолчанию пароль должен быть не короче восьми символов и содержать три вида символов из следующих: прописные буквы, строчные буквы, десятичные цифры, специальные символы. Пароли могут иметь длину до 128 символов. Рекомендуется использовать максимально длинные и сложные пароли.

Обратите внимание на следующие моменты в файле манифеста:

  1. Манифест создаёт следующие объекты: StorageClass, два пода SQL Server, развёрнутые с помощью StatefulSet, и две службы балансировки нагрузки для подключения к соответствующим экземплярам 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

Caution

Пароль должен соответствовать политике паролей по умолчанию 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