Iniciar o laboratório é acender um prédio de três andares. demarrer aperta o interruptor geral, depois um zelador passa em cada andar para verificar que a luz está mesmo acesa antes de lhe entregar as chaves: esse zelador são os healthchecks do Docker Compose. O Kibana nem sequer tem o direito de iniciar antes que o Elasticsearch seja declarado saudável. Em seguida, importer preenche as estantes do Elasticsearch, charger-graphe preenche as do Neo4j, e etat lhe dá a planta do prédio: quem está lá, quem responde, quantos documentos em cada índice. O resto da lição é uma visita guiada às duas salas onde você passará o curso: o console Dev Tools do Kibana e o Neo4j Browser.
demarrer encadeia três etapas de docker compose: pull (baixa as imagens, ~6 GB na primeira vez, nada depois), up -d (cria e inicia os contêineres em segundo plano), depois um laço de espera que consulta docker inspect a cada 3 segundos até que cada contêiner esteja healthy.
A palavra healthy vem do docker-compose.yml. Cada serviço declara ali um teste que o Docker reexecuta regularmente:
| Serviço | Teste executado no contêiner | Ritmo |
|---|---|---|
| elasticsearch | curl -fsS http://localhost:9200/_cluster/health deve conter "status":"green" ou "yellow" | a cada 10 s, 30 tentativas, 30 s de carência na inicialização |
| kibana | curl -fsS http://localhost:5601/api/status deve conter "level":"available" | a cada 10 s, 30 tentativas, 40 s de carência |
| neo4j | wget -qO- http://localhost:7474 responde | a cada 10 s, 30 tentativas, 30 s de carência |
Enquanto o teste falha, o contêiner está starting; quando passa, healthy; após 30 falhas, unhealthy. O serviço kibana declara depends_on: elasticsearch: condition: service_healthy: o Compose só o inicia depois que o Elasticsearch está healthy, o que evita o famoso "Kibana server is not ready yet" na inicialização.
Dois detalhes do arquivo compose merecem uma palavra desde já. xpack.security.enabled=false: sem senha nem certificado para o Elasticsearch, é um laboratório local, nunca uma configuração de produção. cluster.routing.allocation.disk.threshold_enabled=false: o Elasticsearch não passará seus índices para somente leitura se o seu disco ultrapassar 95% (lição 04, falha nº 5).
O kit do laboratório primeiro. Tudo o que segue é executado a partir da raiz do kit — https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-fr — obtido por
git clone https://github.com/hrhouma2/aiopsatlas-recherche-graphes-labo-frou pelo botão Code → Download ZIP (lição 02, etapa 4). Sedir(ouls) não mostrardocker-compose.yml,labo.shelabo.ps1, você não está no lugar certo.
Todas as consultas desta lição: elasticsearch/requetes/01-03-premiere-visite.txt.
Iniciar a stack. A partir da pasta do kit:
./labo.sh demarrer.\labo.ps1 demarrerO script exibe três blocos: == Téléchargement des images == (download das imagens, longo na primeira vez, silencioso depois), == Démarrage == (inicialização, as linhas Container labo-elasticsearch Started do Compose), depois == Attente que chaque service soit prêt == (espera até que cada serviço esteja pronto), onde cada linha se preenche de pontos até prêt (…s). Ele termina com:
Le labo est prêt.
Kibana http://localhost:5601 (Dev Tools : menu ☰ → Management → Dev Tools)
Elasticsearch http://localhost:9200
Neo4j Browser http://localhost:7474 (utilisateur neo4j · mot de passe aiopsatlas2026)
Étape suivante : ./labo.sh importer puis ./labo.sh charger-grapheO que é preciso ver: três prêt (elasticsearch, kibana, neo4j). Conte um a dois minutos após o download. Se uma linha exibir unhealthy, exited ou délai dépassé (tempo limite excedido), vá direto para a lição 04.
Carregar os índices Elasticsearch.
./labo.sh importerO comando cria cada índice com seu mapping (arquivos elasticsearch/mappings/*.json) e depois envia os dados para a API _bulk. Saída real, aqui em um laboratório onde os índices já existiam (o comando é reexecutável sem risco):
== Import dans elasticsearch ==
— index cours existe déjà — conservé
✔ données cours chargées
— index avis existe déjà — conservé
✔ données avis chargées
— index acces existe déjà — conservé
✔ données acces chargées
index docs.count store.size
acces 12000 1.5mb
avis 609 74.6kb
cours 504 183.6kb
Import terminé. Attendu : cours = 504, avis = 609, acces = 12000.O que é preciso ver: na primeira vez, as linhas — se tornam ✔ index cours créé avec son mapping (índice cours criado com seu mapping). Os três contadores devem ser exatamente 504, 609 e 12000; os documentos carregam seu próprio _id, portanto reexecutar a importação nunca cria duplicata.
Carregar o grafo Neo4j.
./labo.sh charger-grapheO script executa neo4j/cypher/01-contraintes.cypher (restrições de unicidade e índices) e depois 02-charger.cypher (LOAD CSV + MERGE para cada arquivo de neo4j/import/). Ele termina com o balanço que a última consulta do script calcula, MATCH (n) RETURN labels(n)[0] AS etiquette, count(*) AS noeuds ORDER BY etiquette:
etiquette, noeuds
"Competence", 22
"Cours", 504
"Etudiant", 300
"Professeur", 30
"Ville", 16
Graphe chargé. Attendu : Competence 22, Cours 504, Etudiant 300, Professeur 30, Ville 16.O que é preciso ver: 872 nós no total. Tudo está em MERGE, portanto reexecutar o comando não cria nenhuma duplicata.
Ler o estado do laboratório. É o comando que você digitará com mais frequência:
./labo.sh etatSaída real (aqui com o perfil OpenSearch ativo, que não aparecerá na sua máquina antes do módulo 5):
== Conteneurs ==
NAME STATUS PORTS
labo-elasticsearch Up 16 minutes (healthy) 0.0.0.0:9200->9200/tcp, [::]:9200->9200/tcp
labo-kibana Up 16 minutes (healthy) 0.0.0.0:5601->5601/tcp, [::]:5601->5601/tcp
labo-neo4j Up 14 minutes (healthy) 0.0.0.0:7474->7474/tcp, [::]:7474->7474/tcp, 0.0.0.0:7687->7687/tcp, [::]:7687->7687/tcp
labo-opensearch Up 16 minutes (healthy) 0.0.0.0:9201->9200/tcp, [::]:9201->9200/tcp
labo-opensearch-dashboards Up 16 minutes 0.0.0.0:5602->5601/tcp, [::]:5602->5601/tcp
== Services ==
✔ Elasticsearch : {"status":"green","number_of_nodes":1}
index : acces 12000 avis 609 cours 504
✔ Kibana répond (http://localhost:5601)
✔ Neo4j répond — nœuds : 872
✔ OpenSearch : {"status":"green","number_of_nodes":1}O que é preciso ver: (healthy) em cada linha de contêiner, green para o cluster, os três contadores de índice e 872 nós. Sem OpenSearch, a última linha diz — OpenSearch non démarré (profil optionnel : ./labo.sh demarrer opensearch) (OpenSearch não iniciado, perfil opcional).
Abrir o Kibana Dev Tools. Acesse http://localhost:5601. O Kibana está em francês (I18N_LOCALE=fr-FR). Clique no menu ☰ no alto à esquerda; na seção Management, escolha Outils de développement (é a tradução de "Dev Tools", "Ferramentas de desenvolvimento"; a trilha de navegação exibe Outils de développement > Console). Você chega à aba Console, subaba Shell: um editor à esquerda, a resposta à direita. O botão Effacer cette entrée (Limpar esta entrada) esvazia o editor de exemplo.
O que é preciso ver: quando você digita uma consulta, um botão ▶ («Cliquer pour envoyer la requête», Clique para enviar a consulta) aparece no fim da linha. Atalho: Ctrl + Enter (⌘ + Enter no Mac) envia a consulta sob o cursor. Embaixo à direita da resposta, o Kibana exibe o código HTTP e a duração, por exemplo 200 - OK 23 ms.
Primeira consulta: a saúde do cluster.
GET _cluster/health{
"cluster_name": "labo",
"status": "green",
"timed_out": false,
"number_of_nodes": 1,
"number_of_data_nodes": 1,
"active_primary_shards": 53,
"active_shards": 53,
"unassigned_shards": 0,
…
"active_shards_percent_as_number": 100
}O que é preciso ver: "status": "green". Os índices do laboratório são criados com number_of_replicas: 0, portanto nenhuma réplica em espera: green em um único nó. O número de shards (53) inclui índices de sistema do Kibana; ele pode variar.
Quem responde, e quais índices existem?
GET /Resposta: "name": "labo-es-1", "cluster_name": "labo", "version": { "number": "9.5.3", …, "lucene_version": "10.5.1" }, "tagline": "You Know, for Search".
GET _cat/indices/cours,avis,acces?v&s=indexhealth status index uuid pri rep docs.count docs.deleted store.size pri.store.size dataset.size
green open acces aii68fsfQXKyt5wqkE1mPA 1 0 12000 0 1.5mb 1.5mb 1.5mb
green open avis W9j_JrwJT4mdzpWcS5k7xg 1 0 609 0 74.6kb 74.6kb 74.6kb
green open cours pmq403ZgSZWeHJY9uNw1Qw 1 0 504 0 183.6kb 183.6kb 183.6kbO que é preciso ver: pri 1, rep 0, e os três contadores. ?v acrescenta a linha de cabeçalho, s=index ordena por nome. Sem o filtro cours,avis,acces, GET _cat/indices?v lista também índices internos que começam com um ponto (.internal.alerts-…): ignore-os.
O nó e as contagens.
GET _cat/nodes?vip heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.24.0.2 39 63 2 0.43 0.38 0.64 cdfhilmrstw * labo-es-1GET cours/_count{ "count": 504, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 } }A mesma coisa para GET avis/_count → "count": 609 e GET acces/_count → "count": 12000. O que é preciso ver: um único nó, master *, e heap.percent em torno de 40% com o ajuste -Xmx1g do compose.
Um primeiro documento.
GET cours/_search{
"query": { "match_all": {} },
"size": 1
}Resposta (truncada):
{
"took": 1,
"hits": {
"total": { "value": 504, "relation": "eq" },
"max_score": 1,
"hits": [
{
"_index": "cours",
"_id": "C0001",
"_score": 1,
"_source": {
"id": "C0001",
"titre": "Docker expliqué simplement",
"categorie": "DevOps", "sujet": "Docker", "niveau": "debutant",
"prix": 129, "note_moyenne": 4.4, "nb_avis": 327,
"professeur": { "id": "P001", "nom": "Karim Caron", "ville": "Gatineau" },
…
}
}
]
}
}O que é preciso ver: hits.total.value = 504 (todos os documentos correspondem a match_all), mas apenas um é retornado graças a size: 1. O Dev Tools envia um GET com corpo como POST por baixo dos panos; os dois são aceitos.
Abrir o Neo4j Browser e conectar-se. Acesse http://localhost:7474. A janela Connect to instance já propõe Protocol neo4j:// e Connection URL localhost:7687 (a porta Bolt, não 7474). Deixe Database user em neo4j, digite o Password aiopsatlas2026, clique em Connect. O que é preciso ver: no alto, um ponto verde e Instance: neo4j://localhost:7687, Database: neo4j, User: neo4j. O painel Database information à esquerda exibe Nodes (872) com os rótulos Competence, Cours, Etudiant, Professeur, Ville, e Relationships (3,712) com COUVRE, ENSEIGNE, HABITE, INSCRIT_A, PREREQUIS_DE. A barra lateral propõe Database overview, Saved Cypher, History, Cypher reference, Parameters, Settings (a interface está em inglês).
Primeira consulta Cypher. No editor no alto (neo4j$), digite e depois clique em Run (ou Ctrl + Enter):
MATCH (c:Cours) RETURN c LIMIT 25O que é preciso ver: um quadro de resultado com três visões Graph, Table, Raw, e à direita Results overview: Nodes (25) · Cours (25). Na visão Graph, 25 bolhas laranja; clique em uma delas para ver suas propriedades (id, titre, categorie, prix…). Na visão Table, os mesmos 25 nós em JSON. O módulo 6 ensina você a escrever essas consultas; por enquanto, você sabe onde digitá-las.
E o OpenSearch? O laboratório também pode iniciar o OpenSearch 3.8.0 e o OpenSearch Dashboards nas portas 9201 e 5602, com ./labo.sh demarrer opensearch e depois ./labo.sh importer opensearch. É um perfil opcional do Compose: ele não é iniciado por padrão, e exige 6 GB de memória para o Docker. Nós o ativamos no módulo 5, para reexecutar as mesmas consultas nos dois motores e comparar. Nada a fazer hoje.
O Kibana exibe "Kibana server is not ready yet" logo após demarrer → O Kibana iniciou mas ainda não terminou de se conectar ao Elasticsearch e de criar seus índices internos. Aguarde 30 segundos e recarregue a página. Se a mensagem persistir além de dois minutos, ./labo.sh journal kibana (lição 04, falha nº 4).
O Dev Tools responde 404 com "type": "index_not_found_exception", "reason": "no such index [cour]" → Erro de digitação no nome do índice (cour em vez de cours). O Elasticsearch não adivinha; GET _cat/indices?v lhe dá os nomes exatos. Variante: 400 com "no handler found for uri [/cours/_serch] and method [GET]" → é o nome da API que está escrito errado (_serch).
O Dev Tools responde 400 com x_content_parse_exception … was expecting double-quote to start field name → O JSON está malformado, na maioria das vezes uma vírgula a mais antes da chave de fechamento ("size": 1, }). O editor sublinha a linha errada em vermelho antes mesmo do envio.
Neo4j Browser: "Connection to instance failed — The client is unauthorized due to authentication failure." (detalhes: Neo.ClientError.Security.Unauthorized) → Senha errada. É aiopsatlas2026 (fixada por NEO4J_AUTH no compose), não a senha padrão neo4j. Digite-a novamente, sem espaço no final.
etat exibe Neo4j répond — nœuds : 0 → O contêiner está rodando, mas você esqueceu o charger-graphe. Execute-o; leva uns trinta segundos.
demarrer, importer, charger-graphe. Os dois últimos são reexecutáveis sem criar duplicatas (identificadores explícitos do lado do Elasticsearch, MERGE do lado do Neo4j).demarrer aguarda o status healthy de cada contêiner; esse status vem dos healthchecks do docker-compose.yml, e o Kibana só inicia depois do Elasticsearch graças a depends_on … service_healthy.etat em um olhar: (healthy) em todos os lugares, cluster green, 504 / 609 / 12000 documentos, 872 nós.GET caminho seguida do seu JSON colado abaixo; Ctrl + Enter para enviar.localhost:7687, usuário neo4j, senha aiopsatlas2026; os resultados se leem em Graph, Table ou Raw.O arquivo 01-03-premiere-visite.txt está no formato exato do Dev Tools: no console, o botão Importer les requêtes (Importar as consultas, ícone no alto à direita do editor) aceita esse arquivo e o cola no editor. Todos os arquivos da pasta elasticsearch/requetes/ funcionam assim; você nunca precisará redigitar uma consulta do curso.