|
| 1 | +--- |
| 2 | +title: Deploy Always on availability group using DH2i cluster solution for SQL Server Containers deployed on Azure Kubernetes Services (AKS) |
| 3 | +description: This tutorial shows how to deploy a SQL Server Always On availability group with DH2i Clustering solution for SQL Server containers on Azure Kubernetes Service. |
| 4 | +ms.custom: seo-lt-2019 |
| 5 | +author: amvin87 |
| 6 | +ms.author: amvin87 |
| 7 | +ms.reviewer: amvin87 |
| 8 | +ms.date: 09/07/2021 |
| 9 | +ms.topic: tutorial |
| 10 | +ms.prod: sql |
| 11 | +ms.technology: linux |
| 12 | +--- |
| 13 | + |
| 14 | +# Deploy Always on availability group using DH2i cluster solution for SQL Server containers deployed on Azure Kubernetes Services (AKS) |
| 15 | + |
| 16 | +This tutorial explains how to configure SQL Server Always On availability group for SQL Server Linux based containers deployed in Kubernetes cluster. In this case, Azure Kubernetes Service(AKS) is used as the kubernetes cluster and the tutorial consists of the following tasks: |
| 17 | + |
| 18 | +1. Deploy Azure Kubernetes Service. |
| 19 | +2. Prepare the SQL Server & DhH2i container image. |
| 20 | +3. Deploy Containers on Azure Kubernetes Service. |
| 21 | +4. Configure the DxEnterprise cluster. |
| 22 | +5. Configure Read_Write_Routing_URL for listener functionality - Optional. |
| 23 | + |
| 24 | +## Prerequisites |
| 25 | +1. You would need an Azure account to deploy Azure Kubernetes Service. For this tutorial a two node cluster is sufficient. |
| 26 | +2. Create Azure Container Registry this will be used in our deployment scripts to pull the custom image and deploy the containers to Azure Kubernetes service. You could use your choice of container registry instead of ACR to push the custom container images. |
| 27 | + |
| 28 | +## Deploy Azure Kubernetes Service |
| 29 | + |
| 30 | +To setup a two-node Kubernetes cluster using the Azure Kubernetes Service, please follow this [quickstart tutorial](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal#create-an-aks-cluster). Once you create the cluster you can connect to the cluster by following the steps documented in the ["connect to the cluster"](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal#connect-to-the-cluster) section of the article. |
| 31 | + |
| 32 | +You should now a two-node kubernetes cluster, and running a command like : kubectl get nodes from your client machine should show results similar to this: |
| 33 | + |
| 34 | +```bash |
| 35 | +C:\>kubectl get nodes |
| 36 | +NAME STATUS ROLES AGE VERSION |
| 37 | +aks-nodepool1-75119571-vmss000000 Ready agent 61d v1.19.9 |
| 38 | +aks-nodepool1-75119571-vmss000001 Ready agent 61d v1.19.9 |
| 39 | +``` |
| 40 | + |
| 41 | +## Prepare the SQL Server & DH2i DxEnterprise custom container image |
| 42 | + |
| 43 | +Next, we are going to prepare the custom container image which will be used in our deployment scripts to deploy SQL Server, .Net and DxEnterprise inside a container deployed using this custom image. The sample dockerfile for the deployment is shared below, you can change the SQL Server version in the below sample dockerfile. |
| 44 | + |
| 45 | +```bash |
| 46 | +FROM mcr.microsoft.com/mssql/server:2019-latest |
| 47 | +USER root |
| 48 | + |
| 49 | +#Install dotnet |
| 50 | +RUN apt-get update \ |
| 51 | + && ACCEPT_EULA=Y apt-get upgrade -y \ |
| 52 | + && apt-get install -y wget \ |
| 53 | + && wget --no-dns-cache https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ |
| 54 | + && dpkg -i packages-microsoft-prod.deb \ |
| 55 | + && apt-get update \ |
| 56 | + && apt-get install -y dotnet-runtime-3.1 zip \ |
| 57 | + && dpkg --purge packages-microsoft-prod \ |
| 58 | + && apt-get purge -y wget \ |
| 59 | + && apt-get clean \ |
| 60 | + && rm packages-microsoft-prod.deb \ |
| 61 | + && rm -rf /var/lib/apt/lists/* |
| 62 | + |
| 63 | +#Download and unpack DxE, setup permissions |
| 64 | +ADD https://repos.dh2i.com/container/ ./dxe.tgz |
| 65 | +RUN tar zxvf dxe.tgz && rm dxe.tgz \ |
| 66 | + && chown -R mssql /var/opt/mssql \ |
| 67 | + && chmod -R 777 /opt/dh2i /etc/dh2i |
| 68 | + |
| 69 | +#Finish setup |
| 70 | +EXPOSE 7979 7985 |
| 71 | +ENV DX_HAS_MSSQLSERVER=1 |
| 72 | +USER mssql |
| 73 | +ENTRYPOINT ["/opt/dh2i/sbin/dxstart.sh"] |
| 74 | +``` |
| 75 | + |
| 76 | +On a Linux machine, you can build the image using the commands shown below: |
| 77 | + |
| 78 | +```bash |
| 79 | +$mkdir dockefiles |
| 80 | +$cd dockerfiles |
| 81 | +$nano Dockerfile |
| 82 | +# paste the sample dockerfile content shared above |
| 83 | +# now build the image using the command: |
| 84 | +$docker build -t <tagname> . |
| 85 | +# you should now be able to see the new image sqlimage when you run the docker images command |
| 86 | +``` |
| 87 | + |
| 88 | +Now tag the image and push it to ACR using the following commands, you need to ensure that you have already logged in to the ACR using the docker login command, for more details see [login to ACR](https://docs.microsoft.com/en-us/azure/container-registry/container-registry-get-started-portal#log-in-to-registry). |
| 89 | + |
| 90 | +```bash |
| 91 | +$docker tag sqlimage/latest amvinacr.azurecr.io/sqlimage:latest |
| 92 | +#now push to the ACR repo: |
| 93 | +$docker push amvinacr.azurecr.io/sqlimage:latest |
| 94 | +#you can browse your ACR through the portal and should see the repo and the tag listed in the ACR. |
| 95 | +``` |
| 96 | +This ensures that you now have the custom image pushed to Azure Container Registry (ACR) and now to integrate your Azure Kubernetes Service with Azure Container Registry, please run the below command, for more details refer this (article)[https://docs.microsoft.com/en-us/azure/aks/cluster-container-registry-integration] |
| 97 | + |
| 98 | +```bash |
| 99 | +az aks update -n myAKSCluster -g amvindomain --attach-acr amvinacr |
| 100 | +``` |
| 101 | + |
| 102 | +## Deploy containers on Azure Kubernetes Service |
| 103 | + |
| 104 | +We will be deploying SQL Server containers as statefulset deployments, a sample deployment file is shared below for reference which deploys the containers on the Azure Kubernetes Service. Please note the below points: |
| 105 | + |
| 106 | +1. We are going to deploy three SQL Server instances, 1-Primary replica and 2-Secondary replicas. You can also optinally added labels to the node, to ensure that primary always runs on a specific node and the secondary replicas run on another node. Here are the steps to label the nodes: |
| 107 | + 1. Get the node names of the cluster using the command: |
| 108 | + ```bash |
| 109 | + kubectl get nodes |
| 110 | + ``` |
| 111 | + 2. Now label the nodes using the commands: |
| 112 | + ```bash |
| 113 | + kubectl label node aks-nodepool1-75119571-vmss000000 <role=ags-primary> |
| 114 | + kubectl label node aks-nodepool1-75119571-vmss000001 <role=ags-secondary> |
| 115 | + ``` |
| 116 | +2. Before you deploy the SQL Server containers, please create the SA password secret on kubernetes using the command: |
| 117 | + |
| 118 | +```bash |
| 119 | +kubectl create secret generic mssql --from-literal=SA_PASSWORD="MyC0m9l&xP@ssw0rd" |
| 120 | +``` |
| 121 | +Replace MyC0m9l&xP@ssw0rd with a complex password |
| 122 | + |
| 123 | +3. Create a manifest (a YAML file) to describe the deployment. The following example describes our current deployment which uses the custom container image created in the preceding steps |
| 124 | + |
| 125 | +```bash |
| 126 | +kind: StorageClass |
| 127 | +apiVersion: storage.k8s.io/v1 |
| 128 | +metadata: |
| 129 | + name: azure-disk |
| 130 | +provisioner: kubernetes.io/azure-disk |
| 131 | +parameters: |
| 132 | + storageaccounttype: Standard_LRS |
| 133 | + kind: Managed |
| 134 | +--- |
| 135 | +apiVersion: apps/v1 |
| 136 | +kind: StatefulSet |
| 137 | +metadata: |
| 138 | + name: mssql-pri |
| 139 | + labels: |
| 140 | + app: mssql |
| 141 | +spec: |
| 142 | + serviceName: "mssql-pri" |
| 143 | + replicas: 1 |
| 144 | + selector: |
| 145 | + matchLabels: |
| 146 | + app: mssql |
| 147 | + template: |
| 148 | + metadata: |
| 149 | + labels: |
| 150 | + app: mssql |
| 151 | + spec: |
| 152 | + securityContext: |
| 153 | + fsGroup: 10001 |
| 154 | + containers: |
| 155 | + - name: mssql |
| 156 | + image: amvinacr.azurecr.io/sqldh2i:latest |
| 157 | + env: |
| 158 | + - name: ACCEPT_EULA |
| 159 | + value: "Y" |
| 160 | + - name: MSSQL_AGENT_ENABLED |
| 161 | + value: "Y" |
| 162 | + - name: MSSQL_ENABLE_HADR |
| 163 | + value: "1" |
| 164 | + - name: SA_PASSWORD |
| 165 | + valueFrom: |
| 166 | + secretKeyRef: |
| 167 | + name: mssql |
| 168 | + key: SA_PASSWORD |
| 169 | + volumeMounts: |
| 170 | + - name: dxe |
| 171 | + mountPath: "/etc/dh2i" |
| 172 | + - name: mssql |
| 173 | + mountPath: "/var/opt/mssql" |
| 174 | + nodeSelector: |
| 175 | + role: ags-primary |
| 176 | + volumeClaimTemplates: |
| 177 | + - metadata: |
| 178 | + name: dxe |
| 179 | + spec: |
| 180 | + accessModes: |
| 181 | + - ReadWriteOnce |
| 182 | + resources: |
| 183 | + requests: |
| 184 | + storage: 1Gi |
| 185 | + - metadata: |
| 186 | + name: mssql |
| 187 | + spec: |
| 188 | + accessModes: |
| 189 | + - ReadWriteOnce |
| 190 | + resources: |
| 191 | + requests: |
| 192 | + storage: 8Gi |
| 193 | +--- |
| 194 | +apiVersion: apps/v1 |
| 195 | +kind: StatefulSet |
| 196 | +metadata: |
| 197 | + name: mssql-sec |
| 198 | + labels: |
| 199 | + app: mssql |
| 200 | +spec: |
| 201 | + serviceName: "mssql-sec" |
| 202 | + replicas: 2 |
| 203 | + selector: |
| 204 | + matchLabels: |
| 205 | + app: mssql |
| 206 | + template: |
| 207 | + metadata: |
| 208 | + labels: |
| 209 | + app: mssql |
| 210 | + spec: |
| 211 | + securityContext: |
| 212 | + fsGroup: 10001 |
| 213 | + containers: |
| 214 | + - name: mssql |
| 215 | + image: amvinacr.azurecr.io/sqldh2i:latest |
| 216 | + env: |
| 217 | + - name: ACCEPT_EULA |
| 218 | + value: "Y" |
| 219 | + - name: MSSQL_AGENT_ENABLED |
| 220 | + value: "Y" |
| 221 | + - name: MSSQL_ENABLE_HADR |
| 222 | + value: "1" |
| 223 | + - name: SA_PASSWORD |
| 224 | + valueFrom: |
| 225 | + secretKeyRef: |
| 226 | + name: mssql |
| 227 | + key: SA_PASSWORD |
| 228 | + volumeMounts: |
| 229 | + - name: dxe |
| 230 | + mountPath: "/etc/dh2i" |
| 231 | + - name: mssql |
| 232 | + mountPath: "/var/opt/mssql" |
| 233 | + nodeSelector: |
| 234 | + role: ags-secondary |
| 235 | + volumeClaimTemplates: |
| 236 | + - metadata: |
| 237 | + name: dxe |
| 238 | + spec: |
| 239 | + accessModes: |
| 240 | + - ReadWriteOnce |
| 241 | + resources: |
| 242 | + requests: |
| 243 | + storage: 1Gi |
| 244 | + - metadata: |
| 245 | + name: mssql |
| 246 | + spec: |
| 247 | + accessModes: |
| 248 | + - ReadWriteOnce |
| 249 | + resources: |
| 250 | + requests: |
| 251 | + storage: 8Gi |
| 252 | +--- |
| 253 | +apiVersion: v1 |
| 254 | +kind: Service |
| 255 | +metadata: |
| 256 | + name: mssql-pri-0 |
| 257 | +spec: |
| 258 | + type: LoadBalancer |
| 259 | + selector: |
| 260 | + statefulset.kubernetes.io/pod-name: mssql-pri-0 |
| 261 | + ports: |
| 262 | + - name: sql |
| 263 | + protocol: TCP |
| 264 | + port: 1433 |
| 265 | + targetPort: 1433 |
| 266 | + - name: dxe |
| 267 | + protocol: TCP |
| 268 | + port: 7979 |
| 269 | + targetPort: 7979 |
| 270 | +--- |
| 271 | +apiVersion: v1 |
| 272 | +kind: Service |
| 273 | +metadata: |
| 274 | + name: mssql-sec-0 |
| 275 | +spec: |
| 276 | + type: LoadBalancer |
| 277 | + selector: |
| 278 | + statefulset.kubernetes.io/pod-name: mssql-sec-0 |
| 279 | + ports: |
| 280 | + - name: sql |
| 281 | + protocol: TCP |
| 282 | + port: 1433 |
| 283 | + targetPort: 1433 |
| 284 | + - name: dxe |
| 285 | + protocol: TCP |
| 286 | + port: 7979 |
| 287 | + targetPort: 7979 |
| 288 | +--- |
| 289 | +apiVersion: v1 |
| 290 | +kind: Service |
| 291 | +metadata: |
| 292 | + name: mssql-sec-1 |
| 293 | +spec: |
| 294 | + type: LoadBalancer |
| 295 | + selector: |
| 296 | + statefulset.kubernetes.io/pod-name: mssql-sec-1 |
| 297 | + ports: |
| 298 | + - name: sql |
| 299 | + protocol: TCP |
| 300 | + port: 1433 |
| 301 | + targetPort: 1433 |
| 302 | + - name: dxe |
| 303 | + protocol: TCP |
| 304 | + port: 7979 |
| 305 | + targetPort: 7979 |
| 306 | +``` |
| 307 | + |
| 308 | +Copy the preceding code into a new file, named sqldeployment.yaml, update the values like port, image and storage details as per your configuration. Create the deployment using the command below: |
| 309 | + |
| 310 | +```bash |
| 311 | +kubectl apply -f <Path to sqldeployment.yaml file> |
| 312 | +``` |
| 313 | +Once the deployment completes when you run the kubectl get all command you should see result as shown below: |
| 314 | + |
| 315 | +```bash |
| 316 | +C:\>kubectl get all |
| 317 | +NAME READY STATUS RESTARTS AGE |
| 318 | +pod/mssql-pri-0 1/1 Running 0 33h |
| 319 | +pod/mssql-sec-0 1/1 Running 0 33h |
| 320 | +pod/mssql-sec-1 1/1 Running 0 33h |
| 321 | +
|
| 322 | +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE |
| 323 | +service/kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 33h |
| 324 | +service/mssql-pri-0 LoadBalancer 10.0.134.183 20.204.22.235 1433:30678/TCP,7979:31136/TCP 33h |
| 325 | +service/mssql-sec-0 LoadBalancer 10.0.74.50 20.204.23.32 1433:31009/TCP,7979:30114/TCP 33h |
| 326 | +service/mssql-sec-1 LoadBalancer 10.0.63.62 20.204.74.9 1433:31616/TCP,7979:32190/TCP 33h |
| 327 | +
|
| 328 | +NAME READY AGE |
| 329 | +statefulset.apps/mssql-pri 1/1 33h |
| 330 | +statefulset.apps/mssql-sec 2/2 33h |
| 331 | +``` |
| 332 | +As you can see, we have three SQL Server instances each using its own storage with three loadbalancer services exposing port 1433(SQL) and 7979 (DxEnterprise Cluster) so you can connect to each of the SQL Server instance using the External-IP address and the SA_PASSWORD is the same password that you provided when creating the mssql secret in the preceding steps. |
| 333 | + |
| 334 | +## Configure the DxEnterprise Cluster on the Containers deployed |
| 335 | + |
| 336 | +DxEnterprise is high availability clustering software from DH2i that supports SQL Server availability groups, including in containers. A fully featured [developer](https://dh2i.com/dxenterprise-dxodyssey-developer-edition) edition is available for non-production use. To configure the DxEnterprise cluster in containers, follow the steps in the sections "Configure the Primary and Create the Availability Group", "Join the Remaining Containers to the DxEnterprise Cluster" and "Configure the Availability Group Database(s)" respectively from this [DH2i guide](https://dh2i.com/wp-content/uploads/DxEnterprise-v21.0-SQL-Server-for-Kubernetes-StatefulSet-on-Azure-Quick-Start-Guide.pdf) |
| 337 | + |
| 338 | +With this, you should have an Always On availability group created and database(s) added to the group supporting high availability. |
| 339 | + |
| 340 | +## Steps to configure Read/write connection redirection: (Optional) |
| 341 | + |
| 342 | +Once, the availability group is created you can enable the Read/write connection redirection from secondary to primary using the below steps. Refer, Read_write_routing_URL to know more. This fulfils the listener requirements |
| 343 | + |
| 344 | +```bash |
| 345 | +USE [master] |
| 346 | +GO |
| 347 | +ALTER AVAILABILITY GROUP [ag_name] |
| 348 | +MODIFY REPLICA ON N'<name of the primary replica>' WITH (SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL)) |
| 349 | +GO |
| 350 | +USE [master] |
| 351 | +GO |
| 352 | +ALTER AVAILABILITY GROUP [AGS1] |
| 353 | +MODIFY REPLICA ON N'<name of the secondary-0 replica>' WITH (SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL)) |
| 354 | +GO |
| 355 | +USE [master] |
| 356 | +GO |
| 357 | +ALTER AVAILABILITY GROUP [AGS1] |
| 358 | +MODIFY REPLICA ON N'<name of the secondary-1 replica>' WITH (SECONDARY_ROLE(ALLOW_CONNECTIONS = ALL)) |
| 359 | +GO |
| 360 | +USE [master] |
| 361 | +GO |
| 362 | +ALTER AVAILABILITY GROUP AGS1 MODIFY REPLICA ON N'<name of the primary replica>' WITH |
| 363 | +(PRIMARY_ROLE (READ_WRITE_ROUTING_URL = 'TCP://<External IP address of primary -0>:1433')) |
| 364 | +GO |
| 365 | +USE [master] |
| 366 | +GO |
| 367 | +ALTER AVAILABILITY GROUP AGS1 MODIFY REPLICA ON N'<name of the secondary-0 replica>' WITH |
| 368 | +(PRIMARY_ROLE (READ_WRITE_ROUTING_URL = 'TCP://<External IP address of secondary -0>:1433')) |
| 369 | +GO |
| 370 | +USE [master] |
| 371 | +GO |
| 372 | +ALTER AVAILABILITY GROUP AGS1 MODIFY REPLICA ON N'<name of the secondary-1 replica>' WITH |
| 373 | +(PRIMARY_ROLE (READ_WRITE_ROUTING_URL = 'TCP://<External IP address of secondary -1>:1433')) |
| 374 | +GO |
| 375 | +``` |
| 376 | + |
| 377 | +## Next Steps |
| 378 | + |
| 379 | +1. [Deploy SQL Server containers on Azure Kubernetes Service](https://docs.microsoft.com/en-us/sql/linux/tutorial-sql-server-containers-kubernetes?view=sql-server-ver15) |
| 380 | +2. [Deploy SQL Server Read Scale AG on SQL Server Linux based containers deployed on kubernetes](https://techcommunity.microsoft.com/t5/sql-server/configure-sql-server-ag-read-scale-for-sql-containers-deployed/ba-p/2224742) |
0 commit comments