Introduction
In this tutorial (with accompanying demo video), we will demonstrate a simple and effective way to deploy a Microsoft SQL Server Always On Availability Group (AG) in Kubernetes with minikube and DxOperator – DH2i’s SQL Server Operator for Kubernetes.
We will use minikube to create a single-node Kubernetes cluster for demonstration purposes. Within this environment, we will deploy a SQL Server Always On Availability Group consisting of one primary and two secondary replicas (configurable with synchronous or asynchronous replication), managed by DxOperator.
By the end of this guide, you should be able to:
- Create a single-node minikube cluster
- Deploy a SQL Server Always On Availability Group with one primary and two secondary replicas using DxOperator
- Create a SQL Server Always On Availability Group Listener
- Connect to the SQL Server Always On Availability Group
- Perform a failover from the primary to a secondary replica
Prerequisites
- X86-64 Windows 10 or 11, 16 GB RAM and 100 GB disk
- Hyper-V installed
- Kubernetes
kubectlinstalled - Microsoft SSMS installed
- Microsoft
sqlcmdutility installed - DH2i DxEnterprise Developer License
Install Minikube on Windows
First, install minikube on Windows by following minikube’s instructions.
Create the Cluster
We will create a Kubernetes environment that will consist of a single master node. This is usually sufficient for most testing. We will use the Hyper-V driver for this. We can create the cluster using the Hyper-V driver by opening a PowerShell terminal as Administrator and running the following command:
minikube start --driver=hyperv --memory 8092MB The cluster may take several minutes to create, but minikube will output updates as things are happening. Once it says “Done!”, use the following command to see the node you just created:
kubectl get nodes
Deploy a SQL Server Availability Group using DxOperator
Install DxOperator
First, you need to download the DxOperator YAML:
curl.exe --output v1-cu2.yaml --url https://dxoperator.dh2i.com/dxesqlag/files/v1-cu2.yaml
Then, apply the YAML file to the Kubernetes cluster:
kubectl apply -f v1-cu2.yaml
Create a DxEnterpriseSqlAg
Create secrets for the DxEnterprise passkey (a user defined value, e.g., 1234), license key, and the SQL Server SA account using the following commands. Make sure to fill in values for the commands below.
kubectl create secret generic dxe --from-literal=DX_PASSKEY=<pass> --from-literal=DX_LICENSE=<license_key> kubectl create secret generic mssql --from-literal=MSSQL_SA_PASSWORD=<pass>
Copy the example DxEnterpriseSqlAg.yaml file below to your local machine.
There are many configuration options for the DxEnterpriseSqlAg custom resource beyond those that are shown in this example. View the DxEnterpriseSqlAg API Reference for more information.
apiVersion: dh2i.com/v1
kind: DxEnterpriseSqlAg
metadata:
name: dxesqlag
spec:
synchronousReplicas: 3
asynchronousReplicas: 0
# ConfigurationOnlyReplicas are only allowed with availabilityGroupClusterType set to EXTERNAL
configurationOnlyReplicas: 0
availabilityGroupName: AG1
# Listener port for the availability group (uncomment to apply)
#availabilityGroupListenerPort: 14033
# For a contained availability group, add the option CONTAINED
availabilityGroupOptions: null
# Valid options are EXTERNAL (automatic failover) and NONE (no automatic failover)
availabilityGroupClusterType: EXTERNAL
createLoadBalancers: true
template:
metadata:
labels:
label: example
annotations:
annotation: example
spec:
dxEnterpriseContainer:
image: "docker.io/dh2i/dxe:latest"
imagePullPolicy: Always
acceptEula: true
clusterSecret: dxe
vhostName: VHOST1
joinExistingCluster: false
# QoS – guaranteed (uncomment to apply)
#resources:
#limits:
#memory: 1Gi
#cpu: '1'
# Configuration options for the required persistent volume claim for DxEnterprise
volumeClaimConfiguration:
storageClassName: null
resources:
requests:
storage: 1Gi
mssqlServerContainer:
image: "mcr.microsoft.com/mssql/server:latest"
imagePullPolicy: Always
mssqlSecret: mssql
acceptEula: true
mssqlPID: Developer
# Only set this to a value if you created a ConfigMap
mssqlConfigMap: null
# QoS – guaranteed (uncomment to apply)
#resources:
#limits:
#memory: 2Gi
#cpu: '2'
# Configuration options for the required persistent volume claim for SQL Server
volumeClaimConfiguration:
storageClassName: null
resources:
requests:
storage: 2Gi
# Additional side-car containers, such as mssql-tools (uncomment to apply)
#containers:
#- name: mssql-tools
#image: "mcr.microsoft.com/mssql-tools"
#command: [ "/bin/sh" ]
#args: [ "-c", "tail -f /dev/null" ] Apply the DxEnterpriseSqlAg custom resource using kubectl apply:
kubectl apply -f DxEnterpriseSqlAg.yaml
Verify the status of the pods:
kubectl get pods -w
After giving the cluster at least a couple of minutes to setup, verify the state of the AG using dxcli get-ags-detail:
kubectl exec -c dxe dxesqlag-0 -- dxcli get-ags-detail vhost1 ag1 | kubectl exec -ic dxe dxesqlag-0 -- dxcli format-xml
Create an Availability Group Listener
Kubernetes load balancers provide access to cluster resources via an externally accessible IP address: you define the load balancer configuration in a YAML, and Kubernetes takes care of load-balancing incoming connections for you.
The previous DxEnterpriseSqlAg.yaml example contained a commented-out line for the spec.availabilityGroupListenerPort. Uncomment out this line, then run kubectl apply again to update the custom resource with the new listener port.
Copy the the load balancer configuration Service.yaml file below to your local machine.
#Example load balancer service
#Access for SQL server, AG listener, and DxE management
apiVersion: v1
kind: Service
metadata:
name: dxesqlag-cluster-lb
spec:
type: LoadBalancer
selector:
dh2i.com/entity: dxesqlag
ports:
- name: sql
protocol: TCP
port: 1433
targetPort: 1433
- name: listener
protocol: TCP
port: 14033
targetPort: 14033
- name: dxe
protocol: TCP
port: 7979
targetPort: 7979 Apply the service YAML:
kubectl apply -f Service.yaml
List the service and note its public IP address:
kubectl get service
Open a new PowerShell terminal as Administrator and run:
minikube tunnel To verify connectivity, try connecting to the SQL instance (port 1433) or AG listener (port 14033) via SSMS/sqlcmd using the load balancer public IP address:
sqlcmd -S <ip_address> -U sa -P <sql_password> Failover Test
To verify that your AG is working correctly, you can simulate a failover on the primary.
Verify the state of the AG before failover. To do this, run the command dxcli get-ags-detail. Look for the status of the database for each AG member in the output details and find whichever pod is the primary.
kubectl exec -c dxe dxesqlag-1 -- dxcli get-ags-detail vhost1 ag1 | kubectl exec -ic dxe dxesqlag-1 -- dxcli format-xml
Delete the primary pod:
kubectl delete pod dxesqlag-0
Wait a few seconds, then run the dxcli get-ags-detail command again to check the status of the databases:
kubectl exec -c dxe dxesqlag-1 -- dxcli get-ags-detail vhost1 ag1 | kubectl exec -ic dxe dxesqlag-1 -- dxcli format-xml
After a few minutes, the deleted pod should automatically rejoin the cluster and synchronize with the Availability Group.
After the pod rejoins the cluster and synchronizes, you can set its role back to primary by running the command dxcli vhost-start-node:
kubectl exec -c dxe dxesqlag-0 -- dxcli vhost-start-node vhost1 dxesqlag-0
Cleanup
Cleaning up after testing is a good practice to save space on your computer. To clean up what you just created, we need to delete the deployments and the nodes. Here are the commands to delete both:
Removing Custom Resources and DxOperator
Delete the DxEnterpriseSqlAg custom resource:
kubectl delete -f DxEnterpriseSqlAg.yaml
Delete the persistent volume claims (PVCs):
kubectl delete pvc --all
Delete the DxOperator YAML.
Warning: Running this command will delete the DxEnterpriseSqlAg CRD. If any DxEnterpriseSqlAg custom resources remain in the Kubernetes cluster, Kubernetes will destroy them as part of the deletion process. Only run this command if you intend on removing every DxEnterpiseSqlAg too!
To list all DxEnterpriseSqlAgs, run ‘kubectl get dxesqlag’.
kubectl delete -f v1-cu2.yaml
Delete Minikube VM
To delete minikube, run the following command:
minikube delete --all If you’re interested in learning more about DH2i’s approach to smart high availability technology, get signed up for a one-on-one demo today.
Most Recent Posts:
- AI That Never Sleeps: How DH2i is Building the Missing Layer of Trust in AI Infrastructure
- SQL Server 2025 Enters the AI Era - But Can Your Infrastructure Keep Up?
- SQL Server 2025: Why High Availability and Disaster Recovery Are Essential for AI
- DxEnterprise for high availability now certified for RHEL 9.6
- DH2i Brings Mission-Critical HA Capability to the Table for SQL Server 2025-Backed AI Applications