Entre para a LISTA VIP da Black Friday

00

DIAS

00

HORAS

00

MIN

00

SEG

Clique para saber mais
Alura > Cursos de DevOps > Cursos de Confiabilidade > Conteúdos de Confiabilidade > Primeiras aulas do curso Confiabilidade: garantindo o estado de integridade de sistemas através de logs

Confiabilidade: garantindo o estado de integridade de sistemas através de logs

Problemas de uma aplicação sem logs - Apresentação

Boas-vindas ao curso de Confiabilidade: garantindo o stado de Integridade de sistemas através de logs. Sou o Kleber Costa e serei o seu instrutor ao longo deste curso.

Este primeiro vídeo é introdutório, para você entender sobre o que se trata o curso e sobre o que será aplicado. Vamos ao sumário entender os tópicos deste vídeo.

Sumário

Vamos ao primeiro tópico. A aplicabilidade deste curso se destina às seguintes pessoas:

Aplicabilidade

Isto é, pessoas da área de tecnologia que tenham uma atuação técnica. Mesmo que você não esteja em uma dessas áreas, mas queira fazer parte de alguma delas, este curso vai agregar bastante conteúdo e te dar uma boa direção. Dado que este assunto é fundamental para o desenvolvimento de qualquer produto ou sistema.

Requisitos

É preciso que você possua conhecimentos básicos de Java, de Maven e Spring Boot. Se possuir conhecimento sobre Java e Maven, já vai conseguir lidar com Spring Boot com bastante facilidade.

É fundamental também, que você saiba como tratar estruturas condicionais. Saber o básico de Docker e já tê-lo instalado na sua máquina e, conhecer sobre o Docker Compose.

Se estiver usando o sistema operacional Windows, o executável do Docker já traz o Compose junto. Caso use o macOS ou Linux, é preciso que conheça o Docker voltado para o sistema operacional que possui na sua máquina. Isso porque há algumas mudanças de configurações, principalmente em arquivos do Docker Compose para o macOS.

Basicamente são esses os requisitos para você realizar este curso sem ter muitas dificuldades.

Ementa

No primeiro capítulo vamos apresentar os problemas de uma aplicação sem log e qual o impacto que isso gera em um time de desenvolvimento. Isso vai envolver termos como MTTD (Mean Time To Detect ou, em português Tempo "Médio de Detecção"), MTTR (Mean Time To Response ou, em português "Tempo Médio de Resposta") e análise de causa raiz.

Logo, vamos tratar de problemas que enfrentamos ao identificar incidentes ou situações de degradação que vão gerar incidentes. As dificuldades que possuímos em não ter registros específicos sobre os eventos da aplicação e como isso impacta no tempo de solução de um problema e, principalmente, como impacta na análise de causa raiz, no RCA.

No segundo capítulo, trataremos as boas práticas de Logging. Isto é, o que um log precisa para nos auxiliar nas dificuldades apresentadas no primeiro capítulo. Na terceira parte aprenderemos sobre os níveis de Log e sua hierarquia, como implementamos na prática. Como usufruímos desse tipo de organização hierárquica.

No quarto ponto, abordaremos sobre a refatoração, vamos reestruturar o nosso modelo de logs. Visto que neste ponto já teremos feito o log em stand out put, log em disco, trabalhado com políticas de empacotamento, compactação e rotação.

Por fim, reestruturaremos para um modelo de uma API, uma aplicação vai ser containerizada e escalada horizontalmente. Ou seja, não é recomendável escrever em disco, vamos refatorar tudo para deixar nesse modelo. Porém, não deixaremos de dar atenção para situações de ambientes legados, em que essa prática é comum.

Em seguida, entraremos na situação de tratamento de exceções com o log sendo utilizado. E também, no momento de responder um cliente com um código mais leve, não retornando um stack trace.

No último capítulo teremos um log estruturado em JSON — um formato amigável e espero por alguns softwares que operam como centralizadores de log e nos auxiliam no trabalho com os dados coletados. Ferramentas como o Elasticsearch, o Planck, o Grafana Loki, o Graylog e assim por diante.

O nosso objetivo é preparar essa aplicação para utilizarmos o Grafana Loki. Logo, no final do curso já estaremos com um log bem estruturado com uma aplicação tratada com log. Pronta para realmente centralizar os logs em uma aplicação, colhendo os benefícios dessa implementação.

Registro de eventos

É fundamental sabermos que o log é um evento. Isto é, quando nos referimos a registro de eventos significa que vamos realizar o armazenamento de uma informação. Esta precisa conter um nível de explicação que leve qualquer um que for fazer uma análise sobre o registro ao entendimento completo sobre o evento.

Definindo Logs:

Todo evento importante para a aplicação precisa ser registrado em forma de log. Logo, é necessário compreendermos o que um log oferece. É indispensável que ele possua data e hora do evento, que seja armazenado e tratado como registro imutável — visto que não vai nos auxiliar somente na hora de um troubleshooting, também é um registro auditável.

Isso significa que serve para uma auditoria, para responder por um questionamento legal, em alguns casos. Por isso, é importante que o log seja tratado, armazenado, que tenha uma política de retenção e que seja cultivado como registro imutável.

O log oferece rastreabilidade em termos de execução. Dado ser possível, através de um log estruturado, entender qual o caminho de execução de uma funcionalidade nas regras de negócio. Afinal, quando chamamos a rota, qual o caminho que a aplicação percorre para processar determinada requisição? É o log que entrega isso.

O nível de severidade que o evento possui é outra característica mandatória do log. Isso para informar se é um evento crítico, um erro, um aviso ou é só uma informação. É possível trabalhar com o nível para depurar a aplicação, realizar um debug ou fazer uma rastreabilidade no beach, usando um trace.

Um trace em termos de log, não confunda com rastreamento pilar da observabilidade.

Agora que já vimos o que um log precisa oferecer, vamos aprender o que ele deve possuir.

Log devem possuir:

O primeiro ponto é possuir um código do evento executado, o segundo é ter uma mensagem personalizada em alto nível. Este para que a pessoa escalada para dar suporte a produção, consiga entender de uma forma simples o que aconteceu.

A condição tratada no caso de uma exceção e, também, um período de retenção especificado. Seja para um log escrito em disco, isto é, para um armazenamento em uma aplicação ou software de centralização de logs.

Esses são os aspectos que devem constituir um log.

Vamos entender agora qual o ambiente do curso. É necessário que você tenha o Docker com o Docker Compose instalado. O Docker vai ser responsável por cuidar de um container, um database. Sendo a dependência de conexão, a camada de persistência de dados.

A aplicação é em Java 11, é preciso que tenha o OpenJDK 11 ou outra versão do Java 11 instalado e o Maven também. Consumiremos essa aplicação usando o Postman, e vou usar a IDE Eclipse, mas você pode usar a que preferir.

A nossa aplicação já possui uma camada de observabilidade implantada. Ela tem o Micrometer, que serve para a exposição de métricas, logo é um facade que funciona pegando as informações do Actuator. Este é a camada de externalização de métricas da JVM (Java Virtual Machine ou, em português "Máquina Virtual JAVA").

Vamos configurar de forma correta o SLF4J e o Logback, recursos nativos do Spring Boot, para termos uma camada madura de logs. Com logs estruturados e com as regras de negócio da aplicação implementadas da forma correta. Isso para que futuramente seja possível centralizar os logs no Grafana Loki.

Esse é o nosso objetivo, basicamente esse é o ambiente do curso.

São essas as informações que queria passar para vocês neste vídeo, espero que gostem e que este curso agregue valor na sua trajetória. Te vejo no próximo vídeo!

Problemas de uma aplicação sem logs - Configurando o ambiente #1

Neste vídeo vamos configurar o ambiente, gostaria de já informar que utilizo o Linux. É importante que na plataforma, em "Downloads de arquivos", você baixe o arquivo .zip com o conteúdo da aula e o descompacte na sua máquina.

Após descompactar, teremos o diretório api-logs, clicaremos duas vezes nele com o botão direito do mouse. Dentro desse diretório, encontraremos alguns outros diretórios, que são:

O logs, o postgres, em que estão os arquivos necessários para o build do container Postgres, e o docker-compose.yml, em que está o código responsável para que o Docker consiga buildar o container do Postgres e subir o database.

Em seguida, vamos abrir o Eclipse e após descompactar o pacote do arquivo .zip, é necessário importarmos o projeto. Para isso, do lado esquerdo da tela em "Package Explorer", clicaremos na terceira opção "Import Projects".

Vai abrir uma tela com o título "Import" em que selecionaremos a pasta Maven, vai ser exibido algumas outras opções em que escolheremos a "Existing Maven Projects" (em português, "Projeto Maven Existente"). Em seguida, vamos clicar no botão "Next" na parte inferior central.

Na tela seguinte, com o título "Import Maven Projects", para passarmos onde está o projeto, selecionaremos o primeiro botão "Browse" do lado direito, o path (em português, "caminho").

Novamente vai exibir uma tela com o título "Select Root Folder". Clicaremos na pasta api-logs, em seguida, no diretório logs, dentro deste temos um arquivo pom.xml que vai ser identificado pelo Eclipse como pertencente a um projeto Maven. No canto superior direito da tela, vamos selecionar o botão "Open".

Note que na tela "Import Maven Projects" em "Projects" já foi encontrado o pom.xml. No canto inferior direito dessa tela, pressionaremos o botão "Finish". Com isso, vai retornar para o Eclipse. Perceba que do lado esquerdo, em "Package Explorer", vai ser exibida a pasta logs.

Vamos clicar na seta ao lado esquerdo da pasta logs para expandir. O Eclipse pode demorar um pouco para baixar as dependências e configurar o projeto como Maven. No meu caso, já apareceu, visto que o projeto já estava importado e eu o excluí para refazer o processo de importação com vocês.

O ideal é aguardar um pouco para baixar as dependências. Inicialmente, perceba que no seu pode não ter todos esses arquivos configurados, igual consta no meu, dado que são gerados conforme as dependências do Maven são baixadas e configuradas.

Após esperar as dependências serem baixadas, em "src > pom.xml", na pasta pom.xml, clicaremos com o botão direito do mouse. Vai abrir um menu e nele selecionaremos "Maven" e, em seguida, "Update Project". Exibirá uma tela com o título "Update Maven Project", isso é só por descargo de consciência, logo apenas pressionaremos o botão "Ok", do lado inferior direito.

Dessa forma, já estamos com o projeto configurado. Temos um projeto usando Maven, separado em pacotes, conforme podemos observar na "src/main/java". Esse projeto é uma API Rest, que possui a funcionalidade de gravar as informações dos novos cursos na Alura. Como funciona?

Vai ter um curso novo que vai ser rodado, executado e construído. Precisamos armazenar as informações desse curso como, quem é o instrutor(a), quais os conteúdos, quais as dependências, a carga horária, a categoria que se encaixa. Basicamente, é isso que faz essa API Rest.

Expandindo a pasta "src/main/java", clicaremos na primeira opção "br.com.alura.logs", vai ser exibido o arquivo "CursoApplication.java". Este é o pacote principal da aplicação, logo é essa a classe em que temos que executar para que a aplicação suba. Ela que é o application do Spring Boot.

Porém, antes de fazermos isso, é necessário subirmos o banco de dados. Para tal, abriremos o terminal e executaremos o comando cd api-logs/, para entrarmos na pasta api-logs.

Comando para executar no terminal:

cd api-logs/

Lembrando que estou usando o Linux, por isso alguns comandos que usarei não se aplicam para você, caso esteja em um sistema operacional diferente. Se estiver utilizando o Windows, pegue a interface gráfica do Docker, sobe ele e garanta que o Docker Compose já esteja configurado. O executável do Windows no Docker, traz o Docker, o Docker Compose e o Kubernetes.

Logo, roda tudo nesse executável do Windows, você vai na interface gráfica e sobe o Docker, caso ele não esteja funcionando. Se estiver no macOS, o ideal é ler a documentação do Docker e do Docker Compose, garantir que estão instalados e subir o serviço no Mac ou configure para subir o start up do Mac. Isso é um pré-requisito do curso.

Voltando, dentro de api-logs vamos executar o comando de listagem. Note que temos o arquivo docker-compose.yml, ao rodarmos o cat docker-compose.yml serão exibidos alguns campos.

Comandos de listagem:

ls

Comando para exibir os campos:

cat docker-compose.yml

O campo "image" é um postgres, o "container_name" (nome do container) é um "database-api-cursos". Se for derrubado como consta em "restart" vai receber um "unless-stopped", portanto ele não sobe sozinho. Há motivos para ser desta forma. Em "healthcheck" temos o teste de healthcheck, não vou explicar esse campo por ser uma informação irrelevante neste curso.

Em "environment" temos as variáveis de ambiente. Quem é o usuário, qual a senha (password), qual o usuário que a aplicação vai usar para se conectar, dado que não é o root que se conecta na base de dados e sim o usuário Alura. Em seguida, a senha do usuário Alura e, por fim, o nome da base de dados que ele vai acessar no Postgres, chamada de "logsbd".

No campo "ports" temos a porta que vai ser rodada,vai ser executado um bind na porta da sua máquina. Logo, vai subir o container e vai ficar na porta 5432 e executar um bind na 5432 TCP (Transmission Control Protocol ou, em português "protocolo de Controle de Transmissão") da sua máquina.

Em seguida, em "volumes" são os volumes utilizados para inicialização de criação de tabelas, toda a parte de trabalho no Postgres.

No terminal, em data não pega nenhuma informação. Mas encontramos em data o diretório inicial que o Postgres vai usar quando ele popular a base de dados. Mas podemos ficar despreocupados, pois não é uma pasta em que precisaremos acessar.

Porém, dentro de db, se rodarmos o comando cat postgres/db/01-init.sh vai ser exibido um SQL feito para ser executado para criar o usuário e fazer as definições corretas para acessarmos a aplicação.

Comando para rodar no terminal.

cat postgres/db/01-init.sh

Para seguir, com o Docker instalado e rodando na máquina, no terminal rodaremos o comando docker-compose up. Isso para subirmos o container, pode demorar um pouco. Dado que é necessário ele montar e subir o container e baixar imagem.

Note que temos uma linha escrita "CREATE DATABASE", informando que foi criada a base de dados. Analisando mais um pouco, temos um log bem estruturado, com timestamp, com log code e uma mensagem direta e simples de entender.

2022-05-24 13:00:54.053 UTC [1] LOG: database system is shut down

Dessa forma, conseguimos compreender como é importante um log bem estruturado. Voltando para o Eclipse, vamos subir a aplicação selecionando o botão "Run" ("▶") no menu superior da tela.

Perceba que no console do Eclipse, na parte inferior, mostra que está subindo. Houve um problema, explodiu um exception com o log multiline, entre outros erros. Com isso, já entramos no conteúdo do curso. Partiremos do pressuposto que essa aplicação foi desenvolvida pelo seu time de desenvolvimento.

Analisando esse retorno, como você identificaria o problema? A aplicação não possui log definido. Para ir para a produção, essa aplicação precisa ter um log estruturado. Dado que, por padrão, se estiver usando sistem out em algum momento no Java, você já está logando.

Logo, já possui o necessário para realizar um log. Porém, nessa aplicação não temos um log bem estruturado, na verdade, nem uma configuração de uma API de log sendo utilizada.

Observando o retorno no console do Eclipse, temos uma linhando informando "FATAL: database "logs" does not exist". O "FATAL" é um nível de log disparado pela JVM ao encontrar um problema que impede que a aplicação suba.

Ao analisarmos no terminal, o container retornou a mesma informação: "FATAL: database "logs" does not exist". Foi o Postgres que devolveu esse log, isto é, tentaram acessar uma base de dados que não existe no Postgres.

Voltando para o Eclipse, lembra quando analisamos o Docker Compose a pouco tempo atrás? O nome da base de dados era "logsbd". Por isso, vamos à pasta src/main/resources para abrir a application.properties e corrigiremos o nome para "logsdb". Salvaremos o arquivo selecionando "Ctrl + S".

application.properties:

//código omitido

spring.datasource.url=jdbc:postgresql://localhost:5432/logsdb

//código omitido

Ao salvarmos, essa aplicação está com o DevTools configurado. Logo, o ideal é ela não subir novamente, visto que a aplicação nem chegou a subir. Mas se tivesse rodando, ela iria recomeçar na hora e conseguiríamos visualizar se está funcional.

Voltaremos para o arquivo CursoApplication.java e rodaremos a aplicação de novo, selecionando o botão "Run" na parte central da tela. Analisando no console, está aparentemente tudo subindo conforme o esperado. Repare que temos uma linha informando "Started CursoApplication", isso significa que foi startada.

Nesse primeiro contato, já começamos a entender a importância de um log nos informando os eventos que ocorrem na aplicação. Por esse vídeo é isso, no próximo vamos entender melhor essa aplicação e já começaremos a estruturar os logs. Até mais!

Problemas de uma aplicação sem logs - Configurando o ambiente #2

Neste vídeo vamos entendermos qual o trabalho de uma API, como ela espera uma requisição e como a consumimos.

Voltando ao Eclipse, essa API está em execução e podemos parar clicando no botão ⏹ no menu na parte superior do console. Depois, selecionaremos o botão com dois "❌" para limpar o console e rodaremos a aplicação novamente pressionando o botão de "Run" com o ícone ▶, na parte superior do Eclipse.

No pacote com o conteúdo da aula 1 há uma collection Alura Logs, vamos importá-la para o Postman. Dentro dessa collection temos alguns métodos, como POST, GET, PUT e DELETE. São operações permitidas por essa API.

Essa API realiza o registro dos cursos que serão construídos para a Alura. Do lado esquerdo do Postman, clicaremos na segunda opção GET, que possui a URI "http://localhost:8080/cursos". Sendo a rota chamada de "cursos" e a aplicação, por estar subindo localmente, "localhost:8080".

Para enviar um GET, pressionaremos o botão "Send" do lado direito da tela. Perceba que em "Body" é retornado um conteúdo com os campos vazios, foi entregue apenas o conteúdo paginado, já que existe uma paginação configurada. Isso porque não inserimos nenhum registro na base de dados.

Selecionando a primeira opção POST com a URI "http://localhost:8080/cursos", já temos um registro em "Body" preparado para ser inserido. A API espera um objeto JSON que possua os campos: "numeroMatricula", "numeroCurso", "nomeCurso", "categoriaCurso", "preRequisito", "nomeProfessor" e "periodoCurso". O JPA (Java Persistence API ou, em português "API de persistência Java") que trata da persistência desses campos, a aplicação já está configurada com as regras de negócio.

Registro do POST com a URI "http://localhost:8080/cursos":

{
    "numeroMatricula": "230419831",
    "numeroCurso": "1403198901",
    "nomeCurso": "Instrumentação e Refatoração de Logs",
    "categoriaCurso": "Site Reliability Engineer",
    "preRequisito": "Formação DevOps",
    "nomeProfessor": "Kleber Costa",
    "periodoCurso": "10 horas"
}

Como membro do time de desenvolvimento dessa API, a nossa função é configurar a parte referente à logs que não foi estruturada.

Faremos a inserção selecionando o botão "Send" do verbo POST. Note que retornou um "Status: 201 Created", isso significa que o registro foi inserido na base. Se analisarmos em "Body" foi gerado um ID e um novo campo chamado de "dataInscrição", derivado do date do Java que faz esse papel de forma automática quando um registro é inserido na base.

Retorno no Postman do POST:

{
    "id": "af2287d5-46e0-44e4-8775-edce80febc4e",
    "numeroMatricula": "23041983",
    "numeroCurso": "1403198901",
    "nomeCurso": "Instrumentação e Refatoração de Logs",
    "categoriaCurso": "Site Reliability Engineer",
    "preRequisito": "Formação DevOps",
    "nomeProfessor": "Kleber Costa",
    "periodoCurso": "10 horas",
    "dataInscrição": "2022-05-25T02:28:49Z"
}

Vamos realizar mais uma inclusão, para isso alteraremos apenas o final dos campos "numeroMatricula" e "numeroCurso" para "02" e modificaremos algumas outras informações:

{
    "numeroMatricula": "230419832",
    "numeroCurso": "1403198902",
    "nomeCurso": "Instrumentação de uma API Spring",
    "categoriaCurso": "Site Reliability Engineer",
    "preRequisito": "Formação DevOps",
    "nomeProfessor": "Kleber Paiva",
    "periodoCurso": "12 horas"
}

Vamos clicar no botão "Send" para inserir. Perceba que voltou um "Status: 201 Created", com isso temos dois registros inseridos agora. Se analisarmos o GET, selecionando a segunda opção do lado esquerdo da tela em "Alura Logs" e, em seguida, clicando no botão "Send". Perceba que é possível visualizarmos o retorno dessas inclusões que acabamos de realizar. Você pode incluir quantos cursos quiser e, inclusive, usar na quarta opção temos um GET paginado que retorna manipulando a parte de paginação.

Na terceira opção do lado esquerdo da tela temos também o PUT para realizar um update em um registro. Perceba que já temos alguns dados pré configurados, vamos alterar novamente alguns campos e remover o "dataInscrição":

Verbo PUT:

{
    "numeroMatricula": "2304198333",
    "numeroCurso": "1403198933",
    "nomeCurso": "Pilar número um da observabilidade",
    "categoriaCurso": "Site Reability Enginner",
    "preRequisito": "Formação DevOps",
    "nomeProfessor": "Kleber de Paiva Siqueira Costa",
    "periodoCurso": "20 horas"
}

Agora, precisamos passar o ID do recurso que queremos atualizar. Voltando para o GET copiaremos o segundo ID "5d019c2e-7b95-40a0-82d3-cfd0579a3120" e colaremos na rota do verbo PUT.

URI PUT:

http://localhost:8080/cursos/5d019c2e-7b95-40a0-82d3-cfd0579a3120

Em seguida, clicaremos no botão "Send" do lado direito da tela e, é retornado um "Status: 200 Ok". Isso significa que fizemos um update. Retornando para o verbo GET selecionaremos o botão "Send" e, note que na resposta é exibido o curso "Pilar número um da observabilidade".

Agora, testaremos a remoção de um registro. Para tal, pegaremos o ID "af2287d5-46e0-44e4-8775-edce80febc4e" em GET, selecionaremos a penúltima opção do método do lado esquerdo da página, que fica o DELETE e vamos passar esse ID na rota. Em seguida, clicaremos no botão "Send" do lado direito da tela. Isso nos retorna a mensagem "Curso excluído com sucesso!".

URI DELETE

http://localhost:8080/cursos/af2287d5-46e0-44e4-8775-edce80febc4e

Se formos na aba do GET e enviarmos novamente, temos o único registro que sobrou como retorno. No POST clicaremos no botão "Send", para enviar novamente o registro e incluiremos mais um com as seguintes alterações:

POST:

{
    "numeroMatricula": "2304198334",
    "numeroCurso": "1403198934",
    "nomeCurso": "Instrumentação de uma API Spring",
    "categoriaCurso": "Site Reability Enginner",
    "preRequisito": "Formação DevOps",
    "nomeProfessor": "Kleber Costa",
    "periodoCurso": "12 horas"
}

Para analisar se conseguimos gerar o mesmo registro duas vezes, vamos clicar duas vezes no botão "Send". O retorno foi a mensagem "O número da matrícula do curso já está em uso", isso significa que existe uma validação de campos.

Agora, isso gerou algum log? É importante entendermos se no Eclipse em CursoApplication, está sendo gerado alguma camada de log. Não, e esse é o problema que queremos resolver. Precisamos que esses eventos sejam registrados.

No Postman geraremos um caso que retorna um erro. Vamos ao verbo PUT, na URI temos o ID "5d019c2e-7b95-40a0-82d3-cfd0579a31" que alteraremos o final para "66". Em seguida, clicaremos no botão "Send". Voltou a mensagem "Curso não encontrado!" com um status "404 Not Found", não é um erro mas é uma informação que já deveria ter um evento correspondente.

Na aba do método DELETE removeremos as letras "c4e" de "af2287d5-46e0-44e4-8775-edce80febc4e", para tentarmos deletar um ID que não existe. Também retornou a mensagem "Curso não encontrado!" com um status "404 Not Found".

Se observarmos a aplicação no Eclipse, vamos perceber que nenhum desses eventos gerou um log. Conduziremos essa aplicação a casos que geram erro 500 também, para validarmos que não há nenhuma informação sendo logada.

Encerramos a aula por aqui. O objetivo deste vídeo é que você possa popularizar a base de dados e conhecer as operações que podemos executar com a API. É importante entendermos que ao fazer uma atualização o que é validade é o número da matrícula e do curso, logo não devem ser iguais. Sempre que um curso é atualizado, é gerado um novo número de matrícula e curso, mas o ID segue sendo o mesmo, sendo imutável

Até o próximo vídeo!

Sobre o curso Confiabilidade: garantindo o estado de integridade de sistemas através de logs

O curso Confiabilidade: garantindo o estado de integridade de sistemas através de logs possui 206 minutos de vídeos, em um total de 45 atividades. Gostou? Conheça nossos outros cursos de Confiabilidade em DevOps, ou leia nossos artigos de DevOps.

Matricule-se e comece a estudar com a gente hoje! Conheça outros tópicos abordados durante o curso:

Aprenda Confiabilidade acessando integralmente esse e outros cursos, comece hoje!

Conheça os Planos para Empresas