Skip to main content

Projeto

A proposta é conhecer como funciona por dentro de uma aplicação para entender a instrumentação. Se você é devops e tá pensando, isso é problema dos devs, eu não tiro a sua razão não. Por que não fizeram em uma linguagem que dá para automatizar? :D.

O problema que precisamos criar algo para gerar os traces que precisamos.

❯ tree
.
├── docker #Dockerfiles
│ ├── Dockerfile.auth
│ └── Dockerfile.todo
├── docker-compose.yml
├── package-lock.json
├── package.json
├── src
│ └── services
│ ├── auth-service.ts # Código do Auth
│ └── todo-service.ts # Código do TODOS
└── tsconfig.json

4 directories, 8 files

Analisando docker-componse.yaml temos isso. Já estamos montando o volume dentro de cada um dos containers. O volume é a própria pasta do projeto. Por isso não precisamos fazer o COPY . . depois dentro dos dockerfiles.

As aplicações dentro rodam na porta 8080 cada uma delas, então vamos colocar 8080 para o auth e 8081 para o todos para acessarmos externamente.

version: '3.8'

services:
todo:
build:
context: .
dockerfile: docker/Dockerfile.todo
volumes:
- .:/usr/src/app:rw
- /usr/src/app/node_modules
ports:
- 8081:8080
depends_on:
- redis

auth:
build:
context: .
dockerfile: docker/Dockerfile.auth
volumes:
- .:/usr/src/app:rw
- /usr/src/app/node_modules
ports:
- 8080:8080

redis:
image: redis:alpine
ports:
- '6380:6379'
# Minha máquina utiliza o redis de forma pessoal então vou usar externamente a 6380

Um redis, uma aplicação auth e uma todo. O dockerfile das duas aplicações é o mesmo mudando somente o CMD que irá iniciar auth ou todos.

auth dockerfiletodo dockerfile
FROM node:22-alpineFROM node:22-alpine
WORKDIR /usr/src/appWORKDIR /usr/src/app
COPY package*.json ./COPY package*.json ./
RUN npm installRUN npm install
EXPOSE 8080EXPOSE 8080
CMD ["npm", "run", "auth"]CMD ["npm", "run", "todo"]

Nosso projeto será um projeto NodeJS simples que sobe só iniciando com docker-componse up para você ficar feliz.

Agora vamos partir para entender o auth-service.js.

// Importa o framework Express.js para criar aplicações web
import express from 'express';

// Cria uma nova instância do aplicativo Express
const app = express();

// Define uma rota GET para o endpoint '/auth'
app.get('/auth', (req, res) => {
// Quando acessado, retorna um objeto JSON com um username
// req: contém informações da requisição HTTP
// res: usado para enviar a resposta ao cliente
res.json({username: 'David Puziol'})
})
// Inicia o servidor na porta 8080
app.listen(8080, () => {
// Callback executado quando o servidor está pronto
// Imprime mensagem no console confirmando que está rodando
console.log('Auth service is running on port 8080');
})

E temos o todo-service.js que será a nossa aplicação. Estamos criando então a aplicação web que tem uma rota para ser chamada em /todos que faz chamada para um serviço auth:8080/auth que definimos também e estará rodando em outro container chamado auth e tb popula o redis.

// Importa os pacotes necessários
import express from 'express'; // Framework web
import axios from 'axios'; // Cliente HTTP para fazer requisições
import IORedis from 'ioredis'; // Cliente Redis

// Cria uma instância do Express
const app = express();

// Configura conexão com Redis usando o hostname 'redis' (definido no docker-compose)
const redis = new IORedis({ host: 'redis' });

// Define rota GET /todos
app.get('/todos', async (req, res) => {
// Busca informações do usuário do serviço de autenticação
const user = await axios.get('http://auth:8080/auth');

// Busca todas as chaves no Redis que começam com 'todo:'
const todoKeys = await redis.keys('todo:*');

// Array para armazenar os todos
const todos: any[] = [];

// Itera sobre cada chave encontrada
for (const key of todoKeys) {
// Busca o valor armazenado para cada chave
const todoItem = await redis.get(key);
if (todoItem) {
// Converte a string JSON em objeto e adiciona ao array
todos.push(JSON.parse(todoItem));
}
}
// Retorna os todos e informações do usuário
res.json({ todos, user: user.data });
});

// Inicia o servidor na porta 8080
app.listen(8080, () => {
console.log('Todo service is running on port 8080');
});

// Função para inicializar dados de exemplo no Redis
async function init() {
// Cria 4 todos iniciais usando Promise.all para execução paralela
await Promise.all([
redis.set('todo:1', JSON.stringify({ name: 'Configurar OpenTelemetry' })),
redis.set('todo:2', JSON.stringify({ name: 'Implementar tracing' })),
redis.set('todo:3', JSON.stringify({ name: 'Adicionar métricas' })),
redis.set('todo:4', JSON.stringify({ name: 'Configurar exporters' }))
]);
}

// Executa a função de inicialização e trata possíveis erros
init().catch(console.error);

Não instrumentamos nada até agora ok? Só para ficar claro.

Vamos subir o ambiente todo?

# Tanto faz, docker, podman
podman compose up --build

E temos isso ai oh.

curl localhost:8080/auth  
{"username":"David Prata"}

curl localhost:8081/todos
{"todos":[{"name":"Implementar tracing"},{"name":"Configurar exporters"},{"name":"Adicionar métricas"},{"name":"Configurar OpenTelemetry"}],"user":{"username":"David Prata"}}

O projeto está disponível Neste Repositório. Na branch main temos o projeto completo porém o que vimos até agora é a branch base e vamos crescendo ao longo da jornada.

git clone https://gitlab.com/davidpuziol/opentelemetry-project.git
git checkout base