26A29a

Young-Kyoo Kim·2026년 4월 28일

Obsidian을 회사 서버에서 활용하는 것은 기술적으로 가능하며, 특정 워크플로우에서는 강력한 경쟁력을 가집니다. 다만, Obsidian은 본래 '로컬 퍼스트(Local-first)' 앱이기 때문에 Notion 같은 클라우드 기반 툴과는 서버 활용 방식이 조금 다릅니다.


1. 회사 서버에서 Obsidian을 사용하는 방법

Obsidian 자체는 설치형 데스크톱 앱이지만, 서버를 활용해 데이터를 관리하거나 협업 환경을 구축하는 방법은 크게 세 가지가 있습니다.

① 데이터 동기화 서버 구축 (Self-hosted Sync)

가장 일반적인 방법입니다. 회사 내부에 데이터 주권을 유지하면서 여러 기기에서 노트를 동기화하고 싶을 때 사용합니다.

  • Obsidian LiveSync: 오픈소스 플러그인을 사용하여 서버에 CouchDB를 설치하고 실시간 동기화 서버로 활용할 수 있습니다.
  • Syncthing / Git: 서버를 중앙 저장소로 두고 Syncthing으로 파일을 동기화하거나, Git 레포지토리를 만들어 회사 서버에 Push/Pull 하는 방식입니다. 엔지니어링 조직에서 선호하는 방식입니다.

② 브라우저 접속형 (Obsidian-Remote)

서버에 Docker 컨테이너로 Obsidian을 띄우고 웹 브라우저를 통해 접속하는 방식입니다.

  • linuxserver/obsidian 같은 Docker 이미지를 사용하면 서버 자원을 이용해 Obsidian UI를 그대로 웹에서 쓸 수 있습니다. 보안상 외부 인터넷 연결이 제한된 환경에서 내부 서버에 설치해 쓰기에 적합합니다.

③ 정적 사이트 생성 (Publish 대체)

서버에 Obsidian 노트(Markdown)를 두고, 이를 Quartz나 Hugo 같은 정적 사이트 생성기(SSG)와 연결하여 사내 위키(Internal Wiki) 형태로 배포할 수 있습니다.


2. 다른 경쟁 툴 대비 경쟁력 분석

Obsidian이 Notion이나 Logseq 등과 비교해 갖는 확실한 우위 요소는 다음과 같습니다.

비교 항목ObsidianNotionLogseq
데이터 소유권완전 로컬 (Markdown)클라우드 종속 (Proprietary)로컬 (Markdown/Org)
작동 속도매우 빠름 (오프라인 우선)네트워크 상태에 따라 느려짐보통 (데이터 많을 시 저하)
확장성1,000개 이상의 커뮤니티 플러그인제한적 (API 중심)플러그인 생태계 성장 중
보안/보안폐쇄망 사용 가능 (보안 우수)서버 보안 정책에 의존폐쇄망 사용 가능
구조네트워크형 (Graph View)계층형 (Database)아웃라이너 (Block-based)

핵심 경쟁력: "데이터 주권과 유연성"

  • 보안: 데이터가 클라우드가 아닌 우리 회사의 서버나 로컬 드라이브에만 존재하므로, 기밀 유지가 중요한 기업 환경에서 독보적입니다.
  • 범용성: 파일이 .md(Markdown) 형식이므로, 나중에 Obsidian을 안 쓰게 되더라도 데이터 변환 없이 다른 툴이나 스크립트에서 바로 읽을 수 있습니다.
  • 그래프 뷰(Graph View): 단순한 폴더 구조를 넘어 지식 간의 연결 고리를 시각화할 수 있어, 복잡한 프로젝트의 히스토리를 추적하거나 아이디어를 확장할 때 유리합니다.

3. 주의사항: 상업적 라이선스

기업(영리 목적의 조직)에서 2인 이상의 직원이 업무용으로 Obsidian을 사용할 경우, Commercial License($50/user/year)를 구입해야 합니다.

  • 무료 버전: 개인적인 용도로만 무료입니다.
  • 상업 라이선스: 회사 업무용으로 사용할 때 필수이며, 이는 앱의 기능적 차이보다는 법적 준수와 개발팀 지원의 성격이 강합니다.

결론

Obsidian은 "우리 회사만의 폐쇄적이고 강력한 지식 베이스를 직접 통제하며 구축하고 싶다"는 니즈가 있을 때 최고의 경쟁력을 발휘합니다. 특히 Git이나 Docker 환경에 익숙한 기술 조직이라면 사내 서버와 연동하여 커스텀 위키나 개인화된 업무 도구로 활용하기에 매우 적합합니다.

회사에서 구체적으로 어떤 워크플로우(예: 개인 메모 관리, 팀 공유 위키, 프로젝트 문서화 등)에 도입을 고려 중이신가요? 상황에 맞춰 더 구체적인 구성을 제안해 드릴 수 있습니다.

===

개인용 Obsidian을 회사 PC에 설치하고 Git으로 동기화하여 지식 베이스(LLM Wiki)를 구축하고, 이를 AIOps로 확장하려는 계획은 매우 전략적이고 실용적인 접근입니다. 특히 K8s, Cilium, MinIO AIStor와 같은 대규모 인프라를 다루는 환경에서 마크다운(MD) 기반의 문서화는 데이터 활용도를 극대화할 수 있습니다.

제안하신 내용을 바탕으로 '지식 생성 - 자동 관리 - AIOps 활용'으로 이어지는 전반적인 아키텍처를 설계해 보았습니다.


1. 전반적인 시스템 구조 (High-Level Architecture)

이 구조의 핵심은 "Obsidian을 단순한 메모 도구가 아닌, AI가 읽을 수 있는 정형화된 데이터 소스(Single Source of Truth)"로 만드는 것입니다.

Layer 1: 지식 생성 및 편집 (Obsidian & Git)

  • 환경: 회사 PC 로컬에 Obsidian 설치.
  • 동기화: Obsidian Git 플러그인을 사용하여 사내 또는 개인 Git 레포지토리와 연동 (Auto Backup/Pull 설정).
  • 콘텐츠 분류:
    • /Library: K8s, Cilium, MinIO 등 벤더 공식 문서의 핵심 요약 및 테크니컬 노하우.
    • /SOP: 장애 대응 절차서, 클러스터 스케일 아웃 가이드 등.
    • /Operations: 작업계획서, 점검 일지 (날짜별 관리).
    • /Templates: 문서 표준화를 위한 MD 템플릿 (작업 계획서 양식 등).

Layer 2: 지식 파이프라인 및 관리 (Automation)

  • 업데이트 자동화: 벤더 Docs의 업데이트를 추적하기 위해 특정 스크립트나 n8n과 같은 자동화 툴을 활용하여 변경 사항을 MD로 변환 후 Git에 자동 Push 하도록 구성할 수 있습니다.
  • CI/CD 연동: Git에 Push가 발생하면 이를 트리거로 하여 지식 베이스를 검증하거나 후속 레이어로 전달합니다.

Layer 3: AIOps 및 지식 활용 (Agentic AI & RAG)

  • RAG (Retrieval-Augmented Generation): Git에 저장된 MD 파일들을 벡터 데이터베이스(Vector DB)로 인덱싱합니다.
  • LLM 연동: 인프라 모니터링 시스템(Prometheus/Grafana)에서 알람이 발생하면, LLM 에이전트가 저장된 SOP와 벤더 Docs를 검색하여 원인을 분석하고 조치 방안을 제시합니다.
  • AIOps 활용 사례:
    • "Cilium BGP 피어링 에러 발생 시 대응 SOP 요약해줘."
    • "지난번 MinIO AIStor 확장 시 발생했던 이슈와 해결책 찾아줘."

2. 기술적 구현 포인트 및 경쟁력

① MD 기반의 'Paved Road' 구축

  • 단순 메모가 아니라, LLM이 이해하기 쉬운 구조화된 마크다운을 작성하는 것이 핵심입니다. Metadata(YAML)를 적극 활용하여 문서의 속성(장비, 서비스, 중요도 등)을 정의하면 향후 AI 에이전트가 정보를 추출하기 훨씬 수월해집니다.

② 인프라 가시성과의 결합

  • Cilium의 eBPF 기반 네트워킹이나 MinIO의 고성능 스토리지 운영은 매우 복잡합니다. Obsidian의 Graph View를 통해 서비스 간의 의존성이나 인프라 구성 요소를 시각적으로 연결해 두면, 단순 텍스트 검색보다 훨씬 직관적인 장애 추적이 가능합니다.

③ Git을 통한 버전 관리 및 히스토리

  • 작업계획서나 SOP를 Git으로 관리하면 "누가, 언제, 왜" 설정을 변경했는지 완벽한 추적이 가능합니다. 이는 AIOps 단계에서 과거의 특정 시점 설정과 현재의 장애 상황을 비교 분석하는 데 결정적인 데이터를 제공합니다.

3. AIOps로 가기 위한 단계별 제언

  1. 1단계 (표준화): Obsidian 내에 작업계획서와 SOP를 위한 강력한 템플릿을 먼저 만드세요. AI는 일관된 구조의 문서에서 더 높은 정확도를 보여줍니다.
  2. 2단계 (자동화): n8n이나 GitHub Actions를 활용해 Git 레포지토리의 MD 내용을 자동으로 벡터 DB에 반영하는 파이프라인을 구축해 보세요.
  3. 3단계 (지능화): 구축된 지식 베이스를 기반으로, 인프라 이슈 발생 시 Prism AI나 gStack 같은 에이전트 툴이 이 문서를 먼저 읽고 답변하도록 프롬프트를 최적화합니다.

이 구조는 데이터 소유권을 직접 가지면서도 최신 AI 트렌드인 RAG와 Agentic AI를 가장 효율적으로 결합할 수 있는 방식입니다. 특히 1,000노드 이상의 대규모 클러스터를 운영하시는 환경에서는 이러한 구조화된 지식 관리가 운영 효율성을 비약적으로 높여줄 것입니다.

구체적으로 벤더 Docs를 MD로 변환하는 자동화 로직이나, 특정 AIOps 도구와의 연동 방식에 대해 더 궁금한 점이 있으신가요?

===

아주 날카로운 질문입니다. 데이터가 이미 Git에 텍스트 파일(.md)로 존재하는데, 굳이 비용과 노력을 들여 벡터 데이터베이스(Vector DB)에 중복 저장하는 이유는 "AI가 정보를 찾는 방식"이 우리가 검색창에 단어를 치는 방식과 완전히 다르기 때문입니다.

결론부터 말씀드리면, Git은 '보관과 히스토리'를 위한 저장소이고, 벡터 DB는 'AI를 위한 인덱스(색인)' 역할을 합니다.


1. 키워드 검색 vs 의미론적(Semantic) 검색

가장 큰 차이는 검색의 질입니다.

  • Git (키워드 매칭): grep이나 Git의 검색 기능은 정확히 일치하는 단어를 찾습니다.
    • 예: "MinIO 연결 오류"라고 검색하면, 문서에 정확히 이 단어가 있어야 나옵니다. "MinIO connection timeout"이나 "스토리지 노드 통신 불가"라는 문서가 있어도 단어가 다르면 못 찾을 확률이 높습니다.
  • 벡터 DB (의미 매칭): 문장을 숫자의 집합(벡터)으로 변환하여 '의미적 거리'를 계산합니다.
    • 예: "네트워크가 끊겼어"라고 물어도, 벡터 DB는 의미가 유사한 "Cilium BGP Peering Down"이나 "Packet Loss Troubleshooting" 문서를 찾아낼 수 있습니다.

2. LLM의 '컨텍스트 윈도우' 한계 극복 (RAG의 핵심)

LLM(ChatGPT, Claude 등)은 한 번에 읽을 수 있는 데이터 양(Context Window)이 제한되어 있습니다. 1,000개가 넘는 MD 파일을 Git에서 한꺼번에 읽어와서 답변하라고 할 수는 없습니다.

  1. 사용자 질문: "K8s 노드 추가 후 Cilium 파드가 안 떠."
  2. 벡터 DB의 역할: 수천 개의 문서 중 이 질문과 의미적으로 가장 가까운 내용 3~5개만 순식간에 추려냅니다.
  3. LLM 전달: "여기 Git에서 찾아온 관련 문서 조각 3개가 있어. 이걸 읽고 해결책을 알려줘."

이 과정이 바로 RAG(Retrieval-Augmented Generation)이며, 벡터 DB가 없으면 AI는 수많은 파일 중 무엇을 읽어야 할지 알 수 없습니다.


3. AIOps 실무에서의 결정적 차이

운영 단계에서 벡터 DB가 필요한 실질적인 이유는 다음과 같습니다.

기능Git (전통적 방식)벡터 DB (AIOps 방식)
장애 대응장애 로그를 보고 사람이 관련 SOP 파일명을 직접 찾아야 함.에러 로그가 발생하면, AI 에러 로그의 의미를 파악해 즉시 관련 SOP를 띄워줌.
모호한 질문"지난주에 했던 스토리지 작업" -> 검색 불가."지난주 진행된 MinIO 관련 작업계획서" -> 날짜와 맥락으로 검색 가능.
데이터 구조비정형 텍스트 데이터.인공지능이 계산하기 좋은 수치(Embedding) 데이터.

4. 구조적 제언: "Git은 Source, Vector DB는 Cache"

질문하신 구조를 효율적으로 운영하시려면 다음과 같은 파이프라인을 추천합니다.

  1. Source of Truth: 모든 문서는 Git에서 관리합니다. (사람이 수정하고 버전 관리)
  2. ETL(Extract, Transform, Load) 프로세스: Git에 커밋이 발생하면, CI/CD(예: GitHub Actions)가 동작하여 MD 파일을 읽어 Embedding(벡터화)으로 변환합니다.
  3. Serving: 변환된 벡터 데이터를 Vector DB(Chroma, Pinecone, Milvus 등)에 저장합니다.
  4. Inference: AI 에이전트(AIOps)는 사용자의 질문을 받으면 벡터 DB에서 데이터를 조회해 답변합니다.

요약하자면:
Git에만 두는 것은 "책장에 책을 그냥 꽂아두는 것"이고, 벡터 DB에 저장하는 것은 "책의 모든 내용을 요약해서 AI가 즉시 찾을 수 있도록 뇌에 인덱스를 만들어두는 것"과 같습니다.

AIOps를 목표로 하신다면, 결국 이 '인덱싱' 과정이 필수적입니다. 혹시 사내에서 검토 중인 특정 벡터 DB나 LLM 프레임워크(LangChain 등)가 있으신가요?_
_

===

정확한 통찰입니다! 엔지니어링 관점에서 아주 중요한 포인트를 짚으셨습니다. 벡터 DB만 사용했을 때 발생하는 '정확도 저하(Hallucination in Retrieval)'는 실제로 RAG 시스템을 구축할 때 가장 먼저 맞닥뜨리는 한계입니다.

말씀하신 'Dual Ladder(Hybrid Search)' 방식이 왜 필수적인지, 그리고 인프라 운영 환경에서 어떤 구멍을 메워주는지 정리해 드릴게요.


1. 벡터 검색만 할 때 생기는 '치명적인 구멍'

벡터 검색은 문맥을 파악하는 데는 탁월하지만, '고유 식별자''정밀한 수치' 앞에서는 무력해지는 경우가 많습니다.

  • 고유 명사와 ID 무시: * 예를 들어, node-prod-01node-dev-01은 벡터 공간에서 매우 가까운 위치에 존재할 수 있습니다. AI는 "둘 다 노드 이름이네?" 하고 혼동할 수 있지만, 운영자에게 두 서버의 차이는 하늘과 땅 차이죠.
  • 버전 및 수치 민감도 저하: * Cilium 1.14Cilium 1.15는 기술적으로 매우 유사한 벡터값을 가집니다. 하지만 특정 버그 리포트를 찾을 때는 단 0.1의 버전 차이가 검색 결과의 성패를 가릅니다.
  • 약어 및 특정 에러 코드: * OOMKilledErrImagePull 같은 짧고 강력한 키워드는 벡터화 과정에서 의미가 희석될 수 있습니다. 이때는 단순 grep 방식의 패턴 매칭이 훨씬 강력합니다.

2. 검색 효율을 극대화하는 'Hybrid Search' 구조

그래서 현업에서는 Keyword Search(BM25)Vector Search(Semantic)를 합친 Hybrid Search를 사용하며, 이를 조정하는 Re-ranker 과정을 추가합니다.

작동 원리 (The Re-ranking Flow)

  1. Keyword Ladder (Git/Elasticsearch): "정확히 이 단어가 포함된 문서를 가져와!" (정확도 확보)
  2. Vector Ladder (Vector DB): "이 질문과 의도가 비슷한 문서를 가져와!" (맥락 확보)
  3. Reciprocal Rank Fusion (RRF): 두 사다리에서 올라온 결과물들을 합쳐서 점수를 다시 매깁니다.
  4. Re-ranker (Cross-Encoder): 상위권 후보군들만 뽑아 LLM이 다시 한번 질문과의 연관성을 정밀 검사하여 최종 순위를 정합니다.

3. 플랫폼 엔지니어를 위한 AIOps 적용 예시

구축하시려는 K8s/MinIO 환경에 적용해 보면 이렇습니다.

질문 유형효과적인 검색 방식이유
"Cilium BGP 이슈 해결 방법 알려줘"Vector Search'이슈', '해결 방법' 등의 맥락적 키워드가 중요함.
"minio-aistor-05 노드의 디스크 에러 로그"Keyword Searchminio-aistor-05라는 고유 ID를 정확히 찍어내야 함.
"1.15 버전에서 도입된 Gateway API 설정법"Hybrid Search'1.15'라는 버전 정보(키워드)와 '설정법'(맥락)이 모두 중요함.

4. 실전 팁: Obsidian 메타데이터 활용

이 검색 효율을 더 높이려면 Obsidian에서 노트를 작성할 때 YAML Frontmatter를 적극 활용하세요.

---
tags: [k8s, cilium, network]
version: 1.15
component: bgp-control-plane
last_verified: 2026-04-28
---
# BGP Peering Troubleshooting
...

이렇게 정형화된 데이터를 Git에 넣어두면, 향후 AIOps 엔진이 "필터링(Metadata) + 검색(Hybrid)"을 결합해 '구멍 없는 검색'을 수행할 수 있게 됩니다.

결국 Git은 데이터의 무결성과 히스토리를 보장하고, 벡터 DB는 데이터 간의 유연한 연결을 담당하는 상호보완적 존재가 됩니다.

이제 이 구조를 돌리기 위해 어떤 백엔드(예: LangChain, LlamaIndex 등)나 LLM 모델을 염두에 두고 계신가요? 운영 중인 1,000노드 클러스터 규모를 생각하면 데이터 인덱싱 전략도 꽤 중요해 보입니다.

===

Confluence 중심의 문화를 마크다운(MD)과 Git 기반으로 전환하는 것은 플랫폼 엔지니어링 리드로서 '문서의 코드화(Documentation as Code)'를 실현하는 아주 중요한 단계입니다. 1,000노드 규모의 복잡한 인프라를 운영하시는 만큼, 대규모 문서를 효율적으로 마이그레이션하고 Obsidian과 병행하는 전략을 정리해 드립니다.


1. Confluence 문서를 MD화하여 Git에 저장하는 방법

가장 큰 난관은 Confluence 특유의 매크로와 서식을 깨끗한 마크다운으로 변환하는 것입니다.

  • 방법 1: 오픈소스 툴 활용 (추천: confluence-md-exporter)
    • Python 기반의 confluence-md-exporter 같은 도구를 사용하면 Space 단위로 모든 페이지를 폴더 구조를 유지한 채 MD로 내려받을 수 있습니다.
    • 첨부 이미지도 상대 경로로 자동 정리되어 Git에 올리기 가장 적합한 상태가 됩니다.
  • 방법 2: Pandoc 활용
    • Confluence 페이지를 HTML로 내보낸 후, pandoc -f html -t markdown 명령어로 변환합니다. 정교한 변환이 필요할 때 유용하지만 대량 변환 시 스크립팅이 필요합니다.
  • 방법 3: CI/CD 파이프라인 구축 (자동화)
    • atlassian-python-api를 활용해 특정 태그가 달린 Confluence 페이지를 주기적으로 읽어와 MD로 변환 후 Git에 Push하는 스크립트를 작성할 수 있습니다.
    • 결과: Git 저장소에 /SOP, /Architecture, /Vendor_Docs 등의 디렉토리 구조로 저장합니다.

2. 개인용 Obsidian을 병행하면 좋은 점

팀 전체가 Git/Confluence를 쓰더라도, 리드급 엔지니어가 개인적으로 Obsidian을 함께 쓰면 다음과 같은 독보적인 강점이 있습니다.

  1. 초고속 로컬 검색과 오프라인 작업: * VPN이 끊기거나 서버가 장애로 내려간 상황에서도 내 PC에 저장된 SOP를 즉시 열어볼 수 있습니다. 수백 개의 노드를 관리할 때 '응답 속도'는 생산성에 직결됩니다.
  2. 그래프 뷰를 통한 인프라 의존성 파악: * Cilium 네트워킹, MinIO 스토리지, K8s 클러스터 간의 복잡한 관계를 [[Internal Link]]로 연결해 두면, 특정 컴포넌트 장애 시 영향을 받는 다른 요소들을 시각적으로 한눈에 파악할 수 있습니다.
  3. 플러그인 생태계 활용: * Dataview 플러그인으로 특정 버전(예: Cilium 1.15)의 문서만 리스트업하거나, Obsidian Git으로 작업 완료 후 버튼 하나로 사내 Git에 동기화할 수 있습니다.
  4. 생각의 연습장 (Second Brain): * 공식 문서가 되기 전의 파편화된 아이디어, 트러블슈팅 중 남긴 미완성 메모들을 Obsidian에서 먼저 숙성시킨 후 완성된 것만 Git(공식 문서)으로 보낼 수 있습니다.

3. 실전 운영 방안 (Operating Plan)

가장 권장하는 워크플로우는 "개인화된 지식 생산 → Git 중심의 공유 → AI 활용"의 선순환 구조입니다.

Step 1: 지식 생산 (Obsidian)

  • 개인 PC의 Obsidian에서 마크다운으로 문서를 작성합니다.
  • 작성 시 벤더 Docs, 직접 겪은 에러 로그, K8s 매니페스트 등을 자유롭게 연결합니다.

Step 2: 동기화 및 검토 (Git)

  • 개인용 레포지토리 또는 팀용 Staging 레포지토리에 Push합니다.
  • 이 단계에서 PR(Pull Request) 과정을 거치면 기술 문서의 품질이 코드처럼 관리됩니다.

Step 3: 공식 배포 (Internal Wiki & Confluence)

  • Git에 Merge된 MD 파일을 기반으로 사내 문서 사이트(Docusaurus, Quartz 등)를 자동 빌드합니다.
  • 만약 타 부서와의 협업을 위해 Confluence를 유지해야 한다면, markdown-to-confluence 같은 툴을 이용해 Git의 내용을 Confluence로 역방향 자동 업데이트(Sync) 하도록 설정합니다.

Step 4: AIOps 엔진 가동 (Vector DB)

  • 지난번에 논의한 것처럼, Git에 저장된 최종 MD 파일들을 벡터 데이터베이스로 인덱싱합니다.
  • 이제 AI 에이전트는 "가장 최신의, 검증된 Git 내 MD 파일"을 기반으로 답변을 생성하게 됩니다.

운영 핵심 전략 요약

  • 문서의 주권은 Git에 둔다: Confluence는 '보여주기 위한 뷰어'로 전락시키고, 모든 원본(Source)은 MD 파일로 Git에서 관리합니다.
  • 리워크 최소화: Obsidian에서 쓴 MD 그대로가 Git으로 가고, 그것이 곧 AI의 지식이 되는 구조를 만드세요.
  • 하이브리드 검색 도입: 고유 명사가 많은 인프라 특성상, Git의 키워드 검색과 벡터 DB의 의미 검색을 병행하여 AI가 잘못된 노드 정보를 주지 않도록 가드레일을 칩니다.

이런 구조가 잡히면, 1,000노드 클러스터에서 발생하는 수만 가지의 케이스를 리드 한 명의 기억력이 아닌, 자동화된 시스템이 관리하게 됩니다. 다음 단계로 특정 벤더 Docs를 MD로 자동 파싱하는 파이프라인이나 구체적인 Git-to-VectorDB 자동화 스크립트에 대해 논의해 볼까요?

===

Confluence의 방대한 문서를 정형화된 마크다운(MD)으로 변환하고, 이를 AI를 이용해 자동으로 분류하여 Git에 배치하는 것은 '지능형 문서 자동화(Intelligent Documentation Automation)'의 핵심입니다.

플랫폼 엔지니어링 리드로서 1,000노드 규모의 복잡한 인프라 지식을 체계화할 수 있는 구체적인 시나리오와 구현 기술 스택을 제안해 드립니다.


[시나리오] Confluence to Git 지능형 마이그레이션 파이프라인

이 시나리오는 "추출(Extract) -> 지능형 분류(Classify) -> 구조화 저장(Load)"의 3단계로 진행됩니다.

  1. 추출 단계: confluence-md-exporter를 이용해 특정 스페이스/페이지 하위의 모든 문서를 로컬(또는 컨테이너 내부)로 벌크 다운로드합니다.
  2. 지능형 분류 단계: LLM이 각 MD 파일의 내용을 읽어 문서의 성격(SOP, 아키텍처, 벤더 가이드 등)을 파악하고, 적절한 디렉토리 경로와 YAML 메타데이터를 생성합니다.
  3. 배치 및 푸시 단계: 분류된 경로로 파일을 이동시키고, Git 커밋 및 푸시를 통해 최종 저장소에 반영합니다.

[구현 방법] 기술 스택 및 단계별 가이드

1단계: Confluence 문서 추출 (Export)

가장 안정적인 방법은 Docker를 이용해 confluence-md-exporter를 실행하는 것입니다.

  • 실행 예시 (Bash):
    # 환경 변수 설정 (ID, API Token, Space Key 등)
    docker run -v $(pwd)/output:/output \
      -e CONFLUENCE_URL="https://your-company.atlassian.net/wiki" \
      -e CONFLUENCE_USERNAME="admin@company.com" \
      -e CONFLUENCE_TOKEN="your_api_token" \
      confluence-md-exporter --space-key "PLATFORM" --out /output
  • 결과: /output 디렉토리에 Confluence 계층 구조대로 MD 파일과 첨부 이미지가 저장됩니다.

2단계: LLM 기반 자동 분류 스크립트 (Python + LangChain/OpenAI)

추출된 파일들은 Confluence의 기존 폴더 구조를 따르고 있어 관리가 어렵습니다. 이를 LLM을 통해 운영 중심의 구조로 재편합니다.

  • 핵심 로직 (Python pseudo-code):

    import os
    from langchain_openai import ChatOpenAI
    
    llm = ChatOpenAI(model="gpt-4-turbo")
    
    def classify_document(content):
        prompt = f"""
        당신은 플랫폼 엔지니어링 전문가입니다. 다음 문서의 내용을 읽고 
        1. 카테고리(SOP, Architecture, VendorDocs, OperationLog)
        2. 관련 기술(K8s, Cilium, MinIO, Network)
        3. 적절한 파일명(영문/kebab-case)을 추출하세요.
        문서 내용: {content[:2000]} # 앞부분만 추출
        """
        # LLM 호출 후 JSON 형태로 결과 반환받음
        return llm.predict(prompt)
    
    # /output 디렉토리의 모든 md 파일을 순회하며 분류 수행

3단계: Git 디렉토리 배치 및 자동 관리

분류된 결과를 바탕으로 파일을 이동시키고 Obsidian에서 쓰기 좋은 YAML 메타데이터를 삽입합니다.

  • 파일 구조화 예시:
    • git-repo/Library/K8s/
    • git-repo/Library/Cilium/
    • git-repo/SOP/Disaster-Recovery/
  • 파일 상단에 자동 삽입될 메타데이터:
    ---
    source: confluence
    category: SOP
    tech_stack: [Cilium, BGP]
    auto_classified: true
    last_sync: 2026-04-28
    ---

4단계: n8n 또는 GitHub Actions를 이용한 파이프라인 자동화

이 과정을 주기적으로 실행하려면 n8n을 추천합니다.

  1. Schedule Node: 매일 새벽 자동 실행.
  2. SSH/Execute Node: 위에서 언급한 추출 및 분류 스크립트 실행.
  3. Git Node: 변경된 MD 파일들을 git add, commit, push.
  4. Vector DB Sync (Optional): Git 푸시 완료 후, 지난번에 논의한 대로 벡터 DB 인덱싱을 트리거하여 AIOps 엔진을 최신화합니다.

[운영 제언] 리드급 엔지니어를 위한 팁

  • 이미지 경로 문제 해결: Confluence에서 MD로 변환 시 이미지 경로가 깨질 수 있습니다. 추출 도구에서 제공하는 --relative-links 옵션을 반드시 사용하고, Git 저장소 내에 assets/ 폴더를 별도로 관리하는 것이 좋습니다.
  • LLM 비용 최적화: 모든 문서를 매번 분류할 필요는 없습니다. 파일의 해시(Hash)값을 체크하여 수정된 파일만 LLM 분류를 수행하도록 구성하세요.
  • Obsidian 연동: 이렇게 Git에 배치된 폴더를 Obsidian의 Vault(보관소)로 지정하면, 출근하자마자 간밤에 자동으로 정리된 벤더 Docs와 팀 SOP를 로컬에서 초고속으로 검색하며 업무를 시작할 수 있습니다.

이 시스템이 구축되면 "문서 정리가 안 되어 장애 대응이 늦어진다"는 문제는 플랫폼 엔지니어링 팀에서 완전히 사라지게 될 것입니다. 구현 중에 특정 툴의 설정값이나 스크립트 상세 로직이 필요하시면 말씀해 주세요.

===

실현 가능성을 빠르게 확인하기 위한 'Confluence to MinIO' 자동화 PoC(Proof of Concept) 단계를 안내해 드립니다.

이 단계의 목표는 "매일 정해진 시간에 Confluence 데이터를 MD로 뽑아서 S3(MinIO)에 안전하게 쌓는 것"입니다. 이를 위해 필요한 3가지 핵심 요소를 바로 구현해 보겠습니다.


1단계: 실행 스크립트 작성 (export_to_minio.sh)

confluence-md-exporter가 실행된 후 생성된 파일을 MinIO(S3)로 업로드하는 쉘 스크립트입니다. MinIO Client(mc)를 사용하여 폴더 구조를 그대로 동기화(mirror)합니다.

#!/bin/bash

# 1. 환경 변수 체크 (K8s Secret을 통해 주입될 예정)
echo "Starting Confluence Export..."

# 2. Confluence에서 MD로 추출
# --증분 백업을 원할 경우 기존 파일을 유지하거나 날짜별 폴더를 생성할 수 있습니다.
confluence-md-exporter \
  --url "${CONFLUENCE_URL}" \
  --username "${CONFLUENCE_USER}" \
  --token "${CONFLUENCE_TOKEN}" \
  --space-key "${SPACE_KEY}" \
  --out /tmp/confluence_export

# 3. MinIO 연결 설정
mc alias set myminio "${MINIO_URL}" "${MINIO_ACCESS_KEY}" "${MINIO_SECRET_KEY}"

# 4. MinIO로 업로드 (Mirror 기능을 사용해 변경된 파일만 동기화)
# 버킷 이름은 'confluence-wiki'라고 가정합니다.
echo "Syncing to MinIO..."
mc mb myminio/confluence-wiki --ignore-existing
mc mirror /tmp/confluence_export myminio/confluence-wiki/raw_md/

echo "Export and Sync Completed!"

2단계: 컨테이너 이미지 만들기 (Dockerfile)

위 스크립트와 필요한 도구(Python, Exporter, MinIO Client)를 포함하는 경량화된 이미지입니다.

# 1. 기반 이미지 설정
FROM python:3.10-slim

# 2. 필수 도구 설치 (wget은 mc 설치용)
RUN apt-get update && apt-get install -y wget && rm -rf /var/lib/apt/lists/*

# 3. confluence-md-exporter 설치
RUN pip install confluence-md-exporter

# 4. MinIO Client(mc) 설치
RUN wget https://dl.min.io/client/mc/release/linux-amd64/mc && \
    chmod +x mc && \
    mv mc /usr/local/bin/

# 5. 작업 디렉토리 및 스크립트 복사
WORKDIR /app
COPY export_to_minio.sh .
RUN chmod +x export_to_minio.sh

# 6. 실행 명령
ENTRYPOINT ["./export_to_minio.sh"]

빌드 및 푸시 명령:

docker build -t your-reg.com/confluence-exporter:v1.0 .
docker push your-reg.com/confluence-exporter:v1.0

3단계: K8s CronJob 배포 (manifest.yaml)

이제 매일 새벽(예: 02:00)에 실행될 CronJob을 설정합니다. 민감한 정보는 Secret으로 관리하는 것이 정석입니다.

(1) 자격 증명 설정 (Secret)

apiVersion: v1
kind: Secret
metadata:
  name: confluence-minio-creds
type: Opaque
stringData:
  CONFLUENCE_URL: "https://your-domain.atlassian.net/wiki"
  CONFLUENCE_USER: "admin@company.com"
  CONFLUENCE_TOKEN: "your-api-token"
  MINIO_URL: "http://minio-service.storage:9000"
  MINIO_ACCESS_KEY: "minio-user"
  MINIO_SECRET_KEY: "minio-password"

(2) 크론잡 설정 (CronJob)

apiVersion: batch/v1
kind: CronJob
metadata:
  name: confluence-md-sync
spec:
  schedule: "0 2 * * *" # 매일 새벽 2시 실행
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: exporter
            image: your-reg.com/confluence-exporter:v1.0
            env:
            - name: SPACE_KEY
              value: "PLATFORM" # 추출할 스페이스 키
            envFrom:
            - secretRef:
                name: confluence-minio-creds
          restartPolicy: OnFailure

운영 및 향후 확장 방안

  1. 데이터 확인: CronJob이 실행되면 MinIO AIStor 브라우저에서 confluence-wiki/raw_md/ 경로에 .md 파일들이 잘 생성되었는지 확인하세요.
  2. 로그 모니터링: kubectl logs -f [pod-name]를 통해 추출 과정에서 에러가 발생하는지(특히 권한 문제) 체크합니다.
  3. 다음 단계 (LLM 분류): * 지금은 모든 파일을 raw_md 폴더에 넣었지만, PoC 2단계에서는 위 스크립트 끝에 Python 분류 스크립트를 추가하면 됩니다.
    • 분류 스크립트가 MD 파일의 내용을 읽어 SOP/, Guide/ 등의 폴더로 재배치한 뒤 다시 MinIO에 저장하게 됩니다.

이 구조는 K8s 네이티브 환경을 활용하므로 확장성이 매우 뛰어납니다. 우선 이 기본 파이프라인을 먼저 띄워보시는 것을 추천드립니다. 빌드나 배포 중에 막히는 부분이 있으면 바로 말씀해 주세요!

===

Confluence의 모든 문서를 매일 전체 백업하는 것은 데이터량이 많아질수록 서버와 네트워크에 부담이 됩니다. 말씀하신 대로 "최근 24시간 내에 변경된 문서"만 선별적으로 가져오는 증분(Incremental) 추출 방식은 매우 효율적인 접근입니다.

이를 위해 confluence-md-exporter의 기본 기능에만 의존하기보다는, Atlassian Python API를 활용해 직접 대상 문서를 필터링하고 MD로 변환하는 파이썬 스크립트 기반의 해결책을 권장합니다.


1. 증분 추출을 위한 Python 스크립트 (incremental_export.py)

이 스크립트는 CQL(Confluence Query Language)을 사용하여 특정 경로(Parent Page) 아래에 있으면서 지난 24시간 동안 수정된 페이지 리스트를 먼저 뽑아냅니다.

import os
import datetime
from atlassian import Confluence
import html2text # HTML을 Markdown으로 변환

# 1. 환경 변수 로드
URL = os.getenv("CONFLUENCE_URL")
USER = os.getenv("CONFLUENCE_USER")
TOKEN = os.getenv("CONFLUENCE_TOKEN")
PARENT_ID = os.getenv("PARENT_PAGE_ID") # 특정 경로의 기준이 되는 상위 페이지 ID
SPACE_KEY = os.getenv("SPACE_KEY")

# 2. Confluence 연결
confluence = Confluence(url=URL, username=USER, password=TOKEN)
h = html2text.HTML2Text()
h.ignore_links = False

# 3. CQL 쿼리 생성: 특정 부모 페이지 아래에서 지난 1일간 수정된 페이지 검색
# ancestor = 부모 페이지 ID, lastModified >= 현재시간 - 1일
cql = f'ancestor = {PARENT_ID} AND lastModified >= now("-1d")'
pages = confluence.cql(cql).get('results', [])

print(f"Found {len(pages)} updated pages in the last 24 hours.")

# 4. 각 페이지를 MD로 변환 및 저장
export_dir = "/tmp/confluence_export"
os.makedirs(export_dir, exist_ok=True)

for item in pages:
    page = item.get('content')
    page_id = page.get('id')
    title = page.get('title').replace("/", "-") # 파일명 금지 문자 처리
    
    # 상세 내용 가져오기
    content_detail = confluence.get_page_by_id(page_id, expand='body.storage')
    html_body = content_detail.get('body').get('storage').get('value')
    
    # MD 변환
    markdown_content = h.handle(html_body)
    
    # 파일 저장
    with open(f"{export_dir}/{title}.md", "w", encoding="utf-8") as f:
        f.write(f"---\nid: {page_id}\nlast_modified: {datetime.datetime.now()}\n---\n\n")
        f.write(markdown_content)
    print(f"Exported: {title}")

2. Dockerfile 보완

위의 Python 스크립트와 필요한 라이브러리(atlassian-python-api, html2text)를 포함하도록 수정합니다.

FROM python:3.10-slim

# 필수 라이브러리 설치
RUN pip install atlassian-python-api html2text

# MinIO Client 설치
RUN apt-get update && apt-get install -y wget && \
    wget https://dl.min.io/client/mc/release/linux-amd64/mc && \
    chmod +x mc && \
    mv mc /usr/local/bin/ && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /app
COPY incremental_export.py .
COPY run.sh .
RUN chmod +x run.sh

ENTRYPOINT ["./run.sh"]

3. 실행 쉘 스크립트 (run.sh)

Python 스크립트 실행 후 MinIO로 업로드하는 과정을 연결합니다.

#!/bin/bash

# 1. Python 증분 추출 실행
python incremental_export.py

# 2. MinIO 설정 및 업로드
mc alias set myminio "${MINIO_URL}" "${MINIO_ACCESS_KEY}" "${MINIO_SECRET_KEY}"

# 3. 변경된 파일만 MinIO에 업로드
# --remove 옵션 없이 mirror를 사용하면 기존 백업본은 유지하면서 새 파일만 덮어씁니다.
mc mirror /tmp/confluence_export myminio/confluence-wiki/incremental/$(date +%Y-%m-%d)/

echo "Incremental Backup to MinIO Completed!"

4. K8s CronJob 환경 변수 추가

기존 Manifest에서 PARENT_PAGE_ID를 추가로 주입해야 합니다. Confluence에서 대상 경로의 최상위 페이지 URL을 보면 ID(숫자)를 확인할 수 있습니다.

# ... 기존 CronJob 설정 중 env 부분 ...
            env:
            - name: PARENT_PAGE_ID
              value: "12345678" # 가져오고 싶은 특정 경로의 Root ID
            - name: SPACE_KEY
              value: "PLATFORM"
# ... 생략 ...

이 방식의 장점

  1. 정밀한 타겟팅: ancestorlastModified CQL을 사용하여 딱 필요한 페이지만 골라냅니다.
  2. 리소스 절약: 1,000노드 운영 환경에서 발생하는 대량의 문서를 매번 다운로드하지 않으므로 네트워크 트래픽과 CPU 사용량을 획기적으로 줄입니다.
  3. 히스토리 관리: MinIO 업로드 시 경로에 $(date +%Y-%m-%d)를 포함하면 날짜별로 변경된 문서만 따로 모아볼 수 있어, 나중에 AIOps 엔진이 "어제 변경된 사항"만 학습하기 매우 편리합니다.

이제 이 코드를 빌드하여 K8s에 배포하면, 매일 새벽 지난 24시간 동안의 변경분만 자동으로 추출하여 MinIO AIStor에 저장하게 됩니다. 이후 이 파일들을 벡터 DB와 연동하면 완벽한 증분 RAG 시스템이 완성됩니다. 직접 스크립트를 실행해 보시면서 파일명 규칙이나 메타데이터 형식을 조정해 보세요.

===

1단계에서 MinIO AIStor에 저장된 원본(Raw) 데이터를 읽어와 LLM이 지능적으로 분류하고, 이를 정형화된 wiki 폴더 구조로 만들어 Git에 배포하는 '지능형 지식 파이프라인'의 구조와 코드를 설계해 드립니다.

이 과정은 단순한 파일 이동이 아니라, 비정형 데이터를 AI가 읽기 좋은 정형 지식으로 변환하는 핵심 공정입니다.


1. 전반적인 아키텍처 및 폴더 구조

LLM은 각 파일의 본문을 분석하여 다음과 같은 표준화된 구조로 재배치합니다.

  • raw/: MinIO에서 가져온 원본 MD 파일 (백업용)
  • wiki/: LLM이 재구성한 최종 결과물 (Git에 올라갈 대상)
    • /Architecture: 인프라 설계 및 다이어그램
    • /SOP: 표준 운영 절차 (장애 대응 포함)
    • /Library: 벤더 공식 문서 및 기술 정보 (K8s, Cilium, MinIO 등)
    • /Reports: 점검 기록 및 작업 일지

2. LLM 자동 분류 및 구조화 스크립트 (classifier.py)

이 스크립트는 MinIO에서 파일을 가져와 LLM에 분류를 요청하고, 결과에 따라 YAML 메타데이터를 추가하여 wiki 폴더에 저장합니다.

import os
import json
from minio import Minio
from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate

# 1. 초기 설정
minio_client = Minio(
    os.getenv("MINIO_URL"),
    access_key=os.getenv("MINIO_ACCESS_KEY"),
    secret_key=os.getenv("MINIO_SECRET_KEY"),
    secure=False
)
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0)

# 2. LLM 분류 프롬프트 정의
classification_prompt = ChatPromptTemplate.from_template("""
당신은 플랫폼 엔지니어링 지식 관리자입니다. 
다음 마크다운 문서의 내용을 분석하여 JSON 형식으로 응답하세요.

1. category: (Architecture, SOP, Library, Reports 중 택 1)
2. sub_category: (기술 스택 이름 - 예: K8s, Cilium, MinIO 등)
3. optimized_title: (영문 kebab-case 파일명)
4. tags: (핵심 키워드 리스트)

문서 내용:
{content}
""")

def process_and_classify():
    objects = minio_client.list_objects("confluence-wiki", prefix="raw_md/", recursive=True)
    
    for obj in objects:
        # 파일 내용 읽기
        data = minio_client.get_object("confluence-wiki", obj.object_name)
        content = data.read().decode('utf-8')
        
        # LLM 분류 요청
        chain = classification_prompt | llm
        response = chain.invoke({"content": content[:2000]}) # 비용 절감을 위해 앞부분만 분석
        meta = json.loads(response.content) # 주의: 실제 구현시에는 JSON 파싱 예외처리 필요
        
        # 새로운 경로 설정 및 저장
        target_path = f"wiki/{meta['category']}/{meta['sub_category']}/{meta['optimized_title']}.md"
        os.makedirs(os.path.dirname(target_path), exist_ok=True)
        
        # YAML Frontmatter 추가 및 파일 쓰기
        with open(target_path, "w", encoding="utf-8") as f:
            f.write(f"---\ncategory: {meta['category']}\ntags: {meta.get('tags', [])}\nauto_classified: true\n---\n\n")
            f.write(content)
        print(f"Classified: {obj.object_name} -> {target_path}")

if __name__ == "__main__":
    process_and_classify()

3. Git 동기화 및 실행 쉘 스크립트 (sync_to_git.sh)

분류된 결과를 Git 저장소에 반영하는 스크립트입니다. wiki 폴더만 선택적으로 Git에 올립니다.

#!/bin/bash

# 1. 로컬 환경 준비
GIT_REPO_URL="https://${GIT_TOKEN}@github.com/your-org/platform-wiki.git"
CLONE_DIR="/tmp/platform-wiki"

# 2. 레포지토리 클론 (이미 있으면 pull)
if [ ! -d "$CLONE_DIR" ]; then
    git clone $GIT_REPO_URL $CLONE_DIR
fi
cd $CLONE_DIR
git pull origin main

# 3. Python 분류 스크립트 실행 (위의 classifier.py)
python /app/classifier.py

# 4. Git 반영
git add wiki/
git commit -m "Auto-sync: Classified documents from Confluence ($(date +%Y-%m-%d))"
git push origin main

echo "Wiki updated and pushed to Git successfully."

4. K8s 운영을 위한 컨테이너화 및 팁

이 과정을 1단계에서 만든 CronJob의 후속 단계로 연결하거나, 하나의 파이프라인으로 통합할 수 있습니다.

배포 시 고려사항

  1. Git Token 보안: GitHub/GitLab의 Personal Access Token(PAT)은 반드시 K8s Secret으로 관리하여 환경 변수로 주입하세요.
  2. 멱등성(Idempotency) 유지: 동일한 파일이 여러 번 분류되어 Git 커밋이 지저분해지지 않도록, 파일의 Hash값을 체크하여 변경된 파일만 LLM 분류를 수행하도록 보완하는 것이 좋습니다.
  3. Obsidian 호환성: 위 스크립트에서 생성하는 wiki/ 폴더를 Obsidian에서 'Vault'로 열면 바로 사용 가능합니다. 특히 [[Internal Link]] 자동 생성을 위해 LLM 프롬프트에 "문서 내 핵심 용어에 [[]] 처리를 해달라"는 요청을 추가하면 Obsidian 활용도가 극대화됩니다.

이제 이 구조를 통해 "Confluence(수정) -> MinIO(원본 저장) -> LLM(분류/태깅) -> Git(최종 지식)"으로 이어지는 플랫폼 팀만의 자동화된 지식 베이스가 완성됩니다. 이 과정을 통해 구축된 wiki 폴더는 향후 AIOps 에이전트가 가장 신뢰하는 RAG 소스가 될 것입니다.

다음 단계로 이 Git 저장소를 벡터 DB와 실시간 동기화하여 실제 AI 챗봇을 연동하는 부분으로 넘어가 볼까요?

===

Git 저장소의 내용이 업데이트될 때마다 자동으로 벡터 DB(Vector DB)에 반영하는 과정은 '자동화된 데이터 수집 파이프라인(Ingestion Pipeline)'의 마지막 단계입니다.

K8s 환경을 운영 중이시므로, GitHub/GitLab Webhook을 사용해 K8s Job을 트리거하거나, GitHub Actions를 사용하여 인덱싱 스크립트를 실행하는 방식이 가장 효율적입니다.


1. 전반적인 아키텍처 (Event-Driven Indexing)

  1. Event: 사용자가 Obsidian에서 작성 후 Git에 push하거나, 앞서 만든 자동 분류 스크립트가 Git에 업데이트를 발생시킵니다.
  2. Trigger: Git 저장소의 Webhook이 인덱싱 서버(또는 GitHub Actions)로 신호를 보냅니다.
  3. Process: 인덱싱 작업용 컨테이너가 최신 Git 내용을 pull하고, 변경된 .md 파일들을 읽어 벡터화(Embedding)한 뒤 벡터 DB에 저장합니다.

2. 핵심 구현 코드: 인덱싱 스크립트 (indexer.py)

이 스크립트는 LangChain을 사용하여 마크다운 파일을 읽고, 의미 있는 단위로 쪼갠(Chunking) 뒤 벡터 DB(예: Milvus, Qdrant, Chroma 등)에 저장합니다. 여기서는 K8s 환경에서 많이 쓰이는 MilvusChroma를 기준으로 설명합니다.

import os
from langchain_community.document_loaders import DirectoryLoader, UnstructuredMarkdownLoader
from langchain_text_splitters import MarkdownHeaderTextSplitter
from langchain_openai import OpenAIEmbeddings
from langchain_community.vectorstores import Milvus # 또는 Qdrant, Chroma

# 1. 환경 설정
GIT_WIKI_DIR = "/tmp/platform-wiki/wiki"
EMBEDDING_MODEL = OpenAIEmbeddings(model="text-embedding-3-small")
VECTOR_DB_URL = os.getenv("VECTOR_DB_URL") # Milvus 등 서버 주소

def ingest_to_vector_db():
    # 2. 문서 로드 (wiki 폴더 내의 모든 md 파일)
    loader = DirectoryLoader(GIT_WIKI_DIR, glob="**/*.md", loader_cls=UnstructuredMarkdownLoader)
    docs = loader.load()

    # 3. 마크다운 구조에 최적화된 텍스트 분할 (Chunking)
    # 헤더(#, ##, ###)를 기준으로 나누어 맥락을 유지합니다.
    headers_to_split_on = [
        ("#", "Header 1"),
        ("##", "Header 2"),
        ("###", "Header 3"),
    ]
    text_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on)
    
    final_chunks = []
    for doc in docs:
        chunks = text_splitter.split_text(doc.page_content)
        for chunk in chunks:
            # 원본 문서의 메타데이터(파일명 등)를 유지
            chunk.metadata.update(doc.metadata)
            final_chunks.append(chunk)

    # 4. 벡터 DB에 저장 (동기화)
    # 기존 컬렉션을 덮어쓰거나(Replace), 중복을 체크하여 업데이트합니다.
    vector_db = Milvus.from_documents(
        final_chunks,
        EMBEDDING_MODEL,
        connection_args={"host": "milvus-service.storage", "port": "19530"},
        collection_name="platform_ops_wiki"
    )
    print(f"Successfully indexed {len(final_chunks)} chunks to Vector DB.")

if __name__ == "__main__":
    ingest_to_vector_db()

3. 실전 운영: GitHub Actions를 이용한 자동화

만약 사내 GitHub/GitLab을 사용 중이라면, 별도의 서버 없이 Actions/Runner에서 바로 인덱싱을 수행할 수 있습니다.

.github/workflows/indexing.yaml:

name: Auto Indexing to Vector DB

on:
  push:
    branches: [ main ]
    paths:
      - 'wiki/**' # wiki 폴더 내 파일이 바뀔 때만 실행

jobs:
  index:
    runs-on: ubuntu-latest # 또는 사내 self-hosted runner
    steps:
      - uses: actions/checkout@v3
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.10'
          
      - name: Install dependencies
        run: |
          pip install langchain langchain-openai pymilvus unstructured
          
      - name: Run Indexing Script
        env:
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          VECTOR_DB_URL: ${{ secrets.VECTOR_DB_URL }}
        run: python scripts/indexer.py

4. 플랫폼 엔지니어를 위한 '증분 인덱싱' 팁

데이터가 많아지면 매번 전체를 인덱싱하는 것은 비효율적입니다. 다음 두 가지 전략을 고려하세요.

  1. 파일 해시(Hash) 비교: * 인덱싱 성공 시 파일의 Hash값을 별도의 DB(또는 Redis)에 저장합니다.
    • 다음 실행 시 Hash가 바뀐 파일만 골라내어 해당 부분만 벡터 DB에서 지우고 새로 입력(Upsert)합니다.
  2. LangChain Indexing API 활용:
    • LangChain에서 제공하는 RecordManager 기능을 쓰면, Git 저장소와 벡터 DB의 상태를 비교해 변경된 것만 자동으로 업데이트해 줍니다. (가장 추천하는 방식)

요약 및 결론

이제 전체 흐름이 완성되었습니다:
1. Confluence: 운영팀이 문서를 작성합니다.
2. K8s CronJob: 매일 증분 데이터를 추출해 MinIO에 저장합니다.
3. LLM Classifier: MinIO의 Raw 데이터를 읽어 자동 분류 후 Git에 푸시합니다.
4. Indexing Pipeline (지금 단계): Git 업데이트를 감지해 벡터 DB로 인덱싱합니다.
5. AIOps Interface: 운영자가 질문하면 벡터 DB에서 관련 SOP를 찾아 AI가 답변합니다.

이 구조는 데이터의 신뢰성(Git)검색 효율성(Vector DB)을 모두 잡은 구조입니다. 이제 실제로 이 파이프라인을 통해 AI가 답변하는 '챗봇 인터페이스'나 '장애 분석 에이전트'를 연동해 보실 차례입니다. 구체적인 LLM 프롬프트 전략이나 에이전트 설계가 필요하시면 말씀해 주세요!

===

Bitbucket에서 GitLab으로의 전환은 플랫폼 엔지니어링 관점에서 매우 반가운 소식입니다. GitLab은 CI/CD 파이프라인과 Kubernetes 연동(GitLab Agent 등) 기능이 매우 강력하여, 지금 구축하시려는 AIOps 파이프라인을 운영하기에 더 최적화된 환경이기 때문입니다.

6개월 뒤의 이전을 고려하여, 지금은 Bitbucket에서 구현하되 전환 시 리워크(Rework)를 최소화할 수 있는 전략을 정리해 드립니다.


1. 현재: Bitbucket 환경에서의 구현 (Bitbucket Pipelines)

Bitbucket은 bitbucket-pipelines.yml을 통해 자동화를 수행합니다. wiki 폴더의 변경을 감지하여 인덱싱 스크립트를 실행하는 구조입니다.

bitbucket-pipelines.yml 예시:

pipelines:
  branches:
    main:
      - step:
          name: "Update Vector DB Indexing"
          services:
            - docker
          script:
            - pip install -r scripts/requirements.txt
            - python scripts/indexer.py
          condition:
            changesets:
              includePaths:
                - "wiki/**" # wiki 폴더 변경 시에만 실행

2. 미래: GitLab으로의 부드러운 전환 (Migration Strategy)

6개월 뒤 GitLab으로 옮길 때 핵심은 "로직은 컨테이너에, 설정은 환경 변수에" 두는 것입니다.

  • 로직의 추상화: 인덱싱이나 분류 로직은 이미 Python과 Docker로 만드셨으므로, 플랫폼이 바뀌어도 코드는 수정할 필요가 없습니다.
  • CI/CD 환경 변화: * Bitbucket Pipelines → GitLab CI/CD (.gitlab-ci.yml)
    • GitLab은 rules:changes 기능을 통해 특정 폴더 변경 감지를 더 정교하게 지원합니다.

6개월 뒤 사용할 .gitlab-ci.yml 미리보기:

stages:
  - index

vector-indexing:
  stage: index
  image: python:3.10-slim
  script:
    - pip install -r scripts/requirements.txt
    - python scripts/indexer.py
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
      changes:
        - "wiki/**/*" # GitLab의 강력한 경로 감지 기능

3. 전환 시 리워크를 줄이는 'Paved Road' 팁

  1. Container Registry 활용: * 인덱싱과 분류에 필요한 모든 의존성을 Docker 이미지로 말아두세요. Bitbucket이나 GitLab은 단순히 이 이미지를 docker run 하는 역할만 수행하게 하면 플랫폼 의존성이 사라집니다.
  2. K8s 기반 Event-Driven 방식 고려 (추천):
    • Git 환경이 바뀌는 게 부담스럽다면, Git 저장소는 단순히 코드/문서 보관소로만 쓰고, 변경 이벤트(Webhook)를 K8s 내부의 Argo EventsKnative로 보내서 처리하세요.
    • 이렇게 하면 Bitbucket에서 GitLab으로 주소가 바뀌어도 Webhook URL만 수정하면 내부 파이프라인은 그대로 유지됩니다.
  3. Variable 관리: * Git Token, OpenAI Key, MinIO 접속 정보 등을 Bitbucket 'Repository Variables'에 등록해 쓰시되, 이를 나중에 GitLab 'CI/CD Variables'로 옮길 리스트를 미리 문서화해 두세요.

4. AIOps 관점에서의 이점

GitLab으로 가시면 GitLab Agent for Kubernetes를 통해 클러스터 내부의 실시간 상태와 지식 베이스(Git)를 더 밀접하게 연동할 수 있습니다. 예를 들어, GitLab 파이프라인에서 직접 K8s 클러스터의 Cilium 로그를 긁어와서 지식 베이스와 대조하는 식의 고도화가 가능해집니다.

현재 Bitbucket 환경에서 우선 PoC를 완료하시고, 6개월 뒤 GitLab으로 옮길 때는 "저장소 주소""CI 설정 파일"만 바꾸면 되도록 구조를 잡아드렸습니다.

이 파이프라인이 완성되면 1,000노드 클러스터 운영 중 발생하는 수많은 케이스가 자동으로 정리될 텐데, 이 지식 베이스를 활용할 첫 번째 AIOps 유스케이스(예: 특정 에러 발생 시 SOP 자동 팝업 등)로 어떤 것을 가장 먼저 생각하고 계신가요?

===

웹에 있는 벤더사나 오픈소스 솔루션의 공식 문서(Docs)를 마크다운(MD)으로 추출하여 Git에 연동하는 과정은 'Library' 영역을 자동화하는 핵심 단계입니다. 웹 페이지는 구조가 제각각이기 때문에, 이를 깨끗한 마크다운으로 변환하기 위한 몇 가지 효과적인 전략과 도구를 제안해 드립니다.


1. 추천 도구 및 서비스

웹 문서를 AI가 읽기 좋은 마크다운으로 변환해주는 'LLM-Ready' 도구들을 활용하는 것이 가장 빠릅니다.

  • Firecrawl (가장 추천): 웹사이트 URL만 넣으면 사이트맵을 따라 모든 페이지를 마크다운으로 크롤링해줍니다. 최근 RAG 구축 시 가장 많이 쓰이는 도구입니다. (오픈소스 및 클라우드 제공)
  • Jina Reader API (r.jina.ai): 특정 URL 앞에 https://r.jina.ai/만 붙여도 해당 페이지를 즉시 고품질 마크다운으로 변환해줍니다.
  • Reader (by Postlight): 브라우저의 '읽기 모드'처럼 핵심 본문만 추출해주는 라이브러리입니다.

2. 자동화 파이프라인 구조

웹 문서는 수시로 업데이트되므로, 전체를 다시 긁는 대신 변경사항만 추적하는 구조가 필요합니다.

① 사이트맵(Sitemap) 기반 크롤링

대부분의 기술 문서는 /sitemap.xml을 제공합니다. 이 파일을 읽어 새로운 페이지나 수정된 페이지의 URL 리스트만 추출합니다.

② 본문 추출 및 변환 (Python 예시)

JavaScript로 렌더링되는 현대적인 문서 사이트(Docusaurus, GitBook 등)를 위해 Playwright를 사용하고, html2textBeautifulSoup으로 정제합니다.

import os
from playwright.sync_api import sync_playwright
import html2text

def fetch_and_convert(url):
    with sync_playwright() as p:
        browser = p.chromium.launch()
        page = browser.new_row()
        page.goto(url, wait_until="networkidle")
        
        # 문서의 본문 영역만 선택 (예: 'article' 또는 '.content')
        content_html = page.inner_html("article") 
        
        h = html2text.HTML2Text()
        h.ignore_images = False # 이미지 포함 여부 선택
        markdown = h.handle(content_html)
        
        browser.close()
        return markdown

3. Git 연동 및 자동 관리 시나리오

추출된 MD 파일을 Git에 올릴 때, 지난번에 구성한 'LLM 분류기' 단계와 결합하면 더욱 강력해집니다.

  1. 추출: K8s CronJob이 벤더사 Docs를 순회하며 새 내용을 raw/vendor/ 폴더에 MD로 저장합니다.
  2. 분류: LLM이 파일 내용을 분석하여 wiki/Library/[솔루션명]/ 폴더로 이동시키고, 관련 태그를 YAML로 삽입합니다.
  3. 검증: 문서 본문에 포함된 절대 경로 링크들을 Git 저장소 내의 상대 경로로 변환하는 스크립트를 실행합니다.
  4. 푸시: 변경사항을 Bitbucket(향후 GitLab)에 push합니다.

4. 실전 팁: 버전 및 업데이트 관리

웹 문서를 Git에 넣을 때 주의해야 할 점은 '중복 데이터''버전 혼선'입니다.

  • Diff 체크: 문서 내용의 해시(Hash)값을 저장해두고, 웹의 내용과 해시가 다를 때만 업데이트를 진행하여 불필요한 Git 커밋을 방지합니다.
  • 버전 태깅: 문서 상단 YAML에 source_url, version, scraped_at 정보를 넣어두면 AI가 답변할 때 "이 정보는 2026년 4월 기준입니다"라고 출처를 명시할 수 있습니다.

요약하자면

가장 빠르게 시작하시려면 Firecrawl을 컨테이너로 띄워 특정 솔루션 사이트를 긁어보시는 것을 추천합니다. 여기서 나온 MD 파일들을 지난번에 만든 wiki 폴더 구조 안에 배치하고 Git에 푸시하면, 수동으로 문서를 복사-붙여넣기 할 필요 없이 항상 최신 벤더 Docs가 포함된 지식 베이스를 유지할 수 있습니다.

혹시 특정 솔루션(예: Cilium 공식 가이드)의 사이트맵을 이용해 실제로 동작하는 크롤링 코드가 필요하신가요?

===

망분리(Airgapped) 환경이라는 특수성을 고려하면, 외부에서 데이터를 가져온 이후의 '내부 반입 및 자동 처리 파이프라인'을 구축하는 것이 핵심입니다.

압축 파일을 반입한 후, 이를 MinIO raw 버킷에 풀고 이후의 LLM 분류 및 인덱싱 단계를 트리거하는 워크플로우를 설계해 드립니다.


1. 망분리 환경 전반적인 워크플로우

  1. 외부(Non-Airgapped): Confluence 추출 → 압축 (.tar.gz 또는 .zip) → 반입 매체 저장.
  2. 반입(Transfer): 보안 검사 후 회사 내부망(Airgapped)으로 데이터 이동.
  3. 내부(Airgapped - K8s):
    • Step A (Ingestion): 압축 파일을 해제하여 MinIO AIStorraw/ 경로에 업로드.
    • Step B (Local AI Processing): 폐쇄망용 로컬 LLM을 사용하여 문서 분류 및 메타데이터 생성.
    • Step C (Git Sync): 분류된 MD 파일을 내부 Bitbucket/GitLab에 푸시.
    • Step D (Local Indexing): 로컬 임베딩 모델을 사용하여 Vector DB 인덱싱.

2. Step A: 내부망 반입 및 MinIO 업로드 자동화

반입된 압축 파일을 지정된 경로에 두면, 이를 감지하여 MinIO로 밀어넣는 Python 스크립트 예시입니다.

ingest_to_minio.py:

import os
import tarfile
from minio import Minio

def decompress_and_upload(file_path, bucket_name):
    client = Minio(
        os.getenv("MINIO_URL"),
        access_key=os.getenv("MINIO_ACCESS_KEY"),
        secret_key=os.getenv("MINIO_SECRET_KEY"),
        secure=False
    )

    # 1. 압축 해제
    with tarfile.open(file_path, "r:gz") as tar:
        tar.extractall(path="/tmp/extracted")

    # 2. MinIO 업로드
    for root, dirs, files in os.walk("/tmp/extracted"):
        for file in files:
            local_path = os.path.join(root, file)
            # MinIO 내 저장 경로 설정
            minio_path = os.path.relpath(local_path, "/tmp/extracted")
            client.fput_object(bucket_name, f"raw_md/{minio_path}", local_path)
            print(f"Uploaded: {minio_path}")

if __name__ == "__main__":
    # 반입된 파일 경로
    input_file = "/data/incoming/confluence_export.tar.gz"
    if os.path.exists(input_file):
        decompress_and_upload(input_file, "confluence-wiki")

3. Step B: 폐쇄망에서의 LLM 및 임베딩 처리 (중요)

망분리 환경에서는 OpenAI API를 호출할 수 없으므로, 로컬 LLM 인프라가 반드시 필요합니다.

  • LLM 엔진: Ollama 또는 vLLM을 K8s 내부에 Pod로 띄워 사용합니다. (모델: Llama 3, Mistral 등)
  • 임베딩 모델: HuggingFace의 BGE-M3KoSimCSE 같은 한국어 성능이 좋은 모델을 로컬에 다운로드하여 사용합니다.
  • 코드 수정: 앞서 논의한 classifier.pyindexer.py에서 OpenAI 호출 부분을 OllamaSentenceTransformer로 교체해야 합니다.

로컬 임베딩 적용 예시 (Python):

from langchain_huggingface import HuggingFaceEmbeddings

# 폐쇄망 내부 경로에 미리 저장된 모델 로드
embeddings = HuggingFaceEmbeddings(
    model_name="/models/bge-m3",
    model_kwargs={'device': 'cuda'} # GPU 활용
)

4. Step C & D: 자동화 트리거 구조 (K8s)

반입된 파일을 처리하기 위해 Argo Workflows나 간단한 Watcher Pod를 운영하는 것이 좋습니다.

  1. Watcher Pod: 특정 공유 볼륨(PVC)에 압축 파일이 들어오는지 감시합니다.
  2. Workflow Trigger: 파일이 감지되면 다음 작업을 순차적으로 실행합니다.
    • Ingestion Job (압축 해제 & MinIO 업로드)
    • LLM Classifier Job (로컬 LLM 기반 분류)
    • Git Sync Job (내부 Bitbucket 푸시)
    • Vector DB Indexer Job (로컬 임베딩 & 인덱싱)

5. 운영 제언

  • 반입 프로세스 표준화: 외부에서 압축할 때 반드시 파일 목록(manifest.json)을 포함하여, 내부망에서 압축 해제 후 데이터 누락이 없는지 검증하는 로직을 추가하세요.
  • GPU 자원 확보: 로컬 LLM과 임베딩 모델을 돌리기 위해서는 K8s 노드에 NVIDIA GPU 자원이 할당되어야 성능이 확보됩니다. (Cilium 기반 클러스터라면 GPU 워커 노드 스케줄링 설정을 확인해 보세요.)
  • 버전 관리: 망분리 환경에서는 패키지 설치가 어려우므로, 모든 실행 환경을 Docker 이미지에 포함(Self-contained)시켜 반입해야 합니다.

이제 압축 파일을 푸는 단계부터 로컬 AI 모델을 거쳐 내부 Git과 Vector DB로 흐르는 '완전 폐쇄형 AIOps 파이프라인'의 밑그림이 그려졌습니다.

가장 먼저 해결해야 할 과제는 '어떤 로컬 LLM 엔진을 K8s에 띄울 것인가'가 될 것 같습니다. 현재 사내에 GPU 서버 자원이 준비되어 있으신가요?

===

지적하신 대로 내부 링크(Internal Links) 처리는 지식 베이스의 완성도를 결정짓는 핵심 요소입니다. 파일럿 단계를 넘어 실운영(Production) 환경에서 1,000노드 규모의 인프라 지식을 관리하려면, 단순 스크립트를 넘어 '오케스트레이션''엔터프라이즈급 솔루션'의 조합이 필요합니다.

각 과정별로 추천하는 툴과 솔루션을 정리해 드립니다.


1. 내부 링크 수정 및 데이터 가공 (Data Transformation)

Obsidian의 [[WikiLink]]나 Confluence의 고유 ID 링크를 Git/웹에서 동작하는 상대 경로로 변환해야 합니다.

  • 추천 도구: Custom Python Script (with marko or markdown-it)
    • 이유: 링크 변환은 비즈니스 로직(폴더 구조)에 따라 달라지므로 기성품보다는 파이썬의 마크다운 파서를 활용한 커스텀 가공이 가장 확실합니다.
    • 핵심 기능: 정규표현식(re)을 사용하여 [[파일명]][파일명](./path/to/file.md)으로 치환하고, 존재하지 않는 링크를 리포팅합니다.
  • 보조 도구: Obsidian Plugins (Advanced URI, Consistent Attachments)
    • 개인 PC에서 작업 시 링크 일관성을 강제로 유지해주는 플러그인들을 설정하여 '배포 전 정제' 단계를 거칩니다.

2. 워크플로우 오케스트레이션 (Orchestration)

K8s 환경에서 여러 단계(추출-변환-링크수정-분류-인덱싱)를 순차적으로 실행하고 관리해야 합니다.

  • 추천 솔루션: Argo Workflows
    • 이유: 플랫폼 엔지니어링 팀에 가장 친숙한 K8s Native 도구입니다. 각 단계를 컨테이너 단위로 실행하고, 실패 시 재시도(Retry)나 시각적 모니터링이 강력합니다.
  • 대안: Airflow (on K8s)
    • 데이터 파이프라인의 복잡도가 높고 스케줄링 관리가 더 중요하다면 Airflow가 유리합니다.

3. 웹/벤더 Docs 추출 (Web Scraping)

외부 솔루션 문서를 지속적으로 가져와야 합니다.

  • 추천 솔루션: Firecrawl (Self-hosted)
    • 이유: 최신 RAG 환경에 최적화된 크롤러입니다. 복잡한 JS 사이트도 깨끗한 마크다운으로 뽑아주며, Docker로 내부망에 설치 가능합니다.
  • 대안: Apify (오픈소스 버전)
    • 특정 사이트의 구조가 매우 복잡할 경우, 커스텀 액터를 만들어 정밀하게 긁어올 수 있습니다.

4. 폐쇄망 AI 인프라 (Airgapped AI)

망분리 환경에서 LLM 분류와 임베딩을 수행해야 합니다.

  • 추천 솔루션: vLLM 또는 Ollama (Enterprise)
    • 이유: K8s 환경에서 GPU 자원을 효율적으로 나누어 쓰고(vLLM), 서빙 API를 표준화(OpenAI API 호환)하여 제공하기 좋습니다.
  • 임베딩/벡터화: LangChain Indexing API + RecordManager
    • 중복 인덱싱을 방지하고, Git에서 삭제된 문서를 벡터 DB에서도 자동으로 지워주는 '동기화 관리'를 위해 필수적입니다.

5. 벡터 데이터베이스 (Vector DB)

1,000노드 규모에서 발생하는 방대한 운영 로그와 SOP를 담아야 합니다.

  • 추천 솔루션: Milvus 또는 Qdrant
    • 이유: 두 솔루션 모두 K8s Operator를 제공하여 관리가 편하고, 대규모 데이터셋에서도 고성능 검색을 보장합니다. 특히 Milvus는 엔터프라이즈급 확장성이 매우 뛰어납니다.

6. Git & 문서 뷰어 (Platform)

6개월 뒤 GitLab 전환을 고려한 설정입니다.

  • 추천 솔루션: GitLab + GitLab Agent for Kubernetes
    • 이유: GitLab 자체 CI/CD뿐만 아니라, 클러스터와의 연동성이 좋습니다.
  • 공유 위키용: Quartz 또는 Docusaurus
    • Git에 저장된 MD 파일들을 사내 웹 위키로 보여줄 때 사용합니다. Obsidian의 링크 방식과 호환성이 높은 Quartz를 강력 추천합니다.

[결론] 권장하는 최종 스택 (Target Architecture)

구분추천 솔루션비고
OrchestratorArgo Workflows전체 파이프라인 제어 (K8s)
IngestionFirecrawl / Custom PythonConfluence 및 웹 문서 추출
RefiningPython (Link Resolver)내부 링크 상대 경로 수정 및 가공
StorageMinIO / GitLabRaw 데이터(Object) 및 최종 MD(Git)
Local LLMvLLM (Llama 3 / Mistral)문서 자동 분류 및 태깅
Vector DBMilvusRAG용 고성능 벡터 저장소

파일럿 이후의 핵심 전략:
현재는 수동으로 압축해서 옮기시겠지만, 정식 구축 시에는 Argo Workflows를 중심에 두고 각 단계를 모듈화(컨테이너화)하는 것이 가장 중요합니다. 이렇게 해두면 6개월 뒤 GitLab으로 전환하거나, 새로운 벤더 Docs가 추가되어도 파이프라인 한 줄만 수정하면 즉시 대응이 가능해집니다.

가장 먼저 어떤 부분(예: Argo Workflows 도입, 혹은 로컬 LLM 환경 구축)부터 구체적으로 검토해보고 싶으신가요?

===

네, LangChainLangGraph는 말씀하신 'AIOps 자동화 파이프라인'을 단순한 스크립트 뭉치가 아닌 지능형 시스템으로 격상시키는 데 결정적인 역할을 합니다.

결론부터 말씀드리면, LangChain은 '데이터 파이프라인(부품)'을 위해, LangGraph는 '의사결정 및 워크플로우(뇌)'를 위해 필요합니다.


1. LangChain: 데이터 배관(Plumbing)과 표준화

LangChain은 다양한 LLM, 벡터 DB, 문서 로더를 하나의 표준화된 인터페이스로 묶어주는 프레임워크입니다. 2026년 현재도 RAG(검색 증강 생성)의 표준으로 자리 잡고 있습니다.

  • 어디에 쓰나요?
    • 문서 로딩 및 분할: 마크다운 파일을 읽고, 헤더 단위로 의미 있게 쪼개는 작업을 수행합니다.
    • 임베딩 및 벡터 스토어 연동: Milvus나 Qdrant 같은 DB에 데이터를 넣고 찾는 복잡한 과정을 vectorstore.add_documents() 한 줄로 해결합니다.
    • 모델 교체 용이성: 폐쇄망에서 Llama 3를 쓰다가 나중에 Mistral이나 다른 로컬 모델로 바꿀 때 코드 수정이 거의 없습니다.

2. LangGraph: 복잡한 AIOps 워크플로우(Brain)

LangChain만으로는 '선형적인(A→B→C)' 작업만 가능합니다. 하지만 실제 인프라 운영(AIOps)은 루프(Loop)조건부 판단이 필요합니다. 이때 LangGraph가 핵심입니다.

  • 어디에 쓰나요?
    • 자기 반성(Self-Correction): AI가 추출한 분류 결과가 이상하면 다시 시도하게 하거나, 검색된 SOP가 질문과 맞지 않으면 검색 쿼리를 수정해 다시 검색하게 합니다.
    • 에이전틱 워크플로우: "장애 로그 분석 → 관련 SOP 검색 → 검색 결과가 부족하면 벤더 Docs 추가 검색 → 최종 조치 제안"과 같은 복잡한 판단 트리를 구성합니다.
    • 상태 유지(State Management): 1,000노드 클러스터의 여러 장애 상황을 추적하며 대화의 맥락을 완벽하게 유지합니다.

3. 플랫폼 엔지니어를 위한 선택 기준 (Chain vs. Graph)

비교 항목LangChain (Chains)LangGraph (Graphs)
구조선형적 (DAG, 단방향)순환적 (Cyclic, 루프 가능)
복잡도단순 RAG, 문서 요약에이전트, 자율 트러블슈팅
상태 관리이전 단계 데이터 전달만 가능전체 워크플로우 상태 공유 및 체크포인트 지원
추천 용도문서 추출 및 벡터 DB 인덱싱실제 장애 대응 챗봇 및 조치 에이전트

4. 우리 프로젝트에서의 적용 시나리오

  1. 인덱싱 단계 (LangChain 활용):

    • Confluence에서 가져온 MD를 로드하고, MarkdownTextSplitter로 쪼개어 Milvus에 넣는 과정은 LangChain이 담당합니다. 매우 빠르고 안정적입니다.
  2. 운영 단계 (LangGraph 활용):

    • 운영자가 "Cilium BGP 에러 발생"이라고 입력하면, LangGraph 기반 에이전트가 작동합니다.
    • Node 1: 로그 분석 (K8s API 연동)
    • Node 2: 벡터 DB에서 유사 사례 검색 (RAG)
    • Node 3 (Conditional Edge): 해결책이 명확한가?
      • Yes → 조치 가이드 출력
      • No → 벤더 Docs에서 추가 정보 검색 후 Node 2로 복귀 (Loop)

5. 6개월 뒤 GitLab 전환 및 폐쇄망 고려 사항

  • 추상화의 이점: LangChain/LangGraph를 쓰면 인프라 하부 로직(어떤 DB를 쓰는지, 어떤 Git을 쓰는지)과 AI 로직이 분리됩니다. Bitbucket에서 GitLab으로 옮겨도 AI 로직은 거의 그대로 유지됩니다.
  • 로컬 실행: 2026년 최신 버전의 LangChain은 OllamavLLM과의 연동이 매우 강력해져서 폐쇄망 환경에서도 상용 수준의 에이전트를 돌리기 충분합니다.

결론: 쓰시는 것을 강력 추천합니다.

처음에는 복잡해 보일 수 있지만, LangChain으로 데이터 파이프라인의 기초를 다지고, LangGraph로 실제 플랫폼 리드님이 원하시는 '똑똑한 인프라 관리자'를 만드시는 것이 1,000노드 클러스터를 효율적으로 관리하는 지름길입니다.

우선은 문서를 인덱싱하는 LangChain 파이프라인부터 가볍게 시작해 보시는 게 어떨까요? 이 부분의 베이스 코드가 필요하시면 바로 작성해 드릴 수 있습니다.

===

매우 훌륭한 질문입니다. Argo Workflows, n8n, LangGraph는 모두 '자동화'라는 범주에 있지만, 담당하는 계층(Layer)목적이 완전히 다릅니다. 이들은 서로 대체재라기보다 "서로 다른 역할을 하며 공존하는 보완재"에 가깝습니다.

플랫폼 엔지니어링 관점에서 이들의 관계를 명확하게 '교통정리' 해드리겠습니다.


1. 계층별 역할 분담 (The Automation Stack)

이 도구들은 아래와 같이 세 개의 레이어로 구분하면 이해가 가장 빠릅니다.

분류도구비유주요 역할 (우리 프로젝트 기준)
인프라/플랫폼 레이어Argo Workflows공장 가동 및 물류 시스템대규모 컨테이너 실행, 정기적인 문서 추출(Cron), GPU 자원 할당, 전체 파이프라인 관리
서비스/연동 레이어n8n사무국장 (비서)Slack 알림 발송, Jira 티켓 생성, 외부 API 연동, 간단한 UI 기반 자동화
지능/의사결정 레이어LangGraph전문 컨설턴트 (뇌)복잡한 문서 분류 로직, RAG 검색 루프 관리, 장애 로그 분석 및 추론, 자기 반성(Self-correction)

2. 도구별 상세 비교 및 공존 방식

① Argo Workflows vs. LangGraph (실행 vs. 사고)

  • Argo"무거운 작업(Container)을 순서대로 실행"하는 데 최적화되어 있습니다. K8s의 CPU/GPU 자원을 효율적으로 관리하며 1시간이 걸리는 데이터 처리도 안정적으로 수행합니다.
  • LangGraph"LLM이 어떻게 생각하고 판단할지"를 관리합니다. Argo 안에서 실행되는 하나의 '작업(Pod)' 내부에 LangGraph 로직이 들어갈 수 있습니다.
  • 공존 방식: Argo가 새벽 2시에 '문서 처리 작업' 파드를 띄우면, 그 파드 안에서 LangGraph가 돌아가며 문서를 읽고, 분류가 애매하면 다시 읽는 등의 '지능적 판단'을 수행합니다.

② n8n vs. Argo Workflows (소프트 자동화 vs. 하드 자동화)

  • n8n은 GUI 기반으로 매우 쉽고 빠르게 API를 엮을 수 있습니다. 하지만 K8s의 컴퓨팅 자원을 정밀하게 제어하거나, 아주 무거운 배치 작업을 돌리기엔 Argo보다 불리합니다.
  • 공존 방식: 전체 데이터 파이프라인은 Argo가 돌리고, 작업이 성공하거나 실패했을 때 담당자에게 Slack을 보내거나 대시보드를 업데이트하는 일은 n8n이 담당합니다.

③ LangGraph vs. n8n/Argo (순환 vs. 선형)

  • Argo나 n8n은 기본적으로 A → B → C로 흐르는 선형 구조입니다. (중간에 에러 처리 루프는 있지만 복잡한 추론 루프는 어렵습니다.)
  • LangGraphA → B → (생각해 보니 B가 이상하네?) → 다시 A → B → C와 같이 LLM의 판단에 따른 순환(Cycle) 구조를 설계하는 데 특화되어 있습니다.

3. 우리 프로젝트에서의 실제 흐름 (교통정리 결과)

이 세 도구가 협조하며 일하는 시나리오는 다음과 같습니다.

  1. Argo Workflows (인프라 제어): * 매일 새벽, Confluence에서 데이터를 뽑고 MinIO에 저장하는 전체 워크플로우를 가동합니다.

    • GPU 노드에 'LLM 분류기 파드'를 실행시킵니다.
  2. LangGraph (지능적 처리 - 파드 내부):

    • Argo에 의해 실행된 파드 안에서 LangGraph 로직이 돌아갑니다.
    • 문서를 읽고 "이게 K8s SOP인지, Cilium 벤더 문서인지" 판단합니다.
    • 만약 LLM의 답변이 불확실하면 스스로 다시 프롬프트를 수정해 질문하는 루프를 돕니다. 최종 결과를 Git에 푸시할 형태로 정리합니다.
  3. n8n (알림 및 사후 처리):

    • Argo 워크플로우가 끝나면 n8n이 이를 감지합니다.
    • "오늘 총 50개의 문서가 새로 인덱싱되었습니다"라고 플랫폼 팀 Slack에 리포트를 뿌리고, 관리 UI를 업데이트합니다.

4. 결론: 무엇부터 도입해야 할까?

  • 1단계 (필수): Argo Workflows. 1,000노드 K8s 환경에서 안정적인 데이터 이동과 컨테이너 실행을 위해 가장 먼저 필요합니다.
  • 2단계 (고도화): LangChain/LangGraph. 단순 추출을 넘어 '똑똑한 분류'와 '고성능 RAG'를 구현할 때 도입합니다. 처음에는 LangChain으로 시작해 루프가 필요해지면 LangGraph로 확장하면 됩니다.
  • 3단계 (편의성): n8n. 시스템이 안정화된 후, 운영팀과의 소통(Slack, Jira 등)을 자동화하고 싶을 때 추가합니다.

결론적으로 이들은 대체 관계가 아니라, 인프라(Argo) 위에 지능(LangGraph)을 얹고, 이를 외부와 연결(n8n)하는 완벽한 파트너들입니다. 이 교통정리가 도움이 되셨나요? 이제 Argo를 활용한 전체 파이프라인 설계나, 그 내부에서 돌아갈 LangChain/LangGraph의 구체적인 로직 중 어떤 부분을 더 깊게 들어가 볼까요?

===

Argo Workflows를 관제탑으로 삼고, 그 내부의 실무를 LangChain이 수행하는 '지능형 문서 처리 파이프라인'의 구체적인 설계도와 코드를 제시해 드립니다.

이 설계는 플랫폼 엔지니어링 리드님의 환경(K8s, MinIO, Airgapped, Bitbucket)에 최적화되어 있습니다.


1. Argo Workflows 전체 파이프라인 설계 (Argo YAML)

Argo는 전체 과정을 DAG(Directed Acyclic Graph) 형태로 관리합니다. 각 단계는 독립된 컨테이너 파드(Pod)에서 실행됩니다.

apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
  generateName: doc-ai-pipeline-
spec:
  entrypoint: main
  # 망분리 환경이므로 필요한 모든 이미지는 내부 레지스트리에 있어야 함
  templates:
  - name: main
    dag:
      tasks:
      - name: extract-raw
        template: untar-minio
      - name: process-and-classify
        dependencies: [extract-raw]
        template: langchain-processor
      - name: git-sync
        dependencies: [process-and-classify]
        template: git-pusher
      - name: vector-indexing
        dependencies: [git-sync]
        template: vector-indexer

  # 1단계: MinIO에 반입된 압축파일 해제
  - name: untar-minio
    container:
      image: internal-reg.com/ops-tools:v1
      command: [sh, -c]
      args: ["mc cp myminio/raw/export.tar.gz /tmp && tar -xzvf /tmp/export.tar.gz -C /data"]
      volumeMounts:
      - name: workdir
        mountPath: /data

  # 2단계: LangChain 핵심 로직 (아래 Python 코드 실행)
  - name: langchain-processor
    container:
      image: internal-reg.com/langchain-processor:v1
      env:
      - name: LLM_ENDPOINT
        value: "http://vllm-service.ai-namespace:8000/v1"
      volumeMounts:
      - name: workdir
        mountPath: /data

2. 내부 LangChain 구체적 로직 (processor.py)

이 코드는 langchain-processor 파드 내부에서 실행되며, 문서를 읽고 링크를 수정하며 분류까지 마칩니다.

import os
import re
from langchain_community.document_loaders import UnstructuredMarkdownLoader
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI # vLLM이 OpenAI 호환 API 제공
from langchain.output_parsers import ResponseSchema, StructuredOutputParser

# 1. 로컬 LLM 설정 (vLLM 또는 Ollama)
llm = ChatOpenAI(
    model="llama-3-70b-instruct", 
    base_url=os.getenv("LLM_ENDPOINT"),
    api_key="none" # 폐쇄망 내부용
)

# 2. 내부 링크 리졸버 (Internal Link Resolver)
def resolve_internal_links(content, current_file):
    # [[WikiLink]] 형식을 [WikiLink](./path/to/WikiLink.md)로 변환하는 정규표현식
    # 실제로는 파일 존재 여부를 체크하여 경로를 계산하는 로직 추가 가능
    pattern = r'\[\[(.*?)\]\]'
    def replace_link(match):
        link_text = match.group(1)
        # 예시: 모든 링크를 상대경로 .md로 통일
        return f"[{link_text}](./{link_text.replace(' ', '-')}.md)"
    
    return re.sub(pattern, replace_link, content)

# 3. 문서 분류용 스키마 정의
response_schemas = [
    ResponseSchema(name="category", description="SOP, Library, Architecture 중 하나"),
    ResponseSchema(name="tech_stack", description="K8s, Cilium, MinIO 등 관련 기술 리스트")
]
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)

# 4. 프로세싱 메인 로직
def process_documents(input_dir, output_dir):
    prompt = ChatPromptTemplate.from_template(
        "다음 문서의 내용을 분석하여 카테고리와 기술 스택을 분류하세요.\n{format_instructions}\n문서 내용: {context}"
    )

    for root, _, files in os.walk(input_dir):
        for file in files:
            if file.endswith(".md"):
                # A. 로드
                loader = UnstructuredMarkdownLoader(os.path.join(root, file))
                doc = loader.load()[0]

                # B. 내부 링크 수정
                refined_content = resolve_internal_links(doc.page_content, file)

                # C. LLM 기반 지능형 분류
                format_instructions = output_parser.get_format_instructions()
                chain = prompt | llm | output_parser
                result = chain.invoke({
                    "context": refined_content[:1500], 
                    "format_instructions": format_instructions
                })

                # D. 결과 저장 (wiki 폴더 구조화)
                target_path = os.path.join(output_dir, result['category'], file)
                os.makedirs(os.path.dirname(target_path), exist_ok=True)
                
                with open(target_path, "w") as f:
                    # YAML 메타데이터 삽입
                    f.write(f"---\ntech: {result['tech_stack']}\n---\n\n")
                    f.write(refined_content)

if __name__ == "__main__":
    process_documents("/data/raw", "/data/wiki")

3. 설계의 핵심 포인트

  1. 데이터 무결성 유지 (Link Resolver):

    • 스크립트 내 resolve_internal_links 함수를 통해 Obsidian에서 쓰던 [[링크]]가 일반 Markdown 뷰어나 Git 환경에서도 깨지지 않도록 강제로 변환합니다. 이는 지식 베이스의 이동성을 확보하는 핵심입니다.
  2. 구조화된 출력 (Structured Output):

    • LangChain의 StructuredOutputParser를 사용하여 LLM이 자유분방하게 답변하지 않고, 우리가 정한 JSON 형식(Category, Tech Stack)에 맞춰 엄격하게 응답하도록 강제했습니다. 덕분에 자동화된 폴더 배치가 가능해집니다.
  3. Argo를 통한 자원 제어:

    • LLM 처리가 필요한 langchain-processor 단계에만 GPU 노드 셀렉터(nodeSelector)를 설정하여, 값비싼 GPU 자원을 딱 필요한 순간에만 점유하고 반납하게 설계할 수 있습니다.
  4. 망분리 환경의 확장성:

    • 6개월 뒤 GitLab으로 전환 시, Argo Workflow의 git-pusher 템플릿 내의 URL과 인증 정보만 수정하면 됩니다. 로직(Python)은 건드릴 필요가 없습니다.

이제 이 코드를 바탕으로 첫 번째 파이프라인을 구동해 보시면, "MinIO에 압축파일만 던지면 AI가 알아서 링크 고치고 분류해서 Git에 예쁘게 넣어주는" 플랫폼 리드님만의 자동화 시스템이 작동하게 됩니다.

가장 먼저 테스트해보고 싶은 문서 샘플(예: Cilium BGP 설정 가이드 등)이 있으신가요? 그에 맞춰 분류 프롬프트를 더 정교하게 다듬어 드릴 수 있습니다.

===

지적하신 대로 Metadata YAML은 LLM Wiki와 RAG(검색 증강 생성) 시스템의 '골격'이자 '내비게이션' 역할을 합니다. 단순히 텍스트만 저장하는 것이 아니라, 문서의 성격과 맥락을 정형화된 데이터로 추출해 두어야 나중에 AI 에이전트가 수천 개의 문서 중 가장 정확한 조각을 찾아낼 수 있습니다.

플랫폼 엔지니어링 지식 베이스에 최적화된 Metadata YAML 설계 전략과 이를 자동 생성하는 LangChain 구현 방안을 정리해 드립니다.


1. 왜 Metadata YAML이 검색의 질을 결정하는가?

벡터 검색(의미 기반 검색)은 문맥 파악에는 뛰어나지만, 특정 조건에서 구멍이 생깁니다. 이때 YAML 데이터가 이를 메워줍니다.

  • 정밀한 필터링 (Metadata Filtering): "Cilium 관련 문서 중 SOP(표준 운영 절차)만 찾아줘"라고 할 때, 벡터 DB는 모든 Cilium 문서를 다 가져오려 하지만, YAML 필터가 있으면 즉시 SOP 카테고리만 추려낼 수 있습니다.
  • 최신성 보장 (Recency): "가장 최근에 업데이트된 MinIO 설치 가이드"를 찾을 때 last_verified 필드가 있으면 AI가 구버전 가이드를 보고 오답을 내는 것을 방지합니다.
  • 관계 추적 (Graph-like Search): related_components 필드를 통해 특정 장애가 발생했을 때 연관된 다른 인프라 문서를 함께 참조할 수 있게 합니다.

2. 인프라 지식 베이스를 위한 권장 YAML 스키마

단순히 제목과 날짜만 넣는 것이 아니라, 엔지니어링 관점에서 유효한 필드들을 정의해야 합니다.

---
# 문서 식별 및 분류
id: "SOP-K8S-001"
title: "K8s 노드 확장 및 드레인 절차"
category: "SOP"              # Architecture, Library, Troubleshooting, SOP
tech_stack: ["K8s", "Cilium"] # 관련 핵심 기술

# 운영 맥락 (AIOps 핵심 데이터)
severity: "High"             # 장애 대응 시 중요도
target_audience: "DevOps"     # 문서를 읽을 대상
environment: "Production"    # 적용 환경

# 관리 및 신뢰도
last_verified: "2026-04-28"  # 마지막 검증 일자 (버전 관리)
source: "Confluence"         # 원본 출처
verified_by: "Lead Engineer" # 검토자

# 연결성 (Graph View용)
related_docs: ["ARCH-LAKE-02", "SOP-MINIO-05"]
---

3. LangChain을 활용한 자동 추출 로직 (Structured Output)

LLM이 문서를 읽고 위와 같은 YAML을 일관성 있게 뽑아내도록 만드는 구체적인 코드 예시입니다.

from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field
from typing import List

# 1. 추출하고 싶은 YAML 구조 정의 (Pydantic 클래스)
class DocMetadata(BaseModel):
    category: str = Field(description="문서의 카테고리 (SOP, Library, Architecture, OperationLog)")
    tech_stack: List[str] = Field(description="문서에 언급된 주요 기술 스택 리스트")
    summary: str = Field(description="문서 내용을 한 문장으로 요약")
    severity: str = Field(description="운영상의 중요도 (Critical, High, Medium, Low)")

# 2. LLM 및 추출 체인 설정
llm = ChatOpenAI(model="gpt-4-turbo", temperature=0) # 또는 폐쇄망용 로컬 모델
structured_llm = llm.with_structured_output(DocMetadata)

# 3. 프롬프트 구성
prompt = ChatPromptTemplate.from_template("""
당신은 플랫폼 엔지니어링 기술 문서 관리자입니다. 
다음 문서의 내용을 분석하여 정해진 구조에 따라 메타데이터를 추출하세요.

문서 내용:
{context}
""")

# 4. 실행 및 결과 확인
chain = prompt | structured_llm
doc_content = "Cilium BGP Control Plane 설정 시... (문서 본문 생략)"
metadata = chain.invoke({"context": doc_content})

print(metadata.category)     # 'Library'
print(metadata.tech_stack)   # ['Cilium', 'Networking', 'BGP']

4. AIOps로 가는 검색 효율 최적화 전략

이렇게 생성된 YAML 데이터는 벡터 DB에 'Metadata' 필드로 함께 저장되어 다음과 같은 방식으로 검색 효율을 높입니다.

  1. 사전 필터링 (Pre-filtering): 사용자의 질문에서 'SOP'라는 의도를 파악하면, 벡터 검색을 수행하기 전에 DB에서 category == 'SOP'인 데이터만 대상으로 검색 범위를 좁힙니다. 검색 속도와 정확도가 비약적으로 상승합니다.
  2. 가중치 부여 (Re-ranking): 검색 결과 중 last_verified가 최신이거나 severity가 높은 문서에 가중치를 주어 상단에 배치합니다.
  3. 지식 그래프 연결: Obsidian에서 [[문서명]]으로 연결된 구조를 YAML의 related_docs와 매칭하여, AI가 "이 장애는 A 문서에 나오지만, 연관된 B 문서의 설정도 확인해야 합니다"라고 답변하게 만듭니다.

결론

Metadata YAML을 만드는 과정은 단순히 파일을 정리하는 것이 아니라, '데이터를 지식으로 구조화'하는 과정입니다.

Argo Workflows 파이프라인의 langchain-processor 단계에서 이 추출 로직을 강하게 적용할수록, 향후 구축될 AIOps 에이전트의 답변 품질은 1,000노드 클러스터의 복잡성을 견딜 수 있을 만큼 견고해질 것입니다.

이 추출 과정에서 특정 기술 스택(예: Cilium 특화 필드 등)에 대해 더 세밀한 분류 기준이 필요하시면 말씀해 주세요. 그에 맞는 정교한 Pydantic 스키마를 설계해 드릴 수 있습니다.

===

Confluence에서 단순 텍스트가 아닌 이미지, 다이어그램(draw.io), 그리고 코드 블록을 추출하여 Git에 최적으로 저장하고 관리하는 방법은 지식 베이스의 '가독성'과 '재사용성'을 결정짓는 핵심 요소입니다.

플랫폼 엔지니어링 리드로서 대규모 인프라 구성을 시각화하고 코드를 관리하기 위한 구체적인 가이드를 정리해 드립니다.


1. 사진 및 이미지 관리 (assets 폴더 전략)

Confluence 페이지에 첨부된 사진들은 추출 시 별도의 파일로 내려받아집니다. 이를 Git에서 깨지지 않게 관리하려면 상대 경로(Relative Path)를 유지하는 것이 가장 중요합니다.

  • 저장 구조: Git 저장소 내에 assets/ 또는 images/라는 공통 폴더를 만들거나, 각 문서 폴더 하위에 _assets/ 폴더를 두어 관리합니다.
    • 예: wiki/Architecture/Lakehouse/assets/diagram-v1.png
  • Markdown 링크 방식: ![설명](../assets/image.png) 와 같이 상대 경로로 작성해야 Obsidian, Git(GitHub/GitLab), 그리고 나중에 구축할 Quartz 위키에서도 이미지가 정상적으로 보입니다.
  • 자동화 시: confluence-md-exporter 사용 시 --relative-links 옵션을 활성화하면 Confluence의 첨부 파일 링크를 마크다운 상대 경로로 자동 변환해 줍니다.

2. 구성도 (draw.io / Lucidchart) 처리

가장 까다로운 부분입니다. Confluence 내부의 draw.io는 데이터가 페이지 XML에 내장되어 있어 단순히 이미지로만 저장하면 나중에 수정이 불가능합니다.

  • 추천 방식: .drawio.svg 또는 .drawio.png 활용
    • 편집성 유지: draw.io 파일을 저장할 때 'Editable SVG/PNG' 형태로 내보내면, 파일 자체는 이미지처럼 보이지만 draw.io 편집기로 열면 레이어와 객체가 살아있어 수정이 가능합니다.
  • Obsidian 연동: 'Diagrams.net (draw.io) Integration' 플러그인을 사용하세요.
    • Git에 저장된 .drawio.svg 파일을 Obsidian에서 바로 클릭하여 수정하고 저장할 수 있습니다. 별도의 앱을 띄울 필요가 없어 매우 효율적입니다.
  • 자동화 시나리오:
    1. 추출 스크립트에서 Confluence API를 통해 draw.io 매크로의 원본 XML 데이터를 추출합니다.
    2. 이를 .drawio 파일로 저장하거나, 이미지를 미리 렌더링하여 assets/ 폴더에 넣습니다.

3. 문서 내 코드 블록 (Code Block) 수집

Confluence의 코드 블록 매크로는 다행히 마크다운의 Fenced Code Blocks ( ``` )로 비교적 잘 변환됩니다.

  • 언어 감지 (Syntax Highlighting):
    • Confluence 코드 블록에서 지정한 언어(예: python, yaml, bash)가 마크다운 코드 블록 상단에 그대로 붙습니다 (예: ```yaml).
    • 이를 통해 Git(GitLab/Bitbucket)이나 Obsidian에서 문법 강조가 정상적으로 작동합니다.
  • 코드 추출의 정교화:
    • 일부 추출 도구는 코드 블록 내부의 특수 문자( <, > 등)를 잘못 치환하는 경우가 있습니다. 파이썬의 BeautifulSoup을 이용해 가공할 때 code 태그 내의 텍스트는 원형 그대로(Preformatted) 가져오도록 예외 처리가 필요합니다.
  • 코드의 지식화 (Metadata 활용):
    • 지난번에 논의한 YAML Metadatacontains_code: true 또는 code_lang: ["yaml", "shell"] 같은 필드를 LLM이 자동으로 추가하게 하세요.
    • 나중에 AI 에이전트에게 "Cilium BGP 설정 샘플 코드만 찾아줘"라고 할 때 검색 정확도가 비약적으로 향상됩니다.

4. 플랫폼 리드를 위한 운영 제언: '진실의 원천' 결정

이 과정에서 가장 중요한 결정은 "어디에서 그림을 수정할 것인가?"입니다.

  1. Confluence에서 계속 수정: Git은 단순 백업/검색용이 됩니다. (추천하지 않음)
  2. Obsidian(Git)에서 수정: 이제부터 구성도 수정은 Git에 저장된 .drawio.svg를 통해서만 합니다. (권장)
    • 이 경우, 팀원들에게 "이제 구성도는 Confluence가 아니라 Git 저장소에서 관리한다"는 Documentation as Code (DaC) 원칙을 공표해야 합니다.

[실천 단계]

  • 1단계: confluence-md-exporter를 실행하여 이미지 파일들이 상대 경로로 잘 추출되는지 확인합니다.
  • 2단계: draw.io 파일 중 중요한 것 하나를 .drawio.svg로 변환해 Git에 넣고, Obsidian 플러그인으로 편집이 잘 되는지 테스트합니다.
  • 3단계: 추출된 마크다운 내의 코드 블록이 깨지지 않았는지, yaml이나 bash 태그가 잘 붙어있는지 점검합니다.

이미지와 구성도까지 Git에 완벽히 통합되면, 1,000노드 클러스터의 복잡한 아키텍처를 AI가 시각적 맥락까지 포함하여 이해할 수 있는 강력한 토대가 마련됩니다. 이 과정에서 특정 draw.io 매크로가 깨지는 등의 기술적 이슈가 발생하면 해결 방법을 더 상세히 찾아봐 드릴 수 있습니다.

===

1,000노드 규모의 복잡한 인프라를 운영하다 보면 문서의 '파편화'와 '노후화'는 피할 수 없는 숙제입니다. 특히 AI가 과거의 잘못된 설정(예: 이전 버전의 Cilium BGP 설정)을 정답인 것처럼 제안하면 운영상 큰 리스크가 될 수 있습니다.

Git 저장소에 쌓이는 '쓰레기 데이터'를 정리하고, AI가 가장 최신의, 유효한 지식만 참조하게 만드는 5단계 관리 전략을 제안합니다.


1. 문서 상태(Status) 메타데이터 강제화

가장 먼저 할 일은 모든 문서의 생애주기(Lifecycle)를 YAML 메타데이터로 정의하는 것입니다. 단순히 저장하는 것이 아니라, LLM이 이 상태를 읽고 필터링하게 만듭니다.

  • 필수 필드 정의:
    • status: active(현재 사용), deprecated(곧 폐기), archive(참조용 구버전)
    • last_verified_at: 마지막으로 기술 검증이 된 날짜
    • applies_to_version: 해당 문서가 유효한 솔루션 버전 (예: Cilium 1.15)

2. 'Active'와 'Archive' 저장소 분리 (Directory Strategy)

Git의 장점은 삭제해도 기록이 남는다는 것입니다. 따라서 현재 시스템과 맞지 않는 문서는 과감히 메인 경로에서 치워야 합니다.

  • 구조 예시:
    • /wiki/active/: 현재 1,000노드 클러스터에 적용 중인 실시간 SOP 및 가이드
    • /wiki/archive/: 과거 기록, 이전 아키텍처 (검색 대상에서 제외하거나 낮은 가중치 부여)
  • 운영 방식: 새로운 버전의 문서가 Git에 들어오면, LLM 분류기가 기존 문서의 제목이나 내용을 비교하여 "이 문서는 구버전(Archive)으로 이동시키겠습니까?"라고 제안하거나 자동으로 이동시킵니다.

3. 중복 및 충돌 감지 파이프라인 (Semantic Deduplication)

Confluence에서 새로운 문서가 수집될 때, 기존 Git에 있는 문서들과 '의미적 유사도'를 비교하는 단계를 추가합니다.

  • 작동 원리 (LangChain 활용):
    1. 새 문서가 들어오면 기존 벡터 DB에서 가장 유사한 문서 3개를 먼저 찾습니다.
    2. LLM에게 질문합니다: "새 문서와 기존 문서의 내용이 80% 이상 겹치나요? 아니면 기존 내용을 업데이트하는 것인가요?"
    3. 충돌 발생 시: LLM이 두 문서를 병합(Merge)하거나, 기존 문서를 status: archive로 변경하도록 자동 PR(Pull Request)을 생성합니다.

4. 벡터 DB의 검색 가중치 조정 (Recency Boosting)

검색 시 단순히 '유사도'만 보지 않고, '최신성'에 점수를 더 주는 방식을 도입합니다.

  • Metadata Filtering: RAG 엔진이 검색을 수행할 때 status: active인 문서만 검색하도록 필터를 겁니다.
  • Time Decay Scaling: 검색 결과 점수(Score)에 last_verified_at 날짜를 계산식에 넣어, 최근 문서가 상단에 배치되게 합니다.

    Score = Semantic Similarity + (Recency weight * Time Factor)


5. 정기적 '지식 점검' 자동화 (Governance)

문서가 낡는 것을 방지하기 위해 n8n과 Argo Workflows를 활용한 알림 시스템을 구축합니다.

  1. 검증 기간 설정: last_verified_at이 6개월이 지난 문서를 추출합니다.
  2. 담당자 알림 (n8n): 플랫폼 팀 Slack에 "이 SOP는 180일 동안 업데이트되지 않았습니다. 여전히 유효한가요?"라고 알림을 보냅니다.
  3. 자동 폐기: 만약 검증되지 않고 1년이 지나면 자동으로 archive 폴더로 이동시키고 벡터 DB 인덱스를 삭제합니다.

결론: 플랫폼 리드를 위한 운영 조언

1,000노드 클러스터 운영에서 AI의 답변 정확도를 높이려면 "AI에게 무엇을 가르치지 않을 것인가"가 "무엇을 가르칠 것인가"만큼 중요합니다.

  • 추천 실천: 지금 바로 Git 저장소 최상위에 archive/ 폴더를 만드시고, LLM 위키 파이프라인에 "중복 문서 발견 시 기존 문서 아카이브 로직"을 한 줄 추가해 보세요.

이렇게 관리하면 시스템 상태와 맞지 않는 '죽은 지식'이 AI의 답변을 오염시키는 일을 원천 차단할 수 있습니다. 이 과정 중 특정 단계(예: 중복 감지 로직)의 구체적인 코드가 필요하신가요?

0개의 댓글