Uno de los problemas que muchos clientes se encuentran al intentar migrar instancias OnPremise a Cloud es la ausencia de un «storage compartido» sencillo. Existen alternativas, apoyadas en software de terceros o bien soluciones SDS, que nos permiten configurar una instancia Failover Cluster en Azure pero no están exentas de complejidad y añaden bastante coste al TCO de la solución.

En este mismo blog publicamos hace ya un par de años un post sobre esta configuración usando Azure Files (SQL Server Failover Cluster Instance y Azure File Storage )  y hace un año en otro post se utilizó una alternativa basada en S2D (Azure cocktail: SQL Server Failover Cluster, Storage Spaces Direct (S2D) y templates ARM) . En ambos casos disponer de un storage compartido con buen rendimiento simplificaría mucho las cosas y permitiría soportar cargas de tipo SQL que no eran razonables (la mayoría desgraciadamente) para el rendimiento de Azure Files «no premium».

En este post vamos a mostrar cómo podemos montar una instancia SQL Server Failover Cluster apoyándonos en Azure Files Premium (preview) y mostraremos algunas pinceladas del rendimiento que podemos obtener de este tipo de almacenamiento. Debemos siempre tener en cuenta que cada carga SQL Server es «especial» a su manera, y que sin una exhaustiva monitorización del servidor OnPremise es fácil pasar por alto necesidades puntuales a nivel de entrada/salida mucho más elevadas que los valores medios que suelen considerarse.

La configuración de Azure Files Premium es muy sencilla y simplemente tendremos que crear los shares, indicarles una quota, un tamaño máximo y en base a dicho tamaño se calcularán los límites de rendimiento:

El throughput máximo se calculará mediante la fórmula 100 MB/s+(0.1)*MB  con lo que con 1 TB por ejemplo tendremos una throuhput máximo esperado de 200 MB/s. Las IOPS del baseline se calculan a razón de una IOPS por cada GB provisionado mientras que el burst permitido será de 3 IOPS por cada GB. En el caso de un disco de 1 TB por ejemplo tendremos 1000 IOPS con 3000 IOPS de burst.

Una vez creados los shares, podremos acceder a ellos desde una máquina virtual montándolos usando para ello los tokens que nos proporciona Azure. Por ejemplo, podemos montar el share «data1tb» en el volumen Z: mediante el comando net use:

net use Z: \\premiumazurefiles.file.core.windows.net\data1tb /u:AZURE\premiumazurefiles 0Ins0mAslDeEeY2/tQG0bl3lQ+vDpAlXt3JvSXkb8lsAjhkV2PLCahsJBzgVt8NXf8wNvlpjeP6FaoXnUhxCHg==

Si queremos que la máquina pueda montar de forma automática el share, deberemos almacenar los credenciales necesarios con el comando cmdkey:

cmdkey /add:premiumazurefiles.file.core.windows.net /user:AZURE\premiumazurefiles /pass:0Ins0mAslDeEeY2/tQG0bl3lQ+vDpAlXt3JvSXkb8lsAjhkV2PLCahsJBzgVt8NXf8wNvlpjeP6FaoXnUhxCHg==

Es importante que tengamos en cuenta que este storage de credenciales está ligado a la cuenta de usuario de Windows. Por tanto, si queremos que una cuenta de servicio, la que levanta el SQL Server por ejemplo, pueda hacer uso de ellos deberemos «impersonar» dicho usuario y a continuación lanzar el comando. También si queremos que cuentas de sistema, como Local System Account tengan acceso tendremos que hacer lo propio. Para ese caso concreto nos apoyaríamos en la herramienta de SysInternals PsExec (https://docs.microsoft.com/en-us/sysinternals/downloads/psexec) con la que si utilizamos el parámetro -s podremos impersonar precisamente a esa cuenta Local System Account. Una vez tenemos accesible el share podemos ya realizar la instalación de SQL Server y a continuación realizar alguna prueba de rendimiento.

La instalación de un failover cluster en Azure se realiza de forma similar a OnPremise con algunas pequeñas diferencias. Una es relativa a la no disponibilidad de IPs flotantes de forma nativa. Para ello tenemos que preparar la “IP flotante simulada” mediante un load balancer ILB:

https://docs.microsoft.com/en-us/azure/virtual-machines/windows/sql/virtual-machines-windows-portal-sql-create-failover-cluster#step-5-create-azure-load-balancer

La otra diferencia principal es que las instancias en failover cluster suelen configurarse sobre discos que están conectados con los nodos y compartidos con el cluster, bien mediante Fiber Channel, iSCSI, etc. En este caso vamos a utilizar Azure files Premium (preview) para proporcionar este storage compartido vía SMB 3.0.

El primer paso será conformar un cluster de Windows que en nuestro caso únicamente contará con dos nodos sql2 y sql3 así como una máquina virtual para el Active Directory :

Una vez instalada la feature de Failover Clustering crearemos un cluster añadiendo los dos nodos:

Al tratarse de un número par de nodos es muy conveniente que tengamos algún voto adicional para el desempate en caso de fallo de uno de ellos. En este caso he optado por un «cloud witness» que utiliza una cuenta de storage independiente. Para maximizar la disponibilidad esta cuenta de storage de Azure debería estar ubicada en un datacenter distinto:

Una vez configurado el cluster, realizaremos una instalación de SQL Server en cluster normal, con la diferencia que en el apartado de discos compartidos no tendremos ninguno disponible:

Cuando configuremos las rutas para las bases de datos de sistema, tendremos que especificar la ruta UNC al shared que hemos creado:

El warning únicamente indica que no puede comprobar si tenemos o no permisos en dicha carpeta. Una vez pasado este punto el resto de la instalación ocurre ya con normalidad y al finalizar si miramos la ubicación de nuestras bases de datos veremos que son efectivamente sobre dicha ruta UNC:

Como excepciones tenemos la base de datos tempdb (que está ubicada en C: pero mejor sería en D: y aprovechar el SSD local de estas máquinas) y luego la resourcedb, que es una base de datos interna propia de la instancia y que es «parte de la instalación» local del nodo. A continuación, realizaremos unas pequeñas pruebas en escenarios «complejos» para este tipo de situaciones. Básicamente, aquellos que sean sensibles a las latencias de acceso a disco, como las escrituras pequeñas en el log de transacciones. Para ello crearemos una pequeña tabla sobre la que realizaremos una serie de inserciones secuenciales y compararemos estos tiempos con los que podríamos obtener en una instancia OnPremise:

create database testAzureFiles 
go
use testAzureFiles
go
create table test (id int identity (1,1) primary key)
go
set statistics time off 
set nocount on 
insert into test default values 
go 1000
insert into test default values 
go 10000
insert into test default values 
go 100000

Los tiempos obtenidos son los siguientes:

Como podemos ver en estos escenarios la latencia de escritura del disco manda, y las mayores latencias de este tipo de escenarios hacen que el rendimiento sea comparativamente malo con un entorno OnPremise. El rendimiento sinembargo es muy similar tanto en el disco de 1TB como en el de 5 TB ya que las latencias en realidad son muy similares. Ejemplos de este tipo de operaciones sensibles a latencias serían, por ejemplo, procesos de facturación iterativos en serie, cursores de escritura, etc.

Si realizamos una operación bulk la situación es algo mejor, con menores diferencias, aunque aún sigue siendo algo más lento que un entorno OnPremise medio, por lo que sería importante testear aquellos procesos que importen datos de forma masiva para verificar que funcionan aceptablemente bien en un entorno Azure como este:

create database testAzureFiles2
go
use testAzureFiles2
go
create table test (id int identity (1,1) primary key)
go
set nocount on 

select top (1000000) replicate('a',1000) a into temp from sys.objects s1, sys.objects s2,  sys.objects s3, sys.objects s4
select top (10000000) replicate('a',1000) a into temp2 from sys.objects s1, sys.objects s2,  sys.objects s3, sys.objects s4

 

Si pasamos a las pruebas sintéticas por no alargarnos demasiado vamos a centrarnos en dos, lecturas y escrituras secuenciales para verificar el throughput alcanzable y por otra parte operaciones random de pequeño tamaño, 8 KB, para validar el número de IOPS de lectura/escritura que podemos alcanzar. También es interesante analizar las latencias de estas operaciones para ver cómo de estables son. Para estas pruebas utilizaremos DiskSpd (https://gallery.technet.microsoft.com/DiskSpd-a-robust-storage-6cd2f223) con el parámetro -L para que devuelva las latencias detalladas. Observamos que las latencias parecían algo elevadas, con 71 ms ya en el percentil 90th:

Analizando el comportamiento por thread vimos algo extraño con los thread pares, que estaban realizando un número muy inferior de IOPS que los impares:

La razón de fondo era que estábamos utilizando una VM de tipo B, burstable, y además de pequeño tamaño, lo cual nos estaba perjudicando en el rendimiento de entrada/salida. Debemos tener en cuenta los límites de las propias máquinas, si dispone de HT o no, etc. para evitar obtener resultados inferiores a los deseados por limitaciones de la propia VM. Tras un cambio a una DS16v3 obtuvimos valores mucho mejores tanto a nivel de reparto de IOs por thread como de estabilidad de las latencias manteniéndose en valores de menos de 4 ms en lectura hasta el percentil 95th:

Para las escrituras los valores se mantienen por debajo de los 7ms hasta el percentil 95th:

Debemos tener en cuenta que hablamos aún de una preview, por lo que muy posiblemente estos casos de picos de latencia puntuales mejoren una vez se disponga de la versión final pública. Lo ideal sería que los valores máximos que vemos en los 99th no fuesen tan elevados y solamente alcanzáramos unos pocos milisegundos como máximo. Por ejemplo, en un test en un disco local es raro llegar a los tres dígitos en el peor caso:

Sin embargo, como curiosidad, vemos que hay dos threads que realizan una cantidad de io claramente inferior al resto y con más latencia:

No es el objetivo analizar esta problemática por lo que solo indicaremos que, en este caso, el «freno» de estos cores era provocado por carga sobre mismos cores de otra máquina virtual OnPremise y por tanto nos afectaba «robando» ciclos de CPU a los cores virtuales de esta máquina virtual. Es relativamente habitual encontrar comportamientos «anómalos» cuando exprimimos máquinas virtuales y cuyo origen está en los «vecinos ruidosos» del mismo host.

Volviendo al rendimiento de los Azure Files Premium (preview), concluimos que la latencia es de unos 4ms en lecturas y unos 7ms en escrituras, con algunos picos puntuales. Si analizamos el rendimiento para cada uno de los shares que hemos creado y lo comparamos con los rendimientos teóricos calculados obtendremos lo siguiente:

Podemos ver que a nivel de IOPS se respetan los límites y se alcanzan las velocidades burst sin problemas. En el caso de los throughput máximos en lectura y escritura los excedemos en los discos de 500 GB, 1 TB y 2 TB. Las velocidades de escritura son en general inferiores a las de lectura, pero esto es algo bastante habitual por otra parte (como en un RAID10). Únicamente en el caso del disco de 5 TB (el mayor tamaño admitido en la preview) no alcanzamos los valores teóricos ni en lectura ni mucho menos en escritura. Es posible que para alcanzar las máximas velocidades sea necesario el uso de varias tarjetas de red a nivel de VM, posibilidad que no hemos llegado a explorar. En todo caso, hablamos de rendimientos muy buenos comparados con Azure Files «standard» y que ya cubren muchos escenarios de bases de datos OLTP de tamaño medio. Por tanto si por carga de entrada/salida nos encaja esta solución sería muy recomendable considerar esta opción para simplificar la infraestructura y facilitar las migraciones de instancias SQL Server en Failover Cluster OnPremise a Cloud.

Como posible mejora a Azure Files Premium creemos que sería muy interesante poderla combinar con la nueva funcionalidad de discos SSD Ultra (https://azure.microsoft.com/en-us/blog/announcing-ultra-ssd-the-next-generation-of-azure-disks-technology-preview/) que nos permite personalizar el rendimiento en IOPS desde 100 hasta 160000 con valores bastante elevados alcanzables con discos relativamente pequeños:

Ultra SSD Managed Disk Offerings

Disk size (GiB)481632641282565121,024-65,536 (in increments of 1 TiB)
IOPS range100-1,200100-2,400100-4,800100-9,600100-19,200100-38,400100-76,800100-153,600100-160,000
Throughput Cap (MBps)3006001,2002,0002,0002,0002,0002,0002,000

 

En conclusión, con Azure Files Premium se abre un abanico de posibilidades para migrar instancias SQL Server clusterizadas a Azure. El rendimiento que obtenemos es razonablemente bueno y es similar a muchos almacenamientos iSCSI de gama media del mercado. Aquellas cargas que sean muy sensibles a la latencia de acceso a disco o que requieran de muy alto rendimiento de entrada/salida (por ejemplo DW) no serían tan buenas candidatas (algo similar nos pasaría con otras soluciones S2D, iSCSI en Cloud, etc.).

Para estas opciones, si queremos movernos a Azure, probablemente tendremos que prescindir de utilizar una instancia clusterizada y utilizar instancias independientes con discos Ultra SSD (o Storage Pools agregando discos premium). Recomendamos encarecidamente antes de migrar medir el rendimiento de nuestro sistema OnPremise ya que, por ejemplo, si es un DW y está correctamente configurado sería bastante habitual obtener más de los 2 GB/s que tenemos como límite incluso con los nuevos SSD Ultra.

Rubén Garrigós

Rubén Garrigós is an expert in high-availability enterprise solutions based on SQL Server design, tuning, and troubleshooting. Over the past fifteen years, he has worked with Microsoft data access technologies in leading companies around the world. He currently is a Microsoft SQL Server and .NET applications architect with SolidQ. Ruben is certified by Microsoft as a Solution Expert on the Microsoft Data Platform (MSCE: Data Platform) and as a Solution Expert on the Microsoft Private Cloud (MSCE: Private Cloud). As a Microsoft Certified Trainer (MCT), Ruben has taught multiple official Microsoft courses as well as other courses specializing in SQL Server. He has also presented sessions at official events for various Microsoft technologies user groups.