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 dockerfile | todo dockerfile |
---|---|
FROM node:22-alpine | FROM node:22-alpine |
WORKDIR /usr/src/app | WORKDIR /usr/src/app |
COPY package*.json ./ | COPY package*.json ./ |
RUN npm install | RUN npm install |
EXPOSE 8080 | EXPOSE 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