A Voyage AI se une ao MongoDB para impulsionar aplicativos de AI mais precisos e confiáveis no Atlas.

Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Desenvolvedor do MongoDB
Centro de desenvolvedores do MongoDB
chevron-right
Produtos
chevron-right
Atlas
chevron-right

Pausar automaticamente clusters inativos

Brian Leonard10 min read • Published Jan 19, 2022 • Updated Sep 09, 2024
Atlas
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Artigo
star-empty
star-empty
star-empty
star-empty
star-empty
Alguns recursos mencionados abaixo serão descontinuados em 30, 2025 de setembro. Saiba mais.
Há alguns anos, escrevi um artigo sobre como pausar e/ou dimensionar clusters usando Atlas Triggers agendados. Este artigo representa uma reviravolta nesse conceito, adicionando uma preocupação que pausará clusters em toda a organização com base na inatividade. Especificamente, estou analisando o histórico deacesso ao banco de dados para determinar a atividade.
É importante observar esta limitação de registro:
Se um cluster experimentar um pico de atividade e gerar uma quantidade extremamente grande de mensagens de log, o Atlas poderá parar de coletar e armazenar novos logs por um período de tempo.
Portanto, esse script pode dar um falso positivo de que um cluster está inativo quando, na verdade, acontece exatamente o oposto. Dado, no entanto, que a intenção desse script é gerenciar ambientes inferiores, que não são de produção, não vejo os falsos positivos como uma grande preocupação.

Arquitetura

A implementação usa um trigger. O trigger chama uma série de Atlas App Services, que usam as Atlas API para iterar sobre os projetos da organização e seus clusters associados, testando a inatividade do cluster (conforme explicado na introdução) e, finalmente, pausando o cluster se ele estiver realmente inativo.
Arquitetura

Chaves de API

Para chamar as API Administrativas do Atlas, primeiro você precisará de uma API Key com o roleProprietário da Organização. As API Keys são criadas no Access Manager, que você encontrará no menu Organização à esquerda:
Access Manager
ou na barra de menu na parte superior:
Access Manager
Organization Access Manager
Clique em Criar chave de API. Dê uma descrição à chave e certifique-se de definir as permissões para Proprietário da organização:
Criar chave API
Ao clicar em Avançar, você verá suas chaves Públicas e Privadas. Salve sua chave privada , pois o Atlas nunca mais a mostrará a você.
Como uma camada adicional de segurança, você também tem a opção de definir uma lista de acesso IP para essas chaves. Estou pulando esta etapa, para que minha chave funcione de qualquer lugar.
Criar chave API

Implantação

Crie um projeto para automação

Como essa solução funciona em toda a sua organização do Atlas, eu gosto de hospedá-la em seu próprio projeto do Atlas dedicado.
Criar projeto

Criar um aplicativo do App Services.

O Atlas App Services fornece um poderoso backend de desenvolvimento de aplicativos como serviço. Para começar a usá-lo, basta clicar na aba App Services.
Serviços de App
Você verá que o App Services oferece vários modelos para você começar. Para esse caso de uso, basta selecionar a primeira opção para Criar seu próprio aplicativo:
Bem-vindo ao Atlas App Services
Em seguida, você verá opções para vincular uma fonte de dados, nomear seu aplicativo e escolher um modelo de implantação. A iteração atual desse utilitário não usa uma fonte de dados, então você pode ignorar essa etapa (o App Services criará um cluster gratuito para você). Você também pode deixar o modelo de implantação como padrão (Global), a menos que queira limitar o aplicativo a uma região específica.
Dei o nome de Atlas Cluster Automation ao aplicativo:
Bem-vindo ao Atlas App Services
Nessa situação, você tem duas opções:
  1. Basta importar o aplicativo App Services e ajustar qualquer uma das funções para atender às suas necessidades.
  2. Crie o aplicativo do zero (pule para a próxima seção).

Importar opção

Etapa 1: armazene a chave secreta da API.

A extração depende da chave secreta da API, portanto, a importação falhará se ela não for configurada previamente.
Use o menuValues à esquerda para Criar um Segredo chamado AtlasPrivateKeySecret contendo a chave privada que você criou anteriormente (o segredo não está entre aspas):
Valor secreto

Etapa 1: Instale a CLI do Atlas App Services (realm-cli)

O Realm CLI está disponível em npm. Para instalar a versão 2 da Realm CLI no seu sistema, verifique se o Node.jsestá instalado e execute o seguinte comando no shell:
npm install -g mongodb-realm-cli

Etapa 2: extraia o arquivo do aplicativo

Baixe e extraia o AtlasClusterAutomation.zip.

Etapa 3: faça login no Atlas

Para configurar seu aplicativo com o realm-cli, você deve se conectar ao Atlas usando suas chaves de API:
1✗ realm-cli login --api-key="<Public API Key>" --private-api-key="<Private API Key>"
2Successfully logged in

Etapa 4: Obtenha a IDdo aplicativo Atlas App Services

Selecione o App Settings e copie o ID do seu aplicativo:
ID do aplicativo
ID do aplicativo

Etapa 5: importe o aplicativo

Execute o seguinte realm-cli push comando no diretório em que você extraiu a exportação:
1realm-cli push --remote="<Your App ID>"
2
3...
4A summary of changes
5...
6
7? Please confirm the changes shown above Yes
8Creating draft
9Pushing changes
10Deploying draft
11Deployment complete
12Successfully pushed app up:
Após a importação, substitua o `AtlasPublicKey' pelo valor da chave pública da sua API.
Valor da chave pública

Revise o aplicativo importado

O aplicativo importado inclui 5 Atlas Functions:
Funções
E o Trigger Agendado que chama a funçãopausaInactiveClusters:
Acionadores
O trigger está programado para disparar a cada 30 minutos. Observe que a funçãopausaClusters que o trigger chama atualmente apenas registra a atividade do cluster. Isso é para que você possa monitorar e verificar se a função se comporta conforme o desejado. Quando estiver pronto, descomente a linha que chama a funçãopausaCluster:
1 if (!is_active) {
2 console.log(`Pausing ${project.name}:${cluster.name} because it has been inactive for more then ${minutesInactive} minutes`);
3 //await context.functions.execute("pauseCluster", project.id, cluster.name, pause);
Além disso, a funçãopauseClusters pode ser configurada para excluir projetos (como os dedicados a cargas de trabalho de produção):
1 /*
2 * These project names are just an example.
3 * The same concept could be used to exclude clusters or even
4 * configure different inactivity intervals by project or cluster.
5 * These configuration options could also be stored and read from
6 * and Atlas database.
7 */
8 excludeProjects = ['PROD1', 'PROD2'];
Agora que você revisou o rascunho, como etapa final, implante o aplicativo do App Services.
Revisar rascunho e implantar
Revisar rascunho e implantar

Opção "construa você mesmo"

Para entender o que está incluído no aplicativo, estas são as etapas para criá-lo do zero.

Etapa 1: armazene as chaves de API

As funções que precisamos criar chamarão a API de administração do Atlas, portanto, precisamos armazenar nossas chaves públicas e privadas de API, o que vamos fazer usando Valores e segredos. O código de amostra que forneço referência a esses valores como AtlasPublicKey e AtlasPrivateKey, portanto, use esses mesmos nomes, a menos que queira alterar o código em que eles são referenciados.
Você encontrará Values no menu Build:
Valores
Primeiro, crie um Valor, AtlasPublicKey, para sua chave pública (observe que a chave está entre aspas):
Valor da chave pública
Crie um Segredo, AtlasPrivateKeySecret, contendo sua chave privada (o segredo não está entre aspas):
Valor secreto
O Segredo não pode ser acessado diretamente, portanto, crie um segundo Valor, AtlasPrivateKey, que se vincule ao segredo:
Valor da chave privada
Revisar rascunho e implantar

Etapa 2: crie as funções

As quatro funções que precisam ser criadas são auto-explicativas, então não vamos fornecer um monte de explicações adicionais aqui.

getProjects

Essa função autônoma pode ser testada no console do App Services para ver a lista de todos os projetos da sua organização.
1/*
2 * Returns an array of the projects in the organization
3 * See https://docs.atlas.mongodb.com/reference/api/project-get-all/
4 *
5 * Returns an array of objects, e.g.
6 *
7 * {
8 * "clusterCount": {
9 * "$numberInt": "1"
10 * },
11 * "created": "2021-05-11T18:24:48Z",
12 * "id": "609acbef1b76b53fcd37c8e1",
13 * "links": [
14 * {
15 * "href": "https://cloud.mongodb.com/api/atlas/v1.0/groups/609acbef1b76b53fcd37c8e1",
16 * "rel": "self"
17 * }
18 * ],
19 * "name": "mg-training-sample",
20 * "orgId": "5b4e2d803b34b965050f1835"
21 * }
22 *
23 */
24exports = async function() {
25
26 // Get stored credentials...
27 const username = await context.values.get("AtlasPublicKey");
28 const password = await context.values.get("AtlasPrivateKey");
29
30 const arg = {
31 scheme: 'https',
32 host: 'cloud.mongodb.com',
33 path: 'api/atlas/v1.0/groups',
34 username: username,
35 password: password,
36 headers: {'Content-Type': ['application/json'], 'Accept-Encoding': ['bzip, deflate']},
37 digestAuth:true,
38 };
39
40 // The response body is a BSON.Binary object. Parse it and return.
41 response = await context.http.get(arg);
42
43 return EJSON.parse(response.body.text()).results;
44};

getProjectClusters

Depois quegetProjects é chamado, o trigger itera sobre os resultados, passando o projectId para essa funçãogetProjectClusters.
Para testar essa função, você precisa fornecer um projectId. Por padrão, o console fornece "Hello world!", portanto, testo essa entrada e forneço alguns valores padrão para facilitar o teste.
1/*
2 * Returns an array of the clusters for the supplied project ID.
3 * See https://docs.atlas.mongodb.com/reference/api/clusters-get-all/
4 *
5 * Returns an array of objects. See the API documentation for details.
6 *
7 */
8exports = async function(project_id) {
9
10 if (project_id == "Hello world!") { // Easy testing from the console
11 project_id = "5e8f8268d896f55ac04969a1"
12 }
13
14 // Get stored credentials...
15 const username = await context.values.get("AtlasPublicKey");
16 const password = await context.values.get("AtlasPrivateKey");
17
18 const arg = {
19 scheme: 'https',
20 host: 'cloud.mongodb.com',
21 path: `api/atlas/v1.0/groups/${project_id}/clusters`,
22 username: username,
23 password: password,
24 headers: {'Content-Type': ['application/json'], 'Accept-Encoding': ['bzip, deflate']},
25 digestAuth:true,
26 };
27
28 // The response body is a BSON.Binary object. Parse it and return.
29 response = await context.http.get(arg);
30
31 return EJSON.parse(response.body.text()).results;
32};

clusterIsActive

Essa função contém a lógica que determina se o cluster pode ser pausado.
A maior parte do trabalho nessa função é manipular o carimbo de data/hora no log de acesso ao banco de dados para que ele possa ser comparado com a hora atual e a janela de lookback.
Além de retornar verdadeiro (ativo) ou falso (inativo), a função registra suas descobertas, por exemplo:

Checking if cluster 'SA-SHARED-DEMO' has been active in the last 60 minutes
1 Wed Nov 03 2021 19:52:31 GMT+0000 (UTC) - job is being run
2 Wed Nov 03 2021 18:52:31 GMT+0000 (UTC) - cluster inactivity before this time will be reported inactive
3 Wed Nov 03 2021 19:48:45 GMT+0000 (UTC) - last logged database access
4Cluster is Active: Username 'brian' was active in cluster 'SA-SHARED-DEMO' 4 minutes ago.
Como getClusterProjects, há um bloco que você pode usar para fornecer alguns ID de projeto de teste e nomes de cluster para facilitar o teste no console do App Services.
1/*
2 * Used the database access history to determine if the cluster is in active use.
3 * See https://docs.atlas.mongodb.com/reference/api/access-tracking-get-database-history-clustername/
4 *
5 * Returns true (active) or false (inactive)
6 *
7 */
8exports = async function(project_id, clusterName, minutes) {
9
10 if (project_id == 'Hello world!') { // We're testing from the console
11 project_id = "5e8f8268d896f55ac04969a1";
12 clusterName = "SA-SHARED-DEMO";
13 minutes = 60;
14 } /*else {
15 console.log (`project_id: ${project_id}, clusterName: ${clusterName}, minutes: ${minutes}`)
16 }*/
17
18 // Get stored credentials...
19 const username = await context.values.get("AtlasPublicKey");
20 const password = await context.values.get("AtlasPrivateKey");
21
22 const arg = {
23 scheme: 'https',
24 host: 'cloud.mongodb.com',
25 path: `api/atlas/v1.0/groups/${project_id}/dbAccessHistory/clusters/${clusterName}`,
26 //query: {'authResult': "true"},
27 username: username,
28 password: password,
29 headers: {'Content-Type': ['application/json'], 'Accept-Encoding': ['bzip, deflate']},
30 digestAuth:true,
31 };
32
33 // The response body is a BSON.Binary object. Parse it and return.
34 response = await context.http.get(arg);
35
36 accessLogs = EJSON.parse(response.body.text()).accessLogs;
37
38 now = Date.now();
39 const MS_PER_MINUTE = 60000;
40 var durationInMinutes = (minutes < 30, 30, minutes); // The log granularity is 30 minutes.
41 var idleStartTime = now - (durationInMinutes * MS_PER_MINUTE);
42
43 nowString = new Date(now).toString();
44 idleStartTimeString = new Date(idleStartTime).toString();
45 console.log(`Checking if cluster '${clusterName}' has been active in the last ${durationInMinutes} minutes`)
46 console.log(` ${nowString} - job is being run`);
47 console.log(` ${idleStartTimeString} - cluster inactivity before this time will be reported inactive`);
48
49 clusterIsActive = false;
50
51 accessLogs.every(log => {
52 if (log.username != 'mms-automation' && log.username != 'mms-monitoring-agent') {
53
54 // Convert string log date to milliseconds
55 logTime = Date.parse(log.timestamp);
56
57 logTimeString = new Date(logTime);
58 console.log(` ${logTimeString} - last logged database access`);
59
60 var elapsedTimeMins = Math.round((now - logTime)/MS_PER_MINUTE, 0);
61
62 if (logTime > idleStartTime ) {
63 console.log(`Cluster is Active: Username '${log.username}' was active in cluster '${clusterName}' ${elapsedTimeMins} minutes ago.`);
64 clusterIsActive = true;
65 return false;
66 } else {
67 // The first log entry is older than our inactive window
68 console.log(`Cluster is Inactive: Username '${log.username}' was active in cluster '${clusterName}' ${elapsedTimeMins} minutes ago.`);
69 clusterIsActive = false;
70 return false;
71 }
72 }
73 return true;
74
75 });
76
77 return clusterIsActive;
78
79};

pauseCluster

Por fim, se o cluster estiver inativo, passamos o ID do projeto e o nome do cluster para pauseCluster. Essa função também pode retomar um cluster, embora esse recurso não seja utilizado neste caso de uso.
1/*
2 * Pauses the named cluster
3 * See https://docs.atlas.mongodb.com/reference/api/clusters-modify-one/
4 *
5 */
6exports = async function(projectID, clusterName, pause) {
7
8 // Get stored credentials...
9 const username = await context.values.get("AtlasPublicKey");
10 const password = await context.values.get("AtlasPrivateKey");
11
12 const body = {paused: pause};
13
14 const arg = {
15 scheme: 'https',
16 host: 'cloud.mongodb.com',
17 path: `api/atlas/v1.0/groups/${projectID}/clusters/${clusterName}`,
18 username: username,
19 password: password,
20 headers: {'Content-Type': ['application/json'], 'Accept-Encoding': ['bzip, deflate']},
21 digestAuth:true,
22 body: JSON.stringify(body)
23 };
24
25 // The response body is a BSON.Binary object. Parse it and return.
26 response = await context.http.patch(arg);
27
28 return EJSON.parse(response.body.text());
29};

pauseInactiveClusters

Esta função será chamada por um trigger. Como não é possível passar um parâmetro para um gatilho agendado, ele usa uma janela de retrospectiva codificada de 60 minutos que você pode alterar para atender às suas necessidades. Você pode até armazenar o valor em um banco de dados do Atlas e construir uma interface do usuário para gerenciar sua configuração :-).
A função avaliará todos os projetos e clusters na organização em que está hospedada. Entendendo que há prováveis projetos ou clusters que você nunca quer pausado, a função também inclui uma array excludeProjects, onde você pode especificar uma lista de nomes de projetos a serem excluídos da avaliação.
Por fim, você notará que a chamada para pauseCluster foi comentada. Sugiro que você execute essa função por alguns dias e analise os logs do Trigger para verificar se ela se comporta como esperado.
1/*
2 * Iterates over the organizations projects and clusters,
3 * pausing clusters inactive for the configured minutes.
4 */
5exports = async function() {
6
7 minutesInactive = 60;
8
9 /*
10 * These project names are just an example.
11 * The same concept could be used to exclude clusters or even
12 * configure different inactivity intervals by project or cluster.
13 * These configuration options could also be stored and read from
14 * and Atlas database.
15 */
16 excludeProjects = ['PROD1', 'PROD2'];
17
18 const projects = await context.functions.execute("getProjects");
19
20 projects.forEach(async project => {
21
22 if (excludeProjects.includes(project.name)) {
23 console.log(`Project '${project.name}' has been excluded from pause.`)
24 } else {
25
26 console.log(`Checking project '${project.name}'s clusters for inactivity...`);
27
28 const clusters = await context.functions.execute("getProjectClusters", project.id);
29
30 clusters.forEach(async cluster => {
31
32 if (cluster.providerSettings.providerName != "TENANT") { // It's a dedicated cluster than can be paused
33
34 if (cluster.paused == false) {
35
36 is_active = await context.functions.execute("clusterIsActive", project.id, cluster.name, minutesInactive);
37
38 if (!is_active) {
39 console.log(`Pausing ${project.name}:${cluster.name} because it has been inactive for more then ${minutesInactive} minutes`);
40 //await context.functions.execute("pauseCluster", project.id, cluster.name, true);
41 } else {
42 console.log(`Skipping pause for ${project.name}:${cluster.name} because it has active database users in the last ${minutesInactive} minutes.`);
43 }
44 }
45 }
46 });
47 }
48 });
49
50 return true;
51};

Etapa 3: Criar o gatilho programado

Sim, ainda estamos usando um scheduled trigger, mas desta vez o trigger será executado periodicamente para verificar se há inatividade do cluster. Agora, seus desenvolvedores que trabalham tarde da noite não terão mais o cluster pausado abaixo deles.
ScheduledTrigger

Etapa 4: Implementar

Como uma etapa final, você precisa implantar o aplicativo Atlas App Services.
Revisar rascunho e implantar

Resumo

A gênese deste artigo foi que um cliente, quando apresentado meu artigo anterior sobre agendamento de pausas de cluster, perguntou se o mesmo poderia ser alcançado com base na inatividade. É minha confiança que, com as APIs do Atlas, tudo pode ser alcançado. A única questão era o que constitui inatividade? Dado o batimento cardíaco e a replicação que ocorrem naturalmente, sempre há algum "activity" no cluster. Por fim, decidi pelo acesso ao banco de dados como guia. Com o tempo, essa métrica pode ser combinada com algumas métricas adicionais ou alterada para algo completamente diferente, mas os ossos do processo estão aqui.

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Artigo
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Como usar o MongoDB Atlas e os LLMs do IBM watsonx.ai em seus aplicativos de GenAI sem interrupções


Mar 12, 2025 | 9 min read
Artigo

Colocando o RAG em produção com o chatbot de IA da documentação do MongoDB


Aug 29, 2024 | 11 min read
Notícias e Anúncios

Desbloqueie o valor dos dados no MongoDB Atlas com a análise inteligente do Microsoft Fabric


Nov 17, 2023 | 6 min read
Início rápido

Início rápido 2: pesquisa vetorial com MongoDB e OpenAI


May 06, 2024 | 12 min read
Sumário
  • Arquitetura