[Série][AKS] Provisionando Bancos de Dados no Azure Kubernetes Service – Vitess

Fala minha gente! Espero que estejam todos bem!

Voltando com a série onde utilizamos nosso AKS para provisionar e escalar bancos de dados em containers falaremos desta vez de um dos bancos distribuídos que mais escalam horizontalmente: Vitess!

O Vitess é um projeto graduado da CNCF que entrega um cluster banco de dados MySQL distribuído por meio de uma camada de replicação que permite que você replique dados entre diversas instancias de MySQL, transformando o próprio MySQL em storage distribuído para seus dados. Além disso o Vitess tem a capacidade de utilizar Shardings que é a divisão de dados de uma determinada tabela ou conjunto de tabelas entre clusters diferentes com o intuito de isolar dados focados em uma determinada região do mundo, aumento a performance de escrita e leitura.

Para quem quiser conhecer mais a fundo como o Vitess funciona e sua arquitetura eu apresento aqui nesta palestra que fiz para o KCD Brasil de 2022:

Vamos começar, para isso precisamos criar uma pasta e baixar os arquivos que utilizaremos em nosso lab:

mkdir vitess
cd vitess
git clone https://github.com/vitessio/vitess
cd vitess/examples/operator

Vamos criar nosso namespace para encapsular os recursos que iremos provisionar:

kubectl create namespace vitess

Agora vamos criar o Operator para depois provisionarmos nosso cluster:

kubectl apply -f operator.yaml -n vitess

Agora vamos provisionar nosso cluster Vitess, para isso precisamos primeiro criar a configuração que será utilizada pelos nós do Cluster, crie um arquivo chamado vitess-cluster-config.yaml com o conteúdo:

apiVersion: v1
kind: Secret
metadata:
  name: vitess-cluster-config
type: Opaque
stringData:
  users.json: |
    {
      "user": [{
        "UserData": "user",
        "Password": ""
      }]
    }
  init_db.sql: |
    # This file is executed immediately after mysql_install_db,
    # to initialize a fresh data directory.

    ###############################################################################
    # Equivalent of mysql_secure_installation
    ###############################################################################
    # We need to ensure that super_read_only is disabled so that we can execute
    # these commands. Note that disabling it does NOT disable read_only.
    # We save the current value so that we only re-enable it at the end if it was
    # enabled before.
    SET @original_super_read_only=IF(@@global.super_read_only=1, 'ON', 'OFF');
    SET GLOBAL super_read_only='OFF';

    # Changes during the init db should not make it to the binlog.
    # They could potentially create errant transactions on replicas.
    SET sql_log_bin = 0;
    # Remove anonymous users.
    DELETE FROM mysql.user WHERE User = '';

    # Disable remote root access (only allow UNIX socket).
    DELETE FROM mysql.user WHERE User = 'root' AND Host != 'localhost';

    # Remove test database.
    DROP DATABASE IF EXISTS test;

    ###############################################################################
    # Vitess defaults
    ###############################################################################

    # Vitess-internal database.
    CREATE DATABASE IF NOT EXISTS _vt;
    # Note that definitions of local_metadata and shard_metadata should be the same
    # as in production which is defined in go/vt/mysqlctl/metadata_tables.go.
    CREATE TABLE IF NOT EXISTS _vt.local_metadata (
      name VARCHAR(255) NOT NULL,
      value VARCHAR(255) NOT NULL,
      db_name VARBINARY(255) NOT NULL,
      PRIMARY KEY (db_name, name)
      ) ENGINE=InnoDB;
    CREATE TABLE IF NOT EXISTS _vt.shard_metadata (
      name VARCHAR(255) NOT NULL,
      value MEDIUMBLOB NOT NULL,
      db_name VARBINARY(255) NOT NULL,
      PRIMARY KEY (db_name, name)
      ) ENGINE=InnoDB;

    # Admin user with all privileges.
    CREATE USER 'vt_dba'@'localhost';
    GRANT ALL ON *.* TO 'vt_dba'@'localhost';
    GRANT GRANT OPTION ON *.* TO 'vt_dba'@'localhost';

    # User for app traffic, with global read-write access.
    CREATE USER 'vt_app'@'localhost';
    GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,
      REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,
      LOCK TABLES, EXECUTE, REPLICATION CLIENT, CREATE VIEW,
      SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER
      ON *.* TO 'vt_app'@'localhost';

    # User for app debug traffic, with global read access.
    CREATE USER 'vt_appdebug'@'localhost';
    GRANT SELECT, SHOW DATABASES, PROCESS ON *.* TO 'vt_appdebug'@'localhost';

    # User for administrative operations that need to be executed as non-SUPER.
    # Same permissions as vt_app here.
    CREATE USER 'vt_allprivs'@'localhost';
    GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,
      REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,
      LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,
      SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER
      ON *.* TO 'vt_allprivs'@'localhost';

    # User for slave replication connections.
    # TODO: Should we set a password on this since it allows remote connections?
    CREATE USER 'vt_repl'@'%';
    GRANT REPLICATION SLAVE ON *.* TO 'vt_repl'@'%';

    # User for Vitess filtered replication (binlog player).
    # Same permissions as vt_app.
    CREATE USER 'vt_filtered'@'localhost';
    GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, FILE,
      REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES,
      LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW,
      SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER
      ON *.* TO 'vt_filtered'@'localhost';

    FLUSH PRIVILEGES;

    RESET SLAVE ALL;
    RESET MASTER;

    # custom sql is used to add custom scripts like creating users/passwords. We use it in our tests
    # {{custom_sql}}

    # We need to set super_read_only back to what it was before
    SET GLOBAL super_read_only=IFNULL(@original_super_read_only, 'ON');
  rbac.yaml: |
    rules:
    - resource: "*"
      actions:
        - "get"
        - "create"
        - "put"
        - "ping"
      subjects: ["*"]
      clusters: ["*"]
    - resource: "Shard"
      actions:
        - "emergency_failover_shard"
        - "planned_failover_shard"
      subjects: ["*"]
      clusters: ["*"]

Agora vamos criar o arquivo que vai provisionar o cluster propriamente dito, chame-o de vitess-cluster.yaml e insira:

apiVersion: planetscale.com/v2
kind: VitessCluster
metadata:
  name: vitess
  namespace: vitess
spec:
  images:
    vtctld: vitess/lite:latest
    vtadmin: vitess/vtadmin:latest
    vtgate: vitess/lite:latest
    vttablet: vitess/lite:latest
    vtbackup: vitess/lite:latest
    vtorc: vitess/lite:latest
    mysqld:
      mysql80Compatible: vitess/lite:latest
    mysqldExporter: prom/mysqld-exporter:v0.11.0
  cells:
  - name: zone1
    gateway:
      authentication:
        static:
          secret:
            name: vitess-cluster-config
            key: users.json
      replicas: 1
      resources:
        requests:
          cpu: 100m
          memory: 256Mi
        limits:
          memory: 256Mi
  vitessDashboard:
    cells:
    - zone1
    extraFlags:
      security_policy: read-only
    replicas: 1
    resources:
      limits:
        memory: 128Mi
      requests:
        cpu: 100m
        memory: 128Mi
  vtadmin:
    rbac:
      name: vitess-cluster-config
      key: rbac.yaml
    cells:
      - zone1
    apiAddresses:
      - http://localhost:14001
    replicas: 1
    readOnly: false
    apiResources:
      limits:
        memory: 128Mi
      requests:
        cpu: 100m
        memory: 128Mi
    webResources:
      limits:
        memory: 128Mi
      requests:
        cpu: 100m
        memory: 128Mi

  keyspaces:
  - name: commerce
    durabilityPolicy: none
    turndownPolicy: Immediate
    vitessOrchestrator:
      resources:
        limits:
          memory: 128Mi
        requests:
          cpu: 100m
          memory: 128Mi
      extraFlags:
        recovery-period-block-duration: 5s
    partitionings:
    - equal:
        parts: 1
        shardTemplate:
          databaseInitScriptSecret:
            name: vitess-cluster-config
            key: init_db.sql
          tabletPools:
          - cell: zone1
            type: replica
            replicas: 3
            vttablet:
              extraFlags:
                db_charset: utf8mb4
              resources:
                limits:
                  memory: 256Mi
                requests:
                  cpu: 100m
                  memory: 256Mi
            mysqld:
              resources:
                limits:
                  memory: 512Mi
                requests:
                  cpu: 100m
                  memory: 512Mi
            dataVolumeClaimTemplate:
              accessModes: ["ReadWriteOnce"]
              resources:
                requests:
                  storage: 10Gi
  updateStrategy:
    type: Immediate

Agora basta executar ambos:

kubectl create -f vitess-cluster-config.yaml -n vitess
kubectl create -f vitess-cluster.yaml -n vitess

Vamos ver agora como ficou toda nossa estrutura e componentes:

kubectl get all -n vitess
kubectl get pvc -n vitess
kubectl get pv -n vitess

 Após provisionamento dos recursos vamos agora instalar algumas coisas necessárias para operar neste banco de dados, começando pelo mysql-client:

brew install mysql-client

Agora vamos instalar o Vitess localmente para que possamos ter o client vtctlclient disponível para importamos os dados em nossos bancos de dados:

brew install vitess

Para podermos acessar no cluster vamos precisar fazer alguns Port Forwards, altere o arquivo pf.sh deixando assim:

#!/bin/sh

kubectl -n vitess port-forward --address localhost "$(kubectl -n vitess get service --selector="planetscale.com/component=vtctld" -o name | head -n1)" 15000 15999 & process_id1=$!
kubectl -n vitess port-forward --address localhost "$(kubectl -n vitess get service --selector="planetscale.com/component=vtgate,!planetscale.com/cell" -o name | head -n1)" 15306:3306 & process_id2=$!
kubectl -n vitess port-forward --address localhost "$(kubectl -n vitess get service --selector="planetscale.com/component=vtadmin" -o name | head -n1)" 14000:15000 14001:15001 & process_id3=$!
sleep 2
echo "You may point your browser to http://localhost:15000, use the following aliases as shortcuts:"
echo 'alias vtctlclient="vtctlclient --server=localhost:15999 --logtostderr"'
echo 'alias vtctldclient="vtctldclient --server=localhost:15999 --logtostderr"'
echo 'alias mysql="mysql -h 127.0.0.1 -P 15306 -u user"'
echo "Hit Ctrl-C to stop the port forwards"
wait $process_id1
wait $process_id2
wait $process_id3

Feito isso execute:

./pf.sh &
alias vtctlclient="vtctlclient --server=localhost:15999"
alias mysql="mysql -h 127.0.0.1 -P 15306 -u user"

No comando acima também criamos dois alias para comandos que vamos executar para acessar o cluster Vitess, sendo assim, vamos executar alguns scripts no Vitess, o primeiro cria o Schema\Banco de Dados Commerce, o segundo cria o VSchema:

vtctlclient ApplySchema -- --sql="$(cat create_commerce_schema.sql)" commerce
vtctlclient ApplyVSchema -- --vschema="$(cat vschema_commerce_initial.json)" commerce

Agora vamos logar em nosso cluster Vitess com o comando mysql, depois vamos executar:

mysql
show databases;
use commerce;
show tables;

É isso gente! Devo fazer outro post falando sobre Sharding exclusivamente para o Vitess, onde iremos estender este lab para mostrar o quão escalável ele é!

Até a próxima!

[Série][AKS] Provisionando Bancos de Dados no Azure Kubernetes Service – MongoDB

Fala meu povo!

Depois de alguns dias de descanso dos posts por aqui, estamos de volta!

Dessa vez veremos como provisionar um cluster de MongoDB no nosso querido e amado AKS!

Do ultimo post que fizemos até este tive problemas na minha subscription do MSDN que tem um limite de gastos e precisei alterar a subscription e por consequencia tive que recriar o cluster de AKS, segue as especificações deste novo cluster:

Agora que temos nosso cluster no ar, vamos baixar as credenciais para utiliza-lo:

az aks get-credentials --resource-group aks-database-labs --name aks-database-labs

Agora vamos verificar se existem 3 nodes assim como o cluster anterior:

kubectl get nodes

Uma vez que nosso cluster esta ok vamos começar a provisionar nosso cluster mongodb. Primeira coisa será isolar nossos recursos em um namespace dedicado:

kubectl create namespace mongodb

Para este lab utilizaremos o Operator do MongoDB Community Edition, versão indicada para testes e ambientes não produtivos, para ambientes produtivos indico a utilização do MongoDB Enterprise para, além de ter todas as features, também ter um suporte especializado do fornecedor. Para baixar os arquivos necessários, execute o git clone:

git clone https://github.com/mongodb/mongodb-kubernetes-operator.git

Uma vez com os arquivos em maquina, vamos agora alterar o arquivo cluster_role_binding.yaml para inserir no namespace para criação do ServiceAccount e ClusterRole que serão utilizados pelo Operator:

vim deploy/clusterwide/cluster_role_binding.yaml

O arquivo deve ficar desta forma:

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: mongodb-kubernetes-operator
subjects:
- kind: ServiceAccount
  namespace: mongodb
  name: mongodb-kubernetes-operator
roleRef:
  kind: ClusterRole
  name: mongodb-kubernetes-operator
  apiGroup: rbac.authorization.k8s.io

Agora vamos começar a provisionar nosso Operator, para isso vamos executar alguns yamls que irão criar as rbacs e acessos necessários para o nosso cluster:

kubectl apply -f deploy/clusterwide
kubectl apply -k config/rbac --namespace mongodb

Agora vamos registrar uma nova API em nosso AKS, essa API irá conter todos os componentes necessários para seguirmos com a criação do nosso operator e cluster replica-set:

kubectl apply -f config/crd/bases/mongodbcommunity.mongodb.com_mongodbcommunity.yaml
kubectl get crd/mongodbcommunity.mongodbcommunity.mongodb.com

Agora vamos validar a criação das nossas RBACs executando:

kubectl apply -k config/rbac/ --namespace mongodb
kubectl get role mongodb-kubernetes-operator --namespace mongodb
kubectl get rolebinding mongodb-kubernetes-operator --namespace mongodb
kubectl get serviceaccount mongodb-kubernetes-operator --namespace mongodb

Agora vamos criar nosso Operator efetivamente executando o seguinte yaml:

kubectl create -f config/manager/manager.yaml --namespace mongodb
kubectl get pods --namespace mongodb

Uma vez que nosso Operator esteja rodando com sucesso, vamos agora criar um ReplicaSet que irá provisionar nosso cluster de MongoDB Community, para isso crie um arquivo chamado mongodb.yaml com o conteúdo abaixo:

---
apiVersion: mongodbcommunity.mongodb.com/v1
kind: MongoDBCommunity
metadata:
  name: mongodb
spec:
  members: 3
  type: ReplicaSet
  version: "4.2.6"
  security:
    authentication:
      modes: ["SCRAM"]
  users:
    - name: akslabuser
      db: admin
      passwordSecretRef: # a reference to the secret that will be used to generate the user's password
        name: akslabuserpassword
      roles:
        - name: clusterAdmin
          db: admin
        - name: userAdminAnyDatabase
          db: admin
      scramCredentialsSecretName: my-scram
  additionalMongodConfig:
    storage.wiredTiger.engineConfig.journalCompressor: zlib

---
apiVersion: v1
kind: Secret
metadata:
  name: akslabuserpassword
type: Opaque
stringData:
  password: P@ssW0rdC0mpl3x

Agora crie o Replica Set:

kubectl create -f mongodb.yaml -n mongodb
kubectl get pods -n mongodb

Varios componentes foram criados junto neste provisionamento em background, vamos ver como ficou toda nossa estrutura? Execute:

kubectl get all -n mongodb
kubectl get pvc -n mongodb
kubectl get pv -n mongodb

Agora temos nosso cluster MongoDB executando em nosso AKS, se precisarmos adicionar algum Arbiter para ajudar na eleição de novos primarys e auxiliar na gestão do cluster basta incluir no arquivo de provisionamento do Replica Set:

Execute o comando para alterar o Replica Set:

kubectl apply -f mongodb.yaml -n mongodb

Para saber as strings de conexão e informações para logar no cluster execute o seguinte comando:

kubectl get secret mongodb-admin-akslabuser -n mongodb -o json | jq -r '.data | with_entries(.value |= @base64d)'

Para validar se o seu cluster esta funcionando apropriadamente vamos entrar em algum dos pods, logar no MongoDB e executar o comando rs.status() para verificar o cluster:

kubectl -n mongodb exec --stdin --tty mongodb-0 -- /bin/bash

Uma vez dentro do Pod mongodb-0 execute:

mongo "mongodb+srv://akslabuser:P%40ssW0rdC0mpl3x@mongodb-svc.mongodb.svc.cluster.local/admin?replicaSet=mongodb&ssl=false"

E agora para verificar o Status do nosso Replica Set, basta executar:

rs.status()

Caso vocês queiram se aprofundar mais neste lab e em outros componentes desta estrutura é só consultar o projeto no Github:

https://github.com/mongodb/mongodb-kubernetes-operator

Gente, por hoje é só, lembrando que este é um lab, caso queira executar o MongoDB em ambiente produtivo e em K8s indico seguir com a versão Enterprise.

Até o próximo Post!

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure – Criando Módulo Terraform para Azure Cache for Redis

Fala meu povo e minha pova!

Mais uma manhã com conteúdo top! Continuando nossa série de posts de mão na massa, de construção de pipelines DevOps para entrega de infraestrutura de banco de dados no Azure, neste post veremos como criar uma instancia de banco de dados Redis gerenciado! O Redis é um banco de dados NoSQL, chave-valor que tem o intuito de armazenar dados em cache, ou seja, em memória Ram, o que o torna extremamente mais performático que um banco de dados relacional, principalmente para leitura.

O Redis é uma tecnologia que utilizamos para retirar carga de leitura estática dos bancos de dados relacionais e até alguns NoSQL que ficam no backend das nossas aplicações, os tipos de sistemas que mais se beneficiam desta tecnologia são: Login, configurações estáticas, armazenamento de estado, entre outros.

Vamos iniciar nosso módulo, para quem ainda não viu os posts anteriores sobre o projeto e a criação dos repositórios git, aconselho ver os seguintes posts:

Crie o repositório pipeline_iac_azure_cache_redis e dentro dele crie os seguintes arquivos:

1 – Vamos criar o arquivo rg.tf que será responsável por provisionar nosso resource group:

resource "azurerm_resource_group""rg"{  
name     = var.rg  
location = var.regiao
}

2 – Agora crie o arquivo variables.tf aonde iremos declarar nossas variáveis dinâmicas que serão utilizadas posteriormente para criar os recursos de bancos de dados e o próprio resource group:

variable "rg" {
    description = "Resource Group que será criado|utilizado na criação dos recursos de banco de dados"
}

variable "regiao" {
    description = "Região ao qual os recursos serão criados"
}

variable "ambiente" {
    type = map
    default = {
        d = "dev",
        h = "hml",
        p = "prd"
    }
}

variable "env" {
    default = "d"

    validation {
        condition = contains(["d","h","p"],var.env)
        error_message = "Argument 'env' must be either 'd' (dev), 'h' (hml) or 'p' (prd)"
    }
}

variable "nome_sistema" {
    description = "Nome do sistema ao qual os recursos serão destinados"
}

variable "tamanho_servidor" {
    description = "Especifica o tipo de perfil do servidor de cache Redis que será criado. Valores possíveis: Basic, Standard e Premium"
    default = {
        d = "Basic",
        h = "Standard",
        p = "Premium"
    }
}

variable "capacidade_servidor" {
    description = "Tamanho do servidor cache Redis. Valores validos baseado na variavel tamanho_servidor: C (Basic/Standard) are 0, 1, 2, 3, 4, 5, 6, and for P (Premium) family are 1, 2, 3, 4, 5."
    default = 1
}

variable "versao_redis" {
    description = "Versão do Redis que será utilizado, por padrão será a mais recente disponivel neste momento: 6"
    default = "6"
}

3 – Depois disso vamos criar o arquivo locals.tf onde algumas variáveis serão trabalhadas com base em tomadas de decisão, principalmente focada no ambiente produtivo, aonde precisamos ter ambientes mais robustos e redundantes:

locals {
    ambiente                = lookup(var.ambiente,var.env)
    server_sku_name         = lookup(var.tamanho_servidor,var.env)
    server_family           = local.server_sku_name == "Premium" ? "P" : "C"
    
    tags = {
        env         = var.env
        ambiente    = local.ambiente
        sistema     = var.nome_sistema
    }
}

4 – Agora vamos criar nossos recursos de cache de bancos de dados, crie um arquivo redis.tf aonde iremos criar um servidor de Redis:

resource "azurerm_redis_cache" "redis_cache_db" {
  name                = "rediscachedb-${var.nome_sistema}-${local.ambiente}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  redis_version       = var.versao_redis  
  capacity            = var.capacidade_servidor
  family              = local.server_family
  sku_name            = local.server_sku_name
  enable_non_ssl_port = false
  minimum_tls_version = "1.2"

  redis_configuration {
  }

  tags = merge(local.tags)
}

Alguns pontos importantes que podemos verificar acima são: configurações de tls habilitadas para garantir a criptografia de dados que estão trafegando do Redis para aplicação e vice-versa (Podemos ver isso na imagem abaixo) e o campo redis_configuration, neste campo podemos configurar o Redis internamente setando configurações especificas desta tecnologia. Para mais informações sobre essas configurações, olhar: https://learn.microsoft.com/en-us/azure/azure-cache-for-redis/cache-configure#default-redis-server-configuration

5 – Quase acabando, vamos criar um arquivo output.tf para sabermos qual é o ID e hostname do banco de dados que estamos provisionando:

output "azure_cache_redis_id" {
    value = azurerm_redis_cache.redis_cache_db.id
}

output "azure_cache_redis_hostname" {
    value = azurerm_redis_cache.redis_cache_db.hostname
}

6 – Por fim, vamos declarar os valores das nossas variáveis no arquivo terraform.tfvars:

rg              = "TerraformAzurCacheRedis"
regiao          = "eastus"
env             = "p"
nome_sistema    = "login"

Uma vez criado todos os componentes do nosso módulo Terraform, vamos agora executa-lo para verificar se esta tudo funcionando como esperado:

terraform init
terraform validate
terraform plan
terraform apply --auto-approve

Podemos seguir agora para destruição desses recursos que não utilizaremos agora, executando:

terraform destroy

Vamos agora subir nossos códigos para o Github:

git add .
git commit -m "Modulo Terraform Azure Cache for Redis"
git push

É isso gente! Terminamos de criar nossos módulo Terraform para provisionamento de nossas infras de banco de dados, nos próximos capítulos iremos construir nossas pipelines de provisionamento dessas infraestruturas de forma totalmente automatizada!

Fiquem ligades! Até a próxima!

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure – Criando Módulo Terraform para Azure Database for MySQL

Fala meu poves!

De volta com essa série maravilinda para mais um episódio de criação de módulo Terraform para entregar um banco de dados MySQL desta vez, vocês verão que a estrutura é muito parecida com o módulo de PostgreSQL, mudando apenas algumas coisas relativas a própria engine do MySQL.

Para quem ainda não viu os posts anteriores sobre o projeto e a criação dos repositórios git, aconselho ver os seguintes posts:

Crie o repositório pipeline_iac_azure_db_mysql e dentro dele crie os seguintes arquivos:

1 – Vamos criar o arquivo rg.tf que será responsável por provisionar nosso resource group:

resource "azurerm_resource_group" "rg" {
  name     = var.rg
  location = var.regiao
}

2 – Agora crie o arquivo variables.tf aonde iremos declarar nossas variáveis dinâmicas que serão utilizadas posteriormente para criar os recursos de bancos de dados e o próprio resource group:

variable "rg" {
    description = "Resource Group que será criado|utilizado na criação dos recursos de banco de dados"
}

variable "regiao" {
    description = "Região ao qual os recursos serão criados"
}

variable "ambiente" {
    type = map
    default = {
        d = "dev",
        h = "hml",
        p = "prd"
    }
}

variable "env" {
    default = "d"

    validation {
        condition = contains(["d","h","p"],var.env)
        error_message = "Argument 'env' must be either 'd' (dev), 'h' (hml) or 'p' (prd)"
    }
}

variable "nome_sistema" {
    description = "Nome do sistema ao qual os recursos serão destinados"
}

variable "storage_account_tier" {
    description = "Tipo de Storage Account que pode ser Standard ou Premium. Por padrão será Standard"
    default = "Standard"
}

variable "storage_account_repl_type" {
    description = "Tipo de Replicação de Storage Account que pode ser LRS, GRS, RAGRS, ZRS, GZRS and RAGZRS. Por padrão será LRS"
    default = "LRS"
}

variable "admin_user_login" {
    description = "Usuario para logar no servidor e banco de dados"
    default = "admindb"
}

variable "admin_user_passwd" {
    description = "Senha do usuario para logar no servidor e banco de dados"
    sensitive = true
}

variable "db_collation" {
    description = "Collation do banco de dados."
    default = "utf8_unicode_ci"
}

variable "db_charset" {
    description = "Conjunto de caracteres do banco de dados. Por padrão será UTF8"
    default = "utf8"
}

variable "db_storage_max_size_mb" {
    description = "Tamanho máximo do banco de dados em Megabytes. Por padrão 5120MB"
    default = 5120
}

variable "db_storage_auto_grow" {
    description = "Habilita crescimento automatico de storage."
    default = false
}

variable "tamanho_servidor" {
    description = "Especifica o tipo de perfil do servidor de banco de dados que será criado. https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/mysql_server"
    default = {
        d = "B_Gen4_1",
        h = "B_Gen5_2",
        p = "GP_Gen5_8"
    }
}

variable "versao_mysql" {
    description = "Versão do MySQL que será utilizado, por padrão será a mais recente disponivel neste momento: 8.0"
    default = "8.0"
}

variable "dias_retencao_backup" {
    description = "Quantidade de dias que o backup ficará disponível para ser restaurado. Por padrão 7 dias"
    default = 7
}

variable "reundancia_regiao_backup" {
    description = "Ativa a redundancia de backup para outras região para casos de DR."
    default = false
}

3 – Depois disso vamos criar o arquivo locals.tf onde algumas variáveis serão trabalhadas com base em tomadas de decisão, principalmente focada no ambiente produtivo, aonde precisamos ter ambientes mais robustos e redundantes:

locals {
    ambiente                = lookup(var.ambiente,var.env)
    server_sku_name         = lookup(var.tamanho_servidor,var.env)
    reundancia_regiao_backup = var.env == "p" ? true : var.reundancia_regiao_backup
    db_storage_auto_grow    = var.env == "p" ? true : var.db_storage_auto_grow

    tags = {
        env         = var.env
        ambiente    = local.ambiente
        sistema     = var.nome_sistema
    }
}

4 – Agora vamos criar nossos recursos de bancos de dados, crie um arquivo mysql.tf aonde iremos criar um servidor de banco de dados MySQL e um banco de dados dentro deste servidor:

resource "azurerm_mysql_server" "mysql_server" {
  name                = "mysqlserver-${var.nome_sistema}-${local.ambiente}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  sku_name = local.server_sku_name

  storage_mb                   = var.db_storage_max_size_mb
  backup_retention_days        = var.dias_retencao_backup
  geo_redundant_backup_enabled = local.reundancia_regiao_backup
  auto_grow_enabled            = local.db_storage_auto_grow

  administrator_login          = var.admin_user_login
  administrator_login_password = var.admin_user_passwd
  version                      = var.versao_mysql
  ssl_enforcement_enabled      = true

  tags = merge(local.tags)
}

resource "azurerm_mysql_database" "mysql_database" {
  name                = "mysqldb-${var.nome_sistema}-${local.ambiente}"
  resource_group_name = azurerm_resource_group.rg.name
  server_name         = azurerm_mysql_server.mysql_server.name
  charset             = var.db_charset
  collation           = var.db_collation
}

5 – Quase acabando, vamos criar um arquivo output.tf para sabermos qual é o ID do banco de dados que estamos provisionando:

output "azure_db_mysql_id" {
    value = azurerm_mysql_database.mysql_database.id
}

6 – Por fim, vamos declarar os valores das nossas variáveis no arquivo terraform.tfvars:

rg              = "TerraformAzureMySQLdb"
regiao          = "eastus"
env             = "p"
nome_sistema    = "pagamentos"
admin_user_passwd = "P@ssW0dComPl3x"

Uma vez criado todos os componentes do nosso módulo Terraform, vamos agora executa-lo para verificar se esta tudo funcionando como esperado:

terraform init
terraform validate
terraform plan
terraform apply --auto-approve

Vamos ver como estão nossos recursos na console do Azure:

Como podemos ver na tela acima, versão do MySQL esta conforme declaramos e o sizing do servidor foi baseado na variavel env o qual escolhemos um ambiente produtivo, logo, com um sizing maior dentre outras melhorias, isso pode ser confirmado nas tags também.

Agora podemos excluir todos os recursos executando:

terraform destroy

Vamos agora mandar todo esse código para o Github? Só executar:

git add .
git commit -m "Modulo Terraform Azure Database for MySQL"
git push

É isso galera! Mais um módulo pronto, no próximo post faremos nosso ultimo módulo Terraform e depois partiremos para a construção das pipelines! Então fiquem ligades galera!

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure – Criando Módulo Terraform para Azure Database for PostgreSQL

Fala meu povo!

Mais um capitulo da nossa saga de criar módulos Terraform para no futuro construirmos nossa pipeline de entrega de bancos de dados no Azure DevOps. Desta vez vamos construir um módulo Terraform que entrega um banco de dados Azure Database for PostgreSQL. Vamos lá!

Para quem ainda não viu os posts anteriores sobre o projeto e a criação dos repositórios git, aconselho ver os seguintes posts:

Crie o repositório pipeline_iac_azure_db_postgresql e dentro dele crie os seguintes arquivos:

1 – Vamos criar o arquivo rg.tf que será responsável por provisionar nosso resource group:

resource "azurerm_resource_group" "rg" {
  name     = var.rg
  location = var.regiao
}

2 – Agora crie o arquivo variables.tf aonde iremos declarar nossas variáveis dinâmicas que serão utilizadas posteriormente para criar os recursos de bancos de dados e o próprio resource group:

variable "rg" {
    description = "Resource Group que será criado|utilizado na criação dos recursos de banco de dados"
}

variable "regiao" {
    description = "Região ao qual os recursos serão criados"
}

variable "ambiente" {
    type = map
    default = {
        d = "dev",
        h = "hml",
        p = "prd"
    }
}

variable "env" {
    default = "d"

    validation {
        condition = contains(["d","h","p"],var.env)
        error_message = "Argument 'env' must be either 'd' (dev), 'h' (hml) or 'p' (prd)"
    }
}

variable "nome_sistema" {
    description = "Nome do sistema ao qual os recursos serão destinados"
}

variable "storage_account_tier" {
    description = "Tipo de Storage Account que pode ser Standard ou Premium. Por padrão será Standard"
    default = "Standard"
}

variable "storage_account_repl_type" {
    description = "Tipo de Replicação de Storage Account que pode ser LRS, GRS, RAGRS, ZRS, GZRS and RAGZRS. Por padrão será LRS"
    default = "LRS"
}

variable "admin_user_login" {
    description = "Usuario para logar no servidor e banco de dados"
    default = "admindb"
}

variable "admin_user_passwd" {
    description = "Senha do usuario para logar no servidor e banco de dados"
    sensitive = true
}

variable "db_collation" {
    description = "Collation do banco de dados."
    default = "English_United States.1252"
}

variable "db_charset" {
    description = "Conjunto de caracteres do banco de dados. Por padrão será UTF8"
    default = "UTF8"
}

variable "db_storage_max_size_mb" {
    description = "Tamanho máximo do banco de dados em Megabytes. Por padrão 5120MB"
    default = 5120
}

variable "db_storage_auto_grow" {
    description = "Habilita crescimento automatico de storage."
    default = false
}

variable "tamanho_servidor" {
    description = "Especifica o tipo de perfil do servidor de banco de dados que será criado. https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/postgresql_server"
    default = {
        d = "B_Gen4_1",
        h = "B_Gen5_2",
        p = "GP_Gen5_8"
    }
}

variable "versao_postgresql" {
    description = "Versão do PostgreSQL que será utilizado, por padrão será a mais recente disponivel neste momento: 11"
    default = "11"
}

variable "dias_retencao_backup" {
    description = "Quantidade de dias que o backup ficará disponível para ser restaurado. Por padrão 7 dias"
    default = 7
}

variable "reundancia_regiao_backup" {
    description = "Ativa a redundancia de backup para outras região para casos de DR."
    default = false
}

3 – Depois disso vamos criar o arquivo locals.tf onde algumas variáveis serão trabalhadas com base em tomadas de decisão, principalmente focada no ambiente produtivo, aonde precisamos ter ambientes mais robustos e redundantes:

locals {
    ambiente                = lookup(var.ambiente,var.env)
    server_sku_name         = lookup(var.tamanho_servidor,var.env)
    reundancia_regiao_backup = var.env == "p" ? true : var.reundancia_regiao_backup
    db_storage_auto_grow    = var.env == "p" ? true : var.db_storage_auto_grow

    tags = {
        env         = var.env
        ambiente    = local.ambiente
        sistema     = var.nome_sistema
    }
}

4 – Agora vamos criar nossos recursos de bancos de dados, crie um arquivo postgresql.tf aonde iremos criar um servidor de banco de dados PostgreSQL e um banco de dados dentro deste servidor:

resource "azurerm_postgresql_server" "pgsql_server" {
  name                = "postgresql-${var.nome_sistema}-${local.ambiente}"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  sku_name = local.server_sku_name

  storage_mb                   = var.db_storage_max_size_mb
  backup_retention_days        = var.dias_retencao_backup
  geo_redundant_backup_enabled = local.reundancia_regiao_backup
  auto_grow_enabled            = local.db_storage_auto_grow

  administrator_login          = var.admin_user_login
  administrator_login_password = var.admin_user_passwd
  version                      = var.versao_postgresql
  ssl_enforcement_enabled      = true

  tags = merge(local.tags)
}

resource "azurerm_postgresql_database" "pgsql_database" {
  name                = "pgsqldb-${var.nome_sistema}-${local.ambiente}"
  resource_group_name = azurerm_resource_group.rg.name
  server_name         = azurerm_postgresql_server.pgsql_server.name
  charset             = var.db_charset
  collation           = var.db_collation
}

5 – Quase acabando, vamos criar um arquivo output.tf para sabermos qual é o ID do banco de dados que estamos provisionando:

output "azure_db_pgsql_id" {
    value = azurerm_postgresql_database.pgsql_database.id
}

6 – Por fim, vamos declarar os valores das nossas variáveis no arquivo terraform.tfvars:

rg              = "TerraformAzurePostgreSQLdb"
regiao          = "eastus"
env             = "p"
nome_sistema    = "pagamentos"
admin_user_passwd = "P@ssW0dComPl3x"

Uma vez criado todos os componentes do nosso módulo Terraform, vamos agora executa-lo para verificar se esta tudo funcionando como esperado:

terraform init
terraform validate
terraform plan
terraform apply --auto-approve

Vamos ver como estão nossos recursos na console do Azure:

Como podemos ver na tela acima, versão do PostgreSQL esta conforme declaramos e o sizing do servidor foi baseado na variavel env o qual escolhemos um ambiente produtivo, logo, com um sizing maior dentre outras melhorias, isso pode ser confirmado nas tags também.

Para excluir todos esses recursos uma vez que não vamos utiliza-los agora, basta executar:

terraform destroy

E é isso por hoje gente, agradeço novamente pela atenção de vocês e no próximo post iremos construir um módulo MySQL! Não perca! Até a próxima.

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure – Criando Módulo Terraform para CosmosDB (MongoDB)

Fala minha gente! Espero que estejam todos bem!

Neste post iremos construir um módulo Terraform que será utilizado posteriormente em nossa pipeline no Azure DevOps, desta vez construiremos um IaC para provisionar um banco de dados CosmosDB!

Vamos iniciar criando os arquivos de provider.tf:

terraform {

  backend "local" {
  }
 
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "3.48.0"
    }
  }
}

provider "azurerm" {
  features {}
  subscription_id = "16d873b7-07e2-482d-a0d7-d652b5d72e4a"
}

E rg.tf para criar nosso Resource Group aonde ficará nossos recursos do CosmosDB:

resource "azurerm_resource_group" "rg" {
  name     = var.rg
  location = var.regiao
}

Agora vamos criar um arquivo com nossas variáveis chamado variables.tf:

variable "subscription_id" {
    description = "Passar sua subscription do Azure aonde os recursos serão gerados"
}

variable "rg" {
    description = "Resource Group que será criado|utilizado na criação dos recursos de banco de dados"
}

variable "regiao" {
    description = "Região ao qual os recursos serão criados"
}

variable "ambiente" {
    type = map
    default = {
        d = "dev",
        h = "hml",
        p = "prd"
    }
}

variable "env" {
    default = "d"

    validation {
        condition = contains(["d","h","p"],var.env)
        error_message = "Argument 'env' must be either 'd' (dev), 'h' (hml) or 'p' (prd)"
    }
}

variable "nome_sistema" {
    description = "Nome do sistema ao qual os recursos serão destinados"
}

variable "tempo_expiracao_registro" {
    description = "TTL - Time To Live, tempo de expiração do dado na collection do CosmosDB. Default 7 Dias."
    default = 604800 
}

variable "shard_key" {
    description = "Nome da coluna para chave de particionamento|sharding"
}

variable "throughput" {
    description = "Capacidade de RU/s que a collection terá. Por padrão será o minimo de 400."
    default = 400
}

variable "keys" {
    description = "Colunas para determinar a chave de particionamento da tabela. Por default será a coluna _id"
    type        = list(string)
    default     = ["_id"] 
}

Agora com base nas variáveis criadas acima vamos criar nossos locals que tem por base algumas variáveis passadas, criaremos o arquivo locals.tf com o conteúdo:

locals {
    ambiente                = lookup(var.ambiente,var.env)
    default_ttl_seconds    = var.env == "p" && var.tempo_expiracao_registro > 604800 ? var.tempo_expiracao_registro : 86400 #1 dia

    tags = {
        env         = var.env
        ambiente    = local.ambiente
    }
}

Depois de criarmos nossa base do módulo vamos criar os recursos que é o nosso banco de dados CosmosDB. Para quem não esta familiarizado, o CosmosDB é um banco de dados NoSQL multi engine serverless onde podemos criar bancos de dados de varios tipos como NoSQL com Documentos + Linguagem SQL, PostgreSQL gerenciado, Cassandra (Wide-Column oriented), Tabela, Gremlin (Grafos) e por fim MongoDB que entrega document-json. Abaixo temos todas essas ofertas na console do Azure:

Este módulo que estamos criando tem o intuito de entregar um banco de dados CosmosDB com engine\api MongoDB, justamente para termos uma opção voltada a documentos entre os bancos que estamos provisionando em nossas pipelines, sendo assim, criaremos um arquivo cosmosdb_mongodb.tf com o conteúdo:

resource "azurerm_cosmosdb_account" "cosmosdbmongoacc" {
  name                = "${var.nome_sistema}-cosmosdb-account-${local.ambiente}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  offer_type          = "Standard"
  kind                = "MongoDB"

  capabilities {
    name = "EnableAggregationPipeline"
  }

  capabilities {
    name = "mongoEnableDocLevelTTL"
  }

  capabilities {
    name = "MongoDBv3.4"
  }

  capabilities {
    name = "EnableMongo"
  }

  consistency_policy {
    consistency_level       = "BoundedStaleness"
    max_interval_in_seconds = 300
    max_staleness_prefix    = 100000
  }

  geo_location {
    location          = azurerm_resource_group.rg.location
    failover_priority = 1
  }

  tags                = merge(local.tags)

}

resource "azurerm_cosmosdb_mongo_database" "mongodb_database" {
  name                = "cosmosdb-mongo-${var.nome_sistema}-${local.ambiente}"
  resource_group_name = azurerm_resource_group.rg.name
  account_name        = azurerm_cosmosdb_account.cosmosdbmongoacc.name
}

resource "azurerm_cosmosdb_mongo_collection" "mongodb_collection" {
  name                = "cosmosdb-${var.nome_sistema}-${local.ambiente}"
  resource_group_name = azurerm_cosmosdb_account.cosmosdbmongoacc.resource_group_name
  account_name        = azurerm_cosmosdb_account.cosmosdbmongoacc.name
  database_name       = azurerm_cosmosdb_mongo_database.mongodb_database.name

  default_ttl_seconds = local.default_ttl_seconds
  shard_key           = var.shard_key
  throughput          = var.throughput

  index {
    keys   = var.keys
    unique = true
  }
}

Vamos então imprimir na saída da criação dos recursos os ids de criação do banco de dados e da collection, criando o arquivo output.tf:

output "cosmodb_database_id" {
    value = azurerm_cosmosdb_mongo_database.mongodb_database.id
}

output "cosmodb_collection_id" {
    value = azurerm_cosmosdb_mongo_collection.mongodb_collection.id
}

Agora por fim, iremos declarar os valores das nossas variáveis para não precisarmos digita-las durante a execução do módulo, crie o arquivo terraform.tfvars com o conteúdo a seguir:

subscription_id = "sua_subscription_id_aqui"
rg              = "TerraformAzureCosmosDBMongoDB"
regiao          = "eastus"
nome_sistema    = "webapp"
env             = "p"
shard_key       = "uniqueKey"
throughput      = 1000

Assim ficou a estrutura do nosso módulo terraform:

Vamos agora executar nosso módulo para verificar se todos os recursos serão executados com êxito, lembrando que você precisará executar o login no azure via cli para conseguir rodar o terraform:

az login #logue na Azure com sua conta
terraform init
terraform validate
terraform plan
terraform apply --auto-approve

Por fim temos nossos recursos criados e podemos ver no console do Azure:

Para nos conectarmos na nossa instancia de banco de dados mongodb precisamos ir em Settings (Configurações) no menu do lado esquerdo da tela e clicar em Connection String, então teremos as seguintes informações:

No meu caso que utilizo JetBrains DataGrip posso utilizar a URL de conexão a base destacada na imagem acima, basta escolher o Datasource Mongodb e configurar para passar apenas a URL de autenticação e clicar em Test Connection e depois em OK:

Pronto, mais um banco de dados provisionado com sucesso no Azure! Agora podemos destruir essa infraestrutura que não precisa ficar disponível neste momento, para isso basta executar:

terraform destroy

Agora vamos subir nosso código para o Github para ficar guardadinho lá esperando a pipeline que faremos posteriormente:

git add .
git commit -m "Modulo Terraform Azure CosmosDB"
git push

É isso por hoje gente, obrigado por me acompanhar nesta série maravilhosa onde estamos desenvolvendo nossa infraestrutura como código!

No próximo post iremos construir mais um banco de dados! Fiquem ligades!

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure – Criando Módulo Terraform para Azure SQL Database

Opaaaa!

Estamos de volta para colocar a mão no código! Vamos iniciar o desenvolvimento da infraestrutura como código do Azure SQL Database para posteriormente o mesmo ser adicionado à pipeline que iremos criar no Azure DevOps. Vamos lá.

Para instalar o Terraform verifique o link a seguir:

https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli

Depois de instalar o Terraform vamos instalar o Visual Studio Code, pelo link:

https://code.visualstudio.com/download

Abra o VS Code e clique no icone de Extensões do lado esquerdo e escreva no campo de busca superior Terraform, clique no icone que tiver Hashicorp Terraform e selo da Hashicorp em baixo, depois disso clique em Instalar (Install):

Agora vamos abrir as pasta aonde estão nossos repositórios: Clique na parte superior em Arquivos (File), Abrir Pasta (Open Folder…) e encontre a pasta onde estão seus repositórios e clique em Abrir (Open):

Agora vamos iniciar o desenvolvimento, crie os seguintes arquivos dentro da estrutura de pasta da pipeline de iac do azure sql database:

Abra o arquivo provider.tf e insira código a seguir:

terraform {

  backend "local" {
  }
 
  required_providers {
    azurerm = {
      source = "hashicorp/azurerm"
      version = "3.48.0"
    }
  }
}

provider "azurerm" {
  features {}
  subscription_id = "seu_subscription_id"
}

Precisamos declarar as variáveis que serão utilizados nos recursos abaixo, edite o arquivo varibles.tf:

variable "rg" {
    description = "Resource Group que será criado|utilizado na criação dos recursos de banco de dados"
}

variable "regiao" {
    description = "Região ao qual os recursos serão criados"
}

variable "ambiente" {
    type = map
    default = {
        d = "dev",
        h = "hml",
        p = "prd"
    }
}

variable "env" {
    default = "d"

    validation {
        condition = contains(["d","h","p"],var.env)
        error_message = "Argument 'env' must be either 'd' (dev), 'h' (hml) or 'p' (prd)"
    }
}

variable "nome_sistema" {
    description = "Nome do sistema ao qual os recursos serão destinados"
}

variable "storage_account_tier" {
    description = "Tipo de Storage Account que pode ser Standard ou Premium. Por padrão será Standard"
    default = "Standard"
}

variable "storage_account_repl_type" {
    description = "Tipo de Replicação de Storage Account que pode ser LRS, GRS, RAGRS, ZRS, GZRS and RAGZRS. Por padrão será LRS"
    default = "LRS"
}

variable "admin_user_login" {
    description = "Usuario para logar no servidor e banco de dados"
    default = "admindb"
}

variable "admin_user_passwd" {
    description = "Senha do usuario para logar no servidor e banco de dados"
    sensitive = true
}

variable "db_collation" {
    description = "Collation do banco de dados."
    default = "SQL_Latin1_General_CP1_CI_AS"
}

variable "db_max_size" {
    description = "Tamanho máximo do banco de dados. Por padrão 10GB"
    default = 10
}

variable "db_sku_name" {
    description = "Especifica o tipo de perfil de banco de dados que será criado. As opções são: GP_S_Gen5_2,HS_Gen4_1,BC_Gen5_2, ElasticPool, Basic,S0, P2 ,DW100c, DS100. Por padrão será Basic. https://azure.microsoft.com/en-us/pricing/details/azure-sql-database/single/"
    default = "Basic"
}

Vamos criar mais algumas regras onde, para o caso de o ambiente ser produtivo onde vamos forçar a utilização de features que nos permitam ter redundância e performance como storage premium e replica de leitura (ainda não disponível para esta versão do provider, no futuro atualizaremos o post.). Para criar as condições basta comparar o resultado de uma variável com um valor seguido do ? onde a primeira parte é em caso verdadeiro utilizar o valor setado e os : aplica-se uma condição se-não em caso retorno falso da primeira expressão setar este valor, ficando da seguinte forma: condição ? (então) verdadeiro : (se-não) falso. Abra o arquivo locals.tf e cole o texto:

locals {
    ambiente                = lookup(var.ambiente,var.env)
    storage_account_tier    = var.env == "p" ? "Premium" : var.env
    read_replica_count      = var.env == "p" ? 1 : 0
    zone_redundant          = var.env == "p" ? true : false

    tags = {
        env         = var.env
        ambiente    = local.ambiente
    }
}

Após o provider vamos criar o Resource Group que irá comportar nossos recursos, abra o arquivo rg.tf:

resource "azurerm_resource_group" "rg" {
  name     = var.rg
  location = var.regiao
}

Agora vamos abrir o arquivo azuresqldb.tf agora e começar a desenvolver a criação do resource de banco de dados:

resource "azurerm_storage_account" "storage_account" {
  name                     = "sc${var.nome_sistema}${local.ambiente}"
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  account_tier             = local.storage_account_tier
  account_replication_type = var.storage_account_repl_type
}

resource "azurerm_sql_server" "mssql_server" {
  name                         = "sqlserver-${var.nome_sistema}-${local.ambiente}"
  resource_group_name          = azurerm_resource_group.rg.name
  location                     = azurerm_resource_group.rg.location
  version                      = "12.0"
  administrator_login          = var.admin_user_login
  administrator_login_password = var.admin_user_passwd

  tags                          = local.tags
}

resource "azurerm_sql_database" "mssql_db" {
    name                    = "sqldb-${var.nome_sistema}-${local.ambiente}"
    resource_group_name     = azurerm_resource_group.rg.name
    location                = azurerm_resource_group.rg.location
    server_name             = azurerm_sql_server.mssql_server.name
    collation               = var.db_collation


    tags                    = merge(local.tags)

}

Para saber qual é o ID do banco de dados gerado vamos criar um output no arquivo output.tf:

output "azure_db_sql_id" {
    value = azurerm_sql_database.mssql_db.id
}

Por fim, para não precisar digitar as variáveis obrigatórias no momento do plan e apply, vamos declarar as variáveis e valores das mesmas no arquivo terraform.tfvars:

rg              = "TerraformAzureSQLdb"
regiao          = "eastus"
env             = "p"
nome_sistema    = "contabilidade"
admin_user_passwd = "P@ssW0dComPl3x"

Após popular todos nossos arquivos vamos agora executar nosso Terraform para verificar se tudo esta sendo criado de forma esperada antes de criarmos a pipeline.

Para iniciar vamos executar:

terraform init

Este comando irá baixar o provider do Azure, o azurerm.

Depois disso vamos validar a sintaxe do código declarado:

terraform validate

Como podemos ver ele mostra um aviso dizendo que o recurso que cria o server que irá alocar o banco de dados esta deprecado e que na versão 4.0 será descontinuado, isso também esta explicito na documentação do recurso, logo após o warning podemos ver que a validação foi um sucesso, vamos seguir com o plan:

terraform plan

Veremos todos os recursos que serão criados e as configurações determinadas nos arquivos azuresqldb.tf. Vamos agora criar efetivamente com o apply:

terraform apply --auto-approve

Recursos criados, vamos conferir no console:

Agora vou liberar o acesso ao server a partir do meu ip, basta clicar no recurso do azure sql server, depois clicar em Network, terá um campo para adicionar o seu ip externo para ser liberado, clique no + e depois em salvar:

Com isso já podemos configurar nossa conexão e esta tudo funcionado conforme o exemplo abaixo:

Como tudo funcionou bem podemos agora destruir nossa infraestrutura pois o objetivo era montar o módulo de criação do nosso azure sql database. Para destruir a infraestrutura basta executar:

terraform destroy

Escreva “yes” para confirmar a destruição dos recursos:

Por fim vamos subir as alterações para o Github, executando:

git add .
git commit -m "Modulo Terraform Azure SQL Database"
git push

No próximo post vamos desenvolver mais um módulo terraform, fiquem ligados!

Até a próxima pessoal!

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure – Criando repositório no Git

Vamos por a mão na massa em nosso projeto de construção de algumas pipelines no Azure DevOps para entregar instancias de bancos de dados em diversas tecnologias no Azure.

Para começar, vamos criar os repositórios das tecnologias no Github, para isso, caso não tenha conta, crie uma, caso já tenha faça login:

Todos os projetos estarão disponíveis no meu profile: https://github.com/williamloliveira

Acesse seu profile:

Clique em repositórios e depois em Novo:

Na tela seguinte preencha os campos da seguinte forma:

  • Nome do Repositório: pipeline_iac_azure_sql_db
  • Descrição: Repositório onde estarão os códigos responsáveis pela pipeline e criação da infraestrutura como código de criação de um banco de dados no Azure SQL Databases.
  • Maque o repositório como público.
  • Maque para adicionar um arquivo README para que possamos documentar algumas coisas posteriormente neste repositório
  • Adicione um arquivo .gitignore do tipo Terraform para que possamos ignorar alguns arquivos que serão criados no repositório durante nossos testes
  • Por fim clique em Criar Repositório

Faça o mesmo para as demais tecnologias, sendo os nomes dos repositórios:

  • pipeline_iac_azure_cache_redis
  • pipeline_iac_azure_db_mysql
  • pipeline_iac_azure_db_postgresql
  • pipeline_iac_azure_cosmosdb

Por fim teremos todos os nossos repositórios devidamente criados:

Agora vamos configurar os repositórios em nossa maquina, para isso precisamos ter o Git instalado, se você utiliza Linux ou Mac provavelmente já o tem rodando, para o caso de rodar Windows, segue o link de instalação: https://git-scm.com/download/win

Após instalar o Git, execute o comando para saber se esta tudo ok:

git -v

Com o git ok, vamos clonar nossos repositórios para dentro de nossa maquina com os seguintes comandos:

git clone https://github.com/williamloliveira/pipeline_iac_azure_cosmosdb.git
git clone https://github.com/williamloliveira/pipeline_iac_azure_db_postgresql.git
git clone https://github.com/williamloliveira/pipeline_iac_azure_db_mysql.git
git clone https://github.com/williamloliveira/pipeline_iac_azure_cache_redis.git
git clone https://github.com/williamloliveira/pipeline_iac_azure_sql_db.git

Se entrarmos em uma das pastas e listarmos nossos arquivos veremos que teremos o arquivo README.md, .gitignore e a pasta .git:

Bom, já temos a estrutura para seguirmos com o desenvolvimento, no próximo capitulo começaremos a desenvolver nossos módulos Terraform para provisionar nossa infraestrutura.

Não perca! Até a próxima!

[Série][DBRE] Pipeline de IaC para provisionamento de bancos de dados no Azure

Fala galera!

Mais uma série iniciando aqui no blog! Dessa vez vamos iniciar o desenvolvimento de um projeto onde iremos construir um pipeline de provisionamento de bancos de dados no Azure utilizando Visual Studio Code, Github, Azure DevOps e Terraform!

A ideia aqui é demonstrar principalmente para DBA’s, Desenvolvedores e SysAdmins tradicionais como funciona um fluxo de entrega de infraestrutura de banco de dados em cloud publica, mais especificamente no Azure.

Uma outra motivação é demonstrar um pouco mais sobre processos aonde um DBRE pode atuar no dia a dia, uma vez que deixa de ser um DBA tradicional para atuar mais próximo dos desenvolvedores e sysadmins. Parte do trabalho do DBRE é retirar o Toil (trabalho manual\repetitivo) tanto da sua área como das demais, aumentando a eficiência da área de banco de dados e conseguindo mais tempo para trabalhar em entregas de valor para a organização.

Abaixo temos o fluxo de entrega de infraestrutura de banco de dados no Azure:

1 – Iniciaremos desenvolvendo o código da infraestrutura no VS Code;

2 – Vamo criar um repositório do Github e commitar o mesmo de forma a termos nosso código disponível e versionado;

3 – Depois criaremos uma pipeline que irá verificar quando houver um novo commit em nosso repositório e irá iniciar a pipeline para criação da infraestrutura;

4 – Aqui o terraform será executado iniciando o provisionamento da infra de banco de dados;

5 – Por fim teremos um banco de dados provisionado em algum desses serviços: Azure Database For MySQL, Azure Database for PostgreSQL, Azure CosmosDB, Azure Cache for Redis e Azure SQL Database.

Para este projeto iremos desenvolver um módulo Terraform e uma pipeline para cada tipo de banco de dados, logo, será bastante trabalho mas iremos fazer em dozes homeopáticas para ficar bem prático e diminuir a curva de aprendizado.

Então fique ligado para os próximos posts dessa série maravilinda!

Até mais!

[Hands-On] SQL Server FailOver Cluster Instance no Azure Virtual Machines – Passo a Passo (Revisado)

Fala galera!

Dando uma quebra na série de posts sobre bancos de dados executando no Azure Kubernetes Service, falaremos hoje de um tipo de cluster de banco de dados que eu gosto demais que é o SQL Server Failover Instance. Este é um post que fiz no blog da minha consultoria Data Tuning e resolvi trazer para meu blog pessoal revisando o conteúdo.

Para quem trabalha em ambientes tradicionais de SQL Server já deve ter ouvido falar de um dos modelos de Cluster mais consolidados do mercado: o Microsoft Failover Cluster.

O Failover Cluster nada mais é que uma feature de Alta Disponibilidade implementada no Windows Server que nos possibilita criar ambientes resilientes e com contigencia de maquina. O SQL Server, por sua vez, se beneficia desta feature para disponibilizar de forma redundante instancias de banco de dados.

No ano passado a Microsoft colocou em General Availability uma das features que eu pelo menos ainda não vi nas demais clouds que é a de Discos Compartilhados (Shared Disks) para servidores Windows. A partir dai podemos contar com esta capacidade para prover serviços onde há a necessidade de storage centralizada para habilitar redundancia de servidores.

Neste post veremos como criar uma infraestrutura completa de SQL Server Failover Cluster Instance. Os coponentes que vamos provisionar são:

  • Um servidor Windows Server 2019 de domínio ou Active Directory Domain Controller;
  • Dois servidores Windows Server 2019 com SQL Server 2019 Developer CU8 em Failover Cluster;
  • 3 discos Premium SSD que serão compartilhados entre os servidores de SQL Server;
  • Um balanceador de carga (Internal Load Balancer ou ILB) para conectividade com a instancia SQL Server em Cluster.

Criação dos Pré-Requisitos

Iremos iniciar pela criação do Resource Group no Azure para concentrar nossos recursos.

Um dos pré-requisitos para utilização de Shared Disks no Azure é que as maquinas que farão uso deste recurso precisam ficar próximas, ou seja, no mesmo Data Center, desta forma precisamos criar um Placement Group e um Avalability Sets. Mais infos na documentação da Microsoft: Share an Azure managed disk across VMs – Azure Virtual Machines | Microsoft Docs

Aqui basta selecionar o Resource Group, colocar um nome para o recurso e selecionar a quantidade de Fault Domains, neste caso coloquei o máximo. Depois clique em Advanced:

Selecione o Proximinity Placement Group já criado e crie o recurso:

Criação dos Servidores no Azure

Agora vamos criar a instancia que será utilizada como Domain Controller da nossa infraestrutura:

Preencha as informações na aba Basic se atentando para a utilização do Availability Set uma vez que é pré requisito para este tipo de implementação:

Continuando na aba Basic, escolha o sistema operacional (Neste caso Windows Server 2019) e o tamanho da maquina (Neste caso estou utilizando uma instancia pequena para média que é uma Standard_B2ms), coloque um usuario e senha para acessar a maquina e deixe a porta RDP habilitada para acesso externo (Isso apenas se for Lab, para produção não habilite a porta para acesso pela internet!):

Na aba Disks, escolha um tipo de disco, neste caso utilizei um Standad SSD que é mais que o suficiente para uma maquina de Active Directory.

Na Aba Networking, crie uma Rede e selecione o CIDR de rede que será utilizado para nossa infraestrutura, também crie um IP público para acesso ao servidor. Os demais parâmetros deixe como esta e siga para próxima aba:

Na aba Management, desça até enxergar a opção de Auto-shutdown, habilite essa opção para ambientes que podem ser desligado em um determinado momento do dia assim como este ambiente de laboratório:

Na aba Advanced, garanta que o Proximity Placement Group criado no começo da post esteja selecionado. Clique em Review + Create para criar a VM de AD:

Agora vamos criar as instancias de SQL Server que vão seguir a mesma linha da instancia de Domain Controller, repita os passos a seguir tanto para instancia SQLVM01 como para SQLVM02:

Após criado todos os sevidores a visão das Virtual Machines ficará desta forma:

Instalação do Domínio Active Directory – Domain Controller

Agora vamos instalar nosso Domain Controller, clique na máquina domaincontroller, depois clique em conect e RDP, você irá ser redirecionado para uma tela para baixar o arquivo de Remote Desktop com o IP da instancia para acessa-la:

Uma vez com acesso ao servidor vamos instalar o Active Directory e promover esta maquina para Domain Controller.

Após acessar o servidor remotamente, a tela acima irá abrir automáticamente após a inicialização. Clique em Add roles and features:

Navegue até a opção Server Roles do lado direito, após chegar nesta tela, ative apenas a opção indicada na imagem:

Após a tela anterior selecione a opção Telnet Client para instalar o Telnet no servidor, usaremos ele lá no final do post. Após isso, clique em Next, Next e siga com a instalação:

Após a instalação, no canto superior direito da tela de Server Manager haverá essa bandeirinha com um símbolo de interrogação, clique nela e depois clique na opção indicada: Promote this sever to a domain controller:

Nesta tela, clique em Add a new forest e coloque um nome para o seu domínio, neste caso o nome será datatuning.com (A Microsoft geralmente utiliza para seus exemplos o CONTOSO). Depois disso clique em Next:

Deixe tudo como esta e apenas coloque uma senha forte para o usuário Administrator do domínio:

Coloque o nome NetBIOS que será o nome mais simplificado, neste caso coloquei apenas DATATUNING:

Ignore os pontos de atenção e instale:

Após instalado o servidor automáticamente será incluido ao domínio criado:

Após instalado o Active Directory e ter promovido o servidor à Domain Controller precisamos agora colocar um DNS Server estático para este servidor e para os demais (que vão pegar automáticamente esta configuração). No Azure toda a configuração de rede tem ser feita pela console ou pela cli, isso por que quem controla a rede é a plataforma e não os servidores, mesmo que sejam colocados no dominio.

É importante fazer essa configuração pois se não as máquinas de SQL Server não reconhecerão o servidor de domínio.

Para isso, vá até o Portal da Azure, acesse os servidores de SQL, vá até a opção Networking e clique na placa de rede do servidor, após acessar a placa clique na opção DNS Servers, marque a opção custom e coloque o IP do servidor de domínio que neste caso é o 10.0.0.4.

Após efetuar essas alterações, reinicie todos os servidores SQLVM01 e SQLVM02:

Após efetuada essas configurações agora iremos colocar nossos servidores SQL Server no domínio. Para isso, acesse os servidores remotamente e siga os passos a seguir:

Após acessar os servidores de banco de dados, abra o Explorer e clique com o botão direito em This PC e depois em Properties para abrir a tela de System. Após chegar nesta tela, clique em Advanced system settings.

Depois, na Computer Name, clique no botão Change

Marque a opção Domain e coloque o nome do domínio. No usuário e senha, utilize a conta de domínio do admindt (Criada na instalação do AD) e senha e clique em OK.

Se estiver tudo certo com a rede e com o usuário e senha o seguinte Pop-Up deverá aparecer. Caso o servidor não esteja conseguindo chegar no servidor de domínio, revise a configuração de DNS Servers efetuada anteriormente:

Após a inclusão do servidor de domínio poderemos confirmar que o mesmo foi incluido entrando em System. Repita os passos para o servidor SQLVM02.

Instalação e Configuração do Failover Cluster

Uma vez que os servidores estão no domínio agora poderemos seguir com a instalação do Cluster Failover nos servidores. Lembram que no começo do Post eu disse que o Cluster Failover é uma feature do Windows Server e não do SQL Server que apenas se beneficia dela? Então, agora vamos ativar esta feature nos servidores SQLVM01 e SQLVM02. Só seguir o passo a passo simples abaixo:

Após instalado a feature aperte Super(simbolo do Windows) + Q ou vá até a aba de buscas do Windows e procure por Failover Cluster Manager. Após abrir o mesmo, clique em Create Cluster…:

Aqui iremos selecionar todos os nós que participarão do Cluster SQL Server, sendo assim, iremos selecionar as máquinas SQLVM01 e SQLVM02:

Após selecioná-las, clique em Next:

Coloque um nome para o Cluster, este será o Network Name do Cluster Failover e será registrado no DNS do AD que instalamos. Clique em Next, Next e crie o Cluster.

Após a instalação a tela abaixo aparecerá:

Após a instalação ser concluída, verifique na opção Nodes do FCI Manager se ambos os nós do Cluster já estão fazendo parte do mesmo:

Criação e Configuração dos Discos Compartilhados – Shared Disks

Agora vamos criar os discos compartilhados (Shared Disks) que serão gerenciados pelo Cluster Failover. Aqui você pode utilizar tanto o Console existente na própria Azure como o seu PowerShell. Via PS você precisará instalar o Azure CLI. A seguir esta o comando para criação dos 3 Shared Disks que iremos utilizar em nossas VMs, todos serão Premium SSD com 1TB de espaço:

az disk create -g dt_disk_shared -n azuresqlfcidisk1 --size-gb 1024 -l eastus --sku Premium_LRS --max-shares 2
az disk create -g dt_disk_shared -n azuresqlfcidisk2 --size-gb 1024 -l eastus --sku Premium_LRS --max-shares 2
az disk create -g dt_disk_shared -n azuresqlfcidisk3 --size-gb 1024 -l eastus --sku Premium_LRS --max-shares 2

Este é o resultado depois de criado:

Agora vamos relacionar o disco criado às nossas VMs de SQL Server. Só executar o comando a seguir no PowerShell (Neste caso utilizei o console da Azure):

$resourceGroup = "dt_disk_shared"
$location = "eastus"
$vm = "sqlvm01"
$vm = Get-AzVm -ResourceGroupName $resourceGroup -Name "sqlvm01"
$dataDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName "azuresqlfcidisk1"
$vm = Add-AzVMDataDisk -VM $vm -Name "azuresqlfcidisk1" -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 0
update-AzVm -VM $vm -ResourceGroupName $resourceGroup

$resourceGroup = "dt_disk_shared"
$location = "eastus"
$vm = "sqlvm02"
$vm = Get-AzVm -ResourceGroupName $resourceGroup -Name "sqlvm01"
$dataDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName "azuresqlfcidisk1"
$vm = Add-AzVMDataDisk -VM $vm -Name "azuresqlfcidisk1" -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 0
update-AzVm -VM $vm -ResourceGroupName $resourceGroup

Resultado esperado após ter o disco relacionado à maquina:

Execute o mesmo comando alterando a variável $vm para “sqlvm02” para relacionar o disco na outra máquina também.

Agora vamos reconhecer os discos no servidor onde o Cluster estiver disponivel, para conferir qual é o nó do cluster que é o owner neste momento abra o Failover Cluster Manager, clique no nome do Cluster e confira o Current Host Server, neste meu caso o owner neste momento é o SQLVM02, então, seguirei os próximos passos neste nó:

Aperte Super + Q ou clique na lupa ao lado do icone Iniciar, escreva Computer Management e abra a tela.

Com a tela aberta, clique em Disk Management, caso os passos os comandos de alocação de discos executados anteriormente na Console do Azure tenham dado certo então automaticamente deverá aparecer um Pop-Up de Initialize Disk. Os discos deverão aparecer como Disk 2, Disk 3 assim por diante, selecione os mesmos, depois selecione em MBR (Master Boot Record) e então clique em OK:

Os discos irão aparecer como unidades sem Volume montado, assim, clique com o botão direito do mouse nos discos que queira montar uma unidade. Um Pop-Up de formatação irá aparecer. Formate o Volume como NTFS, 64K de alocação (Padrão para discos de SQL Server) e de um nome para o volume, neste caso eu coloquei SQLBIN, selecione Perform a quick format e despois clique em OK:

Esse deverá ser o resultado após a formatação do disco:

Uma vez que o disco esteja formatado e disponivel no servidor podemos então adiciona-lo no Cluster. Abra o Failover Cluster Manager, clique em Disks e depois em Add Disk:

Este Pop-Up irá aparecer, marque os discos disponíveis e adicione ao cluster:

Agora possuímos um disco disponível para o cluster que poderá compartilhado para os serviços instalados no mesmo, neste caso será o SQL Server que irá compartilhar os discos entre os dois nós do Cluster. Por isso chamamos estes de Discos Compartilhados:

Agora relacione os demais discos aos servidores, perceba que os comandos são bem parecidos, isso por que estamos adicionando mais dois discos e apresentando estes para os dois nós, assim como fizemos com o primeiro disco. Execute os comandos a seguir no Cloud Shell do Azure como PowerShell:

$resourceGroup = "dt_disk_shared"
$location = "eastus"
$vm = "sqlvm01"
$dataDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName "azuresqlfcidisk2"
$vm = Add-AzVMDataDisk -VM $vm -Name "azuresqlfcidisk2"  -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 2
update-AzVm -VM $vm -ResourceGroupName $resourceGroup

$resourceGroup = "dt_disk_shared"
$location = "eastus"
$vm = "sqlvm01"
$dataDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName "azuresqlfcidisk3"
$vm = Add-AzVMDataDisk -VM $vm -Name "azuresqlfcidisk3"  -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 3
update-AzVm -VM $vm -ResourceGroupName $resourceGroup

$resourceGroup = "dt_disk_shared"
$location = "eastus"
$vm = "sqlvm02"
$dataDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName "azuresqlfcidisk2"
$vm = Add-AzVMDataDisk -VM $vm -Name "azuresqlfcidisk2"  -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 2
update-AzVm -VM $vm -ResourceGroupName $resourceGroup

$resourceGroup = "dt_disk_shared"
$location = "eastus"
$vm = "sqlvm02"
$dataDisk = Get-AzDisk -ResourceGroupName $resourceGroup -DiskName "azuresqlfcidisk3"
$vm = Add-AzVMDataDisk -VM $vm -Name "azuresqlfcidisk3"  -CreateOption Attach -ManagedDiskId $dataDisk.Id -Lun 3
update-AzVm -VM $vm -ResourceGroupName $resourceGroup

Verifique que na imagem as Luns (Logical Unit Numbers) são diferentes, isso por que para alocar os discos nos servidores precisamos dar ID’s diferentes para cada disco dentro dos servidores:

Assim como o outro disco, acesse o Computer Management, vá até Disk Management, selecione os discos adicionados e selecione MBR, depois clique em OK.

Crie os volumes nos discos. Neste caso dei os labels SQLDATA e SQLLOG para separar os arquivos de Dados e Log em discos diferentes, isso é uma boa prática para que não haja concorrencia de Leitura e Escrita entre arquivos de Dados e Log dos bancos de dados. Se possível, faça o mesmo para TEMPDB.

Os discos ficaram apresentados para os servidores desta forma:

Entre no Failover Cluster Manager, clique em Disks e depois em Add Disk. Os dois discos deverão aparecer já marcados. Clique em OK para adicionar os discos:

Uma vez que os discos forem adicionados você terá esta visão no console do Cluster e também se abrir o Meu Computador ou “This PC” você os enxergará e poderá acessa-los.

Instalação do SQL Server em Cluster

Já fizemos um monte de coisas né? Poisé, mas agora vamos iniciar a instalação do nosso SQL Server. Para quem já instalou uma instância de SQL Server Stand-Alone não verá grandes diferenças aqui. A instalação em cluster se diferencia principalmente na parte dos discos e rede, todos os passos serão parecidos com a instalação normal do SQL.

Para este Lab iremos utilizar o SQL Server 2019 Developer Edition, porém, você poderá utilizar a versão de sua escolha, alguns steps serão diferentes pois na versão 2019 algumas configurações de arquivos foram adicionados mas as configs de Cluster permanecem as mesmas desde o 2012.

Vou colocar apenas as telas as quais teremos alguma ação a ser tomada, para as demais é só clicar em Next. Abra o Setup do SQL Server, clique na aba Installation e depois clique em New SQL Server failover cluster installation:

Na tela Feature Selection selecione apenas as opções: Database Engine Service (Ao selecionar esta outras serão automaticamente selecionadas, pode deixar assim mesmo) e todos os Clients que tiver:

Na tela Instance Configuration de um nome para sua instância e deixe o Default instance selecionado.

Na tela Cluster Disk Selection selecione todos os discos disponíveis:

Na tela Cluster Network Configuration tome cuidado pois é aqui que determinaremos qual é o ip que será utilizado para acesso á instância por meio do Cluster:

Marque o Check Box do lado esquerdo, desmarque a opção de DHCP e coloque um IP que não esteja sendo utilizado na sua rede (Isso é muito importante pois se o IP não conseguir ser alocado na instalação do SQL Server a mesma poderá vir a falhar!). Neste caso utilizei o IP 10.0.0.10.

Aqui na tela de Server Configuration coloque um usuário e senha existente no Domínio AD. É importante colocar um usuário de domínio pois seu gerenciamento será centralizado. Para não ter dores de cabeça insira este usuario no grupo Administrators local dos servidores. Marque a opção Grant Perform Volume Maintenance Task privilege to SQL Server Database Engine Service para melhorar a performance na criação de arquivos do SQL Server.

Marque a opção Mixed Mode (caso você queira utilizar autenticação via SQL), sete a senha do usuário SA e inclua seu usuário na server role SysAdmin:

Nesta mesma tela, navegue até a aba TempDB e altere o Path de armazenamento dos arquivos TempDB, neste caso estou armazenando os arquivos em uma pasta criada no volume SQLBIN (F:\). Deixe as demais configs da forma como estão.

Na aba Data Directories altere os caminhos padrão para os arquivos de Data files e Log files para os volumes que adicionamos que são SQLDATA (H:\) e SQLLOG (G:\).

Na aba Memory marque a opção Recommended para deixar a memória acomodada conforme o SQL Server sugerir. Você poderá alterar essa configuração posteriormente, mas tenha em mente que você precisará deixar um pouco de memória para o Sistema Operacional, nesta tela o instalador do SQL Server já faz um cálculo e entre uma quantidade ideal para o funcionamento da instância de banco de dados:

Siga até a tela de instalação.

Após a instalação deveremos ver uma tela como está, caso ocorra algum erro na instalação de algum componente veremos um icone vermelho ao lado do componente e o Status Failure:

Uma vez instalado já conseguiremos visualizar o Resource Group do SQL Server no Failover Cluster Manager, destaque para os discos que foram “attachados” ao recurso do SQL Server:

Agora temos um SQL Server instalado em Cluster más ainda não acabamos! O SQL foi instalado apenas em um dos nós, agora precisamos instalar os binários no outro nó para que possamos chavear os recursos sempre que necessário.

Os passos a partir daqui serão executados no servidor SQLVM01 que é o par que não possui os recursos disponíveis:

No servidor par, abra o instalador do SQL Server, vá até a aba Installation e depois clique em Add node to a SQL Server failover cluster.

Esta é uma tela informativa, mostra em qual servidor iremos instalar os binários do SQL.

Aqui iremos utilizar o mesmo usuário e senha que utilizamos na instalação do outro nó. Este será o usuario que irá gerenciar os serviços do SQL.

Next, next… finish.

Após concluída a instalação veremos a seguinte tela:

Agora sim! Após fazer o Add Node do nosso outro nó do cluster agora poderemos já testar o chaveamento dos recursos do SQL Server no cluster.

Para isso vá até o Console do Failover Cluster Manager em qualquer um dos nós, clique com o botão direito em cima do resource group do SQL, depois em Move e então clique em Best Possible Node. Como temos apenas dois nós o Cluster irá chavear os recursos do servidor SQLVM02 para o SQLVM01:

Chaveamento em andamento, o Cluster irá “baixar” todos os recursos e depois irá iniciar todos no outro nó.

Pronto! Agora o nó que manda é o SQLVM01:

Se você abrir o Meu Computador (This PC) no servidor SQLVM01 verá os discos disponiveis para acesso.

Acessando o SQL Server FCI no Azure

Achou que acabou? Se você em um ambiente OnPremises talvez poderiamos já dar a instalação como concluída, porém, no Azure ainda temos mais recursos a serem criados, pois, como eu disse la em cima do Post a parte de rede do Azure é gerenciada pela plataforma e não pelos servidor, sendo assim, precisamos configurar um componente para que os servidores existentes em nossa cloud possam acessar nossa instancia SQL.

Neste caso iremos provisionar um Internal Load Balancer (ILB), este será o responsável por entregar um IP de acesso para nossa infraestrutura de banco de dados onde os demais servidores e recursos provisionados na Azure possam acessá-lo.

Na tela de criação do load balancer selecione o Resource Group, dê um nome para o recurso, em Type marque Internal e em SKU pode deixar como Basic mesmo. Depois escolha o Virtual network igual o dos servidores e coloque um IP Estático, neste caso coloquei 10.0.0.100.

Após o ILB ter sido criado, acesse o recurso e configure os seguintes componentes: Backend pool, Health probe e Load Balance Rule.

No Backend Pool, de um nome, marque IPv4, escolha a opção Virtual Machines e adicione os servidores que fazem parte do cluster.

No Health probe adicione o protocolo TCP, a porta 59999 que será utilizada para verificar se o recurso (Neste caso o servidor) esta no ar e saudável e aumente o intervalo de verificação para 10 segundos.

No Load Balancer Rule basicamente iremos utilizar a porta 1433 (Padrão do SQL Server) para os campos de porta, selecionar o Backend Pool e Health probe já criados e depois alterar o Floating IP para Enabled.

Pronto, uma vez efetuada essas configurações damos por concluida a instalação do nosso Load Balancer.

Por garantia libere as portas 1433 e 59999 no firewall dos servidores SQL Server e no Security Group dos servidores na console do Azure como está abaixo:

Neste caso, como é um ambiente Lab eu libere para qualquer um acessar. Para ambientes que não sejam de laboratório NUNCA faça isso, libere as portas apenas para os recursos que realmente possam acessar os servidores.

Caso você não queira utilizar o ILB para liberar o acesso aos servidores de SQL Server você poderá utilizar um Distributed Network Name (DNN). Essa configuração é uma boa caso você tenha uma infra que esteja no mesmo dominio que os servidores SQL Server ou que tenham acesso ao mesmo DNS.

Para checar os Pré-requisitos para utilizar esta feature acesse o link na documentação, o mais importante é que esta disponível apenas para SQL Server 2019 Cumulative Update 2, para o nosso Lab ta valendo.

Abra o PowerShell em um dos nós do Failover Cluster e execute os comandos a seguir:

Add-ClusterResource -Name dnn-demo -ResourceType "Distributed Network Name" -Group "SQL Server (MSSQLSERVER)"
Get-ClusterResource -Name dnn-demo | Set-ClusterParameter -Name DnsName -Value FCIDNN
Start-ClusterResource -Name dnn-demo

Um recurso como este da imagem abaixo irá aparecer no Resource group do SQL Server no console do Cluster:

Após efetuada essas configurações, vá até o servidor de Domain Controller, instale o SQL Server Management Studio e acesso o SQL Server, neste caso, você pode utilizar tando o DNN que acabamos de criar como o IP do ILB que criamos anteriormente:

Conclusões

Bom meu povo é isso! Bastante coisa né? Poisé.

Grande parte deste passo a passo pode ser utilizado em um ambiente OnPremises pois a unica diferença será a configuração dos discos, desta forma, se você puder contar com uma storage centralizada em sua infraestrutura você poderá utilizar a melhor infraestrutura de cluster de alta disponibilidade já construida.

Grande parte dos ambientes que eu e meus amigos e sócios aqui na Data Tuning lidamos hoje utilizam essa arquitetura de alta disponibilidade e posso dizer com propriedade e experiência que é uma das mais estáveis e tranquilas de se administrar (Se você não tiver backe-level, claro! rs).

Agradeço imensamente ao mestre Diego Miranda que me ajudou na reprodução deste Lab!

Espero que tenha adicionado alguma coisa para vocês! Fico a disposição para possíveis dúvidas e sugestões de novos temas para posts.

Valeu pessoal!