Настройка отказоустойчивого кластера SQL Server Always On в Kubernetes с помощью DH2i
Развертывание кластеров высокой доступности (Always On Availability Groups — AG) для SQL Server в Kubernetes с использованием DH2i DxEnterprise — это мощное решение. DxEnterprise берёт на себя управление виртуальным IP-адресом (VIP) для слушателя (Listener) и оркестрацию отказоустойчивости, что избавляет от необходимости использовать сложные внешние балансировщики или нативные K8s-операторы, которые часто ограничены в функционале.
Архитектура решения
В данной конфигурации мы используем:
- StatefulSet для запуска подов SQL Server.
- DxEnterprise работает либо как sidecar-контейнер, либо интегрирован в образ SQL Server (рекомендуемый подход DH2i).
- Headless Service для внутренней DNS-резолвинга подов.
- Service типа LoadBalancer (или управление "сырым" VIP через CNI) для предоставления единой точки входа (Listener) для клиентов.
Предварительные требования
- Кластер Kubernetes (версии 1.22+).
- Лицензии на SQL Server (Enterprise) и DH2i DxEnterprise.
- Утилиты:
kubectl, доступ к кластеру, клиентdxcli(можно установить на любую машину с доступом к подам или использовать внутри пода). - Хранилище: StorageClass для создания PersistentVolumeClaims (PVC) под данные SQL и конфигурацию DxEnterprise.
Шаг 1. Создание Secret и ConfigMap
Нам необходимо безопасно передать пароли и конфигурацию.
1. Секреты для паролей:
# Пароль для SA в SQL Server
kubectl create secret generic mssql-secret --from-literal=MSSQL_SA_PASSWORD='YourStrong!Passw0rd'
# Пароль администратора для кластера DxEnterprise
kubectl create secret generic dxenterprise-secret --from-literal=DX_PASSWORD='DxAdmin!Pass123'
2. ConfigMap для конфигурации DxEnterprise:
Создайте файл dxenterprise-cm.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: dxenterprise-config
data:
dxenterprise.conf: |
[CLUSTER]
ClusterName=sql-ag-cluster
VirtualIP=10.0.5.100 # IP, который будет "плавать" между нодами (Listener)
VirtualMask=255.255.255.0
# Если используете облако (AKS/EKS), VIP может управляться через LoadBalancer,
# тогда этот блок настраивается согласно документации DH2i для вашего CNI/Cloud.
Примените:
kubectl apply -f dxenterprise-cm.yaml
Шаг 2. Настройка хранилища (PVC)
SQL Server и DxEnterprise требуют персистентного хранилища.
Создайте файл pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: mssql-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 50Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dxenterprise-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
Примечание:
В реальном StatefulSet для каждого пода будут создаваться свои PVC через volumeClaimTemplates. Здесь упрощенный вид для понимания.
Шаг 3. Создание StatefulSet для SQL Server и DxEnterprise
Создайте файл sql-statefulset.yaml. Мы будем запускать 3 реплики (1 Primary, 2 Secondary).
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: sql-ag
spec:
serviceName: "sql-ag-headless"
replicas: 3
selector:
matchLabels:
app: sql-ag
template:
metadata:
labels:
app: sql-ag
spec:
containers:
- name: mssql-dx
# Используйте официальный образ DH2i или соберите свой, включающий SQL и DxEnterprise
image: dh2i/dxenterprise-sql:2022-latest
ports:
- containerPort: 1433 # SQL
- containerPort: 7979 # DxEnterprise
env:
- name: MSSQL_SA_PASSWORD
valueFrom:
secretKeyRef:
name: mssql-secret
key: MSSQL_SA_PASSWORD
- name: MSSQL_PID
value: "Developer" # Или "Enterprise"
- name: ACCEPT_EULA
value: "Y"
- name: MSSQL_AGENT_ENABLED
value: "true"
# Включение Always On High Availability
- name: MSSQL_ENABLE_HADR
value: "1"
volumeMounts:
- name: mssql-data
mountPath: /var/opt/mssql
- name: dx-data
mountPath: /var/opt/dh2i
- name: dxenterprise-sidecar # Если используется раздельная архитектура
image: dh2i/dxenterprise:latest
env:
- name: DX_PASSWORD
valueFrom:
secretKeyRef:
name: dxenterprise-secret
key: DX_PASSWORD
volumeMounts:
- name: dx-config
mountPath: /etc/dh2i
- name: dx-data
mountPath: /var/opt/dh2i
volumes:
- name: dx-config
configMap:
name: dxenterprise-config
volumeClaimTemplates:
- metadata:
name: mssql-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 50Gi
- metadata:
name: dx-data
spec:
accessModes: [ "ReadWriteOnce" ]
resources:
requests:
storage: 5Gi
Примените:
kubectl apply -f sql-statefulset.yaml
Шаг 4. Создание Service (Headless и Listener)
1. Headless Service (для DNS подов: sql-ag-0.sql-ag-headless, sql-ag-1...):
apiVersion: v1
kind: Service
metadata:
name: sql-ag-headless
spec:
clusterIP: None
selector:
app: sql-ag
ports:
- port: 1433
name: mssql
- port: 7979
name: dxenterprise
2. Service для Listener (Виртуальный IP)
В Kubernetes "плавающий" VIP от DxEnterprise чаще всего пробрасывается наружу через LoadBalancer.
apiVersion: v1
kind: Service
metadata:
name: sql-ag-listener
annotations:
# Специфичные аннотации для вашего облака/CNI, если требуется привязка к конкретному IP
service.beta.kubernetes.io/aws-load-balancer-internal: "true"
spec:
type: LoadBalancer
selector:
app: sql-ag
ports:
- port: 1433
targetPort: 1433
Шаг 5. Инициализация кластера DxEnterprise и SQL AG
После того как поды перешли в статус Running, нужно инициализировать кластер.
Зайдите в первый под (он станет Primary):
kubectl exec -it sql-ag-0 -c dxenterprise-sidecar -- /bin/bash
# Если DxEnterprise интегрирован в один контейнер, просто: kubectl exec -it sql-ag-0 -- /bin/bash
1. Создание кластера DxEnterprise:
# Инициализация на первом узле
dxcli create-cluster -c sql-ag-cluster -p 'DxAdmin!Pass123'
# Добавление второго и третьего узлов (используйте DNS имена подов)
dxcli add-node -n sql-ag-1 -i sql-ag-1.sql-ag-headless -p 'DxAdmin!Pass123'
dxcli add-node -n sql-ag-2 -i sql-ag-2.sql-ag-headless -p 'DxAdmin!Pass123'
2. Настройка SQL Server и создание Availability Group:
Вам нужно подключиться к SQL Server на каждом узле и выполнить T-SQL скрипты.
Подключитесь к Primary (sql-ag-0):
-- Создание сертификатов и endpoints (упрощенный вариант, в продакшене используйте правильную криптографию)
CREATE ENDPOINT [Hadr_endpoint]
STATE=STARTED
AS TCP (LISTENER_PORT = 5022, LISTENER_IP = ALL)
FOR DATA_MIRRORING (ROLE = ALL, AUTHENTICATION = CERTIFICATE [auto_cert], ENCRYPTION = REQUIRED ALGORITHM AES);
GO
-- Создание Availability Group
CREATE AVAILABILITY GROUP [SQL-AG]
WITH (AUTOMATIC_CLUSTER_OPERATION = ON) -- Если используется DH2i, он сам управляет кворумом
FOR DATABASE [YourDatabase]
REPLICA ON
N'sql-ag-0' WITH (ENDPOINT_URL = N'TCP://sql-ag-0.sql-ag-headless:5022', FAILOVER_MODE = AUTOMATIC, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, SEEDING_MODE = AUTOMATIC),
N'sql-ag-1' WITH (ENDPOINT_URL = N'TCP://sql-ag-1.sql-ag-headless:5022', FAILOVER_MODE = AUTOMATIC, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, SEEDING_MODE = AUTOMATIC),
N'sql-ag-2' WITH (ENDPOINT_URL = N'TCP://sql-ag-2.sql-ag-headless:5022', FAILOVER_MODE = AUTOMATIC, AVAILABILITY_MODE = SYNCHRONOUS_COMMIT, SEEDING_MODE = AUTOMATIC);
GO
На репликах sql-ag-1 и sql-ag-2 необходимо выполнить ALTER AVAILABILITY GROUP [SQL-AG] JOIN; и ALTER AVAILABILITY GROUP [SQL-AG] GRANT CREATE ANY DATABASE;.
3. Добавление Virtual IP в DxEnterprise:
Вернитесь в консоль dxcli и добавьте VIP, который будет слушать порт 1433 для клиентов:
dxcli add-virtual-ip -v 10.0.5.100 -m 255.255.255.0 -n sql-ag-0
(DxEnterprise автоматически настроит маршрутизацию и будет перемещать этот VIP на активную ноду при сбое).
Шаг 6. Проверка и тестирование отказоустойчивости
1. Проверка статуса кластера:
dxcli get-cluster-info
dxcli get-ag-info
Вы должны увидеть, что sql-ag-0 является Primary, а VIP привязан к нему.
2. Тестирование Failover:
Удалите под Primary, чтобы симулировать сбой:
kubectl delete pod sql-ag-0
Наблюдайте за логами DxEnterprise на оставшихся нодах:
kubectl logs -f sql-ag-1 -c dxenterprise-sidecar
DxEnterprise обнаружит потерю кворума/связи, переведет sql-ag-1 в роль Primary, запустит failover самой Availability Group (через интеграцию с SQL) и переместит Virtual IP на sql-ag-1.
3. Проверка подключения:
Попробуйте подключиться к базе данных, используя IP-адрес вашего LoadBalancer или "плавающий" VIP. Подключение должно восстановиться автоматически через несколько секунд после завершения failover.
Важные нюансы для Kubernetes (Денис Сергеевич, обратите на них внимание)
1. Управление VIP в CNI:
Если вы используете "голый" Kubernetes (Bare Metal) и стандартные CNI (Calico, Flannel), "плавающий" VIP на уровне L2 может не работать из коробки. DH2i рекомендует использовать их специализированные плагины для CNI или настраивать VIP через BGP (например, с использованием MetalLB в связке с DxEnterprise). Если вы в облаке (AWS, Azure, GCP), используйте type: LoadBalancer, а DxEnterprise будет балансировать трафик внутри кластера.
2. Задержки (Latency):
Убедитесь, что задержка между нодами K8s минимальна (обычно < 10 мс для синхронной репликации). Размещайте поды в разных зонах доступности (Availability Zones), но учитывайте, что (cross-region) задержка потребует изменения AVAILABILITY_MODE на ASYNCHRONOUS_COMMIT.
3. Resource Limits:
Обязательно задайте requests и limits для CPU и RAM в StatefulSet. SQL Server в контейнерах очень чувствителен к нехватке памяти (OOMKilled), что приведет к ложным срабатываниям failover.
4. Лицензирование:
DH2i DxEnterprise лицензируется по ядрам или по нодам. Убедитесь, что ваша лицензия покрывает количество используемых подов/нод.
Мы делимся этой технической информацией, чтобы помочь вам в решении задач — используйте её с пониманием. Статья носит рекомендательный характер, поэтому, пожалуйста, применяйте описанные методы осмотрительно.