트랙 A · 과제 A3
로그 검색 탐색기
모든 작업을 SQL로 처리하는 작은 Kibana를 만들고 11:40에 결제가 실패한 원인을 찾아보세요.
- 60~120분
- 입문자 환영
- 언어 자유 · CLI 또는 웹
- AI 도구 사용 환영
막히는 부분이 있나요? 질문할 곳: #dev 참여할 곳: Apache Doris Slack. 유지 관리자와 다른 참가자가 도와드립니다.
환경 준비
만들 결과물과 Doris로 간단히 구현하는 이유를 알아보고 클러스터를 실행합니다.
만들 결과물
로그 탐색기를 만들고 장애 원인을 밝혀보세요.
200,000줄의 로그, 여섯 개 서비스, 장애가 발생한 어느 아침. BM25 순위로 키워드를 검색하고 서비스·수준·시간으로 필터링하며 건수를 집계합니다. 모두 Doris 테이블 하나에서 일반 SQL로 처리합니다.
다음 기능을 갖춘 작은 로그 탐색기:
- 검색용 검색창 (키워드, 구문) 및 관련성 순위,
- 필터 (서비스, 수준, 시간 범위),
- 패싯 집계 (결과 옆에 서비스·수준별 건수 표시),
- 선택적으로 다음 추가: 타임라인 (5분당 오류 수).
마지막으로 실제 조사를 수행합니다: 11:40에 결제가 실패하기 시작했습니다. 원인이 된 서비스는 무엇이며 실제 문제는 언제 시작됐나요? 탐색기에서 답을 명확히 찾을 수 있어야 합니다.
개략도는 코드 패널 상단에 있습니다
Doris로 구현할 때의 장점
같은 테이블에서 검색과 분석. 역색인이 키워드 쿼리를 처리하고 같은
WHERE절을 다음에 사용합니다:GROUP BY로 패싯을 집계합니다. 별도 시스템과 동기화할 필요가 없습니다.실제 관련성 순위.
score()가 결과를 BM25로 정렬하므로 최신 로그가 아니라 가장 관련 있는 로그가 먼저 나옵니다.저비용 필터.
service및level에는 각각 역색인이 있으며 시간 범위는 정렬 키로 가지치기합니다.
패널: 테이블 정의의 인덱스 부분
시작하기 전에
docker pull apache/doris:all-in-one-4.1.3docker run -d --name doris -p 9030:9030 -p 8030:8030 -p 8040:8040 apache/doris:all-in-one-4.1.3docker exec -it doris mysql -uroot -h127.0.0.1 -P9030curl -LO https://github.com/morningman/demo-env/releases/download/for-hackathon/doris-hackathon-glasgow-2026.zip && unzip doris-hackathon-glasgow-2026.zip && cd doris-hackathon-glasgow-2026
- 호스트
- 127.0.0.1
- 포트
- 9030
- 사용자
- root비밀번호 없음
- 데이터베이스
- hackathon
데이터
hackathon.app_logs : 여섯 개 서비스를 테이블 하나에 저장합니다:
CREATE TABLE app_logs ( ts DATETIME(3) NOT NULL, service VARCHAR(32) NOT NULL, -- api-gateway, auth, catalog, search, checkout, payment level VARCHAR(8) NOT NULL, -- DEBUG / INFO / WARN / ERROR host VARCHAR(32), trace_id VARCHAR(32), status INT, -- HTTP status, when relevant latency_ms INT, message STRING NOT NULL, INDEX idx_msg (message) USING INVERTED PROPERTIES ("parser" = "english", "support_phrase" = "true"), INDEX idx_service (service) USING INVERTED, INDEX idx_level (level) USING INVERTED)DUPLICATE KEY(ts)DISTRIBUTED BY HASH(trace_id) BUCKETS 1PROPERTIES ("replication_num" = "1");seed/a3_app_logs.sql 생성되는 데이터: 2026년 10월 13일의 로그 200,000줄 하나의 INSERT … SELECT … FROM numbers("number" = "200000") 로 생성합니다. 다운로드가 필요 없습니다. 이 데이터 어딘가에 장애 원인이 있습니다.
패널 → 테이블: 전체 테이블 정의
AI로 개발하기
AI 코딩 에이전트와 함께 개발하는 것을 권장합니다. Doris 4.x의 검색 및 AI 기능은 대부분의 모델 학습 데이터보다 최신이므로 SQL을 작성하기 전에 올바른 맥락을 제공하세요:
다음 컨텍스트 프롬프트 를 에이전트에 붙여 넣으세요.
참조할 곳을 안내하세요: 이 페이지에 나열된 참고 문서 및
AGENTS.md(스타터 키트 내). 많은 코딩 에이전트가 이 파일을 자동으로 읽습니다.코드에 연결하기 전에 모든 SQL 문을 실행 중인 클러스터에서 직접 실행하게 하세요.
더 나아가 다음을 연결하세요: Doris MCP Server (참고: 과제 A2)를 연결하면 에이전트가 직접 스키마를 확인하고 쿼리를 테스트할 수 있습니다.
I'm building a log search explorer on Apache Doris 4.1 for a hackathon.Connection: MySQL protocol, 127.0.0.1:9030, user root, empty password, database `hackathon`.Use a plain MySQL driver and raw SQL.Table `app_logs`: ts DATETIME(3), service, level, host, trace_id, status INT, latency_ms INT,message STRING. Inverted indexes on message (english parser, phrase support), service, level.Doris SQL rules:1. Text search: `message MATCH_ANY 'a b'`, `MATCH_ALL`, `MATCH_PHRASE 'a b'`. Never LIKE.2. BM25: `score()` only with a MATCH_* predicate + `ORDER BY score() DESC LIMIT n`, selected plain (`score() AS relevance`): no ROUND() or JOIN in that SELECT. Bind the user's search text as a driver parameter.3. score() cannot be used with GROUP BY. For facet counts run a second query with the same WHERE clause and GROUP BY service, level.4. Time buckets: `minute_floor(ts, 5)`.Build: search box, service/level/time filters, facet counts, and an error timeline.Run every SQL statement against the live cluster first.AI 에이전트가 빠지기 쉬운 Doris 함정
| Trap | What to do instead |
|---|---|
다음 사용: LIKE '%term%' 로 텍스트 검색 | col MATCH_ANY 'a b' (OR), MATCH_ALL (AND), MATCH_PHRASE 'a b' . 이들은 역색인을 사용합니다 |
다음 호출: score() 를 임의의 쿼리에서 사용 | score() 에 필요한 조건: MATCH_* 조건자를 다음 절에 지정: WHERE 및 ORDER BY score() DESC LIMIT n. 그렇지 않으면 Doris가 쿼리를 거부합니다 |
반올림, 조인 또는 순위 계산 대상: score() 를 같은 쿼리에서 수행 | 다음은 그대로 유지하세요: score() AS relevance 를 단일 테이블 쿼리에서 그대로 사용하세요. 반올림, 조인 또는 ROW_NUMBER() 작업은 바깥 쿼리에서 수행하고, 다음 대신 하위 쿼리를 사용하세요: WITH CTE |
score() 를 다음 안에서 사용: GROUP BY / 집계 | 허용되지 않습니다. 패싯 집계에는 동일한 다음 조건을 재사용하세요: WHERE 제외: score() |
| ORM 사용 | 일반 MySQL 드라이버와 직접 작성한 SQL을 권장합니다. ORM은 Doris가 지원하지 않는 문을 생성할 수 있습니다 |
구현하기
6 마일스톤, M0 → M5. 패널은 현재 읽는 단계를 따라갑니다.
로그 생성
스타터 키트 폴더 안에서 실행하세요:
M0 · 로그 생성bashdocker exec -i doris mysql -uroot -h127.0.0.1 -P9030 < seed/a3_app_logs.sql확인 사항몇 초 후 표시: 200,000 줄의 로그, 시작:
00:00:00종료:23:59:59(10월 13일).BM25 키워드 검색
M1 · BM25 키워드 검색sqlUSE hackathon;SELECT ts, service, level, message, score() AS relevanceFROM app_logsWHERE message MATCH_ANY 'timeout refused'ORDER BY relevance DESCLIMIT 20;검색창에 연결하세요. 사용자 입력은 드라이버 매개변수로 전달하세요(
MATCH_ANY %s);a3/starter.py(스타터 키트 내)가 이 동작을 구현합니다.확인 사항20개 결과는 모두
connection refused로그입니다. BM25는 드문 단어에 더 큰 가중치를 부여합니다: refused 는 68개 행에 등장하고, timeout 는 347개 행에 등장합니다. 다음 단어만 검색하면timeout나머지 결과도 볼 수 있습니다.필터
M2 · 필터sqlSELECT ts, service, level, message, score() AS relevanceFROM app_logsWHERE message MATCH_ANY 'timeout' AND level = 'ERROR' AND service IN ('checkout', 'payment') AND ts >= '2026-10-13 11:00:00' AND ts < '2026-10-13 12:30:00'ORDER BY relevance DESCLIMIT 20;확인 사항모든 결과는 timeout이 포함된 ERROR 로그입니다. 두 서비스 중 하나만 표시됩니다.
패싯 집계
score()는 집계할 수 없으므로 패싯 집계는 이를 제외한 동일한 필터를 사용합니다:M3 · 패싯 집계sqlSELECT service, level, COUNT(*) AS hitsFROM app_logsWHERE message MATCH_ANY 'timeout' AND ts >= '2026-10-13 11:00:00' AND ts < '2026-10-13 12:30:00'GROUP BY service, levelORDER BY hits DESC;확인 사항해당 시간대에 일치하는 로그는 163줄이며 모두
ERROR입니다. 집계 합계는 다음을 제외한 동일 검색 건수와 같아야 합니다:GROUP BY: 확인 명령어:SELECT COUNT(*)및 같은WHERE.오류 타임라인
M4 · 오류 타임라인sqlSELECT minute_floor(ts, 5) AS bucket, service, COUNT(*) AS errorsFROM app_logsWHERE level = 'ERROR' AND ts >= '2026-10-13 11:00:00' AND ts < '2026-10-13 12:30:00'GROUP BY bucket, serviceORDER BY bucket, service;확인 사항11:35부터 12:00 사이에 두 서비스의 오류가 급증합니다. 나머지 시간에는 어떤 서비스도 5분당 오류가 4건을 넘지 않습니다.
장애 원인 파악
탐색기로 다음에 답하세요: 결제 실패를 일으킨 서비스는 무엇이며 몇 시 몇 분에 시작됐나요? 답과 이를 입증하는 쿼리를 README에 적으세요.
완성하기
체크리스트를 확인하고 결과물을 공유한 뒤 배지를 받으세요.
완료 기준
체크리스트제출하고 배지 받기
참여 → 제출 → 배지 받기.
다음 이름의 폴더에 결과물을 넣으세요: 본인의 GitHub ID. 코드와 README를 포함하고, README에 기능, 실행 방법, 스크린샷 또는 GIF 한 개, 사용한 Doris 기능을 적어주세요.
풀 리퀘스트 만들기 다음 위치에 추가하세요: morningman/demo-env 저장 경로:
doris-hackathon-glasgow-2026/<your-github-id>/. 포크 후 push하거나 GitHub의 다음 기능을 사용하세요: Add file → Upload files. 그 외에 작성할 항목은 없습니다.공유하기 공유할 곳:
#dev참여할 곳: Apache Doris Slack: 풀 리퀘스트 또는 저장소 링크와 함께 스크린샷이나 짧은 데모를 공유하세요. 질문, 제출, 배지 관련 안내는 모두 이곳에서 진행됩니다.
과제에 참여하는 모든 분께 다음 배지를 드립니다: Apache Doris Contributor 배지. Doris 프로젝트에 병합된 풀 리퀘스트가 없어도 됩니다. 마감일이 없으니 필요한 만큼 시간을 쓰세요. 팀 참여도 가능하지만 필수는 아닙니다. 모든 팀원의 GitHub ID를 다음에 적어주세요: README.
추가 도전 과제
구문 검색 및 입력 자동 완성:
MATCH_PHRASE 'connection refused',MATCH_PHRASE_PREFIX로 입력 중 검색을 구현합니다.트레이스 보기: 로그 한 줄을 클릭하면 동일한 다음 값을 가진 모든 로그를 표시합니다:
trace_id, 시간순으로 표시합니다.쿼리 문자열 검색: 다음을 사용해 보세요:
SEARCH()DSL(Lucene 스타일의field:term AND …). 참고: SEARCH 함수.실시간 로그 추적: 매초 몇 행을 삽입하고 자동으로 새로 고칩니다.
인덱스 유무 비교: 비교 대상:
MATCH_ANY함께하는LIKE '%timeout%'를 전체 테이블에서 실행하고 소요 시간을 표시합니다.
문제 해결 및 참고 자료
| Symptom | Fix |
|---|---|
docker: command not found (macOS) | Docker Desktop을 시작하세요. 이미 실행 중이면 CLI 링크를 연결하세요: sudo ln -s /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker |
error getting credentials 이미지를 내려받을 때 | 삭제할 항목: credsStore 필드가 있는 파일: ~/.docker/config.json (로컬 개발 환경에서만) |
port is already allocated | 다른 프로세스(주로 다른 Doris)가 9030, 8030 또는 8040 포트를 사용 중입니다. 실행 중인 컨테이너를 확인하세요(docker ps), 또는 다른 호스트 포트를 노출하세요. 예: -p 19030:9030, 이 경우 19030에 연결하세요 |
The container name "/doris" is already in use | 이전에 실행한 적이 있습니다: docker start doris. 처음부터 다시 시작하려면 docker rm -f doris (데이터가 삭제됩니다) |
컨테이너가 종료되거나 다음 상태로 바뀌지 않음: (healthy) | 확인: docker logs doris. 대개 메모리 문제입니다. Docker Desktop에 6 GB 이상을 할당하세요(Settings → Resources). Apple Silicon에서는 다음을 추가하지 마세요: --platform linux/amd64 |
permission denied: ./doris.sh | 압축 해제 도구가 실행 권한을 제거했습니다: chmod +x doris.sh, 또는 다음 실행: bash doris.sh start |
Current database is not set | 실행: USE hackathon; 를 먼저 실행하거나 다음으로 연결하세요: -Dhackathon |
score() function requires WHERE clause with MATCH function, ORDER BY and LIMIT | 다음 추가: MATCH_* 조건자, ORDER BY score() DESC 및 LIMIT그리고 다음을 유지하세요: score() 를 다른 식으로 감싸지 마세요(위의 함정 참고) |
| 10분 이상 해결하지 못한 경우 | 게시할 곳: #dev 참여할 곳: Apache Doris Slack |
참고 자료
Apache Doris 해커톤
