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

Fala galera! Espero que estejam todos bem!

Dando continuidade a nossa séria de provisionamento de bancos de dados no Azure Kubernetes Service vamos ter uma sequencia de bancos de dados considerados como distribuídos, ou seja, que assim como Redis que vimos no ultimo post, conseguem escalar horizontalmente tanto em escrita como leitura, mas com a diferença de que eles entregam uma capacidade relacional diferente dos bancos de dados NoSQL como o próprio Redis, Cassandra, MongoDB e outros.

Neste capitulo veremos o famigerado BarataDB ou comumente conhecido por: CockroachDB (rsrs).

O CockroachDB é um banco de dados distribuído, de escalabilidade horizontal, que possiu uma engine NoSQL key-valued store (assim como o Redis) mas que também implementa uma camada de conexão PostgreSQL onde, apesar de possuir um core NoSQL, se comporta como um banco de dados relacional, contendo tabelas, relacionamentos e ACID.

Vamos iniciar o provisionamento do nosso cluster de banco de dados iniciando pela criação de um CRD ou Custom Resource Definition que é uma forma de estendermos as capacidades da API padrão do Kubernetes fazendo com que ela se comporte de forma especifica para a criação de um determinado recurso não nativo do K8s. Para criar o CRD basta executar:

kubectl apply -f https://raw.githubusercontent.com/cockroachdb/cockroach-operator/v2.10.0/install/crds.yaml

Agora, pela primeira vez, vamos criar um Operator que é o componente mais importante quando falamos em gerenciar clusters de bancos de dados no Kubernetes. O Operator é o recurso que gerencia o estado das instancias de bancos de dados, chaveando automaticamente os recursos em caso de falhas, rebalanciando carga, entre outras capacidades de gerenciamento.

Para baixar o yaml nativo do fornecedor basta executar:

curl -O https://raw.githubusercontent.com/cockroachdb/cockroach-operator/v2.10.0/install/operator.yaml

Vamos explorar este arquivo, o primeiro recurso a ser criado será um namespace de nome cockroach-operator-system:

apiVersion: v1
kind: Namespace
metadata:
  labels:
    control-plane: cockroach-operator
  name: cockroach-operator-system

Depois temos a criação de um ServiceAccount que será utilizado para autenticação exclusiva dos Pods referentes ao cluster CockroachDB na API do K8s:

apiVersion: v1
kind: ServiceAccount
metadata:
  labels:
    app: cockroach-operator
  name: cockroach-operator-sa
  namespace: cockroach-operator-system

Após isso temos a criação de RBAC’s ou Role-based Access Control onde serão declarados regras de acesso dos recursos referentes ao cluster Cockroach para dentro do K8s:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  creationTimestamp: null
  name: cockroach-operator-role
rules:
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - mutatingwebhookconfigurations
  verbs:
  - get
  - patch
  - update
- apiGroups:
  - admissionregistration.k8s.io
  resources:
  - validatingwebhookconfigurations
  verbs:
  - get
  - patch
  - update
- apiGroups:
  - apps
  resources:
  - statefulsets
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - apps
  resources:
  - statefulsets/finalizers
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - apps
  resources:
  - statefulsets/scale
  verbs:
  - get
  - update
  - watch
- apiGroups:
  - apps
  resources:
  - statefulsets/status
  verbs:
  - get
  - patch
  - update
- apiGroups:
  - batch
  resources:
  - jobs
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - batch
  resources:
  - jobs/finalizers
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - batch
  resources:
  - jobs/status
  verbs:
  - get
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - watch
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/approval
  verbs:
  - update
- apiGroups:
  - certificates.k8s.io
  resources:
  - certificatesigningrequests/status
  verbs:
  - get
  - patch
  - update
- apiGroups:
  - ""
  resources:
  - configmaps
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - configmaps/status
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - nodes
  verbs:
  - get
  - list
- apiGroups:
  - ""
  resources:
  - persistentvolumeclaims
  verbs:
  - list
  - update
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - delete
  - deletecollection
  - get
  - list
- apiGroups:
  - ""
  resources:
  - pods/exec
  verbs:
  - create
- apiGroups:
  - ""
  resources:
  - pods/log
  verbs:
  - get
- apiGroups:
  - ""
  resources:
  - secrets
  verbs:
  - create
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - serviceaccounts
  verbs:
  - create
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - ""
  resources:
  - services/finalizers
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - ""
  resources:
  - services/status
  verbs:
  - get
  - patch
  - update
- apiGroups:
  - crdb.cockroachlabs.com
  resources:
  - crdbclusters
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - crdb.cockroachlabs.com
  resources:
  - crdbclusters/finalizers
  verbs:
  - update
- apiGroups:
  - crdb.cockroachlabs.com
  resources:
  - crdbclusters/status
  verbs:
  - get
  - patch
  - update
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/finalizers
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - networking.k8s.io
  resources:
  - ingresses/status
  verbs:
  - get
- apiGroups:
  - policy
  resources:
  - poddisruptionbudgets
  verbs:
  - create
  - delete
  - get
  - list
  - patch
  - update
  - watch
- apiGroups:
  - policy
  resources:
  - poddisruptionbudgets/finalizers
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - policy
  resources:
  - poddisruptionbudgets/status
  verbs:
  - get
- apiGroups:
  - rbac.authorization.k8s.io
  resources:
  - rolebindings
  verbs:
  - create
  - get
  - list
  - watch
- apiGroups:
  - rbac.authorization.k8s.io
  resources:
  - roles
  verbs:
  - create
  - get
  - list
  - watch
- apiGroups:
  - security.openshift.io
  resources:
  - securitycontextconstraints
  verbs:
  - use

Criado o Cluster Role (RBAC) temos o vinculo deste com o Service Account criado anteriormente:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: cockroach-operator-rolebinding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cockroach-operator-role
subjects:
- kind: ServiceAccount
  name: cockroach-operator-sa
  namespace: cockroach-operator-system

Agora vamos criar o Service para expor a porta do Operator:

apiVersion: v1
kind: Service
metadata:
  labels:
    control-plane: cockroach-operator
  name: cockroach-operator-webhook-service
  namespace: cockroach-operator-system
spec:
  ports:
  - port: 443
    targetPort: 9443
  selector:
    app: cockroach-operator

Agora temos a criação do Deployment que irá provisionar o Pod contendo o sistema de gerenciamento do cluster CockroachDB considerado o Operator em si:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: cockroach-operator
  name: cockroach-operator-manager
  namespace: cockroach-operator-system
spec:
  replicas: 1
  selector:
    matchLabels:
      app: cockroach-operator
  template:
    metadata:
      labels:
        app: cockroach-operator
    spec:
      containers:
      - args:
        - -zap-log-level
        - info
        env:
        - name: RELATED_IMAGE_COCKROACH_v20_1_4
          value: cockroachdb/cockroach:v20.1.4
        - name: RELATED_IMAGE_COCKROACH_v20_1_5
          value: cockroachdb/cockroach:v20.1.5
        - name: RELATED_IMAGE_COCKROACH_v20_1_8
          value: cockroachdb/cockroach:v20.1.8
        - name: RELATED_IMAGE_COCKROACH_v20_1_11
          value: cockroachdb/cockroach:v20.1.11
        - name: RELATED_IMAGE_COCKROACH_v20_1_12
          value: cockroachdb/cockroach:v20.1.12
        - name: RELATED_IMAGE_COCKROACH_v20_1_13
          value: cockroachdb/cockroach:v20.1.13
        - name: RELATED_IMAGE_COCKROACH_v20_1_15
          value: cockroachdb/cockroach:v20.1.15
        - name: RELATED_IMAGE_COCKROACH_v20_1_16
          value: cockroachdb/cockroach:v20.1.16
        - name: RELATED_IMAGE_COCKROACH_v20_1_17
          value: cockroachdb/cockroach:v20.1.17
        - name: RELATED_IMAGE_COCKROACH_v20_2_0
          value: cockroachdb/cockroach:v20.2.0
        - name: RELATED_IMAGE_COCKROACH_v20_2_1
          value: cockroachdb/cockroach:v20.2.1
        - name: RELATED_IMAGE_COCKROACH_v20_2_2
          value: cockroachdb/cockroach:v20.2.2
        - name: RELATED_IMAGE_COCKROACH_v20_2_3
          value: cockroachdb/cockroach:v20.2.3
        - name: RELATED_IMAGE_COCKROACH_v20_2_4
          value: cockroachdb/cockroach:v20.2.4
        - name: RELATED_IMAGE_COCKROACH_v20_2_5
          value: cockroachdb/cockroach:v20.2.5
        - name: RELATED_IMAGE_COCKROACH_v20_2_6
          value: cockroachdb/cockroach:v20.2.6
        - name: RELATED_IMAGE_COCKROACH_v20_2_8
          value: cockroachdb/cockroach:v20.2.8
        - name: RELATED_IMAGE_COCKROACH_v20_2_9
          value: cockroachdb/cockroach:v20.2.9
        - name: RELATED_IMAGE_COCKROACH_v20_2_10
          value: cockroachdb/cockroach:v20.2.10
        - name: RELATED_IMAGE_COCKROACH_v20_2_11
          value: cockroachdb/cockroach:v20.2.11
        - name: RELATED_IMAGE_COCKROACH_v20_2_12
          value: cockroachdb/cockroach:v20.2.12
        - name: RELATED_IMAGE_COCKROACH_v20_2_13
          value: cockroachdb/cockroach:v20.2.13
        - name: RELATED_IMAGE_COCKROACH_v20_2_14
          value: cockroachdb/cockroach:v20.2.14
        - name: RELATED_IMAGE_COCKROACH_v20_2_15
          value: cockroachdb/cockroach:v20.2.15
        - name: RELATED_IMAGE_COCKROACH_v20_2_16
          value: cockroachdb/cockroach:v20.2.16
        - name: RELATED_IMAGE_COCKROACH_v20_2_17
          value: cockroachdb/cockroach:v20.2.17
        - name: RELATED_IMAGE_COCKROACH_v20_2_18
          value: cockroachdb/cockroach:v20.2.18
        - name: RELATED_IMAGE_COCKROACH_v20_2_19
          value: cockroachdb/cockroach:v20.2.19
        - name: RELATED_IMAGE_COCKROACH_v21_1_0
          value: cockroachdb/cockroach:v21.1.0
        - name: RELATED_IMAGE_COCKROACH_v21_1_1
          value: cockroachdb/cockroach:v21.1.1
        - name: RELATED_IMAGE_COCKROACH_v21_1_2
          value: cockroachdb/cockroach:v21.1.2
        - name: RELATED_IMAGE_COCKROACH_v21_1_3
          value: cockroachdb/cockroach:v21.1.3
        - name: RELATED_IMAGE_COCKROACH_v21_1_4
          value: cockroachdb/cockroach:v21.1.4
        - name: RELATED_IMAGE_COCKROACH_v21_1_5
          value: cockroachdb/cockroach:v21.1.5
        - name: RELATED_IMAGE_COCKROACH_v21_1_6
          value: cockroachdb/cockroach:v21.1.6
        - name: RELATED_IMAGE_COCKROACH_v21_1_7
          value: cockroachdb/cockroach:v21.1.7
        - name: RELATED_IMAGE_COCKROACH_v21_1_9
          value: cockroachdb/cockroach:v21.1.9
        - name: RELATED_IMAGE_COCKROACH_v21_1_10
          value: cockroachdb/cockroach:v21.1.10
        - name: RELATED_IMAGE_COCKROACH_v21_1_11
          value: cockroachdb/cockroach:v21.1.11
        - name: RELATED_IMAGE_COCKROACH_v21_1_12
          value: cockroachdb/cockroach:v21.1.12
        - name: RELATED_IMAGE_COCKROACH_v21_1_13
          value: cockroachdb/cockroach:v21.1.13
        - name: RELATED_IMAGE_COCKROACH_v21_1_14
          value: cockroachdb/cockroach:v21.1.14
        - name: RELATED_IMAGE_COCKROACH_v21_1_15
          value: cockroachdb/cockroach:v21.1.15
        - name: RELATED_IMAGE_COCKROACH_v21_1_16
          value: cockroachdb/cockroach:v21.1.16
        - name: RELATED_IMAGE_COCKROACH_v21_1_17
          value: cockroachdb/cockroach:v21.1.17
        - name: RELATED_IMAGE_COCKROACH_v21_1_18
          value: cockroachdb/cockroach:v21.1.18
        - name: RELATED_IMAGE_COCKROACH_v21_1_19
          value: cockroachdb/cockroach:v21.1.19
        - name: RELATED_IMAGE_COCKROACH_v21_1_20
          value: cockroachdb/cockroach:v21.1.20
        - name: RELATED_IMAGE_COCKROACH_v21_1_21
          value: cockroachdb/cockroach:v21.1.21
        - name: RELATED_IMAGE_COCKROACH_v21_2_0
          value: cockroachdb/cockroach:v21.2.0
        - name: RELATED_IMAGE_COCKROACH_v21_2_1
          value: cockroachdb/cockroach:v21.2.1
        - name: RELATED_IMAGE_COCKROACH_v21_2_2
          value: cockroachdb/cockroach:v21.2.2
        - name: RELATED_IMAGE_COCKROACH_v21_2_3
          value: cockroachdb/cockroach:v21.2.3
        - name: RELATED_IMAGE_COCKROACH_v21_2_4
          value: cockroachdb/cockroach:v21.2.4
        - name: RELATED_IMAGE_COCKROACH_v21_2_5
          value: cockroachdb/cockroach:v21.2.5
        - name: RELATED_IMAGE_COCKROACH_v21_2_7
          value: cockroachdb/cockroach:v21.2.7
        - name: RELATED_IMAGE_COCKROACH_v21_2_8
          value: cockroachdb/cockroach:v21.2.8
        - name: RELATED_IMAGE_COCKROACH_v21_2_9
          value: cockroachdb/cockroach:v21.2.9
        - name: RELATED_IMAGE_COCKROACH_v21_2_10
          value: cockroachdb/cockroach:v21.2.10
        - name: RELATED_IMAGE_COCKROACH_v21_2_11
          value: cockroachdb/cockroach:v21.2.11
        - name: RELATED_IMAGE_COCKROACH_v21_2_12
          value: cockroachdb/cockroach:v21.2.12
        - name: RELATED_IMAGE_COCKROACH_v21_2_13
          value: cockroachdb/cockroach:v21.2.13
        - name: RELATED_IMAGE_COCKROACH_v21_2_14
          value: cockroachdb/cockroach:v21.2.14
        - name: RELATED_IMAGE_COCKROACH_v21_2_15
          value: cockroachdb/cockroach:v21.2.15
        - name: RELATED_IMAGE_COCKROACH_v21_2_16
          value: cockroachdb/cockroach:v21.2.16
        - name: RELATED_IMAGE_COCKROACH_v21_2_17
          value: cockroachdb/cockroach:v21.2.17
        - name: RELATED_IMAGE_COCKROACH_v22_1_0
          value: cockroachdb/cockroach:v22.1.0
        - name: RELATED_IMAGE_COCKROACH_v22_1_1
          value: cockroachdb/cockroach:v22.1.1
        - name: RELATED_IMAGE_COCKROACH_v22_1_2
          value: cockroachdb/cockroach:v22.1.2
        - name: RELATED_IMAGE_COCKROACH_v22_1_3
          value: cockroachdb/cockroach:v22.1.3
        - name: RELATED_IMAGE_COCKROACH_v22_1_4
          value: cockroachdb/cockroach:v22.1.4
        - name: RELATED_IMAGE_COCKROACH_v22_1_5
          value: cockroachdb/cockroach:v22.1.5
        - name: RELATED_IMAGE_COCKROACH_v22_1_7
          value: cockroachdb/cockroach:v22.1.7
        - name: RELATED_IMAGE_COCKROACH_v22_1_8
          value: cockroachdb/cockroach:v22.1.8
        - name: RELATED_IMAGE_COCKROACH_v22_1_10
          value: cockroachdb/cockroach:v22.1.10
        - name: RELATED_IMAGE_COCKROACH_v22_1_11
          value: cockroachdb/cockroach:v22.1.11
        - name: RELATED_IMAGE_COCKROACH_v22_1_12
          value: cockroachdb/cockroach:v22.1.12
        - name: RELATED_IMAGE_COCKROACH_v22_1_13
          value: cockroachdb/cockroach:v22.1.13
        - name: RELATED_IMAGE_COCKROACH_v22_2_0
          value: cockroachdb/cockroach:v22.2.0
        - name: RELATED_IMAGE_COCKROACH_v22_2_1
          value: cockroachdb/cockroach:v22.2.1
        - name: RELATED_IMAGE_COCKROACH_v22_2_2
          value: cockroachdb/cockroach:v22.2.2
        - name: OPERATOR_NAME
          value: cockroachdb
        - name: POD_NAME
          valueFrom:
            fieldRef:
              fieldPath: metadata.name
        - name: NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        image: cockroachdb/cockroach-operator:v2.10.0
        imagePullPolicy: IfNotPresent
        name: cockroach-operator
        resources:
          requests:
            cpu: 10m
            memory: 32Mi
      serviceAccountName: cockroach-operator-sa

E por fim temos a criação dos endpoints de comunicação do cluster considerados como Webhooks, utilizados na hora de provisionarmos do cluster e para a gestão do mesmo pós ser criado:

apiVersion: admissionregistration.k8s.io/v1
kind: MutatingWebhookConfiguration
metadata:
  creationTimestamp: null
  name: cockroach-operator-mutating-webhook-configuration
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: cockroach-operator-webhook-service
      namespace: cockroach-operator-system
      path: /mutate-crdb-cockroachlabs-com-v1alpha1-crdbcluster
  failurePolicy: Fail
  name: mcrdbcluster.kb.io
  rules:
  - apiGroups:
    - crdb.cockroachlabs.com
    apiVersions:
    - v1alpha1
    operations:
    - CREATE
    - UPDATE
    resources:
    - crdbclusters
  sideEffects: None
---
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  creationTimestamp: null
  name: cockroach-operator-validating-webhook-configuration
webhooks:
- admissionReviewVersions:
  - v1
  clientConfig:
    service:
      name: cockroach-operator-webhook-service
      namespace: cockroach-operator-system
      path: /validate-crdb-cockroachlabs-com-v1alpha1-crdbcluster
  failurePolicy: Fail
  name: vcrdbcluster.kb.io
  rules:
  - apiGroups:
    - crdb.cockroachlabs.com
    apiVersions:
    - v1alpha1
    operations:
    - CREATE
    - UPDATE
    resources:
    - crdbclusters
  sideEffects: None

Para criar o Operator basta executar o comando:

kubectl create -f operator.yaml

Como podemos ver acima todos os recursos descritos foram criados a partir de um único arquivo separados por —.

Agora para facilitar a criação dos demais recursos iremos setar o context de nossa sessão com o Kubernetes para utilizar sempre o namespace criado:

kubectl config set-context --current --namespace=cockroach-operator-system

Desta forma não precisamos mais daqui pra frente passar nos comandos kubectl a sintaxe -n ou –namespace para acessar os recursos referentes ao namespace do Cockroach.

Vamos agora criar efetivamente nosso cluster de banco de dados criando o arquivo cockroachdb-cluster.yaml com o conteúdo:

apiVersion: crdb.cockroachlabs.com/v1alpha1
kind: CrdbCluster
metadata:
  name: cockroachdb
spec:
  dataStore:
    pvc:
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: "100Gi"
        volumeMode: Filesystem
  resources:
    requests:
      cpu: 2000m
      memory: 8Gi
    limits:
      cpu: 2
      memory: 8Gi
  tlsEnabled: true
  image:
    name: cockroachdb/cockroach:v22.2.2
  nodes: 3
  additionalLabels:
    crdb: is-cool

Como podemos ver acima temos dois componentes a serem destacados:

  • apiVersion: aqui estamos usando a API criada pelo Webhook no provisionamento do Operator com o endereço crdb.cockroachlabs.com/v1alpha1;
  • kind: no tipo de recurso a ser criado é dedicado para o Operator provisionado, não usando um recurso conhecido do K8s, isso se da graças aos componentes criados anteriormente.

Para criar o cluster basta executarmos:

kubectl create -f cockroachdb-cluster.yaml

Para verificar todos os componentes criados, vamos executar os comandos:

kubectl get all -o wide
kubectl get pvc -o wide
kubectl get pv -o wide

Temos diversos recursos criados, com destaque para os Nodes escolhidos que fazem referencia ao nodepool2 que criamos e que possuem recurso o suficiente para provisionarmos os recursos solicitados (2 vCPUs e 8 GB de RAM), para o deployment do Operator e o StatefulSet do cluster CDB e por fim como não escolhemos um StorageClass o Default foi definido, se formos no Azure poderemos identifica-los conforme imagem abaixo:

Logo, o padrão para o AKS sempre será alocar um storage de bloco Azure Standard SSD com LRS.

Agora vamos criar o client para que possamos entrar e executar comandos no CockroachDB:

kubectl create \
-f https://raw.githubusercontent.com/cockroachdb/cockroach-operator/v2.10.0/examples/client-secure-operator.yaml

Uma vez provisionado, vamos entrar no cluster CockroachDB utilizando o service cockroachdb-public e vamos executar algumas queries para verificar se esta tudo funcionando:

kubectl exec -it cockroachdb-client-secure -- ./cockroach sql --certs-dir=/cockroach/cockroach-certs --host=cockroachdb-public
CREATE DATABASE dbExemplo;
USE dbExemplo;
CREATE TABLE tbCadastro(id int primary key, nome varchar(100), dtnasc date);
INSERT INTO dbExemplo.tbCadastro(id, nome, dtnasc) VALUES (1,'Paula','1978-04-12'),(2,'Carlos','1994-05-01');
SELECT * FROM dbExemplo.tbCadastro;
CREATE TABLE tbEndereco(id int primary key, idCadastro int not null REFERENCES tbCadastro (id) ON DELETE CASCADE, endereco varchar(1000), numero varchar(15));
INSERT INTO dbExemplo.tbEndereco(id, idCadastro, endereco, numero) VALUES (1,1,'Rua Exemplo', '432'), (2,2,'Rua Exemplo', '654');
SELECT c.nome, c.dtnasc, e.endereco, e.numero FROM dbexemplo.tbcadastro as c INNER JOIN dbexemplo.tbendereco as e ON c.id = e.idcadastro;

Como podemos ver nos comandos executados acima, temos todo o suporte total a linguagem SQL onde criamos um banco de dados, duas tabelas populadas com dados e com relacionamento via Chave Estrangeira (FK).

Vamos dar uma olhada agora no dashboard de gerenciamento do CockroachDB que eu particularmente acho um dos mais bonitos e completos dos bancos de dados que já administrei. Primeiro vamos criar um usuario para acessar o daashboard e depois vamos fazer um port foward para que possamos acessar da nossa maquina:

#ainda dentro da instancia
CREATE USER monitoring WITH PASSWORD 'C0ckr04chDB';
GRANT admin TO monitoring;
\q
kubectl port-forward service/cockroachdb-public 8080

Abra uma nova aba no seu navegador e digite: https://localhost:8080. Uma tela de login vai abrir, coloque o usuario e senha que criamos no banco de dados e o dashboard abrirá:

Aqui podemos ver nosso banco de dados e as tabelas que criamos:

No futuro farei um post mostrando apenas o CockroachDB e como o mesmo funciona, hoje conseguimos atingir nosso objetivo provisionando um novo cluster de banco de dados no nosso querido AKS!

Para excluir nosso laboratório basta executar:

kubectl delete -f cockroachdb-cluster.yaml
kubectl delete -f operator.yaml

No próximo post falaremos de mais um banco de dados com escalabilidade horizontal. Fique ligado. Muito obrigado pela atenção. Até a próxima!

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

Continuando nossa série de bancos de dados executando no AKS, no ultimo episódio provisionamos nosso querido e amado SQL Server 2022 em container rodando no Kubernetes com Azure Standard SSD.

Desta vez vamos mostrar como subir um cluster do banco de dados Chave-Valor mais utilizado no mercado e que ainda entrega uma capacidade de Caching de dados por conta da sua engine In-Memory, este é o Redis.

Por característica, bancos de dados voltados para Caching de dados tem o intuito de retirar carga de leitura intensa de bancos de dados relacionais ficando entre a camada de aplicação e o banco de dados tradicional. Isso acontece por conta da engine In-Memory deste banco de dados NoSQL key-valued store que trabalha na escala de nano\micro segundo uma vez que os dados ficam fixados na RAM dos servidores e por sua capacidade de escalabilidade horizontal, que por sua vez, pode entregar cada vez mais capacidade de leitura intensa no menor tempo possível para aplicação.

Para este laboratório me baseei em duas documentações:

O Redis, por padrão, é provisionado em cluster com no mínimo 3 nodes, sendo assim, vamos começar criando nosso famigerado Namespace:

kubectl create namespace redis

Uma vez criado nosso ns agora vamos criar um ConfigMap que será utilizado como base de configuração para nossos Pods Redis. Crie um arquivo configmap.yaml com o conteúdo a seguir:

apiVersion: v1
kind: ConfigMap
metadata:
  name: redis-config
data:
  redis.conf: |
    masterauth R3d!s@2023
    requirepass R3d!s@2023
    bind 0.0.0.0
    protected-mode no
    port 6379
    tcp-backlog 511
    timeout 0
    tcp-keepalive 300
    daemonize no
    supervised no
    pidfile "/var/run/redis_6379.pid"
    loglevel notice
    logfile ""
    databases 16
    always-show-logo yes
    save 900 1
    save 300 10
    save 60 10000
    stop-writes-on-bgsave-error yes
    rdbcompression yes
    rdbchecksum yes
    dbfilename "dump.rdb"
    rdb-del-sync-files no
    dir "/data"
    replica-serve-stale-data yes
    replica-read-only yes
    repl-diskless-sync no
    repl-diskless-sync-delay 5
    repl-diskless-load disabled
    repl-disable-tcp-nodelay no
    replica-priority 100
    acllog-max-len 128
    lazyfree-lazy-eviction no
    lazyfree-lazy-expire no
    lazyfree-lazy-server-del no
    replica-lazy-flush no
    lazyfree-lazy-user-del no
    appendonly yes
    appendfilename "appendonly.aof"
    appendfsync everysec
    no-appendfsync-on-rewrite no
    auto-aof-rewrite-percentage 100
    auto-aof-rewrite-min-size 64mb
    aof-load-truncated yes
    aof-use-rdb-preamble yes
    lua-time-limit 5000
    slowlog-log-slower-than 10000
    slowlog-max-len 128
    latency-monitor-threshold 0
    notify-keyspace-events ""
    hash-max-ziplist-entries 512
    hash-max-ziplist-value 64
    list-max-ziplist-size -2
    list-compress-depth 0
    set-max-intset-entries 512
    zset-max-ziplist-entries 128
    zset-max-ziplist-value 64
    hll-sparse-max-bytes 3000
    stream-node-max-bytes 4kb
    stream-node-max-entries 100
    activerehashing yes
    client-output-buffer-limit normal 0 0 0
    client-output-buffer-limit replica 256mb 64mb 60
    client-output-buffer-limit pubsub 32mb 8mb 60
    hz 10
    dynamic-hz yes
    aof-rewrite-incremental-fsync yes
    rdb-save-incremental-fsync yes
    jemalloc-bg-thread yes

Caso queira saber mais sobre as opções utilizadas, verificar o arquivo de configuração completo em: https://redis.io/docs/management/config-file/

kubectl create -f configmap.yaml -n redis

Agora vamos criar no primeiro StatefulSet, quem leu os posts anteriores verificou que o Kubernetes é primeiramente voltado a execução de containers em Pods que por característica beneficiam aplicações Stateless, ou seja, que não armazenam estado, porém, com o advento e amadurecimento da orquestração de containers no K8s surgiu este recurso que nos auxilia a provisionar cluster de sistemas que precisam armazenar dados.

Crie um arquivo redis-cluster.yaml com o conteúdo:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: redis
spec:
  serviceName: redis
  replicas: 3
  selector:
    matchLabels:
      app: redis
  template:
    metadata:
      labels:
        app: redis
    spec:
      initContainers:
      - name: config
        image: redis:6.2.3-alpine
        command: [ "sh", "-c" ]
        args:
          - |
            cp /tmp/redis/redis.conf /etc/redis/redis.conf
            
            echo "finding master..."
            MASTER_FDQN=`hostname  -f | sed -e 's/redis-[0-9]\./redis-0./'`
            if [ "$(redis-cli -h sentinel -p 5000 ping)" != "PONG" ]; then
              echo "master not found, defaulting to redis-0"

              if [ "$(hostname)" == "redis-0" ]; then
                echo "this is redis-0, not updating config..."
              else
                echo "updating redis.conf..."
                echo "slaveof $MASTER_FDQN 6379" >> /etc/redis/redis.conf
              fi
            else
              echo "sentinel found, finding master"
              MASTER="$(redis-cli -h sentinel -p 5000 sentinel get-master-addr-by-name mymaster | grep -E '(^redis-\d{1,})|([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})')"
              echo "master found : $MASTER, updating redis.conf"
              echo "slaveof $MASTER 6379" >> /etc/redis/redis.conf
            fi
        volumeMounts:
        - name: redis-config
          mountPath: /etc/redis/
        - name: config
          mountPath: /tmp/redis/
      containers:
      - name: redis
        image: redis:6.2.3-alpine
        command: ["redis-server"]
        args: ["/etc/redis/redis.conf"]
        ports:
        - containerPort: 6379
          name: redis
        volumeMounts:
        - name: data
          mountPath: /data
        - name: redis-config
          mountPath: /etc/redis/
      volumes:
      - name: redis-config
        emptyDir: {}
      - name: config
        configMap:
          name: redis-config
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "managed-csi"
      resources:
        requests:
          storage: 500Mi

Destaque para o storageClassName onde utilizaremos um Azure Standard SSD em modo de acesso ReadWriteOnce, necessário para este tipo de implementação. Podemos destacar também que estamos subindo 3 replicas de instancias Redis versão 6.2.3 em imagem alpine (uma das mais leves, pesando apenas alguns megabytes), porta interna default 6379 e com o nosso configmap apontado.

Execute a criação dos componentes:

kubectl create -f redis-cluster.yaml -n redis

Verifique se tudo foi criado com êxito:

kubectl get all -n redis
kubectl get pv -n redis
kubectl get pvc -n redis

Se executarmos um describe no StatefulSet para verificar mais detalhes do provisionamento podemos ver que tudo foi executado com sucesso:

kubectl describe statefulset redis -n redis

Agora que temos nosso cluster no ar, vamos criar o Service necessário para que possamos acessar a instancia Redis, crie um arquivo svc.yaml com:

apiVersion: v1
kind: Service
metadata:
  name: redis
spec:
  type: LoadBalancer
  ports:
  - port: 6379
    targetPort: 6379
    name: redis
  selector:
    app: redis

Agora execute:

kubectl create -f svc.yaml -n redis
kubectl get service -n redis

Vamos verificar se esta tudo certo com a replicação do nosso cluster Redis? Para isso vamos verificar o Log interno de um dos Pods de nosso cluster para verificar se esta tudo funcionando conforme esperado:

kubectl logs redis-1 -n redis | tail -30

Podemos verificar as ultimas 30 linhas do log da instancia redis-1 que ela ficou um tempo tentando se conectar com a instancia PRIMARIA redis-0 e uma vez que conseguiu houve uma sincronização quase instantânea. Olhando para o node redis-0 podemos verificar que tudo esta funcionando apropriadamente também:

kubectl logs redis-0 -n redis

Vamos agora nos conectar na instancia redis-0 e executar alguns comandos no Redis para verificar mais informações:

kubectl -n redis exec -it redis-0 -- redis-cli
auth R3d!s@2023 #senha definida no ConfigMap
info replication

Apenas para validar se a replicação esta de fato funcionando apropriadamente, vamos criar alguns registros no banco de dados para verificar se replicação acontecerá para as replicas:

SET key0 value0
SET key1 value1
SET key2 value2
SET key3 value3
SET key4 value4
KEYS *

Para verificar os valores:

GET key0
GET key1
GET key2

Como podemos confirmar, o Redis é um banco de dados muito simples, cumprindo o papel de ser rápido e eficiente para cache.

Vamos agora para outra replica verificar se os dados estão la:

kubectl -n redis exec -it redis-2 -- redis-cli
auth R3d!s@2023 #senha definida no ConfigMap
info replication
KEYS *
GET key0

Bom gente, é isso por enquanto, podemos ver neste post como é simples executar um cluster Redis no AKS que já nos entrega disco e Load Balancer nativos para que possamos disponibilizar nossos clusters de banco de dados!

No próximo post veremos um banco de dados distribuído que gosto muito de trabalhar! Não deixe de acompanhar! Vlw!

[Série][AKS] Provisionando Bancos de Dados no Azure Kubernetes Service – SQL Server 2022

Nos ultimos posts mostramos como subir um cluster AKS no Azure com extrema facilidade e também falamos sobre como funcionam o storage que será utilizado pelas instancias de bancos de dados, neste post iremos mostrar como executar uma instancia SQL Server 2022 neste nosso cluster AKS, vocês verão como é simples!

Para isolarmos o provisionamento de nossa instancia vamos criar um Namespace. O Namespace é uma forma de isolarmos as aplicações que estão criadas no kubernetes com o intuito de isolarmos projetos, times, empresas, entre outros.

Para criar um namespace executamos o comando:

kubectl create namespace sqlserver

Vamos agora criar um StorageClass definindo o tipo de disco do Azure que iremos utilizar em nossa instancia SQL Server, neste caso utilizaremos um disco Standard SSD com LRS (Local-Reduntant Storage), entregando 11 9’s de disponibilidade garantindo cópias locais deste disco. Para saber quais são os outros tipos de discos consulte o link: https://learn.microsoft.com/en-us/azure/virtual-machines/disks-types

Neste caso estamos utilizando um disco Standard pois estamos em um ambiente controlado, caso esteja indo para produção e considere que seu sistema irá utilizar IO de forma intensiva com alto throughput então considere utilizar um tipo de storage Premium.

Continuando com o provisionamento, vamos criar uma pasta para jogarmos nosso projeto:

mkdir k8smssql
cd k8smssql

Crie um arquivo sc.yaml contendo a especificação de criação de seu StorageClass:

kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
     name: azure-disk
provisioner: kubernetes.io/azure-disk
parameters:
  storageaccounttype: Standard_LRS
  kind: Managed

Agora vamos criar o PersistentVolumeClaim que é a requisição por um storage, este componente requisita um storage baseado em um StorageClass, volumetria bem determinada e qual será o tipo acesso que os Pods poderão fazer ao storage solicitado, no caso abaixo utilizaremos ReadWriteOnce onde apenas o Pod o qual o storage for alocado poderá ler e escrever deste. Crie um arquivo pvc.yaml:

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
  name: mssql-data
  annotations:
    volume.beta.kubernetes.io/storage-class: azure-disk
spec:
  accessModes:
  - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Após criar os arquivos, vamos executa-los com os comandos abaixo, no caso PVC vamos coloca-lo em nosso namespace:

kubectl create -f sc.yaml
kubectl create -f pvc.yaml -n sqlserver

Para listar os recursos criados, basta executar os comandos abaixo:

kubectl get storageclass
kubectl get pvc -n sqlserver

Como podemos ver, existem diversos tipos de StorageClass que já explicamos no post anterior, destacados estão os que acabamos de criar.

Já que fizemos uma requisição por um volume via um Persistent Volume Claim e declaramos um storage gerenciado em Cloud, podemos ver que um volume já foi criado a partir desta requisição, que podemos conferir executando:

kubectl get pv -n sqlserver

Agora que já temos um volume, vamos criar um Secret aonde iremos armazenar a senha do nosso querido usuário SA da nossa instancia SQL Server:

kubectl create secret generic mssql --from-literal=MSSQL_SA_PASSWORD="MSSQL@k8sp@ssw0rd" -n sqlserver

Agora vamos criar nosso Deployment que disponibilizará uma instancia SQL Server 2022 utilizando nosso Persistent Volume e Secret já criados. Crie um arquivo mssql.yaml e insira o conteúdo abaixo:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mssql-deployment
spec:
  replicas: 1
  selector:
     matchLabels:
       app: mssql
  template:
    metadata:
      labels:
        app: mssql
    spec:
      terminationGracePeriodSeconds: 30
      hostname: mssqlinst
      securityContext:
        fsGroup: 10001
      containers:
      - name: mssql
        image: mcr.microsoft.com/mssql/server:2022-latest
        resources:
          requests:
            memory: "4G"
            cpu: "2000m"
          limits:
            memory: "4G"
            cpu: "2000m"
        ports:
        - containerPort: 1433
        env:
        - name: MSSQL_PID
          value: "Developer"
        - name: ACCEPT_EULA
          value: "Y"
        - name: MSSQL_SA_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mssql
              key: MSSQL_SA_PASSWORD
        volumeMounts:
        - name: mssqldb
          mountPath: /var/opt/mssql
      volumes:
      - name: mssqldb
        persistentVolumeClaim:
          claimName: mssql-data

Questões importantes que podemos destacar do yaml acima:

  • Estamos criando um Deployment e não um StatefulSet por que neste caso utilizaremos uma instancia Standalone e não em FCI – Failover Cluster Instance ou AlwaysOn Availability Groups, logo, não precisamos de um Operator para gerenciar o estado deste cluster, tornando a infra mais simples;
  • Estamos solicitando um pod com 2 vCPU’s e 4GB de RAM;
  • A versão que estamos subindo é a Developer.

Para criar basta executar o comando abaixo, depois vamos listar os pods:

kubectl create -f mssql.yaml -n sqlserver
kubectl get pod -n sqlserver

Podemos ver que o Pod com o SQL Server foi criado, mas o status fica em Pending, a instancia não entra em execução, vamos investigar:

kubectl describe pod mssql-deployment-69cf4fff84-hr2w8 -n sqlserver

No final da saída do comando Describe podemos ver que não temos recursos o suficiente para executar nosso banco de dados, neste caso CPU e memória:

Sendo assim, como não podemos escalar verticalmente nossos workers do node pool que já existe, teremos que criar um novo node pool com instancias do tamanho que precisamos e o kube-scheduler se incumbira de criar o pod neste node pool uma vez que há recursos o suficiente lá:

Agora vamos deletar nosso Deployment e criar novamente:

kubectl delete -f mssql.yaml -n sqlserver
kubectl create -f mssql.yaml -n sqlserver

Depois disso vamos verificar se nosso Pod irá subir:

Percebi que estava demorando muito para criar, então fui investigar:

kubectl describe pod mssql-deployment-69cf4fff84-fbsbz -n sqlserver

E novamente temos um problema, porém agora no Persistent Volume que já havia sido alocado para outro Pod:

Para garantir, vamos apagar nosso Deployment e PVC e criar novamente

kubectl delete -f mssql.yaml -n sqlserver
kubectl delete -f pvc.yaml -n sqlserver
kubectl create -f pvc.yaml -n sqlserver
kubectl create -f mssql.yaml -n sqlserver

Agora sim! Nosso pod foi provisionado com sucesso:

Para verificar em qual node ele foi provisionado podemos executar um Describe ou listar com a opção -o wide:

kubectl get pod -n sqlserver -o wide

Como podemos ver ele foi alocado no Node Pool 2 que acabamos de criar:

Agora precisamos criar um Service para que possamos chegar na instancia provisionada, para isso crie um arquivo service.yaml com o conteúdo:

apiVersion: v1
kind: Service
metadata:
  name: mssql-deployment
spec:
  selector:
    app: mssql
  ports:
    - protocol: TCP
      port: 1433
      targetPort: 1433
  type: LoadBalancer

Podemos ver que o tipo de Service é LoadBalancer, isso por que estamos em uma cloud e a mesma disponibiliza este tipo de recurso que podemos utilizar para ganhar um IP Externo para que possamos conectar em nosso recurso.

kubectl create -f service.yaml -n sqlserver
kubectl get service -n sqlserver

Agora basta conectarmos em nossa instancia :

Após conectar execute os comandos abaixo para verificarmos a versão de nosso SQL Server e o nome do hostname:

SELECT @@version;
SELECT @@servername;

Como podemos ver estamos executando nosso Pod com SQL Server 2022 Developer em uma imagem Ubuntu 20.04 e o nome da instancia é o mesmo declarado em nosso deployment, este é o hostname do container que esta executando nosso SQL Server.

Vamos criar um banco de dados, uma tabela neste banco e alguns registros para verificar a persistencia:

CREATE DATABASE k8sdb;

USE k8sdb;

CREATE TABLE exemplo(
        id int identity primary key
        , nome varchar(1000)
        , dtnasc date
);

INSERT INTO exemplo(nome, dtnasc) VALUES ('Maria','1958-01-23'),( 'João', '1990-05-01');

SELECT * FROM exemplo;

É isso gente, neste post vimos como provisionar uma instancia SQL Server e só não foi mais simples por que tivemos alguns problemas por conta de limitações de hardware do nosso cluster, porém, vimos como fazer o troubleshooting deste tipo de problema e como é simples contorna-lo graças a estamos utilizando o AKS!

Nosso projeto para subir um SQL Server 2022 no k8s ficou assim:

E nosso cluster AKS ganhou novos recursos que podemos ver no Resource Group de apoio, agora temos Load Balancer, Volumes, Node Pool novo e um Public IP:

Para limpar nosso ambiente basta executar o delete de cada recurso criado, mas como colocamos tudo dentro de um namespace, podemos excluir o namespace todo:

kubectl delete namespace sqlserver

É isso por hoje gente, no próximo post veremos como subir um cluster de Redis no nosso AKS, muito obrigado pela atenção!

[Série][AKS] Provisionando Bancos de Dados no Azure Kubernetes Service – Introdução a Storage no K8s

Neste post teremos uma introdução de como o Storage utilizado no Kubernetes gerenciado do Azure funciona, no ultimo post vimos como provisionar o cluster.

No Azure temos diversos tipos de serviços que nos entregam capacidade de armazenamento, como (Fonte: Azure Storage):

Armazenamento em bloco de alto desempenho para aplicações críticasArmazenamento em Disco do Azure
Armazenamento de objetos altamente escalável e seguro para arquivos de diversos tipos, data lakes, computação de alto desempenho e machine learningArmazenamento de Blobs do Azure
Data lake altamente escalável e seguro para as workloads analiticos de alto desempenhoAzure Data Lake Storage
Compartilhamento de arquivos em nuvem de nível empresarial simples, seguro e serverlessArquivos do Azure
Compartilhamento de arquivos de nível empresarial do Azure, da plataforma NetAppAzure NetApp Files
Compartilhamento de arquivos na nuvem híbrida para armazenar em cache seus dados locaisSincronização de Arquivos do Azure
Gateway de armazenamento em nuvem para transferir dados de maneira fácil e eficiente entre a nuvem e a bordaAzure Stack Edge
Dispositivos e soluções para transferir dados de e para o Azure de forma rápida e econômicaAzure Data Box
https://azure.microsoft.com/pt-br/products/category/storage/

Exposto os tipos de storage disponíveis no Azure, passamos agora para falar um pouco mais de como os Volumes (que é o objeto do Kubernetes que entrega capacidade de storage) funcionam. Por padrão um container possui um armazenamento efêmero, ou seja, quando o container é recriado ou excluído os dados são perdidos, consideramos este comportamento como stateless ou ausência de armazenamento de estado\dados.

Desta forma para que haja um armazenamento duradouro de dados por parte dos Pods que estão em uso e mesmo que esses sejam recriados no mesmo worker ou em outro nó do cluster precisamos implementar Volumes para nossas aplicações provisionadas no kubernetes. Mas o que compõe um Volume? Vamos ver:

Storage Class

Um SC ou StorageClass é o tipo ou classe de storage que será alocado quando estamos provisionando um Volume no k8s. Um StorageClass é baseado em provisioners dos quais podemos considerar como tipo de storage a ser selecionado mas que na verdade são API focadas na entrega do recurso fim de armazenamento de dados:

Volume PluginInternal ProvisionerConfig Example
AWSElasticBlockStoreAWS EBS
AzureFileAzure File
AzureDiskAzure Disk
CephFS
CinderOpenStack Cinder
FC
FlexVolume
GCEPersistentDiskGCE PD
iSCSI
NFSNFS
RBDCeph RBD
VsphereVolumevSphere
PortworxVolumePortworx Volume
LocalLocal
https://kubernetes.io/docs/concepts/storage/storage-classes/#azure-file

Podemos ver acima vários tipos de Storage distintos: Nativos de Nuvem (AWS EBS, Azure Disk e GCE PD por exemplo), objeto (CephFS), etc.

Como estamos utilizando um k8s gerenciado na Azure, por padrão o AKS já disponibiliza alguns StorageClass no cluster, para verificar basta executar o comando:

kubectl get storageclass

Temos 3 tipos de provisioners disponíveis:

  • kubernetes.io/azure-disk: que é o provisioner padrão do k8s para provisionar Azure Disk;
  • file.csi.azure.com: provisioner (API) nativo da Azure para provisionamento volume utilizando de Azure File;
  • disk.csi.azure.com: provisioner (API) nativo da Azure para entregar volumes de bloco via Azure Disk (idem ao primeiro tipo, porém, com um endpoint diferente).

O primeiro e o terceiro são basicamente iguais, sendo assim qual é a diferença do File para o Disk? A forma de acesso aos volumes por parte do Pod (que é determinada no PVC):

  • File: este tipo pode disponibilizar volumes com os tipos de acesso ReadOnlyMany e ReadWeriteMany onde mais de um Pod poderá acessar o mesmo volume de uma vez, este tipo é voltado para aplicações que podem compartilhar volumes como file shares, WordPress, entre outros;
  • Disk: já este tipo é mais restritivo que o outro onde apenas os tipos de acesso ReadWriteOnce é suportado, ou seja, apenas um Pod poderá acessar o volume o qual foi vinculado, esse tipo é mais voltado para aplicações que não podem compartilhar volumes como bancos de dados e aplicações Stateful como o Kafka.

Para quem esta com curiosidade sobre a sigla CSI (Assim como eu), a mesma é uma abreviação para Container Storage Interface que é um padrão adotado para disponibilizar volumes em Orquestradores de Containers como Kubernetes, Mesos e Nomad. Para quem quiser saber mais sugiro a documentação do K8s e o projeto CSI no Github:

https://kubernetes-csi.github.io/docs/

https://github.com/container-storage-interface/spec

Em nossa série, por estarmos utilizando apenas bancos de dados que, por consequência, são aplicações Stateful, por natureza iremos utilizar apenas Storage Class do tipo managed, onde:

  • managed e managed-csi: são discos Azure Standard SSD com redundancia local ou LRS (Locally Redundancy Storage);
  • managed-premium e managed-csi-premium: são discos Azure Premium SSD com redundancia local ou LRS (Locally Redundancy Storage).

Para mais detalhes sobre cada tipo temos a tabela comparativa abaixo:


Disco Ultra
SSD Premium v2SSD PremiumSSD StandardHDD Standard
Tipo de discoSSDSSDSSDSSDHDD
CenárioCargas de trabalho de E/S intensiva, como SAP HANA, bancos de dados de camada superior (por exemplo, SQL, Oracle) e outras cargas de trabalho de transações pesadas.Cargas de trabalho sensíveis à produção e ao desempenho que exigem consistentemente baixa latência e alta IOPS e taxa de transferênciaCargas de trabalho sensíveis à produção e ao desempenhoServidores Web, aplicativos empresariais pouco usados e desenvolvimento/testeBackup, não crítico, acesso não frequente
Tamanho máximo do disco65.536 GiB (GibiByte)65.536 GiB32.767 GiB32.767 GiB32.767 GiB
Taxa de transferência máxima4\.000 MB/s1\.200 MB/s900 MB/s750 MB/s500 MB/s
IOPS Máxima160.00080.00020.0006.0002.000
Utilizável como disco do sistema operacional?NoNãoSimSimSim
https://learn.microsoft.com/pt-br/azure/virtual-machines/disks-types

Persistent Volume Claim

Um PVC ou Persistent Volume Claim é uma requisição a um Storage Class por um Volume com as especificações bem definidas no yaml de criação deste componente ou uma requisição por um volume já existente para ser disponibilizado para um ou mais Pods (Dependendo do seu tipo de acesso). Abaixo temos um exemplo de um PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-exemplo
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  volumeName: pv-exemplo
  storageClassName: managed-csi

Os pontos mais importantes que podemos destacar nos PVC’s são: capacidade bem determinada, modo de acesso e Storage Class.

Um item importante de ser destacado é o persistentVolumeReclaimPolicy que possui 3 tipos, onde:

  • Retain: caso um PVC seja excluido o Persistent Volume é mantido e caso precise ser reutilizado por outro PVC precisa ser ajustado manualmente;
  • Recycle: o PV será formatado e disponibilizado para algum outro PVC consumi-lo;
  • Delete: remove o PV quando o PVC o qual o mesmo faz relação for removido.

Persistent Volume

Um Persistent Volume é o volume em si que será “attachado” a um Pod quando solicitado, geralmente por meio de um PVC. Abaixo um exemplo de yaml para criação de um PV:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-exemplo
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: managed-csi

Para mais detalhes sobre volumes persistentes no Kubernetes consultem a documentação deste recursos que utilizaremos muito em nossa série:

https://kubernetes.io/docs/concepts/storage/persistent-volumes/

https://learn.microsoft.com/en-us/azure/aks/azure-csi-disk-storage-provision

Bom, acho que é isso para este post, a ideia aqui era dar uma ideia de como utilizaremos volumes no AKS em nossos bancos de dados e é um item que eu considero importante destacar quando falamos de StatefulSets que utilizaremos bastante também mas que eu explicarei ao longo da série e que formos provisionando nossas infraestruturas.

Desde já agradeço por me acompanhar aqui em mais um capitulo e até a próxima!

[Série][AKS] Provisionando Bancos de Dados no Azure Kubernetes Service – Criando o cluster

Fala galera!

Iniciando uma série aqui no Blog com o intuito de demonstrar como podemos utilizar o Kubernetes para executar bancos de dados, neste caso utilizaremos o serviço de Kubernetes gerenciado da Azure, o AKS – Azure Kubernetes Service para esta finalidade.

Bancos de dados, por característica, são aplicações stateful pois armazenam estado ou melhor, dados. Desde a versão 1.7 do Kubernetes temos a capacidade de provisionar StatefulSets que é uma feature muito parecida com o Deployment, porém, possui diferenças concideraveis como:

  • O armazenamento de um determinado pod deve ser provisionado por um PersistentVolume Provisioner com base na classe de armazenamento solicitada ou pré-provisionado por um administrador.
  • Excluir e/ou reduzir um StatefulSet não excluirá os volumes associados ao StatefulSet. Isso é feito para garantir a segurança dos dados, que geralmente é mais valioso do que uma limpeza automática de todos os recursos StatefulSet relacionados.
  • Atualmente, os StatefulSets exigem que um Headless Service (também conhecido por Operator) seja responsável pela identidade de rede dos Pods. Você é responsável por criar este Serviço.
  • Os StatefulSets não fornecem nenhuma garantia sobre o encerramento dos pods quando um StatefulSet é excluído. Para obter o encerramento ordenado e correto dos pods no StatefulSet, é possível reduzir o StatefulSet para 0 antes da exclusão.
  • Ao utilizar Rolling Updates com a Pod Management Policy padrão (OrderedReady), é possível que o processo entre em um estado de falha que irá requerer intervenção manual para ser solucionado.

Para mais detalhes consultar a documentação pelo link: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/

Neste post iremos provisionar nosso cluster AKS deixando-o tudo preparado para seguir com o provisionamento das tecnologias de bancos de dados.

A ideia desta série será mostrar o provisionamento de bancos de dados relacionais (como o SQL Server 2022) e NoSQL demonstrando os detalhes de cada um e disponibilizando exemplos que poderão ser utilizados em seu ecosistema.

Antes de começar precisamos que o Azure CLI esteja instalado em seu sistema operacional para que possamos prosseguir: https://learn.microsoft.com/pt-br/cli/azure/install-azure-cli

No meu caso utilizo MacOS, basta executar o comando abaixo:

brew update && brew install azure-cli

Após ter o Azure CLI instalado, execute:

az login

Insira as informações de usuário e senha da sua conta e pronto, estará logado no Azure via CLI. Umas vez logado, execute o comando abaixo para criar um Resource Group o qual utilizaremos para provisionar nosso cluster AKS:

az group create --name lab_aks_databases --location eastus

Resource Group criado:

Podemos seguir para a criação do nosso cluster AKS:

az aks create -g lab_aks_databases -n aks_database_cluster --enable-managed-identity --node-count 3 --enable-addons monitoring --enable-msi-auth-for-monitoring  --generate-ssh-keys

O cluster demora alguns minutos para ser provisionado, estamos criando um kubernetes com node-pool contendo 3 workers, desta forma podemos garantir uma alta disponibilidade de nossos ambientes de bancos de dados que irão ser provisionados neste ambiente.

Cluster criado:

Após o cluster ficar disponível precisamos instalar o cli do aks pois precisamos do kubectl que é o CLI que iremos utilizar para interagir com nosso cluster AKS, para isso precisamos rodar o seguinte comando:

az aks install-cli

Depois de instalado precisamos baixar as credenciais para logar em nosso cluster, podemos fazer isso com o comando:

az aks get-credentials --resource-group lab_aks_databases --name aks_database_cluster

Este comando irá adicionar em nosso arquivo ~/.kube/config os certificados para que possamos conectar em nosso cluster:

Assim que concluirmos essas configurações já podemos executar o comando kubectl para verificar se os nós de nosso cluster estão ok:

kubectl get nodes -o wide

Podemos conferir a mesma visão no console do Azure clicando na opção Node Pools:

No próximo post iremos provisionar uma instancia SQL Server 2022 em nosso cluster, vocês verão como é simples disponibilizar bancos de dados no AKS!

Até a próxima gente!

[SQL Server] Replicando acessos administrativos para usuários em um SQL Server gerenciado em Cloud

Recentemente tive a necessidade de inserir algumas instancias SQL Server que administro no Hashicorp Vault para que ele gerencia-se a criação de usuários de forma segura e auditada (posteriormente farei um post sobre como efetuar esta integração).

O Vault trabalha com o conceito de roles, cada role possui um comando de criação de usuário e um comando de remoção deste usuário, isso por que um usuário criado pelo Vault tem tempo para existir, isso é uma boa prática de segurança que deve ser observada.

Dado o contexto do Vault, qual o intuito deste post? Simples, o Vault executa um comando SQL para criar o usuário, mas e quando precisamos criar um usuário administrativo em uma instancia de banco de dados gerenciada em Cloud? Pois é. Os serviços gerenciados de bancos de dados em Cloud Pública restringem acessos dos usuários, mesmos dos usuários administrativos\master da instancia.

Outro exemplo de uso dessas procedures é quando não queremos que o usuário tenha permissões de SYSADMIN na instancia, limitando assim o nível de acesso a componentes e features internos da instancia como AlwaysOn, Resource Governor, etc.

Abaixo deixo 2 exemplos de procedures que podem ser criadas para conceder acesso a usuários de forma dinâmica de forma simples.

CREATE PROCEDURE sp_create_user (
        @user_name VARCHAR(100)
        ,@user_password NVARCHAR(512)
        ,@user_type VARCHAR(5) -- admin ou read
)
AS
BEGIN

    SET XACT_ABORT ON
    SET NOCOUNT ON

    DECLARE @command NVARCHAR(MAX)
        , @database_name VARCHAR(50)
        , @cont INT = 1

    SET @command = '
        USE master;

        CREATE LOGIN [' + @user_name +'] WITH PASSWORD=''' + @user_password + ''' , DEFAULT_DATABASE=[master], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF; 

        ALTER SERVER ROLE [PROCESSADMIN] ADD MEMBER [' + @user_name + '];

        ALTER SERVER ROLE [SETUPADMIN] ADD MEMBER [' + @user_name + '];
    '

    EXEC sp_executesql @command;

    IF @user_type = 'admin'
    BEGIN

        WHILE @cont <= (SELECT MAX(database_id) FROM sys.databases)
        BEGIN

            SET @database_name = DB_NAME(@cont)

            IF @database_name IS NOT NULL AND @database_name NOT IN ('master','model','tempdb','Distribuition','rdsadmin','ReportServer','ReportServerTempDB')
            BEGIN

                SET @command = 'USE [' + @database_name + ']; CREATE USER [' + @user_name + '] FOR LOGIN [' + @user_name + '] WITH DEFAULT_SCHEMA=[dbo];'

                EXEC sp_executesql @command;

                IF @database_name NOT IN ('msdb')
                BEGIN

                    SET @command = 'USE [' + @database_name + ']; ALTER ROLE [DB_OWNER] ADD MEMBER [' + @user_name + '];' 

                    EXEC sp_executesql @command;
                
                END

            END

            SET @cont += 1

        END

        SET @command = '
            USE master;

            GRANT ALTER ANY EVENT SESSION TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ADMINISTER BULK OPERATIONS TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER ANY SERVER AUDIT TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER ANY CONNECTION TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER LOGIN TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER ANY LINKED SERVER TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER ANY SERVER ROLE TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER SERVER STATE TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT ALTER TRACE TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT CREATE ANY DATABASE TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT VIEW ANY DEFINITION TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT VIEW ANY DATABASE TO [' +  @user_name + '] WITH GRANT OPTION;
            GRANT VIEW SERVER STATE TO [' +  @user_name + '] WITH GRANT OPTION;
        '

        EXEC sp_executesql @command;

        SET @command = '
            USE msdb; 
            EXEC sp_addrolemember [SQLAgentUserRole],[' + @user_name + ']; 
            GRANT ALTER ANY USER ON DATABASE::[msdb] TO [' + @user_name + '] WITH GRANT OPTION;
            GRANT ALTER ON ROLE::[SQLAgentOperatorRole] TO [' + @user_name + '];   
        '

        EXEC sp_executesql @command; 

    END
    ELSE IF @user_type = 'read'
    BEGIN

        WHILE @cont <= (SELECT MAX(database_id) FROM sys.databases)
        BEGIN

            SET @database_name = DB_NAME(@cont)

            IF @database_name IS NOT NULL AND @database_name NOT IN ('master','model','msdb','tempdb','Distribuition','rdsadmin','ReportServer','ReportServerTempDB')
            BEGIN

                SET @command = '
                    USE [' + @database_name + ']; 
                    CREATE USER [' + @user_name + '] FOR LOGIN [' + @user_name + '] WITH DEFAULT_SCHEMA=[dbo]; 
                    ALTER ROLE [db_datareader] ADD MEMBER [' + @user_name + '];
                '

                EXEC sp_executesql @command;

            END

            SET @cont += 1

        END

    END

END

A procedure a seguir remove o usuário após o tempo especificado no Vault:

CREATE PROCEDURE sp_drop_user (
    @user_name VARCHAR(100)
)
AS
BEGIN

    DECLARE 
        @command NVARCHAR(MAX)
        ,@database_name VARCHAR(50)
        ,@cont INT  = 1

    -- Encerra todas as conexoes do usuario
    IF EXISTS(SELECT 1 FROM sys.dm_exec_sessions WHERE login_name = @user_name)
    BEGIN

        SET @command = (SELECT TOP 1 N'KILL ' + CONVERT(NVARCHAR(11), session_id) + N';' FROM sys.dm_exec_sessions WHERE login_name = @user_name);

        EXEC sp_executesql @command;

    END

    -- Remove o usuario de todos os bancos de dados
    WHILE @cont <= (SELECT MAX(database_id) FROM sys.databases)
    BEGIN

        SET @database_name = DB_NAME(@cont)

        IF @database_name IS NOT NULL AND @database_name NOT IN ('master','model','msdb','tempdb','Distribuition','rdsadmin','ReportServer','ReportServerTempDB')
        BEGIN

            SET @command = '
                USE [' + @database_name + ']; 
                CREATE USER [' + @user_name + '] FOR LOGIN [' + @user_name + '] WITH DEFAULT_SCHEMA=[dbo]; 
                ALTER ROLE [db_datareader] ADD MEMBER [' + @user_name + '];
            '

            EXEC sp_executesql @command;

        END

        SET @cont += 1

    END


    SET @command = 'USE [master]; DROP LOGIN [' + @user_name + '];'

    EXEC sp_executesql @command;

END

É isso gente, em breve faço um post fazendo essa implementação no Hashicorp Vault.

Até a próxima!

O papel do DBRE – Introdução

Fala galera!

Nesta série iremos discutir sobre o que um Database Reliability Engineer faz, quais as diferenças dessa função para o DBA tradicional e o papel que ele desempenha dentro de times de engenharia de confiabilidade ou SRE.

Muito do que será discutido aqui tem haver com minha experiencia de mercado e as práticas discutidas nos livros SRE do Google e DBRE, deixarei as referencias no final de cada post.

Práticas SRE

O livro Database Reliability Engineer dos autores Laine Campbell e Charity Majors descreve já no capitulo introdutório certos principios que todo DBRE deveria práticar, esses são:

  • Proteção de Dados
  • Padrões para Ganho de Escala
  • Eliminação de Tarefas Repetitivas (Toil)
  • Tratar os servidores de bancos de dados mais como gado e menos como pet
  • Aproximação entre Software e Operações (DevOps)
  • Operação como Core dentro dos times de engenharia
  • Declaração de Prioridades
  • Seguro e Funcionando em primeiro lugar, depois vem a Escalabilidade
  • Invista no empoderamento dos times e não em Guard Rails
  • Observabilidade e Estrumentação
  • Auto remediação (Self-Healing ou Self-Actualization como esta no livro)

Vou tentar ser objetivo em cada tópico, resumindo em poucas palavras.

Proteção de Dados

Uma das primeiras coisas que todo DBA em inicio de carreira aprende é como efetuar Backup e Restore de um banco de dados, logo, uma das primeiras coisas que aprendemos é que todo bom ambiente precisa ter uma rotina de backup implementada e ajustada às necessidades de negócio, com RTO e RPO bem definidos. Raro de ver, mas quando vemos um ambiente que efetua Restores constantes é coisa linda de ver.

Outras questões que tocam em proteção são um bom processo de concessão de acessos que de preferencia não permite a criação de usuários nominais no banco de dados, ferramentas que apoiam na concessão de acessos, criando usuários temporários no banco de dados apenas quando necessário. Processos de auditoria implementados são importantes aqui para auxiliar em momentos de troubleshooting junto da área de Sec.

Processo de mudança, controle de configuração e um CMDB bem atualizado e detalhado podem evitar dores de cabeça.

Padrões para Ganho de Escala

No mundo OnPremise automatizar a criação e gestão de infraestrutura pode ser bem complexo dado a característica diversa de hardwares, softwares e afins. Já no mundo Cloud isso não acontece pois as plataformas abstraem a camada de hardware e software e te entregam uma interface facilitada para criação de infra.

Dado este cenário de Cloud, ter padrões pode te ajudar muito no dia a dia, como padrões de nomenclatura, tags, configurações, capacity, ambientes e afins. Quando eu vejo um ambiente bem padronizado fica fácil de identificar os ambientes e até mesmo construir automações e infraestruturas escaláveis dado a facilidade com que identificamos nossa infraestrutura e a entregamos para a camada de sistemas.

Eliminação de Tarefas Repetitivas (Toil)

Se você trabalha em ambientes pequenos, talvez não veja necessidade de automatizar tarefas maçantes, já para quem tem que lidar com dezenas, centenas e até milhares de instancias de bancos de dados, ainda mais em um ambiente que possui escalabilidade horizontal de instancias terá problemas graves se não construir automações.

Essas automações começam ao agendar tarefas administrativas como Reindex e Update Statics\Analyze, passando por extração de dados do ambiente e até a escalabilidade de replicas de leitura para atender a alta demanda das aplicações em determinados momentos do dia.

Creio que todo DBA aprende intrinsecamente a automatizar suas tarefas, dado que certos procedimentos acabam impactando a performance sistêmica das aplicações e precisam ser feitas fora do horário comercial, logo, para não ter que ficar gastando tempo de folga executando rotinas na mão, automatizar este tipo de trabalho é primordial.

Pet vs Catle

Quem nunca quis colocar ou até colocou um nome de Transformers, Vingadores ou Pokemons em seus servidores? Pois é, eu vejo isso até hoje!

Após o nascimento das Clouds e o avanço da virtualização, criar e apagar infraestruturas ficou cada vez mais comum, logo, ter padrões como falamos num tópico anterior é essencial bem como infraestrutura como código, versionada e armazenada em um repositório Git, que é provisionada por uma pipeline tornou-se cada vez mais comum, principalmente para infras distribuídas e que necessitam escalar.

Cada vez mais vemos opções de bancos de dados distribuídos nascendo, seja dentro dos provedores de Cloud ou fora e que oferecem escalabilidade horizontal de verdade, seja sobre Bare Metal, IaaS ou Kubernetes. Dado este cenário, utilizar o conceito de tratar maquinas como gado, que é algo que já acontece com maquinas de aplicação, também poderá ser utilizado para ambientes de bancos de dados.

Vale lembrar que essas novas tecnologias de bancos de dados distribuídos funcionam com replicação de dados em larga escala e com baixa latência, aonde podemos ter mais de um node de um cluster recebendo tanto leitura como escrita e o mesmo resolve conflitos internamente com sucesso. Logo, a aplicação deste conceito para ambientes de bancos de dados será cada vez mais comum onde existir ambientes com apetite por alta escalabilidade com alta disponibilidade.

Aproximação entre Software e Operações (DevOps)

Quanto mais o tempo passa, mais vemos o profissional de T.I. mudando, principalmente o profissional de operações, pois, este, que sempre esteve mais ligado ao hardware, servidores, datacenters e afins viu seu mundo virar código e ser abstraído pelas Clouds. Isso quer dizer que o profissional de operações\infra irá morrer? Claro que não, mas uma coisa é fato: se este profissional não aprender a codificar e se acostumar a transformar sua infraestrutura em código provavelmente irá perder boas oportunidades em boas empresas.

Esse contexto também vale para os DBA’s tradicionais, mas acredito que para este mundo as mudanças não afetarão tanto pois o DBA já tende a estar proximo do desenvolvedor, otimizando sistemas a partir da camada do banco de dados. Todo sistema que armazena dados precisa executar queries, logo, um bom DBA tende a lidar de forma sistêmica e pelo menos com a linguagem SQL, que é o seu core.

O que irá diferenciar os DBA’s tradicionais dos DBRE’s serão as práticas do mundo DevOps, como: Infra As Code, gestão de configuração, observability na camada do banco de dados, coleta de métricas e traceability para rastreabilidade, centralização de logs (Principalmente para ambientes distribuídos), automação além do SQL com linguagens como Python e GO, lidar com ambientes gerencias em Cloud Publica, implementação de Auto Healing, pipelines de infraestrutura e migration, testes, entre outras práticas que vem do mundo de desenvolvimento e DevOps.

Verdade seja dita, mesmo antes do termo DevOps surgir, DBA’s já construíam automações, seja por shell scripts ou PowerShell e até mesmo SQL, principalmente aqueles que precisavam lidar com ambientes médios a grandes. Ter um DBA dentro de uma empresa muitas vezes é visto como luxo, pois são recursos considerados caros e apenas são considerados necessários quando problemas de escala já estão acontecendo, com os DBRE’s não será diferente, logo, saber lidar tanto com o mundo tradicional de operações ao qual o DBA já existia unindo às práticas novas de automação levará a atingir outro patamar na carreira.

Operação como Core dentro dos times de engenharia

No item anterior falei muito sobre o perfil Ops do DBA e como o profissional de operações precisa lidar com o mundo novo e saber codificar, pois bem, o oposto também é verdadeiro.

Acho legal certos termos como: “You Build It, You Run It”. Ou literalmente, você desenvolve, você sustenta. Na teoria parece algo que faz muito sentido, unir desenvolvimento e operações com o intuito de entregar seus produtos de software com a melhor experiencia possível para seus clientes.

Na prática ja vi times de engenharia serem formados com este intuito, os times que deram certo viraram referencia de entrega de software, muito mais robustos, com menos problemas indo para produção, com muito mais agilidade. Mas a realidade é que a maioria dos demais times tiveram dificuldade, por que? Simples, culturalmente o desenvolvedor tende a não se preocupar muito com a infraestrutura ou operações por que entende que há áreas\pessoas especializadas para tal, logo, na maioria dos casos, engenheiros de software terão menos afinidade e menos interesse em desenvolver hard skills de operações.

Quanto mais eu convivo dentro de áreas de SRE mais eu vejo a necessidade de esta função ser muito mais ligada a engenharia de software do que operações em si, porém, se já esta difícil conseguir bons desenvolvedores para entregar apenas software, tão mais difícil será encontrar no mercado engenheiros de confiabilidade com bases fortes nas duas pontas.

Acredito que isso apenas mudará com o investimento pesado das empresas em: primeiro – desenvolver sua própria cultura SRE e segundo – formando seus próprios engenheiros de confiabilidade. Mas aonde o DBA entra nisso tudo? Para um DBA se tornar um DBRE ele terá que ir além da camada de banco de dados, conhecendo mais sobre arquitetura de software, cloud computing, escalabilidade, disponibilidade e práticas DevOps, quanto mais ele estiver próximo de como as aplicações funcionam, mais ele entenderá qual a melhor ferramenta para um determinado cenário serviço ou software.

Declaração de Prioridades

Aqui não irei me prolongar pois é algo obvio: se você entrega ambientes de bancos de dados para sistemas e não se preocupa na entrada com backup, lifecycle dos dados (expurgo), gestão de configuração, monitoração e segurança, logo, seu ambiente esta em risco e consequentemente errado.

Existem certas prioridades no funcionamento de bons ambientes de bancos de dados que não podem ficar para depois, caso isso aconteça provavelmente você terá um problema grande por negligenciar estas atividades, logo, nenhuma prioridade deverá ser maior do que as descritas acima.

Seguro e Funcionando em primeiro lugar, depois vem a Escalabilidade

Distribuir dados? Particionamento de tabelas? Escalabilidade Horizontal? Sharding? Todas essas técnicas podem melhorar muito a disponibilidade e performance das aplicações, mas de adianta ter todas essas coisas se não tivermos um procedimento de backup bem montado com múltiplas copias dos dados, se não fazemos testes de restore, replicar os dados por múltiplas localidades e ter um failover seguro podem diminuir muito a chances de corrupção de dados e aumentar a garantia e consistência destes.

Pensar em escalabilidade no dia 0 pode ser um erro pois muitas vezes não se tem a real visibilidade do quão rápido um sistema ou serviço podem crescer no tempo, por conta disso pense simples: garanta backup, replicação de dados e um ambiente alto disponível.

Invista no empoderamento dos times e não em Guard Rails

A cultura DevOps traz à luz a colaboração entre desenvolvedores e operadores, simples assim, logo, o DBRE deve investir em conhecimento das equipes de desenvolvimento e infraestrutura para que em conjunto possam construir sistemas melhores, com as melhores práticas.

A colaboração leva a perfeição (ou o mais perto dela), trabalhando de forma colaborativa cada vez menos teremos a necessidade de inserir mecanismos de controle como guard-rails para impedir que façam coisas erradas nos ambientes.

Utilizar práticas modernas como a utilização de Infra-as-code auxilia neste processo pois coloca a disponibilização de ambientes de bancos de dados mais próxima do processo de desenvolvimento de software pois torna a infraestrutura em código e possibilita testes e outras boas práticas de desenvolvimento de software.

Observabilidade e Estrumentação

Todo ambiente sistêmico precisa ser monitorado, isso é um fato, mas como podemos ir além da monitoração? Simples, com observability e um bom APM (Application Performance Managament).

Observability te da a capacidade de enxergar uma infinidade de pontos de seus sistemas baseado em diversas métricas, o mais importante aqui é conseguir identificar e criar alertas que te avisem sobre problemas que estão acontecendo e também o comportamento que estão tomando, com isso você ganha a um poder de resposta e predição de problemas, atuando antes que uma degradação ou paralização aconteça, impactando assim a disponibilidade de seu ambiente.

Instrumentalizar seu sistema o auxilia a ter tempestividade para encontrar o RCA (Root Cause Analysis) de um determinado problema que esteja acontecendo. Falamos em tempestividade pois a visibilidade tanto da integração entre os sistemas como da comunicação interna entre os componentes da infraestrutura fica muito mais clara e gráfica, diminuindo muito o tempo de analise e identificação de um problema e aumenta a assertividade nas ações de resolução deste.

A instrumentalização também traz a capacidade de inserir feature flags e outros mecanismos que dão a capacidade de ligar e desligar funcionalidades de seu sistema como a utilização de replicas de leitura de um banco de dados, por exemplo.

Ter uma KB (Knowledge Base) bem construída com procedimentos minimamente bem descritos pode ajudar os times a atuar sem a necessidade de um DBA, como times de operação por exemplo e desta forma o DBRE consegue continuar atuando em melhorias e automações que melhorem ainda mais a disponibilidade dos ambientes de bancos de dados.

Auto remediação

A capacidade de se auto regenerar de um problema é uma capacidade que poucos tem, para sistemas e infraestrutura não é diferente. Construir sistemas que conseguem contornar adversidades de forma automática é uma arte que pouco se vê ainda em tempos atuais e que as grandes de tecnologia procuram incessantemente.

Investir em automação de verdade, indo além do script, trabalhando em conjunto com os sistemas citados anteriormente como APM’s, Observabilidade, CMDB e outros podemos desenvolver mecanismos que aprendem com as mudanças de comportamento constantes que acontecem durante a utilização dos softwares para tomadas de decisão cada vez mais precisas, transformando ambientes de bancos de dados cada vez mais resilientes e robustos.

Chegar até aqui envolve uma grande maturidade e conhecimento do sistema administrado, da stack que esta executando as aplicações, desta forma, o desenvolvimento de uma auto remediação deve ser considerado apenas após a estabilização e experiencia adquiridas no tempo.

Conclusão

Bom gente, chega ao fim aqui este resumo do primeiro capitulo do livro sobre os pontos que estão descritos direta ou indiretamente neste, meu intuito aqui é tentar trazer um pouco do meu dia a dia atuando nesta função que não é muito diferente de um DBA tradicional mas que vejo muitos DBAs não conhecendo muitas dessas práticas descritas acima.

Se você já atua assim mas não se considera um DBRE, não tem problema, é mais uma buzz word neste mundo infinito de jargões tecnológicos. Se ficaram com alguma dúvida ou se gostariam de discutir algum ponto acima, fiquem a vontade de colocar um comentário ou enviar um e-mail que terei o prazer imenso de responder.

Este ano serei muito mais frequente aqui no blog, então fique ligado(a)!

Vlw galera! Até o próximo post!

Comece pela base

Fala galera! Mais de 1 ano desde que criei este blog e não parei para escrever aqui, bora começar então, espero ser bem ativo por aqui trazendo muito conteúdo técnico e de carreira para vocês!

Conforme descrevi em meu primeiro post aqui no blog, comecei cedo na área de tecnologia, por volta de 2005 quando tinha apenas 15 anos. Hoje, com mais do dobro disso, quero passar para vocês um pouco do que foi essencial para meu inicio lá atrás e me acompanha até hoje na profissão: a base!

O que eu tenho mais visto nesses últimos tempos tem sido empresas e\ou profissionais da área vendendo cursos com discurso: faça este curso e esteja preparado para o mercado de trabalho! (Aqueles cursos de 6 meses que prometem salários de 5 mil reais pra cima)

Serei um pouco repetitivo mas acredito que será importante para o contexto. Quando fiz meu primeiro curso na área que foi Montagem e Manutenção de Microcomputadores no SENAI eu me apaixonei já no inicio, eu sou uma pessoa muito visual e tátil, gosto de pegar nas coisas (Por isso gosto de livros em papel, por exemplo) e por conta disso eu fiquei deslumbrado em abrir um gabinete de computador, tocar em uma placa mãe, memória, disco rígido, etc. Ali eu não só aprendi a montar e desmontar um computador (Coisa que faço até hoje, pra mim e para minha familia e amigos) mas também a entender como um computador funciona, que não existia só Windows como Sistema Operacional, o que era Linux, softwares Open Source, redes de computadores e muitas outras matérias fundamentais.

Depois disso minha curiosidade aflorou, comecei a montar e desmontar meu próprio computador, formatar, usar outros sistemas operacionais e a pesquisar coisas relacionadas na internet, experimentar coisas novas. Com 17 anos comecei a fazer Técnico em Informática em uma escola próxima de casa e la aprendi e reforcei muitas outras matérias base como Sistemas Operacionais, Redes de Computadores, Lógica de Programação, Matemática e Estatística, Inglês Básico, Elétrica, etc. Pouco depois comecei a fazer cursos profissionalizantes fornecidos pelo CIEE e o principal foi o preparatório para a certificação CCNA da CISCO. Neste curso aprendi toda a base de Redes que mais uma vez, carrego até hoje em minha carreira principalmente depois do surgimento das Clouds Publicas.

Praticamente junto deste curso da Cisco iniciei na graduação de Engenharia da Computação e logo depois comecei a estagiar na área e sabe quais eram minhas principais funções?

  • Montagem e Manutenção de Notebooks, Computadores e Servidores;
  • Instalação e Configuração de Sistemas Operacionais Windows XP, Vista, Server NT, 2000, 2003 e 2008;
  • Montagem e Configuração de Redes;
  • Instalação e Configuração Sistemas Java e .Net\C#;
  • Instalação e Configuração de Bancos de Dados SQL Server;
  • Desenvolvimento de scripts de atualização e configuração.

Vejam, tudo que estudei e me preparei desde lá de trás eu utilizei em meu primeiro emprego na área e pasmem, eu uso até hoje! Dei todo esse contexto para vocês para elencar algumas matérias que acredito ser essenciais para todo profissional de Tecnologia da Informação, sendo você de Infraestrutura\Operação, Desenvolvedor de Software, Administrador de Banco de Dados (Assim como eu) e outras áreas:

Redes de Computadores

Não importa de qual área você seja, sério, não importa, você PRECISA saber o que é um IP, um protocolo TCP\UDP, Modelo OSI, calcular uma sub-rede, domínios de broadcast e rede, tabela de roteamento. Essas são algumas coisas que toda pessoa de tecnologia precisaria aprender, por que? Por que tudo trafega pela rede! Quase toda aplicação existente atualmente trabalha na internet ou no mínimo em rede, logo, você precisa saber qual o tamanho do pacote que sua aplicação trafega pois banda de internet ainda é limitada, talvez com o 5G veremos menos problemas de aplicativos de celular quebrando por não ter banda o suficiente para trafegar seus pacotes, porém, ainda sim será importante este tipo de noção pois isso impactará diretamente na performance da sua aplicação\aplicativo.

Se você não for um Analista de Redes, que precisa saber de cabo a rabo como uma rede funciona, os conceitos básicos de rede iram te auxiliar muito a entender como toda sua infraestrutura esta funcionando, principalmente na Cloud, sério, a base de qualquer Cloud Computing é redes, você precisa começar pela rede. “A Will, más eu crio uma conta no Azure, AWS ou GCP e ela já me da toda rede pronta, eu já saio criando meus recursos sem precisar me preocupar com isso”. Verdade, de fato as principais Clouds te dão uma VPC (Virtual Private Cloud) já configurada, saindo para internet, já configurada. Para um ambiente de testes e laboratório, ou até mesmo para começar um pequeno negócio essa infra já criada vai te ajudar, mas não irá resolver se o negócio escalar muito, rapidamente você terá problemas como esgotamento de IP’s e conflitos de sub-rede por exemplo.

Tem uma máxima que vocês vão me escutar falar bastante aqui no Blog: Se esta fácil demais, esta errado! Mesmo que já esteja pronto é importante saber como funciona. Por isso estude Redes de Computadores, a base deste conhecimento vai te ajudar por toda sua vida em T.I.

Indico aqui o conteúdo que me ajudou lá atrás: Cisco e seu material para a certificação CCNA. Tem um livro muito bom sobre essa certificação em português escrito pelo Marco Aurélio Filippetti, CCNA 6.0 – Guia completo de estudo.

Lógica de Programação e Estrutura de Dados

Quando entrei na faculdade, ja no primeiro semestre eu peguei um exame, adivinhem em qual matéria? Sim, isso mesmo, Lógica de Programação. Grande parte da minha sala pegou, inclusive. Naquela época o termo DevOps estava nascendo, a AWS ainda estava começando com pouquíssimos serviços, o SysAdmin reinava, o Dev só codava e a vida seguia.

Por que citei toda essa história acima? Lembra que eu falei que sou uma pessoa de infra desde o principio? Então, eu nunca precisei tocar num código na vida, no curso técnico que eu havia feito não ensinaram lógica de programação, já saíram ensinando Visual Basic, arrastando caixinha, programando ações na tela sem explicar muito como tudo de fato funcionava e eu pouco me interessei na época também, falha minha, por que anos depois eu não teria pego exame em lógica, rsrs.

Mas hoje, mesmo ainda me considerando muito mais Ops do que Dev a lógica de programação me ajuda diariamente, seja para dar manutenção em uma Procedures ou Functions no banco de dados, seja para desenvolver scripts e Infra como Código ou até mesmo para ajudar os Devs a resolver algum problema na camada da aplicação a lógica de programação me acompanha. Esse livro aqui embaixo foi o que me salvou na aula de Lógica de Programação, depois de ler e praticar muito com ele eu e alguns de meus amigos conseguimos passar bonito:

Mas e a Estrutura de Dados que citei junto? Quando tive essa matéria na faculdade fiquei maravilhado! Por que é com ela que você aprende como alocar apenas os recursos que realmente precisam na memória, a desenvolver algoritmos de fila e pilhas e muitos outros conceitos avançados que todo o Dev precisaria saber. Naquela época os recursos de hardware eram limitados, não era comum ter mais de 4 ou 8GB de memória no servidor, as linguagens de programação mais conhecidas de hoje como Java e C# por exemplo, ainda tinham que ter suas configs otimizadas para não esbarrar na limitação de recursos para rodar, por conta disso um sistema bem construído em termos de utilização de recursos raramente dava problema de estouro de memória no servidor (Nossa! Quantas vezes me peguei reconfigurando JVM por causa de config errada de Tomcat ou sistemas estourando stack overflow).

Dois livros que aconselho a muitos lerem, mesmo quem já é mais experiente é o Algoritmos Teoria e Prática de Thomas H. Cormen e o A Common-Sense Guide to Data Structures and Algorithms, Second Edition: Level Up Your Core Programming Skills De Jay Wengrow.

Sistemas Operacionais

Neste tópico não me estenderei muito, mas quero citar Sistemas Operacionais aqui para que vocês possam entender que: Linux domina. “Mas Will, eu uso Windão\Mac e eles me atendem super bem, preciso migrar para o Linux?”, a resposta é simple: Não! O que eu quero passar aqui é que distribuições Linux dominam a camada de servidores hoje, ou seja, você esta ai construindo um sistema Java, Python, Javascript, Go, entre outros, o melhor ecosistema para executar suas aplicações é o Linux, seja por que ele é mais modular, aberto e aceita muitas alterações até a camada do seu Kernel.

Hoje falamos muito sobre containers e olha só, containers (na sua melhor forma) só rodam sobre o kernel Linux. Em algum outro post quero entrar mais a dentro em containers com foco em bancos de dados, por isso falaremos mais profundamente sobre containers depois blz?

Mas independente do S.O. com o qual você irá trabalhar busque entender mais profundamente como ele funciona, principalmente aquele para o qual você esta desenvolvendo sua aplicação, seja ele Windows, Linux, IOS, Android, Unix. Isso irá te ajudar em momentos de problema, quanto mais você mergulhar no sistema operacional melhor irá entender como as coisas funcionam.

Já para desenvolver, ai pode utilizar qualquer sistema operacional, qualquer um mesmo, todos tem seus prós e contras, escolha o que melhor te atende.

O canal do mestre Akita on Rails tem muitos vídeos fantasticos sobre os temas que comentei acima e muito mais, inclusive ele tem videos explicando as diferenças entre os principais S.Os de mercado hoje. Nessa lista ele reune os videos sobre Sistemas Operacionais:

Bancos de Dados

Por fim, mas não menos importante eu trago o tema ao qual venho me especializando e trabalhando nos últimos anos que é Banco de Dados.

Qualquer sistema que precisa armazenar dados de forma eficiente dependerá de um banco de dados, logo, saber linguagem SQL (Structured Query Language) é essencial.

O que mais vejo hoje são sistemas sendo desenvolvidos sobre ORM (Object-Reletional Mapping) para lidar com a camada de banco de dados sem precisar lidar com a linguagem SQL, ou seja, você esta lá mandando bronca no seu Javão, C Sharpzão, Pythtão, Javascriptzão e utiliza algumas bibliotecas para te ajudar a não lidar com banco de dados. Vou te contar duas máximas da vida de T.I.: “Se esta fácil, esta errado.” e ”Uma hora a conta chega.”.

“Nossa Will, mas meu código funciona, eu rodo em desenvolvimento e homologação e executa tudo muito rápido mas quando vai para produção não roda , o sistema cai, tudo fica lento”. Poise, tudo no começo é uma maravilha, por que? Por que você quase não tem dados, logo, tudo vai executar rápido, então se você não cria indices nas tabelas no dia 1, quando seu código vai para o ar, se você não se preocupa em saber como o seu ORM esta formando as queries, se você não se preocupa com a estrutura da suas tabelas, la na frente, daqui 3 ou 6 meses ou até mesmo 1 ou 2 anos, quando seu sistema do dia pra noite ficar lento e você ver que é o banco de dados, não culpe a tecnologia falando: ”Nossa, por que fui escolher um banco de dados relacional, deveria ter ido para o MongoDB ou Cassandra, lá eu não ia ter problemas” vai por mim, eu já ouvi muito isso, você não imagina como sua vida estaria hoje utilizando tecnologias NoSQL de forma errada, muito antes deste momento você já estaria chorando sentado num canto escuro.

Este é um tema que quero trazer para vocês com mais detalhes lá na frente pois toca em conceitos de escalabilidade e elasticidade de aplicações que são conceitos mais avançados e necessários quando já se sabe a volumetria (Número de usuários simultâneos, volume de acessos, etc.) ao qual seu sistema será submetido ou quando já esta se tendo problemas dessa natureza em seu ambiente.

O que eu quero passar aqui para vocês é que a camada de banco de dados precisa ser respeitada pois cedo ou tarde você terá problemas, seja você um Dev ou Ops e muitas vezes não terá um DBA por perto para ajudar, então ter os conceitos de banco de dados bem solidificados, saber qual é a melhor tecnologia para um determinado cenário sistemico, conhecer bem a linguagem SQL e conhecer bem uma tecnologia de banco de dados relacional é essencial para que você evite dores de cabeça futuras ou para te ajudar no troubleshooting da sua aplicação.

Trago aqui dois livros para quem esta começando e quer saber mais sobre a base deste tema que é Sistemas de Bancos de Dados e NoSQL Essencial: um Guia Conciso Para o Mundo Emergente da Persistência Poliglota.

Conclusão

Este foi um compilado de temas os quais eu me baseio desde o inicio da minha carreira e acredito fortemente que, seja você de Dev ou Ops, saber esses tópicos com certa profundidade irá te ajudar muito no seu dia a dia na área de tecnologia, se você mira se tornar um profissional mais completo e se destacar, vai por mim, aprenda e tenham essa base sólida, revisando constantemente esses tópicos.

Posso ter deixado algo passar, num futuro trago uma revisão deste post por que esse mundo é muito dinâmico, mas posso te dizer que desde o inicio da minha carreira à 15 anos atrás eu trago muito desses temas comigo até hoje e vejo que muitos não possuem essa base e muitas vezes acabam sofrendo para resolver determinados problemas.

No mais, qualquer dúvida, crítica (construtiva) e sugestões são extremamente bem vindas, este é o lugar aonde quero trazer minha experiencia, ensinar e aprender com todos vocês.

Desde já, muito obrigado! Tamo junto!

whoami

Apaixonado por tecnologia desde os 8 anos de idade quando teve contato com um Compaq Presario em 1998. Iniciou em TI aos 15 anos em um curso de Montagem e Manutenção de Microcomputadores pelo Senai onde definiu ali que cursaria Engenharia da Computação na Faculdade, 3 anos depois foi o que aconteceu. Aos 16 começou a dar aulas de Inclusão Digital para jovens de 16 a 20 anos e para idosos utilizando tecnologia Open Source com Kurumin 7 e aos 18 começou a estagiar em empresas com atividades focadas em Infraestrutura de Redes, Sistemas Operacionais e Servidores.

A 10 anos venho focando em tecnologias de bancos de dados de forma agnóstica já tendo administrado ambientes de pequeno e médio porte com MySQL, PostgreSQL, SQL Server e MongoDB. Atualmente trabalho como Administrador de Banco de Dados no maior banco da América Latina administrando ambientes de grande porte e altíssima criticidade, com foco em operações CloudOps e perfil SRE venho trabalhando com equipes de sustentação de banco de dados e Comunidade Cloud na fundação de ambientes de bancos de dados em Cloud Pública utilizando AWS.

Sou apaixonado pelo empreendedorismo, por conta disso ajudei a fundar duas empresas as quais sou ativo até hoje: Flapper e Data Tuning. Na Flapper fui incumbido de criar e gerenciar toda infraestrutura na empresa que desde sua fundação utiliza serviço da AWS, onde rodamos todos nossos produtos e serviços. Já na Data Tuning, como consultor, atuo como Cloud Solutions Architect em AWS e Azure com foco em tecnologias de bancos de dados sobre IaaS ou PaaS como SQL Server (Azure SQL, Hyperscale, Managed Instances, RDS, etc), PostgreSQL (Azure Database for, Hyperscale, RDS, Aurora, etc) e MySQL (Azure Database for, RDS, Aurora, etc).

Queria dedicar este blog a esse cara da foto, Marcel Inowe. Se hoje eu contribuo para comunidade, principalmente por meio de palestras é por conta deste cara. Com ele aprendo muito, seja performance em SQL Server, seja construindo uma palestra ou fazendo um bom churrasco, é o meu mestre e um amigo querido. Mestre, tamo junto sempre!

Este sou eu gente, neste blog você verá muito conteúdo sobre infraestrutura que é minha paixão, minha jornada aprendendo a codificar e distribuindo conteudo sobre bancos de dados que é minha especialidade bem como Cloud Coputing.

Fico aberto a críticas construtivas e melhorias sempre! Então se você achar qualquer erro ou algo no qual não concorda me mostre e vamos concertar juntos. Desde já agradeço a todos que acompanham este blog.

“Stay hungry, stay foolish”. Whole Earth Catalog