전체 글

총 652개의 글

반응형

kube-prometheus-stack 설치와 Grafana 접속: kind 클러스터 모니터링과 target down 해결

로컬 쿠버네티스 실습 4편 kube-prometheus-stack으로Prometheus·Grafana 띄우기 helm 한 번으로 설치하고, Gateway로 Grafana를 열고, down 6개를 0개로 만든 기록 kube-prometheus-stackGrafanaPrometheusGateway APIkube-prometheus-stack helm 차트를 쓰면 Prometheus, Grafana, Alertmanager, node-exporter, kube-state-metrics가 한 번에 설치됩니다. 실습 PC의 kind 클러스터에서는 81초 만에 모든 파드가 Running이 됐고 메모리는 약 1.4GB를 더 썼습니다. Grafana는 3편의 Gateway에 grafana.localhost 호스트..

NGINX Ingress 대신 Gateway API: kind 클러스터 앱을 Windows localhost로 접속하기

로컬 쿠버네티스 실습 3편NGINX Ingress 대신 Gateway APIkind 앱을 localhost:8080으로Envoy Gateway를 설치하고, 로드밸런서 없는 kind에서 Windows 브라우저까지 연결한 과정Gateway APIEnvoy GatewayHTTPRoutekindkind 클러스터에서 Gateway API를 쓰려면 Gateway API 리소스 정의와 구현체가 필요한데, Envoy Gateway helm 차트 하나로 둘 다 설치됩니다. 남는 문제는 kind에 로드밸런서가 없다는 점입니다. 이 글에서는 Envoy 프록시를 8080 포트가 연결된 제어 플레인 노드에 띄우고 노드의 80번 포트를 직접 받게(hostPort) 설정해서, Windows 브라우저의 localhost:8080이 ..

Headlamp 설치와 사용법: 로컬 쿠버네티스를 화면으로 보고 파드 배포하기

로컬 쿠버네티스 실습 2편 Headlamp 설치와 사용법클릭으로 보고, YAML로 배포하기 WSL2 안의 kind 클러스터를 Windows 앱에서 보고 파드까지 배포해 본 기록 Headlamp쿠버네티스 GUIkindWSL2Headlamp는 쿠버네티스 클러스터를 화면으로 보고 관리하는 데스크톱 앱입니다. Windows에서는 winget으로 설치하고, 사용자 폴더의 .kube\config에 클러스터 접속 정보를 넣으면 별도 설정 없이 클러스터가 목록에 나옵니다. 보기 전용이 아니라 왼쪽 아래 '+ 생성' 버튼에 YAML을 붙여 넣고 '적용'을 누르면 Deployment를 바로 만들 수 있고, 실제로 만든 파드 2개가 Running이 되는 것까지 확인했습니다.1편에서 WSL2 Ubuntu 24.04 안에..

Windows에서 무료 쿠버네티스 실습 환경 만들기: WSL2 + Docker Engine + kind

로컬 쿠버네티스 실습 1편Windows에서 무료 쿠버네티스 실습 환경WSL2 + Docker Engine + kind클라우드 비용 없이 내 PC에 노드 3개짜리 클러스터를 만든 과정과 실제로 겪은 문제WSL2Docker EnginekindkubectlWindows 11에서는 WSL2에 Ubuntu를 설치하고, 그 안에 Docker Engine과 kind를 올리면 클라우드 비용 없이 제어 플레인 1개와 워커 2개로 된 쿠버네티스 클러스터를 만들 수 있습니다. 실제로 해 보니 클러스터 생성은 52초면 끝났고, 시간이 걸린 부분은 WSL 설치 뒤 재부팅과 배포판 설치였습니다. Docker Desktop 없이 Docker 공식 apt 저장소로 설치하고, kubectl·kind·helm은 공식 배포처 파일을 SH..

Bedrock Knowledge Bases 검색 설정과 재정렬: 필요한 문서를 먼저 찾는 방법 | Bedrock 28편

Amazon Bedrock 완전 가이드 · 28편 Knowledge Bases 검색 설정과 재정렬 필요한 문서를 먼저 찾는 방법 Amazon Bedrock Knowledge Bases는 질문할 때마다 검색 설정을 바꿀 수 있습니다. 가져올 청크 수는 numberOfResults(기본 5개, 1~100)로, 검색 범위는 메타데이터 필터로, 결과 순서는 재정렬 모델로, 여러 주제가 섞인 질문은 질의 분해로 조정합니다. 설정을 바꾸기 전에 Retrieve로 검색 결과의 원문·위치·메타데이터·점수를 먼저 보고, 문제가 수집·검색·생성 중 어디서 생겼는지 가른 뒤 한 번에 하나씩 바꿉니다.고객 관리형 Knowledge Base의 비정형 데이터 소스를 기준으로 Retrieve·RetrieveAndGenerat..

Bedrock Knowledge Bases 임베딩과 벡터 저장소: 차원·인덱스·유사도 이해하기 | Bedrock 27편

Amazon Bedrock 완전 가이드 · 27편 Knowledge Bases 임베딩과 벡터 저장소 차원·인덱스·유사도 이해하기 Amazon Bedrock Knowledge Bases에서 임베딩 모델은 청크를 정해진 길이(차원)의 숫자 벡터로 바꾸고, 벡터 저장소는 이 벡터를 인덱스에 저장해 질문 벡터와 가까운 것을 찾습니다. 벡터 인덱스의 차원은 임베딩 모델의 출력 차원과 같아야 하며, 하이브리드 검색은 일부 저장소에서만 쓸 수 있습니다. 임베딩 모델과 저장소는 만든 뒤 바꾸기 어려우므로, 지원 리전·검색 방식·비용 구조를 함께 보고 고릅니다.고객 관리형 Knowledge Base를 기준으로 임베딩 모델과 차원, 벡터 인덱스와 필드 매핑, 지원 벡터 저장소와 S3 Vectors 선택 기준, 의미 ..

Bedrock Knowledge Bases 문서 파싱과 청킹: 검색 품질을 결정하는 전처리 | Bedrock 26편

Amazon Bedrock 완전 가이드 · 26편 Knowledge Bases 문서 파싱과 청킹 검색 품질을 결정하는 전처리 Amazon Bedrock Knowledge Bases는 문서를 수집할 때 먼저 파싱(내용 추출)하고, 그 결과를 청크(chunk)라는 조각으로 나눠 임베딩합니다. 표나 이미지가 중요한 문서라면 텍스트만 읽는 기본 파서 대신 Data Automation이나 모델 기반 파서를 검토하고, 청크는 문서 구조와 질문 유형에 맞는 분할 방식을 고릅니다. 분할 설정은 데이터 소스를 만든 뒤에는 바꿀 수 없으므로, 처음에 실제 문서와 질문으로 시험해 보고 정해야 합니다.고객 관리형 Knowledge Base를 기준으로 파서 세 가지의 차이, 기본·고정 크기·계층적·의미 기반·분할 없음의 ..

데이터 소스와 수집: 문서는 어떻게 검색 대상이 되는가 | Bedrock 25편

Amazon Bedrock 완전 가이드 · 25편 데이터 소스와 수집 문서는 어떻게 검색 대상이 되는가 Amazon Bedrock Knowledge Bases에서 문서는 데이터 소스를 동기화(수집 작업)해야 검색 대상이 됩니다. 동기화는 증분 방식이라 지난 동기화 이후 추가·수정·삭제된 문서만 처리하므로, 원본을 고친 뒤 동기화하지 않으면 검색 결과는 예전 상태로 남습니다. 수집이 끝나면 그 내용은 원본 시스템의 문서 권한과 관계없이 검색 권한(bedrock:Retrieve)을 가진 누구나 조회할 수 있으므로, 사용자별 접근 제어는 별도로 설계해야 합니다.가상의 사내 안내 도우미를 예로 데이터 소스 유형, 동기화와 직접 수집의 차이, 문서 삭제가 검색 결과에 반영되는 방식과 삭제 정책, 수집 작업 ..

Knowledge Bases 구성 선택: 관리형 저장소와 직접 선택한 저장소 | Bedrock 24편

Amazon Bedrock 완전 가이드 · 24편 Knowledge Bases 구성 선택 관리형 저장소와 직접 선택한 저장소 Amazon Bedrock Knowledge Bases는 두 방식으로 구성합니다. 관리형(Bedrock Managed) Knowledge Base는 저장소·임베딩·재정렬을 Bedrock이 맡고, 고객 관리형(Customer-managed) Knowledge Base는 사용자가 고른 벡터 저장소를 직접 만들고 운영합니다. AWS는 사용 편의성·정확도·비용의 균형을 이유로 관리형을 권장합니다. 벡터 저장소를 직접 조정해야 하거나 기존 저장소를 그대로 써야 한다면 고객 관리형을 선택합니다.가상의 사내 안내 도우미를 예로 Knowledge Base와 데이터 소스의 관계, 관리형과 고..

RAG란 무엇인가: 모델이 외부 지식으로 답하는 구조 | Bedrock 23편

Amazon Bedrock 완전 가이드 · 23편 RAG란 무엇인가 모델이 외부 지식으로 답하는 구조 RAG(Retrieval Augmented Generation, 검색 증강 생성)는 질문이 들어올 때 관련 문서를 먼저 검색하고, 찾은 내용을 모델 입력에 함께 넣어 답하게 하는 구조입니다. 모델을 다시 학습시키지 않고도 사내 안내문처럼 모델이 모르는 정보나 자주 바뀌는 정보로 답할 수 있습니다. Amazon Bedrock에서는 Knowledge Bases가 문서 수집과 검색 과정을 관리합니다. 다만 검색이 실패하거나 문서에 답이 없으면 RAG도 정확하게 답할 수 없습니다.가상의 사내 안내 도우미를 예로 RAG가 필요한 이유, Bedrock Knowledge Bases 기준의 수집 단계와 질문 처리..
반응형

kube-prometheus-stack 설치와 Grafana 접속: kind 클러스터 모니터링과 target down 해결

로컬 쿠버네티스 실습 4편
kube-prometheus-stack으로
Prometheus·Grafana 띄우기

helm 한 번으로 설치하고, Gateway로 Grafana를 열고, down 6개를 0개로 만든 기록

kube-prometheus-stackGrafanaPrometheusGateway API

kube-prometheus-stack helm 차트를 쓰면 Prometheus, Grafana, Alertmanager, node-exporter, kube-state-metrics가 한 번에 설치됩니다. 실습 PC의 kind 클러스터에서는 81초 만에 모든 파드가 Running이 됐고 메모리는 약 1.4GB를 더 썼습니다. Grafana는 3편의 Gateway에 grafana.localhost 호스트 이름으로 HTTPRoute를 붙여 Windows 브라우저에서 열었습니다. 설치 직후 Prometheus 수집 대상 28개 중 6개가 down이었는데, kind가 제어 플레인 구성 요소의 메트릭 포트를 127.0.0.1에만 열기 때문이었고 바인딩 주소를 바꾸자 28개 모두 up이 됐습니다.

1~3편에서 만든 kind 클러스터(노드 3개, Envoy Gateway의 lab-gateway가 Windows localhost:8080을 받음)를 이어서 씁니다. 차트는 kube-prometheus-stack 93.1.0, Grafana 13.3.0 기준입니다. 알림 설정, 장기 보관, 운영 환경 보안 설정은 다루지 않습니다.

01

kube-prometheus-stack에 들어 있는 것

kube-prometheus-stack은 쿠버네티스 모니터링에 필요한 구성 요소를 묶은 helm 차트입니다. Prometheus를 직접 설정 파일로 운영하지 않고, Prometheus Operator가 ServiceMonitor 같은 리소스를 읽어 수집 대상을 관리합니다. kube-prometheus-stack 차트

설치된 주요 구성 요소
구성 요소하는 일실습에서 뜬 파드
Prometheus메트릭을 주기적으로 긁어 저장prometheus-…-0 (2/2)
Prometheus OperatorServiceMonitor 등을 읽어 Prometheus 설정 관리…-operator (1/1)
Grafana대시보드로 메트릭 시각화kps-grafana (3/3)
Alertmanager경고를 모아 전달alertmanager-…-0 (2/2)
node-exporter노드 CPU·메모리·디스크 수집노드마다 1개씩 3개
kube-state-metrics디플로이먼트·파드 같은 리소스 상태를 메트릭으로1개
02

helm으로 설치하기

prometheus-community 저장소를 추가하고 설치합니다. --wait를 붙이면 모든 파드가 준비될 때까지 기다린 뒤 끝납니다.

helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update prometheus-community
helm install kps prometheus-community/kube-prometheus-stack -n monitoring --create-namespace --wait --timeout 10m

실습 시점의 차트 버전은 93.1.0이었고 설치는 81초가 걸렸습니다. 모든 서비스는 ClusterIP로 만들어지며, Grafana 서비스 이름은 릴리스 이름을 붙인 kps-grafana(포트 80)입니다. WSL 메모리 사용량은 설치 전 1.9GiB에서 설치 후 3.3GiB가 됐습니다. 참고로 kind에는 metrics-server가 없어서 kubectl top nodes는 Metrics API not available을 냅니다. 이 글의 메트릭은 모두 Prometheus가 따로 수집합니다.

03

Gateway에 Grafana 붙이기: 다른 네임스페이스 라우트 허용

3편의 Gateway(lab-gateway)는 default 네임스페이스에 있고 Grafana는 monitoring 네임스페이스에 있습니다. Grafana용 HTTPRoute를 monitoring에 만들고, grafana.localhost라는 호스트 이름으로 web 앱과 구분합니다.

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: grafana
namespace: monitoring
spec:
parentRefs:
- name: lab-gateway
namespace: default
hostnames:
- grafana.localhost
rules:
- backendRefs:
- name: kps-grafana
port: 80

이 라우트만 먼저 적용해 보면 붙지 않습니다. HTTPRoute 상태에 아래처럼 나오고, grafana.localhost로 보낸 요청은 404가 돌아왔습니다. Gateway 리스너는 기본적으로 같은 네임스페이스의 라우트만 받기 때문입니다. Gateway 리소스 설명

Accepted=False (NotAllowedByListeners) No listeners included by this parent ref allowed this attachment.

Gateway 리스너에 allowedRoutes를 추가해 다른 네임스페이스의 라우트도 받게 합니다. 실습 환경이라 from: All로 열었고, 실제 운영에서는 from: Selector로 라벨이 붙은 네임스페이스만 허용하는 편이 안전합니다.

listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All

적용 후 HTTPRoute는 Accepted=True가 됐고, Gateway의 attachedRoutes는 2(web, grafana)가 됐습니다. 호스트 이름이 없는 web 라우트는 그대로 나머지 요청을 받습니다.

04

Windows에서 grafana.localhost로 접속

Windows는 localhost로 끝나는 이름을 내 PC 주소로 풀어 줍니다. 실습 PC에서 Resolve-DnsName grafana.localhost를 실행하면 ::1이 나왔고, hosts 파일을 고치지 않아도 바로 접속됐습니다.

PS> curl.exe -s http://grafana.localhost:8080/api/health
{
"database": "ok",
"version": "13.3.0",
...
}
PS> curl.exe -s -o NUL -w "HTTP %{http_code}" http://grafana.localhost:8080/login
HTTP 200

브라우저에서 http://grafana.localhost:8080을 열면 Grafana 13.3.0 로그인 화면이 나옵니다. 사용자 이름은 admin이고, 비밀번호는 차트가 만든 시크릿에 들어 있습니다.

kubectl get secret -n monitoring kps-grafana -o jsonpath='{.data.admin-password}' | base64 -d; echo

로그인하면 차트가 미리 넣어 둔 대시보드가 있습니다. 실습 클러스터에는 대시보드용 ConfigMap이 29개 있었고, Dashboards 메뉴를 열면 Alertmanager, CoreDNS, etcd와 Kubernetes / Compute Resources / …, Kubernetes / Networking / …, Node Exporter / … 같은 대시보드가 태그와 함께 나옵니다. 따로 만들지 않아도 노드·네임스페이스·파드 단위로 바로 볼 수 있습니다.

예를 들어 Kubernetes / Compute Resources / Pod 대시보드에서 namespace를 default, pod를 2편에서 만든 web-… 파드로 고르면 그 파드의 CPU와 메모리가 그래프로 나옵니다. 실습 화면에서 nginx 컨테이너는 CPU를 거의 쓰지 않았고(0에 가까움), 메모리는 Working Set 기준 12.0MiB, RSS 8.37MiB였습니다. 요청이 거의 없는 nginx라 값이 작지만, 같은 화면에서 리소스 요청·제한(Quota)과 실제 사용량을 나란히 볼 수 있어 파드 크기를 정할 때 쓸 수 있습니다. 실습 PC에서 어떤 파드가 자원을 많이 쓰는지 찾을 때도 이 대시보드부터 보면 편합니다.

05

target down 6개: kind에서 생기는 이유와 해결

설치 직후 Prometheus 수집 대상을 확인하면 28개 중 22개만 up이었습니다. Prometheus 화면의 Status → Targets에서 볼 수도 있고, API로 세어 볼 수도 있습니다.

설치 직후 down이었던 수집 대상
대상포트오류
kube-controller-manager10257connection refused
kube-scheduler10259connection refused
kube-etcd2381connection refused
kube-proxy (노드 3개)10249connection refused

모두 connection refused라는 점이 실마리입니다. Prometheus는 노드 주소로 접속하는데, kind가 만든 이 구성 요소들은 메트릭 포트를 노드 안의 127.0.0.1에서만 엽니다. 실제로 제어 플레인 노드 안의 정적 파드 매니페스트에는 --bind-address=127.0.0.1이, etcd에는 --listen-metrics-urls=http://127.0.0.1:2381이 들어 있었고, kube-proxy 설정의 metricsBindAddress는 빈 값(기본값 127.0.0.1:10249)이었습니다.

실습 클러스터이므로 바인딩 주소를 0.0.0.0으로 바꿨습니다. kube-proxy는 ConfigMap을 고친 뒤 데몬셋을 다시 시작하고, 나머지 셋은 제어 플레인 노드 컨테이너 안의 매니페스트를 고치면 kubelet이 파드를 다시 만듭니다.

kubectl -n kube-system get cm kube-proxy -o yaml \
| sed 's/metricsBindAddress: .*/metricsBindAddress: 0.0.0.0:10249/' \
| kubectl apply -f -
kubectl -n kube-system rollout restart ds/kube-proxy
docker exec lab-control-plane sh -c "
sed -i 's/--bind-address=127.0.0.1/--bind-address=0.0.0.0/' /etc/kubernetes/manifests/kube-controller-manager.yaml /etc/kubernetes/manifests/kube-scheduler.yaml
sed -i 's#--listen-metrics-urls=http://127.0.0.1:2381#--listen-metrics-urls=http://0.0.0.0:2381#' /etc/kubernetes/manifests/etcd.yaml"

약 40초 뒤 네 구성 요소가 다시 Running이 됐고, 그다음 수집 주기에 28개 모두 up으로 바뀌었습니다. 이때 켜져 있던 경고는 Watchdog 하나뿐이었는데, 경고 전달 경로가 살아 있는지 확인하려고 항상 켜 두는 정상 경고입니다.

백업 파일 위치 주의

매니페스트를 고치기 전에 백업을 같은 /etc/kubernetes/manifests 폴더에 두면 kubelet이 그 파일까지 파드로 읽어 같은 구성 요소가 두 번 뜰 수 있습니다. 이 실습에서는 백업을 폴더 밖(/root/manifest-backup)에 두었습니다. 또 이 수정은 kind 클러스터를 다시 만들면 사라지므로, 다시 만들 때 같은 작업을 반복해야 합니다.

07

자주 묻는 질문

운영 클러스터에서도 bind-address를 0.0.0.0으로 바꿔도 되나요?

그대로 따라 하면 안 됩니다. 메트릭 포트가 모든 네트워크에 열리므로 운영 환경에서는 노드 방화벽이나 네트워크 정책으로 Prometheus만 접근하게 막아야 합니다. EKS 같은 관리형 클러스터는 제어 플레인 구성 요소를 사용자가 직접 바꿀 수 없으므로 수집 방식이 다릅니다.

Grafana를 localhost:8080/grafana 같은 경로로 열 수는 없나요?

가능하지만 Grafana에 하위 경로 설정(root_url과 serve_from_sub_path)을 따로 해야 합니다. 이 글은 설정을 바꾸지 않아도 되는 호스트 이름 방식(grafana.localhost)을 썼습니다.

작성 방식과 다음 편

이 글의 명령과 출력은 1편의 실습 PC에서 직접 실행한 결과이며, 설치와 설정은 kube-prometheus-stack 차트와 Gateway API 공식 문서를 기준으로 했습니다. 실행 기록 정리와 글 작성에 AI 도구를 함께 사용했습니다. 다음 편에서는 Loki를 설치해 파드 로그를 모으고, 같은 Grafana 화면에서 메트릭과 로그를 함께 봅니다.

반응형

NGINX Ingress 대신 Gateway API: kind 클러스터 앱을 Windows localhost로 접속하기

로컬 쿠버네티스 실습 3편
NGINX Ingress 대신 Gateway API
kind 앱을 localhost:8080으로

Envoy Gateway를 설치하고, 로드밸런서 없는 kind에서 Windows 브라우저까지 연결한 과정

Gateway APIEnvoy GatewayHTTPRoutekind

kind 클러스터에서 Gateway API를 쓰려면 Gateway API 리소스 정의와 구현체가 필요한데, Envoy Gateway helm 차트 하나로 둘 다 설치됩니다. 남는 문제는 kind에 로드밸런서가 없다는 점입니다. 이 글에서는 Envoy 프록시를 8080 포트가 연결된 제어 플레인 노드에 띄우고 노드의 80번 포트를 직접 받게(hostPort) 설정해서, Windows 브라우저의 localhost:8080이 Gateway → HTTPRoute → web 파드로 이어지게 만들었습니다. 적용 후 약 20초 만에 Gateway가 준비됐고, 요청 6번이 파드 2개에 3건씩 나뉘어 들어가는 것까지 확인했습니다.

1편에서 만든 kind 클러스터(노드 3개, Windows 8080 → 제어 플레인 80 포트 연결)와 2편에서 만든 web Deployment(nginx 파드 2개)를 그대로 씁니다. Envoy Gateway v1.10.0, Kubernetes v1.37.0 기준이며 HTTPS와 여러 도메인 라우팅은 다루지 않습니다.

01

왜 Ingress가 아니라 Gateway API인가

쿠버네티스에서 외부 요청을 서비스로 보내는 방법은 오랫동안 Ingress였고, 그 구현체로 NGINX Ingress Controller가 가장 많이 쓰였습니다. GitHub에서 확인하면 kubernetes/ingress-nginx 저장소는 2026년 3월 이후 새 커밋이 없습니다. 새로 실습 환경을 만든다면 Ingress의 후속 표준인 Gateway API로 시작하는 편이 낫습니다. Gateway API 공식 문서

Gateway API는 역할을 리소스 세 단계로 나눕니다. 이 구분을 알면 아래 매니페스트가 왜 여러 개인지 이해하기 쉽습니다.

Gateway API의 주요 리소스
리소스 정하는 것 이 글에서 만든 것
GatewayClass 어떤 구현체(컨트롤러)를 쓸지 eg (Envoy Gateway)
Gateway 어느 포트·프로토콜로 요청을 받을지 lab-gateway (HTTP 80)
HTTPRoute 받은 요청을 어느 서비스로 보낼지 web (/ → web 서비스 80)
02

Envoy Gateway 설치: CRD까지 helm 한 번에

kind는 Gateway API 리소스를 기본으로 넣지 않습니다. 설치 전에 kubectl api-resources --api-group=gateway.networking.k8s.io를 실행하면 아무것도 나오지 않습니다. Envoy Gateway의 helm 차트가 리소스 정의(CRD)와 컨트롤러를 함께 설치합니다. Envoy Gateway 빠른 시작

helm install eg oci://docker.io/envoyproxy/gateway-helm --version v1.10.0 -n envoy-gateway-system --create-namespace
kubectl wait --timeout=5m -n envoy-gateway-system deployment/envoy-gateway --for=condition=Available

컨트롤러가 준비되기까지 18초가 걸렸습니다. 설치 중에 인증서를 만드는 eg-gateway-helm-certgen 잡이 한 번 실행되고 Completed로 끝납니다. 설치된 리소스 종류는 GatewayClass, Gateway, HTTPRoute, GRPCRoute, TLSRoute 등 10가지였습니다.

확인해 볼 점: 함께 깔리는 Gateway API 버전

같은 날 Gateway API 최신 릴리스는 v1.7.0이었지만, Envoy Gateway v1.10.0 차트가 설치한 CRD에는 v1.6.1 experimental 채널이 적혀 있었습니다. 구현체가 지원하는 버전이 따로 있으므로, Gateway API CRD를 직접 최신으로 올리기 전에 구현체의 지원 버전을 먼저 확인합니다. 확인 명령: kubectl get crd gateways.gateway.networking.k8s.io -o jsonpath='{.metadata.annotations}'

03

로드밸런서 없는 kind에서 Windows까지 잇는 방법

Envoy Gateway는 Gateway를 만들면 Envoy 프록시 파드와 LoadBalancer 서비스를 만듭니다. 클라우드라면 이 서비스에 외부 주소가 붙지만 kind에는 로드밸런서가 없습니다. 선택지는 세 가지입니다.

kind에서 Gateway에 접속하는 방법
방법 장점 단점
kubectl port-forward 설정 없이 바로 확인 가능(공식 빠른 시작의 방법) 명령을 켜 둔 동안만 접속, 매번 다시 실행
MetalLB 같은 로드밸런서 구현 클라우드와 비슷하게 주소가 붙음 붙는 주소가 WSL 안 Docker 네트워크 대역이라 Windows 브라우저에서 바로 쓰려면 추가 설정이 필요할 수 있음
노드 포트 직접 받기(hostPort) 1편의 8080 연결을 그대로 사용, 항상 접속 가능 Envoy 파드를 특정 노드에 고정해야 함

이 글은 세 번째 방법을 썼습니다. 1편에서 Windows 8080을 제어 플레인 노드 컨테이너의 80번 포트에 연결해 두었으므로, Envoy가 그 노드의 80번 포트를 직접 받으면 됩니다. Envoy 프록시의 모양은 EnvoyProxy라는 Envoy Gateway 전용 리소스로 바꿉니다. EnvoyProxy 설정 안내

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: kind-hostport
namespace: envoy-gateway-system
spec:
provider:
type: Kubernetes
kubernetes:
envoyService:
type: ClusterIP
envoyDeployment:
replicas: 1
pod:
nodeSelector:
ingress-ready: "true"
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
patch:
type: StrategicMerge
value:
spec:
template:
spec:
containers:
- name: envoy
ports:
- containerPort: 10080
hostPort: 80
protocol: TCP

설정마다 이유가 있습니다. envoyService.type: ClusterIP는 붙지 않을 외부 주소를 기다리지 않게 합니다. nodeSelector의 ingress-ready: "true"는 1편에서 제어 플레인에 붙여 둔 라벨이고, 제어 플레인에는 일반 파드가 올라가지 못하게 막는 테인트가 있어서 tolerations로 허용합니다. 마지막 patch가 핵심인데, 노드의 80번 포트를 Envoy 컨테이너의 10080번 포트로 연결합니다.

80이 아니라 10080인 이유는 Envoy Gateway가 1024보다 작은 특권 포트를 컨테이너 안에서 높은 번호로 바꿔 열기 때문입니다. Gateway 리스너를 80으로 만들면 Envoy는 실제로 10080에서 기다립니다. 처음에는 이 점을 모르면 hostPort를 80 → 80으로 잡기 쉽습니다.

04

GatewayClass·Gateway·HTTPRoute 만들기

GatewayClass는 위의 EnvoyProxy를 parametersRef로 가리켜서, 이 클래스로 만드는 모든 Gateway가 그 설정을 따르게 합니다. web Deployment에는 아직 서비스가 없으므로 서비스도 함께 만듭니다.

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: kind-hostport
namespace: envoy-gateway-system
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: lab-gateway
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web
spec:
parentRefs:
- name: lab-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web
port: 80

HTTPRoute의 parentRefs가 어느 Gateway에 붙을지, backendRefs가 요청을 받을 서비스를 정합니다. 서비스(web, 포트 80 → 파드 80)까지 하나의 파일에 넣어 kubectl apply -f로 적용했고, 상태가 준비될 때까지 기다렸습니다.

$ kubectl wait --for=condition=Programmed gateway/lab-gateway --timeout=180s
gateway.gateway.networking.k8s.io/lab-gateway condition met
$ kubectl get gatewayclass,gateway
NAME CONTROLLER ACCEPTED
eg gateway.envoyproxy.io/gatewayclass-controller True
NAME CLASS PROGRAMMED
lab-gateway eg True

적용부터 Programmed까지 약 20초가 걸렸습니다. HTTPRoute 상태에도 Accepted=True와 ResolvedRefs=True가 나와야 합니다. ResolvedRefs가 False라면 backendRefs의 서비스 이름이나 포트가 틀린 경우가 많습니다. Envoy 프록시 파드는 의도대로 lab-control-plane 노드에 떴고, envoy 컨테이너의 10080 포트에 hostPort 80이 붙어 있었습니다.

05

Windows 브라우저로 접속하고 부하 분산 확인

이제 Windows에서 접속합니다. 요청 경로는 Windows localhost:8080 → kind 제어 플레인 노드 80 → Envoy 10080 → web 서비스 → nginx 파드입니다.

PS> curl.exe -s -o NUL -w "HTTP %{http_code}" http://localhost:8080/
HTTP 200
PS> curl.exe -s http://localhost:8080/ | Select-String "<title>"
<title>Welcome to nginx!</title>

Windows 브라우저에서 http://localhost:8080을 열어도 nginx 환영 페이지가 나왔습니다. 마지막으로 요청이 파드 2개로 나뉘는지 봤습니다. Windows에서 /?n=1부터 /?n=6까지 6번 요청한 뒤 각 파드의 nginx 접근 로그를 세면 됩니다.

for p in $(kubectl get pods -l app=web -o name); do
echo "$p $(kubectl logs "$p" --since=5m | grep -c 'GET /?n=')건"
done

결과는 web-…-5gdxz 3건, web-…-k5682 3건이었습니다. Gateway가 서비스 뒤의 파드들로 요청을 나눠 보낸다는 것을 로그로 확인할 수 있습니다.

PowerShell에서 bash 명령을 넘길 때

위 반복문을 PowerShell에서 wsl -- bash -c "..."로 한 줄에 넘기면 $(...) 부분을 PowerShell이 먼저 해석해서 bash 문법 오류가 납니다. 실습 중 실제로 겪은 문제로, 스크립트 파일로 저장한 뒤 wsl -- bash 파일로 실행하면 해결됩니다.

07

자주 묻는 질문

hostPort 대신 port-forward만 써도 되나요?

확인만 할 때는 됩니다. Envoy Gateway 빠른 시작도 로드밸런서가 없으면 kubectl port-forward로 Envoy 서비스에 접속하는 방법을 안내합니다. 다만 명령을 켜 둔 동안에만 접속되므로, 다음 편부터 여러 앱을 계속 띄워 둘 실습 환경에는 hostPort 방식이 편합니다.

Gateway의 ADDRESS에 나오는 주소로 접속하면 되나요?

이 구성에서는 Envoy 서비스를 ClusterIP로 바꿨기 때문에 ADDRESS에는 클러스터 안에서만 쓰는 주소가 나옵니다. Windows에서는 그 주소가 아니라 localhost:8080으로 접속합니다.

작성 방식과 다음 편

이 글의 명령과 출력은 1편의 실습 PC에서 직접 실행한 결과이며, 설치와 설정은 Gateway API와 Envoy Gateway 공식 문서를 기준으로 했습니다. 실행 기록 정리와 글 작성에 AI 도구를 함께 사용했습니다. 다음 편에서는 kube-prometheus-stack으로 Prometheus와 Grafana를 설치하고, Grafana도 이 Gateway를 통해 Windows 브라우저에서 엽니다.

반응형

Headlamp 설치와 사용법: 로컬 쿠버네티스를 화면으로 보고 파드 배포하기

로컬 쿠버네티스 실습 2편
Headlamp 설치와 사용법
클릭으로 보고, YAML로 배포하기

WSL2 안의 kind 클러스터를 Windows 앱에서 보고 파드까지 배포해 본 기록

Headlamp쿠버네티스 GUIkindWSL2

Headlamp는 쿠버네티스 클러스터를 화면으로 보고 관리하는 데스크톱 앱입니다. Windows에서는 winget으로 설치하고, 사용자 폴더의 .kube\config에 클러스터 접속 정보를 넣으면 별도 설정 없이 클러스터가 목록에 나옵니다. 보기 전용이 아니라 왼쪽 아래 '+ 생성' 버튼에 YAML을 붙여 넣고 '적용'을 누르면 Deployment를 바로 만들 수 있고, 실제로 만든 파드 2개가 Running이 되는 것까지 확인했습니다.

1편에서 WSL2 Ubuntu 24.04 안에 만든 kind 클러스터(kind-lab, 노드 3개)를 Windows 11에 설치한 Headlamp 0.46.0으로 연결한 결과입니다. 화면 이름은 Headlamp 한국어 화면 기준이며, 로그 보기·파드 터미널·삭제는 이 글에서 다루지 않습니다.

01

Headlamp란: Kubernetes Dashboard 대신 쓰는 GUI

Headlamp는 쿠버네티스 리소스를 목록과 상세 화면으로 보여 주는 오픈소스 GUI입니다. 데스크톱 앱으로 설치할 수도 있고 클러스터 안에 웹 앱으로 올릴 수도 있습니다. 이 글은 Windows 데스크톱 앱을 씁니다. Headlamp 저장소

쿠버네티스 GUI를 고를 때 예전에는 Kubernetes Dashboard나 Lens를 먼저 떠올렸습니다. 2026년 11월 기준 GitHub에서 확인하면 Kubernetes Dashboard 저장소는 kubernetes-retired 조직으로 옮겨져 보관(archived) 상태이고, Lens의 공개 저장소는 2025년 2월 이후 갱신이 없습니다. Headlamp는 쿠버네티스 공식 하위 프로젝트 조직(kubernetes-sigs)에서 개발되고 있어 새로 시작한다면 먼저 검토할 만합니다.

쿠버네티스 클러스터를 보는 방법 비교
방법형태이런 경우에 맞음
kubectl명령어모든 작업의 기본. 자동화와 스크립트
k9s터미널 대시보드터미널에서 키보드로 빠르게 둘러볼 때
Headlamp데스크톱·웹 앱구조를 화면으로 익히거나 클릭으로 확인할 때
VS Code Kubernetes 확장편집기 사이드바YAML을 편집하면서 클러스터를 같이 볼 때
02

Windows에 Headlamp 설치(winget)

Windows 11에는 패키지 관리자 winget이 기본으로 들어 있습니다. 먼저 패키지 이름을 확인하고 설치합니다.

winget search --id Headlamp --source winget
winget install --id Headlamp.Headlamp -e --source winget

실제로 설치된 버전은 0.46.0이었고, 설치 파일은 Headlamp의 GitHub 릴리스에서 내려받았습니다. winget이 설치 파일 해시를 확인한 뒤 설치합니다.

찾음 Headlamp [Headlamp.Headlamp] 버전 0.46.0
다운로드 중 https://github.com/kubernetes-sigs/headlamp/releases/download/v0.46.0/Headlamp-0.46.0-win-x64.exe
설치 관리자 해시를 확인했습니다.
설치 성공

설치가 끝나면 시작 메뉴에서 Headlamp를 실행할 수 있습니다.

03

WSL2 kind 클러스터를 Headlamp에 연결하기

Headlamp는 Windows 사용자 폴더의 .kube\config 파일을 읽어 클러스터 목록을 만듭니다. kind 클러스터의 접속 정보는 WSL 안에 있으므로 kind get kubeconfig로 꺼내 Windows 쪽에 저장합니다. 이미 .kube\config가 있다면 덮어쓰지 말고 백업부터 합니다.

New-Item -ItemType Directory -Force "$env:USERPROFILE\.kube" | Out-Null
$cfg = (wsl -d Ubuntu-24.04 -u root -- kind get kubeconfig --name lab) -join "`n"
[IO.File]::WriteAllText("$env:USERPROFILE\.kube\config", $cfg + "`n")

[IO.File]::WriteAllText를 쓴 이유는 Windows PowerShell 5.1의 > 리디렉션이 UTF-16으로 저장하기 때문입니다. 저장된 파일의 server 값은 https://127.0.0.1:포트였습니다. kind는 API 서버를 WSL 안의 127.0.0.1에 열어 두는데, WSL2는 이 주소를 Windows에서도 그대로 쓸 수 있게 해 줍니다. Headlamp를 열기 전에 Windows에서 실제로 닿는지 확인할 수 있습니다.

PS> curl.exe -sk -o NUL -w "livez HTTP %{http_code}" https://127.0.0.1:포트/livez
livez HTTP 200

200이 나오면 Headlamp를 엽니다. 실습 PC에서는 클러스터 추가 설정 없이 첫 화면에 kind-lab이 바로 나왔습니다. 클러스터 상세 화면에는 Kubeconfig: 항목에 파일 경로가 표시되는데, 경로에 Windows 계정명이 들어가므로 화면을 캡처해 공유할 때는 가립니다.

04

화면 둘러보기: 노드·파드는 어디서 보나

kind-lab에 들어가면 왼쪽 메뉴가 쿠버네티스 리소스 종류별로 나뉘어 있습니다. 한국어 화면에서 본 메뉴는 아래와 같습니다.

Headlamp 왼쪽 메뉴(한국어 화면)
메뉴볼 수 있는 것
Clusters연결된 클러스터 목록과 노드
맵리소스 사이의 연결 관계
워크로드파드, 디플로이먼트, 스테이트풀 셋, 데몬셋, 레플리카 셋, 잡, 크론잡 등
스토리지 / 네트워크볼륨, 서비스 등
게이트웨이(베타)Gateway API 리소스(다음 편부터 사용)
보안 / 구성서비스 어카운트·역할, ConfigMap·Secret 등
커스텀 리소스CRD로 추가한 리소스

파드 목록은 워크로드 → 파드에서 봅니다. 시스템 파드까지 보려면 위쪽 네임스페이스 선택을 모든 네임스페이스로 바꿉니다. 1편에서 만든 클러스터라면 kube-system의 CoreDNS, kindnet, kube-proxy와 local-path-storage의 프로비저너까지 파드 13개가 Running으로 보입니다.

05

'+ 생성' 버튼으로 파드 배포하기

Headlamp는 보기 전용이 아닙니다. 왼쪽 아래 + 생성 버튼을 누르면 '생성 / 적용' 편집 창이 열립니다. 창에는 에디터·Form·문서·Review Changes 탭과 Upload File/URL 버튼이 있고, 아래쪽에 닫기·Dry Run·적용 버튼이 있습니다. 에디터 탭에 아래 YAML을 붙여 넣었습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
name: web
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80

nginx 컨테이너 하나를 가진 파드를 2개 유지하라는 Deployment입니다. selector.matchLabels와 template.metadata.labels의 app: web이 같아야 Deployment가 자기 파드를 찾습니다. 적용을 누른 뒤 WSL 셸에서 kubectl로 결과를 확인했습니다.

$ kubectl get deploy web
NAME READY UP-TO-DATE AVAILABLE AGE
web 2/2 2 2 20s
$ kubectl get pods -l app=web -o custom-columns=NAME:.metadata.name,STATUS:.status.phase,NODE:.spec.nodeName
NAME STATUS NODE
web-75d8d959fb-5gdxz Running lab-worker
web-75d8d959fb-k5682 Running lab-worker2

Headlamp가 만든 리소스라는 기록도 클러스터에 남습니다. Deployment의 metadata.managedFields를 보면 이 리소스를 바꾼 주체가 Headlamp 0.46.0 (windows ...)로, 그다음이 kube-controller-manager로 적혀 있습니다. Headlamp가 kubectl처럼 API 서버에 직접 요청을 보낸다는 뜻이므로, Headlamp에서 한 작업도 kubectl로 한 작업과 똑같이 클러스터에 반영됩니다.

Dry Run 먼저

적용 옆의 Dry Run은 실제로 만들지 않고 API 서버가 요청을 받아들이는지만 확인하는 버튼입니다. YAML을 처음 써 볼 때는 Dry Run으로 오류가 없는지 본 뒤 적용하면 안전합니다.

06

클러스터가 Unavailable로 나올 때

한동안 쓰지 않다가 Headlamp를 보니 kind-lab 아래에 Unavailable이 표시됐습니다. Windows에서 /livez를 다시 호출하자 응답이 없었고(HTTP 000), wsl -l -v로 보니 Ubuntu-24.04가 Stopped였습니다. 열린 WSL 셸이나 실행 중인 명령이 없으면 WSL이 스스로 꺼지고, 그 안의 Docker와 kind 노드도 함께 멈춥니다.

PS> wsl -l -v
NAME STATE VERSION
* Ubuntu-24.04 Stopped 2
PS> wsl -d Ubuntu-24.04 -u root

WSL을 다시 켜는 것만으로 해결됐습니다. 켜자마자 Docker가 시작되고 kind 노드 컨테이너 3개가 자동으로 다시 떴습니다. 다만 켜진 직후 몇십 초 동안은 kubectl get nodes가 Forbidden 오류를 냈고, 잠시 뒤 다시 실행하자 노드 3개가 모두 Ready였습니다. Headlamp를 쓰는 동안 Windows 터미널에 Ubuntu 탭을 하나 열어 두면 이 문제를 피할 수 있습니다.

이렇게 WSL이 꺼졌다 켜지는 일이 반복되면 Headlamp 파드 목록의 '재시작' 열에 숫자가 쌓입니다. 실습 PC에서는 약 30분 사이에 kindnet·kube-proxy·CoreDNS 같은 시스템 파드가 9회, local-path-provisioner가 12회로 표시됐습니다. kubectl로 직전 종료 이유를 보면 대부분 Unknown, 종료 코드 255였는데, 컨테이너가 스스로 죽은 것이 아니라 WSL과 함께 갑자기 멈췄다는 뜻입니다. local-path-provisioner는 API 서버가 준비되기 전에 먼저 떠서 Error(종료 코드 1)로 몇 번 더 재시작했습니다. 모두 Running으로 돌아왔다면 이 환경에서는 걱정하지 않아도 되는 숫자입니다.

kubectl get pods -A -o custom-columns='NAME:.metadata.name,RESTARTS:.status.containerStatuses[0].restartCount,REASON:.status.containerStatuses[0].lastState.terminated.reason,EXIT:.status.containerStatuses[0].lastState.terminated.exitCode'
08

자주 묻는 질문

Headlamp는 보기 전용인가요?

아닙니다. 이 글처럼 YAML로 리소스를 만들 수 있고, Headlamp 저장소의 README는 생성·수정·삭제, 로그, 터미널 접속을 기능으로 소개합니다. 버튼은 접속한 사용자의 권한에 맞춰 보이므로, kind 기본 kubeconfig처럼 관리자 권한이면 모든 작업 버튼이 나옵니다.

kind 클러스터를 다시 만들면 Headlamp 설정도 바꿔야 하나요?

kind는 클러스터를 만들 때마다 API 서버 포트를 새로 정합니다. 클러스터를 지우고 다시 만들었다면 kind get kubeconfig로 Windows의 .kube\config를 다시 저장해야 합니다.

작성 방식과 다음 편

이 글의 명령과 출력, 화면 이름은 1편의 실습 PC에서 직접 확인한 결과이며, 파드 배포는 Headlamp 화면에서 직접 적용했습니다. 실행 기록 정리와 글 작성에 AI 도구를 함께 사용했습니다. 다음 편에서는 이 클러스터에 Gateway API를 올려 Windows 브라우저에서 localhost:8080으로 web 파드에 접속합니다.

반응형

Windows에서 무료 쿠버네티스 실습 환경 만들기: WSL2 + Docker Engine + kind

로컬 쿠버네티스 실습 1편
Windows에서 무료 쿠버네티스 실습 환경
WSL2 + Docker Engine + kind

클라우드 비용 없이 내 PC에 노드 3개짜리 클러스터를 만든 과정과 실제로 겪은 문제

WSL2Docker Enginekindkubectl

Windows 11에서는 WSL2에 Ubuntu를 설치하고, 그 안에 Docker Engine과 kind를 올리면 클라우드 비용 없이 제어 플레인 1개와 워커 2개로 된 쿠버네티스 클러스터를 만들 수 있습니다. 실제로 해 보니 클러스터 생성은 52초면 끝났고, 시간이 걸린 부분은 WSL 설치 뒤 재부팅과 배포판 설치였습니다. Docker Desktop 없이 Docker 공식 apt 저장소로 설치하고, kubectl·kind·helm은 공식 배포처 파일을 SHA256으로 확인한 뒤 설치합니다.

Windows 11 Pro(빌드 26200), WSL 3.0.3, Ubuntu 24.04 LTS 기준입니다. 설치한 버전은 Docker 29.9.3, kubectl v1.37.2, kind v0.33.0, helm v4.3.1이며 클러스터 노드는 kindest/node:v1.37.0입니다. 회사 PC처럼 관리자 권한이나 가상화 설정이 막힌 환경은 다루지 않습니다.

01

왜 WSL2 + Docker Engine + kind인가

클라우드에서 쿠버네티스를 실습하면 관리형 컨트롤 플레인, 노드, 로드밸런서 비용이 시간 단위로 나옵니다. 로컬 PC에서는 이 비용이 없고, 클러스터를 지웠다가 1분 안에 다시 만들 수 있어서 설정을 망가뜨려 보는 실습에도 부담이 없습니다.

kind(Kubernetes IN Docker)는 쿠버네티스 노드 하나를 Docker 컨테이너 하나로 띄우는 도구입니다. 컨테이너만 늘리면 노드가 늘어나므로 제어 플레인과 워커를 나눈 다중 노드 구성을 PC 한 대에서 만들 수 있습니다. kind 빠른 시작

이 글에서 쓰는 구성 요소
구성 요소 역할 설치 위치
WSL2 + Ubuntu 24.04 리눅스 커널과 systemd가 있는 실행 환경 Windows
Docker Engine kind 노드 컨테이너를 실행 Ubuntu 안
kind Docker 컨테이너로 쿠버네티스 노드를 생성 Ubuntu 안
kubectl 클러스터에 명령을 보내는 CLI Ubuntu 안
helm 다음 편부터 쓸 오픈소스 패키지 설치 도구 Ubuntu 안

Docker Desktop을 쓰지 않은 이유는 두 가지입니다. WSL 안에 Docker Engine만 두면 구성 요소가 적어 문제가 생겨도 원인을 좁히기 쉽고, Docker Desktop은 회사 규모에 따라 유료 구독이 필요할 수 있어서 개인 실습 환경을 설명할 때 조건이 하나 더 붙기 때문입니다.

02

1단계: 가상화 확인과 WSL 설치(재부팅 필수)

먼저 CPU 가상화가 BIOS에서 켜져 있는지 확인합니다. 일반 권한 PowerShell에서 아래 명령을 실행해 둘 다 True면 다음 단계로 갑니다.

$p = Get-CimInstance Win32_Processor | Select-Object -First 1
$p.VirtualizationFirmwareEnabled
$p.SecondLevelAddressTranslationExtensions

실습 PC(Ryzen 5 3600X)에서는 둘 다 True였습니다. 이 PC에는 wsl.exe가 있었지만 wsl --status를 실행하면 WSL이 설치되어 있지 않다는 안내가 나왔습니다. wsl.exe는 Windows에 기본으로 들어 있는 진입점일 뿐이므로, 파일이 있다고 WSL이 설치된 것은 아닙니다.

WSL 설치는 Windows 기능을 켜는 작업이라 관리자 권한 PowerShell에서 실행합니다. WSL 설치 안내

wsl --install -d Ubuntu-24.04

여기서 실제로 막힌 부분이 있습니다. 설치 직후 재부팅하지 않은 상태에서는 wsl --version은 정상으로 나오는데 wsl -l -v는 설치된 배포가 없다고 나왔습니다. 배포판 설치를 다시 시도해도 "시스템을 다시 시작하면 변경 사항이 적용됩니다"라는 메시지만 나왔고, 이때 HypervisorPresent 값은 False였습니다. 가상 머신 플랫폼은 재부팅해야 켜지므로 WSL 설치 뒤에는 반드시 재부팅합니다.

재부팅 뒤에도 배포판이 없다면

재부팅 후 HypervisorPresent는 True가 됐지만 Ubuntu는 자동으로 설치되지 않았습니다. 이때는 일반 권한 PowerShell에서 wsl --install -d Ubuntu-24.04를 한 번 더 실행하면 다운로드와 설치가 바로 진행됩니다. 관리자 권한은 필요 없었습니다.

03

2단계: Ubuntu 안에 Docker Engine 설치

Ubuntu 셸에서 Docker 공식 apt 저장소를 등록하고 설치합니다. Ubuntu 기본 저장소의 docker.io 패키지가 아니라 Docker가 배포하는 패키지를 쓰는 방식입니다. Docker Engine Ubuntu 설치 안내

이 글의 명령은 wsl -d Ubuntu-24.04 -u root로 연 root 셸에서 실행했습니다. 일반 사용자 셸이라면 각 명령 앞에 sudo를 붙입니다.

apt-get update
apt-get install -y ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc
. /etc/os-release
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu ${UBUNTU_CODENAME:-$VERSION_CODENAME} stable" > /etc/apt/sources.list.d/docker.list
apt-get update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

signed-by는 이 저장소의 패키지를 Docker 서명 키로만 검증하도록 묶는 설정입니다. Ubuntu 24.04의 코드명은 noble이라 실제로 등록된 줄은 아래와 같았습니다.

deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu noble stable

WSL의 Ubuntu 24.04는 PID 1이 systemd라서 Docker 서비스가 일반 리눅스처럼 바로 실행됐습니다. 설치 후 확인 결과는 아래와 같습니다.

$ docker version --format 'Client {{.Client.Version}} / Server {{.Server.Version}}'
Client 29.9.3 / Server 29.9.3
$ systemctl is-active docker
active
$ docker run --rm hello-world
Hello from Docker!
04

3단계: kubectl·kind·helm 설치와 체크섬 확인

세 도구는 모두 실행 파일 하나라서 공식 배포처에서 받아 /usr/local/bin에 넣으면 됩니다. 받은 파일이 배포된 파일과 같은지 SHA256 체크섬을 먼저 확인합니다. 아래는 kubectl 예시입니다. kubectl 설치 안내

KV=$(curl -fsSL https://dl.k8s.io/release/stable.txt)
curl -fsSLO "https://dl.k8s.io/release/$KV/bin/linux/amd64/kubectl"
curl -fsSLO "https://dl.k8s.io/release/$KV/bin/linux/amd64/kubectl.sha256"
echo "$(cat kubectl.sha256) kubectl" | sha256sum --check
install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl

kind는 https://kind.sigs.k8s.io/dl/버전/kind-linux-amd64와 같은 위치의 .sha256sum 파일로, helm은 https://get.helm.sh/helm-버전-linux-amd64.tar.gz와 .sha256sum 파일로 같은 방식으로 확인했습니다. 세 파일 모두 OK가 나온 뒤 설치했고, 설치된 버전은 아래와 같습니다.

Client Version: v1.37.2
Kustomize Version: v5.8.1
kind v0.33.0 go1.26.7 linux/amd64
v4.3.1+gc0d08d0
실제로 겪은 오류: curl (23)

최신 버전 번호를 curl ... | grep -m1 tag_name 식으로 뽑았더니 curl: (23) Failure writing output to destination이 나왔습니다. grep -m1이 첫 줄을 찾자마자 파이프를 닫아 curl이 쓰기 오류를 내고, set -o pipefail을 켠 스크립트가 그 자리에서 멈춘 것입니다. 응답을 변수에 먼저 담은 뒤 grep하도록 바꾸자 해결됐습니다.

05

4단계: 노드 3개짜리 kind 클러스터 만들기

노드 구성은 설정 파일로 정합니다. 제어 플레인 1개와 워커 2개를 두고, 다음 편에서 Gateway API를 시험할 수 있게 제어 플레인의 80·443 포트를 Windows 쪽 8080·8443으로 연결했습니다.

kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
name: lab
nodes:
- role: control-plane
kubeadmConfigPatches:
- |
kind: InitConfiguration
nodeRegistration:
kubeletExtraArgs:
node-labels: "ingress-ready=true"
extraPortMappings:
- containerPort: 80
hostPort: 8080
protocol: TCP
- containerPort: 443
hostPort: 8443
protocol: TCP
- role: worker
- role: worker

extraPortMappings는 노드 컨테이너의 포트를 호스트 포트로 내보내는 설정이고, ingress-ready=true 라벨은 이후 게이트웨이 컨트롤러를 이 노드에 두기 위한 표시입니다. 80·443을 그대로 쓰지 않은 이유는 Windows에서 이미 쓰는 포트와 겹치지 않게 하기 위해서입니다.

kind create cluster --config kind-lab.yaml --wait 180s

노드 이미지(kindest/node:v1.37.0) 다운로드를 포함해 52초가 걸렸습니다. 출력 마지막에 Ready after 8s가 나오고 kubectl 컨텍스트가 kind-lab으로 바뀝니다.

06

워커가 NotReady로 나올 때: 확인과 동작 테스트

생성 직후 바로 kubectl get nodes를 실행하면 제어 플레인만 Ready이고 워커 2개는 NotReady였습니다. 워커가 붙은 지 10초밖에 지나지 않은 시점이었고, kind 출력에도 Waiting ≤ 3m0s for control-plane = Ready라고 나오듯 --wait는 제어 플레인만 기다립니다. 모든 노드를 기다리려면 아래 명령을 씁니다.

$ kubectl wait --for=condition=Ready nodes --all --timeout=180s
node/lab-control-plane condition met
node/lab-worker condition met
node/lab-worker2 condition met
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
lab-control-plane Ready control-plane 33s v1.37.0
lab-worker Ready <none> 21s v1.37.0
lab-worker2 Ready <none> 21s v1.37.0

노드 목록만으로는 워커가 실제로 파드를 받는지 알 수 없으므로 nginx 파드 2개를 띄워 봤습니다. 약 6초 만에 둘 다 Running이 됐고 lab-worker와 lab-worker2에 하나씩 배치됐습니다. 확인한 뒤 바로 지웁니다.

kubectl create deployment hello --image=nginx:alpine --replicas=2
kubectl rollout status deploy/hello --timeout=120s
kubectl get pods -l app=hello -o wide
kubectl delete deployment hello
만들어진 클러스터 구성
항목 값
노드 lab-control-plane, lab-worker, lab-worker2 (모두 Docker 컨테이너)
노드 OS·런타임 Debian 13, containerd 2.3.4, 커널은 WSL2 커널 공유
네트워크(CNI) kindnet
스토리지 local-path-provisioner (기본 StorageClass)
WSL에 잡힌 자원 CPU 12개, 메모리 약 23GB (PC 48GB의 절반)
08

자주 묻는 질문

WSL 설치 후 재부팅을 꼭 해야 하나요?

네. 실습 PC에서는 재부팅 전까지 하이퍼바이저가 꺼져 있어(HypervisorPresent False) 배포판이 설치되지 않았습니다. 재부팅 후에도 배포판이 없으면 일반 권한으로 wsl --install -d Ubuntu-24.04를 다시 실행합니다.

일반 사용자로 docker와 kubectl을 쓰려면 어떻게 하나요?

사용자를 docker 그룹에 넣고(sudo usermod -aG docker 사용자명) WSL 셸을 다시 연 뒤, kind export kubeconfig --name lab으로 그 사용자의 kubeconfig에 클러스터 접속 정보를 씁니다. 이 글은 root 셸에서 진행했습니다.

작성 방식과 다음 편

이 글의 명령과 출력은 위에 적은 PC에서 직접 실행한 결과이며, 설치 순서는 Microsoft·Docker·Kubernetes·kind·Helm 공식 문서를 기준으로 했습니다. 실행 기록 정리와 글 작성에 AI 도구를 함께 사용했습니다. 다음 편에서는 이 클러스터를 Windows 앱 Headlamp로 연결해 화면으로 보고, WSL이 꺼졌다 켜졌을 때 클러스터가 다시 살아나는지도 확인합니다.

반응형

Bedrock Knowledge Bases 검색 설정과 재정렬: 필요한 문서를 먼저 찾는 방법 | Bedrock 28편

Amazon Bedrock 완전 가이드 · 28편
Knowledge Bases 검색 설정과 재정렬

필요한 문서를 먼저 찾는 방법

Amazon Bedrock Knowledge Bases는 질문할 때마다 검색 설정을 바꿀 수 있습니다. 가져올 청크 수는 numberOfResults(기본 5개, 1~100)로, 검색 범위는 메타데이터 필터로, 결과 순서는 재정렬 모델로, 여러 주제가 섞인 질문은 질의 분해로 조정합니다. 설정을 바꾸기 전에 Retrieve로 검색 결과의 원문·위치·메타데이터·점수를 먼저 보고, 문제가 수집·검색·생성 중 어디서 생겼는지 가른 뒤 한 번에 하나씩 바꿉니다.

고객 관리형 Knowledge Base의 비정형 데이터 소스를 기준으로 Retrieve·RetrieveAndGenerate 요청의 검색 설정(검색 개수, 메타데이터 필터, 재정렬, 질의 분해)과 검색 문제의 진단 순서를 설명합니다. 관리형 Knowledge Base와 다른 점은 해당 위치에 따로 표시합니다. 두 API의 응답 처리와 생성 프롬프트는 29편, 구조화 데이터 검색은 30편에서 다룹니다.

01

검색 개수와 검색 범위

Knowledge Base에 질문하면 Bedrock은 질문과 가까운 청크를 찾아 기본적으로 최대 5개를 돌려줍니다. 이 개수는 Retrieve나 RetrieveAndGenerate 요청의 numberOfResults로 1부터 100까지 정할 수 있으며, 값을 바꿔도 Knowledge Base 자체 설정은 바뀌지 않고 그 요청에만 적용됩니다. 검색 설정 안내

아래는 가상의 사내 서비스 안내 도우미가 Retrieve를 호출할 때의 요청 본문 구성 예시입니다. Knowledge Base ID는 본문이 아니라 요청 경로(/knowledgebases/KNOWLEDGE_BASE_ID/retrieve)에 들어갑니다.

{
"retrievalQuery": {"text": "회의실 예약은 며칠 전까지 할 수 있나요?"},
"retrievalConfiguration": {
"vectorSearchConfiguration": {
"numberOfResults": 10
}
}
}

numberOfResults는 상한입니다. 질문과 관련된 청크가 적거나 필터로 후보가 줄면 더 적게 돌아옵니다. 26편에서 다룬 계층적 분할을 쓰면 이 값은 자식 청크 수에 해당하고, 같은 부모를 가진 자식 청크는 부모 청크 하나로 바뀌어 반환되므로 결과 수가 요청보다 적을 수 있습니다.

개수를 늘리면 필요한 청크가 결과에 들어올 가능성은 커지지만, RetrieveAndGenerate에서는 모든 결과가 생성 프롬프트에 들어가므로 입력 토큰과 비용이 늘고 질문과 상관없는 내용도 함께 섞입니다. 프롬프트가 글자 수 한도를 넘는다는 오류가 나면 공식 문서도 검색 개수를 줄이거나 더 작은 청크로 다시 수집하라고 안내합니다. 기본값 5개에서 시작하고, 답에 필요한 청크가 결과 밖으로 밀려나는 것을 확인했을 때 늘리는 편이 안전합니다.

검색 범위는 기본적으로 요청한 Knowledge Base에 연결된 모든 데이터 소스입니다. 범위를 좁히는 방법은 세 가지입니다.

  • Knowledge Base 분리: 공개 범위나 업무 영역이 크게 다르면 Knowledge Base를 따로 만들고, 애플리케이션이 호출할 Knowledge Base를 고릅니다(25편).
  • 데이터 소스 지정: Bedrock이 모든 청크에 붙이는 기본 메타데이터 x-amz-bedrock-kb-data-source-id로 필터를 걸면 특정 데이터 소스에서만 찾습니다.
  • 사용자 정의 메타데이터: 부서·연도·문서 유형처럼 문서에 붙인 값으로 필터를 겁니다(다음 섹션).

관리형 Knowledge Base는 vectorSearchConfiguration 대신 managedSearchConfiguration에 같은 이름의 numberOfResults와 filter를 넣습니다. 관리형은 항상 하이브리드 검색을 쓰며 의미 검색만 따로 고를 수 없습니다.

02

명시적·암시적 메타데이터 필터

메타데이터 필터는 결과를 돌려주기 전에 메타데이터 조건에 맞지 않는 청크를 빼는 기능입니다. 애플리케이션이 조건을 직접 넣는 방식을 명시적(수동) 필터, 모델이 질문을 읽고 조건을 만들어 적용하는 방식을 암시적 필터라고 합니다. 두 방식 모두 25편에서 다룬 .metadata.json 파일이나 26편의 변환 Lambda로 문서에 메타데이터가 붙어 있어야 동작합니다. RetrievalFilter API

아래는 시설팀 문서 중 2026년 이후에 작성된 것만 찾는 명시적 필터의 구성 예시입니다. department와 doc_year는 가상의 메타데이터 이름이며, doc_year는 숫자로 저장했다고 가정합니다.

"vectorSearchConfiguration": {
"numberOfResults": 5,
"filter": {
"andAll": [
{"equals": {"key": "department", "value": "facility"}},
{"greaterThanOrEquals": {"key": "doc_year", "value": 2026}}
]
}
}

andAll은 모든 조건을, orAll은 하나 이상의 조건을 만족하는 청크를 남깁니다. 두 연산자는 조건을 2개 이상 5개 이하로 묶으며, 묶음 안에 묶음을 한 단계까지 넣을 수 있습니다. 조건이 하나뿐이면 andAll 없이 equals 같은 연산자를 filter 바로 아래에 둡니다.

주요 필터 연산자와 값 형식
연산자값 형식저장소 조건
equals·notEquals문자열·숫자·불리언공통 연산자
greaterThan·lessThan 등 범위 비교숫자공통 연산자
in·notIn문자열 목록OpenSearch Serverless와 Neptune Analytics에서 가장 잘 지원
startsWith문자열OpenSearch Serverless만 지원
stringContains·listContains문자열OpenSearch Serverless에서 가장 잘 지원, S3 Vectors는 stringContains 불가

필터를 쓰려면 저장소 쪽 조건도 맞아야 합니다. OpenSearch Serverless는 벡터 인덱스가 faiss 엔진이어야 하며, nmslib 엔진으로 만든 인덱스라면 새 인덱스를 만들고 Knowledge Base도 새로 만들어야 합니다. S3 Vectors와 관리형 Knowledge Base는 startsWith와 stringContains를 쓸 수 없습니다. 기존 Aurora 인덱스에 메타데이터를 추가할 때는 메타데이터를 한 열에 모아 저장하도록 사용자 정의 메타데이터 열을 지정하고 그 열에 인덱스를 만드는 방법이 권장됩니다.

암시적 필터는 vectorSearchConfiguration의 implicitFilterConfiguration에 메타데이터 설명과 필터를 만들 모델을 지정합니다. 공식 문서 기준으로 필터 생성은 Anthropic Claude 모델이 지원합니다.

"implicitFilterConfiguration": {
"modelArn": "FILTER_MODEL_ARN",
"metadataAttributes": [
{"key": "department", "type": "STRING", "description": "문서를 관리하는 부서. 예: facility, hr"},
{"key": "doc_year", "type": "NUMBER", "description": "문서를 작성한 연도. 예: 2026"}
]
}

FILTER_MODEL_ARN은 필터를 만들 Claude 모델의 ARN으로 바꿉니다. metadataAttributes에는 1~25개의 속성을 넣고, type은 STRING·NUMBER·BOOLEAN·STRING_LIST 중 하나입니다. 모델은 description을 보고 "올해 시설팀 안내에서 주차 등록 방법 알려줘" 같은 질문에서 부서와 연도 조건을 뽑아내므로, 설명에 값의 예시와 형식을 구체적으로 적을수록 필터가 정확해집니다.

암시적 필터를 쓰면 검색 전에 모델 호출이 한 번 더 일어나므로 응답이 늦어지고 모델 사용 비용이 늘 수 있습니다. 또 모델이 만든 조건이므로 질문 표현에 따라 달라질 수 있습니다. 따라서 사용자가 볼 수 있는 문서를 제한하는 권한 조건은 암시적 필터에 맡기지 않고, 애플리케이션이 인증된 사용자 정보로 만든 명시적 필터나 Knowledge Base 분리로 통제합니다.

03

재정렬의 역할과 비용

Knowledge Base의 기본 순위는 질문 벡터와 청크 벡터가 얼마나 가까운지(하이브리드 검색이면 원문 일치도 포함)로 정해집니다. 재정렬 모델(reranker)은 이렇게 뽑힌 후보 청크를 질문과 함께 다시 읽고 관련성 점수를 새로 계산해 순서를 바꿉니다. 재정렬 결과는 Knowledge Base의 기본 순위를 대체하며, 텍스트 데이터에만 쓸 수 있습니다. 재정렬 모델 안내

재정렬은 "넓게 찾고 좁게 고르는" 구성에 쓰입니다. 아래 구성 예시는 후보 30개를 검색한 뒤 재정렬해 상위 5개만 돌려받습니다.

"vectorSearchConfiguration": {
"numberOfResults": 30,
"rerankingConfiguration": {
"type": "BEDROCK_RERANKING_MODEL",
"bedrockRerankingConfiguration": {
"modelConfiguration": {"modelArn": "RERANK_MODEL_ARN"},
"numberOfRerankedResults": 5
}
}
}

RERANK_MODEL_ARN은 아래 표의 재정렬 모델 ARN으로 바꿉니다. numberOfResults는 재정렬에 넘길 후보 수, numberOfRerankedResults는 재정렬 후 돌려받을 수입니다. numberOfRerankedResults를 numberOfResults보다 크게 주어도 최대 numberOfResults개만 반환됩니다. 예외는 다음 섹션의 질의 분해를 함께 쓸 때로, 이때는 numberOfRerankedResults를 numberOfResults의 5배까지 줄 수 있습니다. 선택 항목인 metadataConfiguration은 재정렬할 때 메타데이터를 모두 참고할지(ALL), 고른 필드만 참고할지(SELECTIVE)를 정합니다.

재정렬 모델과 단일 리전 지원
모델모델 ID지원 리전
Amazon Rerank 1.0amazon.rerank-v1:0도쿄, 캐나다(중부), 프랑크푸르트, 오레곤
Cohere Rerank 3.5cohere.rerank-v3-5:0도쿄, 캐나다(중부), 프랑크푸르트, 버지니아 북부, 오레곤

이 표에 서울 리전은 없습니다. 서울 리전의 Knowledge Base에서 재정렬을 쓰려는 경우 만들기 전에 재정렬 지원 리전을 다시 확인하고, 지원하지 않으면 재정렬 없이 검색 개수·필터·하이브리드 검색으로 조정하거나 지원 리전에 Knowledge Base를 두는 방안을 비교합니다. 다른 리전에 둘 때는 데이터가 저장·처리되는 위치가 바뀌므로 데이터 위치 요건도 함께 검토합니다.

권한은 어느 API로 재정렬하느냐에 따라 필요한 주체가 다릅니다.

재정렬 사용 방식별 필요 권한
사용 방식호출하는 사용자·역할Knowledge Base 서비스 역할
Retrieve + 재정렬bedrock:Retrievebedrock:Rerank, 재정렬 모델에 대한 bedrock:InvokeModel
RetrieveAndGenerate + 재정렬bedrock:RetrieveAndGenerate, bedrock:Rerank, 재정렬 모델과 생성 모델에 대한 bedrock:InvokeModel추가 권한 없음
Rerank API 단독 호출bedrock:Rerank, 재정렬 모델에 대한 bedrock:InvokeModel해당 없음

Cohere Rerank처럼 외부 제공사 모델을 쓰려면 사용하는 IAM 역할에 aws-marketplace:ViewSubscriptions와 aws-marketplace:Subscribe 권한도 필요합니다. 콘솔에서 재정렬 모델을 고를 때 서비스 역할 권한을 자동으로 고칠 수 있지만, 직접 만든 사용자 정의 역할은 Bedrock이 고칠 수 없으므로 정책을 직접 추가합니다. 재정렬 권한

재정렬 요금은 쿼리 단위로 매겨지며, 쿼리 하나는 재정렬 모델을 한 번 호출하는 것으로 최대 100개의 청크를 담을 수 있습니다. 처리된 쿼리 수는 CloudWatch의 SearchUnits 지표로 확인합니다. 재정렬은 질문마다 호출이 하나 더 늘어 비용과 지연이 더해지지만, 생성 모델에 넘기는 청크를 줄이면 생성 단계의 입력 토큰은 줄어듭니다. 단가는 Bedrock 요금 페이지에서 확인합니다.

관리형 Knowledge Base는 서비스가 관리하는 재정렬이 기본으로 켜져 있습니다. managedSearchConfiguration의 rerankingModelType을 CUSTOM으로 주면 직접 고른 재정렬 모델을, NONE으로 주면 재정렬 없이 검색합니다. 서비스 관리형 재정렬(MANAGED)은 관리형 임베딩을 쓰는 Knowledge Base에서만 쓸 수 있습니다.

04

질의 변환·분해와 검색 전략

질의 분해(query decomposition)는 여러 주제가 섞인 질문을 작은 하위 질문으로 나눠 각각 검색하는 기능입니다. 예를 들어 "3층 회의실 예약 방법과 방문객 주차 등록 방법을 알려줘"라는 질문을 회의실 예약과 주차 등록에 관한 두 하위 질문으로 나누면, 한 번의 벡터 검색으로는 한쪽 주제의 청크가 밀려날 수 있는 문제를 줄일 수 있습니다. QueryTransformationConfiguration API

질의 분해는 RetrieveAndGenerate 요청의 orchestrationConfiguration에서 켭니다. 현재 지정할 수 있는 변환 유형은 QUERY_DECOMPOSITION 하나입니다.

"retrieveAndGenerateConfiguration": {
"type": "KNOWLEDGE_BASE",
"knowledgeBaseConfiguration": {
"knowledgeBaseId": "KNOWLEDGE_BASE_ID",
"modelArn": "GENERATION_MODEL_ARN",
"orchestrationConfiguration": {
"queryTransformationConfiguration": {"type": "QUERY_DECOMPOSITION"}
}
}
}

KNOWLEDGE_BASE_ID는 Knowledge Base ID로, GENERATION_MODEL_ARN은 답변을 생성할 모델의 ARN으로 바꿉니다. 이 옵션을 켜면 질문 하나에 Knowledge Base 검색이 여러 번 실행될 수 있어 지연이 늘어납니다. 단순한 질문이 대부분이라면 끄고, 여러 조건이나 비교가 들어간 질문이 많을 때 켜서 결과를 비교합니다.

Retrieve에는 질의 분해 옵션이 없습니다. Retrieve로 검색 결과를 직접 처리하는 구성(29편)에서 같은 효과가 필요하면, 애플리케이션이 질문을 나누고 하위 질문마다 Retrieve를 호출한 뒤 결과를 합치는 방식으로 설계해야 합니다. RetrieveAndGenerate의 orchestration 프롬프트도 질문을 검색어로 바꾸는 단계에 쓰이며, 프롬프트 구성은 29편에서 다룹니다.

검색 설정은 질문 유형에 따라 고릅니다. 아래 표는 증상과 먼저 시도할 설정을 짝지은 것입니다.

질문 유형별 검색 전략
질문·증상먼저 시도할 설정관련 편
오류 코드·제품 번호처럼 글자가 정확해야 하는 질문하이브리드 검색(overrideSearchType)27편
부서·연도·문서 유형 같은 조건이 붙은 질문명시적 또는 암시적 메타데이터 필터이 편 2섹션
맞는 청크가 후보에는 있지만 순위가 낮음검색 개수를 늘리고 재정렬이 편 3섹션
여러 주제가 한 질문에 섞임질의 분해이 편 4섹션
답에 필요한 내용이 여러 청크에 흩어짐검색 개수 조정, 계층적 분할 검토26편

설정은 한 번에 하나씩 바꿉니다. 여러 설정을 동시에 바꾸면 결과가 좋아지거나 나빠진 원인을 알 수 없기 때문입니다. 실제 사용자 질문과 비슷한 질문 20~30개와 각 질문에 맞는 정답 문서를 정해 두고, 설정을 바꿀 때마다 같은 질문으로 정답 문서가 몇 위에 오는지 비교하면 변경 효과를 판단할 수 있습니다.

05

검색 누락과 무관한 결과의 진단

답변이 틀렸을 때 가장 먼저 할 일은 검색과 생성을 분리해 보는 것입니다. Retrieve를 호출하거나 콘솔의 Knowledge Base 테스트 창에서 응답 생성을 끄면, 모델이 답을 만들기 전의 검색 결과를 그대로 볼 수 있습니다. 결과마다 청크 원문(content), 원본 문서 위치(location), 메타데이터(metadata), 관련성 점수(score)가 돌아옵니다. Retrieve로 검색 결과 확인

아래는 AWS SDK for Python(Boto3)으로 검색 결과의 순위·점수·위치·메타데이터만 출력하는 진단용 예시입니다. KNOWLEDGE_BASE_ID를 바꾸고, 실행하는 자격 증명에 해당 Knowledge Base에 대한 bedrock:Retrieve 권한이 있어야 합니다.

import boto3

client = boto3.client("bedrock-agent-runtime", region_name="REGION")
response = client.retrieve(
knowledgeBaseId="KNOWLEDGE_BASE_ID",
retrievalQuery={"text": "방문객 주차 등록은 어떻게 하나요?"},
retrievalConfiguration={"vectorSearchConfiguration": {"numberOfResults": 20}},
)
for rank, result in enumerate(response["retrievalResults"], start=1):
print(rank, round(result.get("score", 0), 4), result.get("location"))
print(" ", result.get("metadata"))

REGION은 Knowledge Base가 있는 리전 코드로 바꿉니다. 평소보다 큰 20개를 가져오는 이유는 정답 문서가 기본 5개 밖에 있는지 확인하기 위해서입니다. 점수는 같은 설정 안에서 결과의 순서를 비교하는 데 쓰고, 재정렬이나 검색 방식을 바꾼 결과끼리 점수 값을 직접 비교하지 않습니다. 점수로 결과를 잘라 내는 기준을 정하려면 정해 둔 질문 세트로 점수 분포를 먼저 확인합니다.

검색 결과를 본 뒤에는 증상에 따라 원인을 좁힙니다.

검색 증상별 점검 순서
증상먼저 확인할 것조치
결과가 0건필터의 키 이름(대소문자 포함)과 값 형식(숫자를 문자열로 저장했는지), OpenSearch Serverless 인덱스 엔진필터 없이 같은 질문을 다시 검색해 필터 문제인지 가르기
정답 문서가 20개 안에도 없음문서가 수집되었는지(수집 작업 상태와 실패 원인), 파싱 결과에 내용이 남았는지25편의 수집 점검, 26편의 파서·분할 점검
정답 문서가 6위 이하질문과 문서의 표현 차이, 코드·고유 명사 포함 여부검색 개수 조정, 하이브리드 검색, 재정렬
무관한 문서가 상위오래된 버전·다른 부서 문서가 섞였는지, 청크가 너무 큰지메타데이터 필터, 재정렬, 분할 크기 조정
결과가 요청한 수보다 적음계층적 분할의 부모 청크 치환, numberOfRerankedResults 상한정상 동작인지 확인 후 개수 조정
검색 결과는 맞는데 답변이 틀림생성 프롬프트와 모델 설정29편의 생성 단계 점검

필터를 걸었는데 결과가 0건이라면, 메타데이터 파일을 추가하거나 고친 뒤 데이터 소스를 다시 동기화했는지도 확인합니다. .metadata.json을 고쳐도 동기화 전에는 저장소의 메타데이터가 바뀌지 않습니다. 반대로 필터를 걸었는데도 다른 부서 문서가 섞인다면, 그 문서에 메타데이터가 아예 없거나 다른 값으로 붙어 있는지 Retrieve 결과의 metadata에서 확인합니다.

반응형

Bedrock Knowledge Bases 임베딩과 벡터 저장소: 차원·인덱스·유사도 이해하기 | Bedrock 27편

Amazon Bedrock 완전 가이드 · 27편
Knowledge Bases 임베딩과 벡터 저장소

차원·인덱스·유사도 이해하기

Amazon Bedrock Knowledge Bases에서 임베딩 모델은 청크를 정해진 길이(차원)의 숫자 벡터로 바꾸고, 벡터 저장소는 이 벡터를 인덱스에 저장해 질문 벡터와 가까운 것을 찾습니다. 벡터 인덱스의 차원은 임베딩 모델의 출력 차원과 같아야 하며, 하이브리드 검색은 일부 저장소에서만 쓸 수 있습니다. 임베딩 모델과 저장소는 만든 뒤 바꾸기 어려우므로, 지원 리전·검색 방식·비용 구조를 함께 보고 고릅니다.

고객 관리형 Knowledge Base를 기준으로 임베딩 모델과 차원, 벡터 인덱스와 필드 매핑, 지원 벡터 저장소와 S3 Vectors 선택 기준, 의미 검색과 하이브리드 검색, 저장소 변경과 비용을 설명합니다. 관리형 Knowledge Base와 다른 점은 해당 위치에 따로 표시합니다.

01

임베딩 모델과 벡터 차원

임베딩 모델은 텍스트를 숫자 목록(벡터)으로 바꿉니다. 이 목록의 길이를 차원이라고 하며, 의미가 비슷한 문장일수록 벡터끼리 가깝게 놓이도록 학습되어 있습니다. 검색은 질문을 같은 모델로 벡터로 바꾼 뒤, 저장된 청크 벡터 중 가까운 것을 찾는 방식으로 이루어집니다. Knowledge Bases 지원 모델

고객 관리형 Knowledge Base의 주요 텍스트 임베딩 모델
모델벡터 형식차원서울 리전 단일 리전 지원
Amazon Titan Text Embeddings V2부동소수점, 바이너리256·512·1024 중 선택지원
Cohere Embed Multilingual v3부동소수점, 바이너리1024표에 없음
Cohere Embed English v3부동소수점, 바이너리1024표에 없음
Amazon Titan Embeddings G1 - Text부동소수점1536표에 없음

한국어 문서가 중심이라면 다국어를 지원하는 모델을 고릅니다. Titan Text Embeddings V2는 100개 이상의 언어를, Cohere Embed Multilingual v3는 이름 그대로 다국어를 지원합니다. 서울 리전에서 Knowledge Base를 만든다면, 단일 리전 지원 표 기준으로는 Titan Text Embeddings V2를 쓸 수 있고 Cohere v3 모델은 표에 없습니다. 이 표는 바뀔 수 있으므로 만들기 전에 다시 확인합니다.

차원이 클수록 의미를 더 세밀하게 담을 수 있지만, 벡터 하나가 차지하는 저장 공간과 검색 계산량도 늘어납니다. 바이너리 벡터는 차원마다 1비트만 써서 부동소수점(차원당 32비트)보다 저장 비용이 훨씬 작지만, 표현이 덜 정밀합니다. 바이너리 벡터를 쓰려면 임베딩 모델과 벡터 저장소가 모두 바이너리를 지원해야 합니다.

관리형(Bedrock Managed) Knowledge Base는 기본적으로 내장 임베딩 모델을 쓰고, 직접 고를 때는 float32 형식의 1,024차원 모델만 지정할 수 있습니다.

02

저장소·인덱스·필드 매핑

고객 관리형 Knowledge Base는 벡터 저장소 안의 인덱스(검색용 데이터 구조)에 청크를 저장합니다. OpenSearch Serverless는 콘솔에서 Knowledge Base를 만들 때 Bedrock이 저장소와 인덱스를 자동으로 만들어 주는 방법도 있습니다. 직접 만든다면 인덱스를 미리 만들고, Bedrock이 어떤 필드에 무엇을 쓸지 연결(필드 매핑)해야 합니다.

아래는 OpenSearch Serverless에 1,024차원 벡터용 인덱스를 만드는 매핑의 구성 예시입니다. 필드 이름은 가상의 예시입니다.

PUT guide-kb-index
{
"settings": {"index": {"knn": true}},
"mappings": {
"properties": {
"guide_vector": {
"type": "knn_vector",
"dimension": 1024,
"method": {"name": "hnsw", "engine": "faiss", "space_type": "l2"}
},
"guide_text": {"type": "text", "index": true},
"guide_metadata": {"type": "text", "index": false}
}
}
}

dimension은 임베딩 모델의 출력 차원과 같아야 합니다. Titan Text Embeddings V2를 512차원으로 쓰기로 했다면 여기도 512여야 하며, 차원이 맞지 않으면 벡터를 인덱스에 저장할 수 없습니다. space_type은 벡터 사이의 거리를 재는 방식(유사도 기준)입니다. Knowledge Base를 만들 때는 세 필드를 다음처럼 연결합니다.

  • 벡터 필드(vectorField): 임베딩 벡터를 저장할 필드. 예시의 guide_vector
  • 텍스트 필드(textField): 청크의 원문을 저장할 필드. 예시의 guide_text
  • 메타데이터 필드(metadataField): Bedrock이 관리하는 메타데이터를 저장할 필드. 예시의 guide_metadata

텍스트 필드를 검색 가능한(filterable) 텍스트로 만들어 두어야 4섹션의 하이브리드 검색을 쓸 수 있습니다. 예시에서 텍스트 필드에 "index": true를 준 이유입니다.

03

지원 저장소와 S3 Vectors 등의 선택 기준

고객 관리형 Knowledge Base가 연결할 수 있는 저장소는 AWS 서비스와 외부 서비스로 나뉩니다. 이미 운영 중인 저장소가 있다면 그것을 연결할 수 있는지부터 확인합니다.

벡터 저장소별 특징
저장소특징맞는 경우
Amazon OpenSearch Serverless콘솔에서 자동 생성 가능, 하이브리드 검색 지원가장 일반적인 시작점
Amazon OpenSearch Service 관리형 클러스터기존 클러스터 재사용이미 OpenSearch 클러스터를 운영 중
Amazon Aurora PostgreSQL(pgvector)SQL 데이터베이스 안에 벡터 저장, 하이브리드 검색 지원관계형 데이터와 함께 관리하고 싶을 때
Amazon S3 VectorsS3 기반 벡터 저장, 사용량 기반 비용검색량 대비 문서가 많고 비용이 중요할 때
Amazon Neptune Analytics그래프 기반 검색(GraphRAG)문서 간 관계를 따라가는 질문(30편)
Pinecone·Redis Enterprise Cloud·MongoDB Atlas외부 서비스, MongoDB는 하이브리드 검색 지원이미 해당 서비스를 쓰고 있을 때

S3 Vectors는 벡터를 S3 방식으로 저장하는 서비스로, AWS는 비용이 낮은 저장소로 소개합니다. 대신 Knowledge Base 저장소로 쓸 때 다음 제약을 알아 둬야 합니다. Knowledge Bases와 S3 Vectors

  • 메타데이터 크기: 벡터당 메타데이터는 최대 40KB이고, 이 중 필터에 쓸 수 있는 메타데이터는 2KB까지입니다.
  • 원문 저장 위치: 벡터 인덱스를 만들 때 청크 원문(AMAZON_BEDROCK_TEXT)과 Bedrock이 관리하는 메타데이터(AMAZON_BEDROCK_METADATA)를 필터 불가 메타데이터 키로 지정해야 큰 청크와 필터용 메타데이터를 함께 담을 수 있습니다. 표가 있는 문서에서 붙는 표 전체·페이지 전체 맥락(26편)도 메타데이터 크기에 포함됩니다. 이 키 목록은 인덱스를 만든 뒤 바꿀 수 없습니다.
  • 분할 방식: 메타데이터 크기 한도 때문에 계층적 분할은 권장되지 않습니다(26편).
  • 검색 방식: 하이브리드 검색 지원 저장소에 포함되지 않아 의미 검색만 쓸 수 있습니다.
04

의미 검색과 하이브리드 검색

의미 검색(semantic search)은 벡터만 비교합니다. "휴게실 쓸 수 있는 시간"으로 물어도 "운영 시간"이 적힌 문서를 찾을 수 있다는 장점이 있지만, 오류 코드나 제품 번호처럼 글자가 정확히 같아야 하는 검색에는 약할 수 있습니다. 하이브리드 검색은 벡터 비교에 원문 텍스트 검색을 더해 두 장점을 함께 노립니다. 예를 들어 "출입증 오류 E-1024"처럼 코드가 들어간 질문에서 해당 코드가 적힌 안내문을 찾는 데 도움이 됩니다.

하이브리드 검색은 Amazon RDS(Aurora PostgreSQL), OpenSearch Serverless, MongoDB 저장소 중 검색 가능한 텍스트 필드가 있는 경우에만 쓸 수 있습니다. 조건이 맞지 않으면 요청해도 의미 검색으로 동작합니다. 검색 방식 설정

검색 방식은 Retrieve나 RetrieveAndGenerate 요청의 검색 설정에서 정합니다. 값을 지정하지 않으면 Bedrock이 저장소 구성에 맞는 방식을 고릅니다.

"retrievalConfiguration": {
"vectorSearchConfiguration": {
"overrideSearchType": "HYBRID"
}
}

HYBRID는 벡터와 원문을 함께, SEMANTIC은 벡터만 검색합니다. 어느 쪽이 나은지는 질문 유형에 달려 있으므로, 코드나 고유 명사가 들어간 질문과 일반 문장 질문을 섞어 두 방식을 비교해 봅니다. 검색 개수와 필터 등 나머지 검색 설정은 28편에서 다룹니다.

05

저장소 변경·재수집·비용 고려사항

임베딩 모델이나 벡터 저장소를 바꾸는 일은 사실상 Knowledge Base를 새로 만드는 일입니다. 모델이 바뀌면 기존 벡터와 새 질문 벡터를 비교할 수 없고, 저장소가 바뀌면 벡터를 옮길 곳이 달라지므로 모든 문서를 다시 수집해야 합니다. 관리형 Knowledge Base도 내장 임베딩과 직접 지정한 임베딩 사이를 바꾸려면 새로 만들어야 합니다.

비용은 세 부분으로 나눠 봅니다.

임베딩과 저장소 관련 비용
비용 항목발생 시점줄이는 방법
임베딩 모델 호출수집할 때(청크마다), 검색할 때(질문마다)증분 동기화 유지, 불필요한 재수집 피하기
저장소 저장 비용보관하는 동안 계속차원 축소나 바이너리 벡터 검토, 오래된 문서 정리
저장소 검색·운영 비용검색 요청과 저장소 가동검색량에 맞는 저장소 선택

저장소마다 과금 구조가 다르다는 점이 중요합니다. 검색이 적고 문서가 많다면 사용량에 따라 내는 저장소가 유리할 수 있고, 검색이 많고 하이브리드 검색이 필요하다면 그에 맞는 저장소가 필요합니다. 고르기 전에 예상 문서 수, 하루 질문 수, 하이브리드 검색 필요 여부를 정리하고 각 저장소의 요금 페이지로 계산해 봅니다.

Knowledge Base를 삭제해도 직접 만든 벡터 저장소는 남아 비용이 계속 나갈 수 있습니다(25편). 시험용으로 만든 저장소는 시험이 끝나면 따로 정리합니다.

반응형

Bedrock Knowledge Bases 문서 파싱과 청킹: 검색 품질을 결정하는 전처리 | Bedrock 26편

Amazon Bedrock 완전 가이드 · 26편
Knowledge Bases 문서 파싱과 청킹

검색 품질을 결정하는 전처리

Amazon Bedrock Knowledge Bases는 문서를 수집할 때 먼저 파싱(내용 추출)하고, 그 결과를 청크(chunk)라는 조각으로 나눠 임베딩합니다. 표나 이미지가 중요한 문서라면 텍스트만 읽는 기본 파서 대신 Data Automation이나 모델 기반 파서를 검토하고, 청크는 문서 구조와 질문 유형에 맞는 분할 방식을 고릅니다. 분할 설정은 데이터 소스를 만든 뒤에는 바꿀 수 없으므로, 처음에 실제 문서와 질문으로 시험해 보고 정해야 합니다.

고객 관리형 Knowledge Base를 기준으로 파서 세 가지의 차이, 기본·고정 크기·계층적·의미 기반·분할 없음의 동작, 표와 이미지 처리, 사용자 정의 변환 Lambda와 메타데이터, 분할 방식 변경 시 영향을 설명합니다. 관리형 Knowledge Base와 다른 점은 해당 위치에 따로 표시합니다.

01

기본 파서·모델 기반 파서·Data Automation

파싱은 원본 파일에서 검색에 쓸 내용을 뽑아내는 단계입니다. 고객 관리형 Knowledge Base는 데이터 소스마다 세 가지 파서 중 하나를 고릅니다. 관리형(Bedrock Managed) Knowledge Base는 멀티모달 파일용 내장 파서를 쓰므로 이 선택이 없습니다. 파싱 옵션

고객 관리형 Knowledge Base의 파서 비교
파서추출하는 내용비용맞는 문서
기본 파서텍스트만(.txt·.md·.html·.docx·.xlsx·.pdf 등)사용 요금 없음본문 글이 대부분인 안내문
Bedrock Data Automation텍스트와 표·차트·그림·이미지처리한 페이지·이미지 수에 따라 과금별도 지시 없이 멀티모달 문서를 처리
모델 기반 파서텍스트와 표·차트·그림·이미지파싱 모델의 입력·출력 토큰에 따라 과금추출 지시(프롬프트)를 직접 조정하고 싶을 때

Data Automation이나 모델 기반 파서는 문서 안의 그림·표·이미지를 따로 추출해, Knowledge Base를 만들 때 지정한 S3 위치에 파일로 저장할 수 있습니다. 이렇게 저장된 파일은 검색 응답이나 출처 표시에 함께 나올 수 있습니다. 다만 Knowledge Bases의 Data Automation 파서는 현재 미국 서부(오리건) 리전에서 미리보기로 제공되므로, 다른 리전에서는 모델 기반 파서를 검토합니다. 모델 기반 파서로는 Claude·Nova·Llama 4의 비전 모델 계열을 쓸 수 있습니다.

비용에서 주의할 점이 있습니다. 두 고급 파서 중 하나를 고르면, 그 데이터 소스의 .pdf 파일은 텍스트만 있더라도 모두 고급 파서로 처리되고 요금이 발생합니다. 그래서 표가 있는 .pdf 파일과 텍스트뿐인 .pdf 파일이 섞여 있다면, 표가 있는 파일만 별도 데이터 소스로 나눠 고급 파서를 적용하는 구성을 검토합니다.

02

고정 크기·계층적·의미 기반 분할

파싱한 내용은 청크로 나뉘어 각각 임베딩됩니다. 검색은 청크 단위로 이루어지므로, 어떻게 나누느냐가 어떤 근거가 검색되는지를 정합니다. 청킹 동작 방식

분할 방식별 동작
방식나누는 기준주요 설정주의할 점
기본약 300토큰, 문장 경계 유지없음가장 무난한 시작점
고정 크기지정한 최대 토큰 수maxTokens, overlapPercentage고급 파서 결과는 페이지·섹션 경계를 넘어 합치지 않음
계층적큰 부모 청크와 작은 자식 청크부모·자식 최대 토큰, overlapTokens검색 결과 수가 요청보다 적을 수 있음
의미 기반문장 사이의 의미 차이maxTokens, bufferSize, breakpointPercentileThreshold분할에 모델을 쓰므로 추가 비용
분할 없음문서 하나를 청크 하나로없음인용에 쪽 번호가 표시되지 않음

계층적 분할은 정확한 검색과 넉넉한 맥락을 함께 노립니다. 검색은 작은 자식 청크로 하고, 모델에 넘길 때는 그 자식이 속한 큰 부모 청크로 바꿔 줍니다. 아래는 데이터 소스를 만들 때 넣는 계층적 분할 설정의 구성 예시입니다.

{
"vectorIngestionConfiguration": {
"chunkingConfiguration": {
"chunkingStrategy": "HIERARCHICAL",
"hierarchicalChunkingConfiguration": {
"levelConfigurations": [
{"maxTokens": 1500},
{"maxTokens": 300}
],
"overlapTokens": 60
}
}
}
}

levelConfigurations의 첫 항목은 부모 청크, 두 번째 항목은 자식 청크의 최대 토큰 수입니다. 자식 청크만 임베딩되므로 자식 크기는 임베딩 모델의 입력 한도 안이어야 합니다. overlapTokens는 이웃한 청크끼리 겹치는 토큰 수로, 문장이 경계에서 잘려 뜻을 잃는 것을 줄입니다. 값은 시작점 예시이며 실제 문서로 시험해 조정합니다. 벡터 저장소로 S3 Vectors를 쓴다면 계층적 분할은 권장되지 않습니다. 부모·자식 토큰 합이 크면 벡터당 메타데이터 크기 한도에 걸릴 수 있기 때문입니다.

관리형 Knowledge Base는 기본 분할, 고정 크기 분할, 분할 없음 중에서만 고를 수 있으며 계층적·의미 기반 분할은 쓸 수 없습니다.

03

표·이미지·문서 구조 처리

기본 파서는 텍스트만 꺼내므로, 표가 있으면 행과 열의 관계가 사라진 글자 나열이 될 수 있습니다. 예를 들어 층별 휴게실 운영 시간 표가 "3층 09:00 18:00 5층 08:00 20:00"처럼 이어지면, 모델이 어느 시간이 어느 층인지 헷갈릴 수 있습니다. 고급 파서는 이런 표를 구조를 유지한 형식(마크다운)으로 추출합니다.

고급 파서로 처리했거나 HTML·마크다운 문서처럼 구조가 있는 내용은, 최대 토큰을 정하는 분할 방식(분할 없음을 제외한 모든 방식)에서 다음과 같이 다뤄집니다.

  • 청크는 페이지나 섹션 같은 논리적 경계를 넘어 합쳐지지 않습니다. 최대 토큰을 늘려도 경계를 넘는 큰 청크는 만들어지지 않습니다.
  • 표나 그림이 있는 페이지의 청크가 검색되면, 그 청크 대신 페이지 전체나 표 전체가 결과로 돌아올 수 있습니다. 그래서 표의 일부만 검색돼도 같은 표의 다른 행을 함께 받을 수 있습니다.
  • 같은 페이지·표에서 나온 청크는 하나의 결과로 합쳐지므로, 결과 수가 요청한 개수(numberOfResults)보다 적을 수 있습니다.
  • 페이지나 표 전체가 1만 자를 넘으면 이 확장이 적용되지 않고 일치한 청크만 돌아옵니다.

표를 행 단위처럼 잘게 나눠 검색하고 싶다면, 분할 없음을 고르고 다음 섹션의 사용자 정의 변환 Lambda에서 직접 나누는 방법이 안내되어 있습니다.

이미지·음성·영상 같은 멀티모달 파일은 텍스트 분할 방식이 적용되지 않습니다. Nova 멀티모달 임베딩을 쓰면 임베딩 모델 단계에서 나뉘고(음성·영상은 1~30초 단위, 기본 5초), Data Automation 파서를 쓰면 먼저 텍스트(음성 전사 내용·장면 요약)로 바뀐 뒤 텍스트 분할 방식이 적용됩니다.

04

사용자 정의 변환과 메타데이터

기본 제공 분할로 부족할 때는 분할이 끝난 뒤(POST_CHUNKING) Lambda 함수를 실행하도록 설정할 수 있습니다. Bedrock은 분할된 문서를 지정한 S3 중간 저장 위치에 저장하고 Lambda를 호출하며, Lambda는 청크를 다시 나누거나 청크마다 메타데이터를 붙여 돌려줍니다.

{
"vectorIngestionConfiguration": {
"customTransformationConfiguration": {
"intermediateStorage": {
"s3Location": {"uri": "INTERMEDIATE_S3_URI"}
},
"transformations": [
{
"stepToApply": "POST_CHUNKING",
"transformationFunction": {
"transformationLambdaConfiguration": {"lambdaArn": "TRANSFORM_LAMBDA_ARN"}
}
}
]
}
}
}

INTERMEDIATE_S3_URI는 Lambda가 읽고 쓸 S3 경로로, TRANSFORM_LAMBDA_ARN은 변환 Lambda의 ARN으로 바꿉니다. Knowledge Base가 이 Lambda와 중간 저장 위치를 쓸 수 있도록 권한도 함께 구성해야 합니다.

Lambda로 할 수 있는 대표적인 작업은 다음과 같습니다.

  • 청크마다 메타데이터 추가: 청크가 속한 장 제목이나 문서 유형을 붙여 검색 필터에 씁니다.
  • 불필요한 내용 제거: 모든 쪽에 반복되는 머리글·바닥글이나 저작권 문구를 지워 검색 잡음을 줄입니다.
  • 직접 분할: 표를 행 단위로 나누는 것처럼 기본 방식으로 안 되는 분할을 구현합니다.

메타데이터는 두 수준에서 붙일 수 있습니다. 문서 전체에 같은 값을 붙일 때는 25편에서 다룬 .metadata.json 파일을 쓰고, 청크마다 다른 값이 필요할 때 Lambda를 씁니다. Lambda는 수집할 때마다 실행되므로, 처리 시간과 호출 비용이 동기화 시간에 더해집니다.

05

분할 방식을 바꾸면 달라지는 결과

분할 설정(chunkingConfiguration)은 데이터 소스를 만든 뒤에는 바꿀 수 없습니다. 다른 분할 방식을 시험하려면 새 데이터 소스를 만들고 모든 문서를 다시 수집해야 합니다. 그때 임베딩 비용이 다시 발생하고, 고급 파서나 의미 기반 분할을 쓴다면 그 비용도 함께 듭니다. CreateDataSource API

분할 방식이 바뀌면 같은 질문에도 검색되는 근거가 달라집니다.

청크 크기와 방식이 결과에 주는 영향
변경좋아지는 점나빠질 수 있는 점
청크를 작게질문과 정확히 맞는 부분을 찾기 쉬움앞뒤 맥락이 잘려 근거가 부족한 답변
청크를 크게한 청크에 맥락이 충분함무관한 내용이 섞이고 입력 토큰 증가
계층적 분할작은 단위로 찾고 큰 단위로 답함결과 수 감소, 부모 청크만큼 입력 증가
의미 기반 분할주제가 바뀌는 지점에서 나뉨분할 비용 추가, 결과 예측이 어려움
분할 없음문서 단위로 그대로 전달쪽 번호 인용·필터 불가, 긴 문서는 비효율

어느 방식이 나은지는 문서와 질문에 달려 있으므로 직접 비교해야 합니다. 실제 사용자가 할 질문 20~30개를 정하고, 분할 방식별로 같은 질문을 검색해 필요한 근거가 상위 결과에 들어오는지 기록합니다. 사내 안내문처럼 짧은 항목이 많은 문서는 기본 분할로 시작하고, 긴 규정 문서에서 근거가 잘린다면 계층적 분할을 비교해 보는 순서를 권합니다. 검색 결과와 답변을 체계적으로 평가하는 방법은 38편에서 다룹니다.

반응형

데이터 소스와 수집: 문서는 어떻게 검색 대상이 되는가 | Bedrock 25편

Amazon Bedrock 완전 가이드 · 25편
데이터 소스와 수집

문서는 어떻게 검색 대상이 되는가

Amazon Bedrock Knowledge Bases에서 문서는 데이터 소스를 동기화(수집 작업)해야 검색 대상이 됩니다. 동기화는 증분 방식이라 지난 동기화 이후 추가·수정·삭제된 문서만 처리하므로, 원본을 고친 뒤 동기화하지 않으면 검색 결과는 예전 상태로 남습니다. 수집이 끝나면 그 내용은 원본 시스템의 문서 권한과 관계없이 검색 권한(bedrock:Retrieve)을 가진 누구나 조회할 수 있으므로, 사용자별 접근 제어는 별도로 설계해야 합니다.

가상의 사내 안내 도우미를 예로 데이터 소스 유형, 동기화와 직접 수집의 차이, 문서 삭제가 검색 결과에 반영되는 방식과 삭제 정책, 수집 작업 상태 해석과 실패 원인, 원본 권한과 검색 권한의 차이를 설명합니다.

01

S3·외부 서비스·웹·사용자 정의 연결

데이터 소스는 Knowledge Base가 문서를 가져오는 연결입니다. 연결할 수 있는 유형은 Knowledge Base 구성 방식에 따라 다릅니다. 관리형(Bedrock Managed)은 S3, SharePoint, Confluence, 웹 크롤러, Google Drive, OneDrive, 사용자 정의 커넥터를 지원하고, 고객 관리형(Customer-managed)은 S3와 사용자 정의 커넥터를 지원합니다.

데이터 소스 유형별 수집 방식
유형문서를 가져오는 방식맞는 경우
S3버킷의 지정 위치를 동기화할 때 읽음문서를 파일로 모아 두고 정기적으로 반영
외부 서비스(SharePoint·Confluence 등)커넥터가 서비스에 접속해 크롤링협업 도구의 문서를 옮기지 않고 연결(관리형)
웹 크롤러지정한 웹 페이지를 크롤링공개 안내 페이지를 검색 대상으로 삼음(관리형)
사용자 정의애플리케이션이 API로 문서를 직접 넣음사용자가 올린 파일처럼 실행 중에 생기는 문서

Confluence 같은 외부 서비스 커넥터는 각 서비스가 제공하는 변경 추적 기능을 이용해 바뀐 내용만 가져올 수 있습니다. 첫 동기화에서는 기본적으로 전체 내용을 크롤링합니다. 서비스별 인증 정보와 크롤링 범위 설정은 커넥터마다 다르므로 해당 커넥터 문서를 따릅니다.

02

전체 수집과 변경 반영

S3나 외부 서비스 데이터 소스는 문서를 추가·수정·삭제할 때마다 동기화해야 Knowledge Base에 반영됩니다. 동기화는 증분 방식이라 지난 동기화 이후 바뀐 문서만 처리합니다. 바뀐 문서는 파싱·분할·임베딩·인덱싱을 처음부터 다시 거칩니다. 데이터 소스 동기화

동기화할 때 문서별 처리
원본 상태동기화 결과
바뀌지 않음건너뜀
내용이나 메타데이터 변경다시 파싱·분할·임베딩·인덱싱
새 문서 추가새 문서만 수집
문서 삭제벡터 저장소에서 제거

동기화는 콘솔의 Sync 버튼이나 StartIngestionJob API로 시작합니다. 아래 Python 구성 예시는 수집 작업을 시작하고 작업 ID를 받아 둡니다. KB_ID와 DATA_SOURCE_ID는 사용할 Knowledge Base와 데이터 소스의 ID로, SOURCE_REGION은 Knowledge Base가 있는 리전으로 바꿉니다. 실행하려면 bedrock:StartIngestionJob 권한이 필요하고, 결과 조회에는 bedrock:GetIngestionJob 권한이 필요합니다.

import boto3

agent = boto3.client("bedrock-agent", region_name="SOURCE_REGION")
job = agent.start_ingestion_job(
knowledgeBaseId="KB_ID",
dataSourceId="DATA_SOURCE_ID",
)["ingestionJob"]
print(job["ingestionJobId"], job["status"])

수집 작업은 비동기로 실행되므로 이 호출이 끝났다고 문서가 검색되는 것은 아닙니다. 작업이 완료될 때까지 기다린 뒤 결과를 확인해야 하며, 이는 4섹션에서 다룹니다. 원본이 자주 바뀐다면 문서 변경 이벤트나 정해진 일정에 맞춰 이 API를 호출하는 작업을 직접 구성합니다. 고객 관리형에서는 다시 처리한 문서만큼 임베딩 모델 호출 비용이 생기므로, 증분 동기화라도 불필요하게 자주 실행하지 않습니다.

사용자 정의 데이터 소스는 동기화 대신 IngestKnowledgeBaseDocuments API로 문서를 바로 넣습니다. 문서 ID를 애플리케이션이 직접 정하므로, 같은 ID로 다시 넣으면 새 문서가 추가되지 않고 기존 문서가 갱신됩니다. 일부만 고치는 방식은 없고 수정할 때마다 문서 전체를 다시 수집합니다. 사용자가 실행 중에 올리는 파일처럼 정기 동기화로 다루기 어려운 문서에 맞습니다.

03

문서 삭제와 검색 결과의 관계

원본에서 문서를 지워도 검색 결과에서 바로 사라지지는 않습니다. S3나 외부 서비스 데이터 소스는 다음 동기화 때 삭제가 반영되어 벡터 저장소에서 제거됩니다. 예를 들어 폐지된 휴게실 안내문을 S3에서 지운 뒤 동기화하지 않으면, 도우미는 계속 그 안내문을 근거로 답할 수 있습니다. 사용자 정의 데이터 소스로 직접 넣은 문서는 동기화로 지워지지 않으므로, 삭제 API로 명시적으로 지울 때까지 남습니다.

데이터 소스나 Knowledge Base 자체를 삭제할 때는 데이터 삭제 정책(data deletion policy)이 적용됩니다. 이 정책은 그 데이터 소스에서 만들어진 벡터 데이터를 지울지 남길지 정합니다. CreateDataSource API

데이터 삭제 정책
정책데이터 소스·Knowledge Base 삭제 시주의할 점
DELETE그 데이터 소스에서 만든 벡터 데이터를 삭제벡터 저장소 자체는 삭제되지 않음
RETAIN벡터 데이터를 벡터 저장소에 남김남은 데이터는 직접 정리해야 함

관리형 Knowledge Base는 DELETE만 지원하며 기본값도 DELETE입니다. 고객 관리형에서 RETAIN을 고르면, Bedrock 리소스를 지운 뒤에도 벡터 저장소에 문서 조각이 남습니다. 민감한 문서를 다뤘다면 남은 데이터를 어떻게 정리할지 미리 정해 둡니다. 어느 정책이든 벡터 저장소를 직접 만들었다면 저장소 자체는 따로 삭제해야 비용이 멈춥니다.

04

수집 작업 상태와 실패 원인

수집 작업의 결과는 GetIngestionJob API나 콘솔의 동기화 기록에서 확인합니다. 응답에는 작업 상태(status), 문서별 처리 통계(statistics), 실패 사유 목록(failureReasons)이 있습니다. GetIngestionJob API

앞에서 시작한 작업의 결과를 조회하는 예시입니다. job은 2섹션 예시에서 받은 값입니다.

result = agent.get_ingestion_job(
knowledgeBaseId="KB_ID",
dataSourceId="DATA_SOURCE_ID",
ingestionJobId=job["ingestionJobId"],
)["ingestionJob"]
stats = result.get("statistics", {})
print(result["status"], stats.get("numberOfDocumentsScanned"), stats.get("numberOfDocumentsFailed"))
for reason in result.get("failureReasons", []):
print(reason)

작업 상태가 완료(COMPLETE)로 나와도 모든 문서가 들어갔다는 뜻은 아닙니다. 상태는 작업 전체의 진행 결과이고, 개별 문서의 실패는 통계에 따로 집계됩니다. 그래서 상태와 함께 다음 통계를 봅니다.

  • numberOfDocumentsScanned: 이번 작업에서 확인한 원본 문서 수
  • numberOfNewDocumentsIndexed·numberOfModifiedDocumentsIndexed: 새로 들어가거나 갱신된 문서 수
  • numberOfDocumentsSkipped: 바뀌지 않아 건너뛴 문서 수
  • numberOfDocumentsDeleted: 원본 삭제가 반영된 문서 수
  • numberOfDocumentsFailed: 수집에 실패한 문서 수

예를 들어 시설 안내 문서 10개를 올렸는데 작업이 실패하거나 확인한 문서 수가 0이라면, 실패 사유와 함께 서비스 역할의 S3 읽기 권한과 데이터 소스의 경로 설정을 먼저 확인합니다. 확인한 문서는 10개인데 실패가 3개라면 그 문서들의 파일 형식이나 크기, 파싱 문제를 실패 사유에서 찾습니다. 수집은 됐는데 원하는 내용이 검색되지 않는다면 수집이 아니라 분할이나 검색 설정의 문제일 수 있으며, 이는 26·28편에서 다룹니다.

05

원본 접근 권한과 검색 권한의 차이

가장 오해하기 쉬운 부분은 권한입니다. 원본 시스템에서 문서마다 열람 권한을 걸어 두었더라도, 동기화로 수집된 내용은 검색 권한(bedrock:Retrieve)을 가진 누구나 조회할 수 있습니다. 공식 문서도 원본에서 권한이 제한된 데이터까지 여기에 포함될 수 있다고 안내합니다. 원본 권한은 서비스 역할이 문서를 읽을 수 있는지를 정할 뿐, 검색하는 사용자가 누구인지는 따지지 않습니다. 커넥터 권한 안내(Confluence 예)

예를 들어 인사팀만 볼 수 있는 급여 규정을 일반 안내 문서와 같은 Knowledge Base에 수집하면, 사내 안내 도우미를 쓰는 모든 직원의 질문에 급여 규정이 근거로 검색될 수 있습니다. 이를 막는 방법은 구성 방식에 따라 다릅니다.

  • 공개 범위별로 Knowledge Base 분리: 전 직원용과 인사팀용을 따로 만들고, 애플리케이션이 사용자에 따라 호출할 Knowledge Base를 고릅니다. 가장 단순하고 경계가 분명합니다.
  • 메타데이터 필터: 문서마다 공개 범위 같은 메타데이터를 붙이고, 애플리케이션이 로그인한 사용자에 맞는 필터를 검색 요청에 넣습니다. S3 데이터 소스에서는 문서 옆에 같은 이름의 .metadata.json 파일을 둡니다.
  • 관리형 KB의 ACL 인식 검색: 지원 커넥터에서 ACL 인식을 켜면 원본의 사용자·그룹 권한을 문서와 함께 수집하고, 검색할 때 전달한 사용자 정보에 맞는 문서만 돌려줍니다. ACL 설정은 데이터 소스를 만들 때 정해야 하며 나중에 켜거나 끌 수 없습니다.

아래는 S3에 둔 시설안내.docx 옆에 함께 올리는 메타데이터 파일의 구성 예시입니다. 파일 이름은 원본 파일 이름 뒤에 .metadata.json을 붙인 형태이며, 속성 이름과 값은 가상의 예시입니다.

{
"metadataAttributes": {
"audience": "all",
"department": "facility"
}
}

어느 방법이든 사용자를 확인하는 책임은 애플리케이션에 있습니다. 메타데이터 필터는 애플리케이션이 넣은 조건대로만 걸러지고, 관리형 KB의 ACL 인식 검색도 전달받은 사용자 정보가 진짜인지 확인하지 않습니다. 공식 문서는 ACL 인식 검색이 보안 경계가 아니므로, 앞단의 사용자 인증 없이 이것만으로 접근을 통제하지 말라고 안내합니다. 즉 사용자 인증은 애플리케이션이 하고, Knowledge Base는 인증된 사용자 정보로 결과를 거르는 역할만 맡습니다.

반응형

Knowledge Bases 구성 선택: 관리형 저장소와 직접 선택한 저장소 | Bedrock 24편

Amazon Bedrock 완전 가이드 · 24편
Knowledge Bases 구성 선택

관리형 저장소와 직접 선택한 저장소

Amazon Bedrock Knowledge Bases는 두 방식으로 구성합니다. 관리형(Bedrock Managed) Knowledge Base는 저장소·임베딩·재정렬을 Bedrock이 맡고, 고객 관리형(Customer-managed) Knowledge Base는 사용자가 고른 벡터 저장소를 직접 만들고 운영합니다. AWS는 사용 편의성·정확도·비용의 균형을 이유로 관리형을 권장합니다. 벡터 저장소를 직접 조정해야 하거나 기존 저장소를 그대로 써야 한다면 고객 관리형을 선택합니다.

가상의 사내 안내 도우미를 예로 Knowledge Base와 데이터 소스의 관계, 관리형과 고객 관리형의 차이, 임베딩 모델과 저장소의 연결, 서비스 역할의 데이터 접근 범위, 직접 구현한 RAG와의 선택 기준을 설명합니다.

01

Knowledge Base와 데이터 소스의 관계

Knowledge Base는 검색의 단위이고, 데이터 소스는 문서를 가져오는 위치입니다. 애플리케이션은 Knowledge Base를 대상으로 검색하고, Knowledge Base는 연결된 데이터 소스에서 문서를 수집해 검색할 수 있는 형태로 저장합니다. 하나의 Knowledge Base에 여러 데이터 소스를 연결할 수 있습니다.

가상의 사내 안내 도우미라면 다음처럼 구성할 수 있습니다.

  • Knowledge Base: 사내 안내 KB(검색 대상, 임베딩 설정, 저장소를 가짐)
  • 데이터 소스 1: S3 버킷의 시설 안내 문서
  • 데이터 소스 2: 사내 협업 도구의 인사 공지(관리형이라면 기본 커넥터로 연결)

수집(동기화)은 데이터 소스 단위로 실행합니다. 시설 안내 문서만 바뀌었다면 해당 데이터 소스만 다시 동기화하면 됩니다. 반면 검색은 Knowledge Base 단위라서, 한 번의 검색으로 두 데이터 소스의 문서가 함께 결과에 나올 수 있습니다. 특정 데이터 소스의 문서만 찾으려면 메타데이터 필터로 범위를 좁히며, 이는 28편에서 다룹니다.

어떤 데이터 소스를 연결할 수 있는지는 구성 방식에 따라 다릅니다. 관리형 Knowledge Base는 S3, SharePoint, Confluence, 웹 크롤러, Google Drive, OneDrive, 사용자 정의 커넥터를 지원하고, 고객 관리형은 S3와 사용자 정의 커넥터를 지원합니다. 데이터 소스별 수집 방식은 25편에서 다룹니다.

02

관리 범위에 따른 구성 방식

두 방식의 가장 큰 차이는 저장소를 누가 운영하느냐입니다. 관리형은 임베딩·텍스트·메타데이터·원본 파일을 담는 저장소를 Bedrock이 만들고 확장합니다. 고객 관리형은 사용자가 지원되는 벡터 저장소를 골라 직접 만들고, 용량과 업데이트를 관리합니다. 대신 저장소에 직접 접근할 수 있습니다. 관리형과 고객 관리형 비교

구성 방식별 주요 차이(공식 비교 요약)
항목관리형(Bedrock Managed)고객 관리형(Customer-managed)
저장소Bedrock이 만들고 자동 확장사용자가 선택·생성·운영
임베딩 모델내장 관리형 모델 기본 제공, Bedrock 임베딩 모델로 변경 가능Bedrock 임베딩 모델 중 선택
재정렬내장 관리형 재정렬 기본 제공필요하면 Bedrock 재정렬 모델 선택
검색 방식에이전트형 검색과 의미·하이브리드 검색사용자가 검색 전략 선택
문서 파싱멀티모달 파일용 내장 파서기본 파서·모델 기반 파서·Data Automation 중 선택
AgentCore Gateway 연동지원지원하지 않음

관리형에 포함된 임베딩 모델과 재정렬 모델은 추가 비용 없이 제공됩니다. 고객 관리형에서는 벡터 저장소 운영 비용과 재정렬 모델 사용 비용을 따로 계산해야 합니다. 또 관리형 Knowledge Base는 지원 리전이 따로 정해져 있으므로, 사용할 리전에서 쓸 수 있는지 먼저 확인합니다.

Bedrock에는 이 두 방식 외에, Amazon Redshift의 정형 데이터를 자연어로 조회하는 정형 데이터용 Knowledge Base도 있습니다. 문서가 아니라 표 형태의 업무 데이터를 묻는 경우에 쓰며, 30편에서 다룹니다.

03

임베딩 모델과 저장소의 연결

임베딩 모델은 문서 조각과 질문을 숫자 벡터로 바꾸는 모델입니다. 수집할 때와 검색할 때 같은 모델을 써야 두 벡터를 비교할 수 있습니다. 그래서 임베딩 모델은 Knowledge Base를 만들 때 정하고, 저장소에는 그 모델이 만든 벡터가 쌓입니다.

관리형에서는 내장 임베딩 모델을 그대로 쓰는 것이 기본입니다. 다른 모델을 쓰려면 Knowledge Base를 만들 때 지정해야 하며, float32 형식의 1,024차원 벡터를 만드는 Bedrock 임베딩 모델만 고를 수 있습니다. 고객 관리형에서는 Bedrock 임베딩 모델 중 자유롭게 고를 수 있지만, 직접 만든 벡터 인덱스의 차원과 형식을 그 모델의 출력에 맞춰야 합니다.

임베딩 모델을 나중에 바꾸는 일은 가볍지 않습니다. 모델이 다르면 같은 문장도 다른 벡터가 되므로, 이미 저장된 벡터와 새 질문의 벡터를 비교할 수 없습니다. 모델을 바꾸려면 모든 문서의 벡터를 새로 만들어야 합니다. 따라서 처음에 한국어 문서로 검색 품질을 시험해 보고 모델을 정하는 것이 좋습니다. 차원·인덱스·유사도 설정은 27편에서 자세히 다룹니다.

04

서비스 역할과 데이터 접근

고객 관리형 Knowledge Base에는 두 종류의 권한 주체가 있습니다. 하나는 Knowledge Base가 수집과 검색을 위해 맡는 서비스 역할(service role)이고, 다른 하나는 Knowledge Base를 만들거나 검색을 호출하는 사용자·애플리케이션의 권한입니다. 애플리케이션은 데이터 소스에 직접 접근하지 않고, bedrock:Retrieve 같은 검색 권한만 있으면 됩니다. 서비스 역할은 데이터 소스 읽기, 임베딩 모델 호출, 벡터 저장소 쓰기·조회 권한을 가집니다. 저장소나 데이터를 KMS 키로 암호화했다면 복호화 권한도 필요합니다. Knowledge Bases 서비스 역할

콘솔에서 만들면 Bedrock이 서비스 역할을 자동으로 만들 수 있습니다. 직접 만든 역할을 쓴다면 Bedrock만 그 역할을 맡을 수 있도록 신뢰 정책을 작성합니다. 아래는 신뢰 정책 구성 예시입니다. ACCOUNT_ID는 Knowledge Base를 만들 계정 ID로, KNOWLEDGE_BASE_ARN_PATTERN은 해당 계정·리전의 Knowledge Base ARN 형식(끝이 knowledge-base/*)으로 바꿉니다.

{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "bedrock.amazonaws.com"},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {"aws:SourceAccount": "ACCOUNT_ID"},
"ArnLike": {"aws:SourceArn": "KNOWLEDGE_BASE_ARN_PATTERN"}
}
}]
}

aws:SourceAccount와 aws:SourceArn 조건은 다른 계정의 리소스가 이 역할을 대신 쓰는 상황(confused deputy)을 막습니다. Knowledge Base를 만든 뒤에는 ARN 끝의 knowledge-base/*를 실제 Knowledge Base ID로 좁힙니다.

운영에서 특히 주의할 점이 있습니다. 데이터 소스를 만들거나 수정하고 수집을 시작하는 작업은 새 역할을 전달하지 않고, Knowledge Base에 이미 연결된 서비스 역할로 실행됩니다. 따라서 데이터 소스 관리 권한과 검색 권한을 가진 사람은 자신에게 S3 권한이 없어도, 서비스 역할이 읽을 수 있는 위치라면 어디든 수집해 Retrieve로 내용을 볼 수 있습니다. 서비스 역할의 S3 권한은 실제로 필요한 버킷과 접두사로만 제한하고, 데이터 소스 관련 작업 권한은 관리자 권한처럼 다룹니다.

05

직접 구현한 RAG와의 선택 기준

Knowledge Bases를 쓰지 않고 RAG를 직접 구현할 수도 있습니다. 애플리케이션이 문서를 나누고, 임베딩 모델을 호출하고, 벡터 데이터베이스에 저장·검색한 뒤, Converse API로 답변을 만드는 방식입니다. 모든 단계를 직접 제어할 수 있지만 그만큼 만들고 운영할 부분이 늘어납니다. AWS 벡터 데이터베이스 선택 가이드

세 가지 구성의 선택 기준
상황관리형 KB고객 관리형 KB직접 구현
운영 부담을 최소화하고 싶다적합저장소 운영 필요모든 단계 운영 필요
S3 외 협업 도구 문서를 연결한다기본 커넥터 지원문서를 직접 넣는 사용자 정의 데이터 소스로 구현직접 구현
벡터 저장소 설정을 직접 조정해야 한다불가(Bedrock이 저장소 관리)적합적합
이미 운영 중인 벡터 DB를 써야 한다불가(Bedrock이 저장소 관리)지원 저장소라면 적합적합
분할·검색 로직을 완전히 바꿔야 한다제한적제한적(사용자 정의 변환 가능)적합

사내 안내 도우미처럼 문서를 모아 검색하는 것이 목적이고 저장소를 직접 다룰 이유가 없다면 관리형부터 검토합니다. 기존 벡터 저장소를 재사용해야 하거나 인덱스 설정을 직접 조정해야 할 때 고객 관리형을 고르고, Knowledge Bases가 제공하지 않는 분할·검색 방식이 꼭 필요할 때 직접 구현을 검토합니다.

반응형

RAG란 무엇인가: 모델이 외부 지식으로 답하는 구조 | Bedrock 23편

Amazon Bedrock 완전 가이드 · 23편
RAG란 무엇인가

모델이 외부 지식으로 답하는 구조

RAG(Retrieval Augmented Generation, 검색 증강 생성)는 질문이 들어올 때 관련 문서를 먼저 검색하고, 찾은 내용을 모델 입력에 함께 넣어 답하게 하는 구조입니다. 모델을 다시 학습시키지 않고도 사내 안내문처럼 모델이 모르는 정보나 자주 바뀌는 정보로 답할 수 있습니다. Amazon Bedrock에서는 Knowledge Bases가 문서 수집과 검색 과정을 관리합니다. 다만 검색이 실패하거나 문서에 답이 없으면 RAG도 정확하게 답할 수 없습니다.

가상의 사내 안내 도우미를 예로 RAG가 필요한 이유, Bedrock Knowledge Bases 기준의 수집 단계와 질문 처리 단계, 검색 결과를 입력에 넣는 방식, 프롬프트·미세 조정과의 차이, RAG로 해결할 수 없는 문제를 설명합니다.

01

모델의 학습 지식과 최신 업무 정보

기반 모델은 학습할 때 본 데이터로 답합니다. 그래서 학습 이후에 바뀐 정보나 회사 안에서만 쓰는 정보는 알지 못합니다. 예를 들어 사내 휴게실 운영 시간이 지난달 바뀌었다면, 모델은 그 사실을 모릅니다. 그런데도 질문을 받으면 그럴듯한 시간을 지어내 답할 수 있습니다.

가장 단순한 해결책은 안내문 전체를 매번 입력에 넣는 것입니다. 안내문이 몇 쪽이라면 이 방법으로 충분하고, 반복 비용은 22편의 프롬프트 캐싱으로 줄일 수 있습니다. 하지만 문서가 수백 개로 늘어나면 컨텍스트 창에 다 들어가지 않고, 들어가더라도 질문과 무관한 내용 때문에 비용과 지연이 커집니다.

RAG는 문서 전체 대신 질문과 관련된 부분만 찾아 입력에 넣습니다. 정보가 바뀌면 모델이 아니라 문서를 고치면 되므로, 자주 바뀌는 업무 정보에 맞습니다.

02

수집 단계와 질문 처리 단계

Bedrock Knowledge Bases 기준으로 RAG는 두 단계로 나뉩니다. 수집 단계(pre-processing)는 문서를 검색할 수 있는 형태로 미리 바꿔 두는 작업이고, 질문 처리 단계(runtime execution)는 질문이 들어올 때마다 검색하고 답하는 작업입니다. Knowledge Bases 동작 방식

수집 단계는 문서가 추가되거나 바뀔 때 실행됩니다.

  1. 문서를 텍스트로 변환합니다.
  2. 텍스트를 다루기 쉬운 크기의 조각(chunk)으로 나눕니다.
  3. 임베딩 모델로 각 조각을 숫자 벡터로 바꿉니다.
  4. 벡터를 원본 문서와의 연결 정보와 함께 벡터 인덱스에 저장합니다.

질문 처리 단계는 질문마다 실행됩니다.

  1. 같은 임베딩 모델로 질문을 벡터로 바꿉니다.
  2. 벡터 인덱스에서 질문과 의미가 가까운 조각을 찾습니다.
  3. 찾은 조각을 질문과 함께 모델 입력에 넣습니다.
  4. 모델이 그 내용을 근거로 답변을 만듭니다.

두 단계는 실패하는 지점도 다릅니다. 수집 단계에서 문서 변환이나 분할이 잘못되면 질문을 아무리 잘해도 필요한 조각이 검색되지 않습니다. 질문 처리 단계에서는 검색은 됐는데 모델이 근거를 잘못 해석하는 문제가 생길 수 있습니다. 답이 틀렸을 때는 어느 단계의 문제인지부터 나눠 봐야 합니다.

두 단계 비교
항목수집 단계질문 처리 단계
실행 시점문서 추가·변경 시질문마다
주요 결과벡터 인덱스근거가 포함된 답변
대표 실패변환·분할 오류, 수집 누락관련 없는 조각 검색, 근거 해석 오류
자세히 다루는 편25~27편28~29편

Knowledge Bases는 구성 방식에 따라 관리형, 직접 선택한 벡터 저장소를 쓰는 비정형 문서용, Amazon Redshift의 정형 데이터를 조회하는 정형 데이터용으로 나뉩니다. 이 차이와 선택 기준은 24편에서 다룹니다.

03

검색 결과를 입력에 넣는 방식

검색된 조각은 그대로 답이 되는 것이 아니라, 모델이 참고할 근거로 입력에 들어갑니다. 아래는 휴게실 질문에 대해 조각 두 개를 찾았을 때 모델에 전달되는 입력의 구성 예시입니다. 안내문 내용과 문서 이름은 가상의 예시입니다.

[지시]
아래 근거만 사용해 답하세요. 근거에 없으면 확인이 필요하다고 답하고, 사용한 근거 번호를 표시하세요.

[근거 1] 출처: 시설안내.docx
휴게실은 평일 오전 9시부터 오후 6시까지 운영합니다.

[근거 2] 출처: 공지_2026-10.txt
10월부터 휴게실 운영 종료 시간이 오후 7시로 바뀝니다.

[질문]
휴게실은 몇 시까지 이용할 수 있나요?

이 구성에서 중요한 부분은 세 가지입니다. 지시는 근거 밖의 내용을 지어내지 말라고 범위를 정합니다. 근거마다 출처를 붙여 두면 답변에 인용을 표시할 수 있고, 틀린 답이 나왔을 때 어떤 조각 때문인지 추적할 수 있습니다. 또 근거 1과 2처럼 서로 다른 내용이 함께 검색될 수 있으므로, 최신 문서를 우선한다는 기준이 필요하면 지시나 메타데이터로 정해 줘야 합니다.

넣는 조각 수도 판단 대상입니다. 조각을 많이 넣으면 필요한 내용을 놓칠 가능성은 줄지만, 무관한 내용이 섞이고 입력 토큰이 늘어납니다. 또 검색 결과는 질문마다 달라지므로, 프롬프트 캐싱을 함께 쓴다면 검색 결과를 캐시 지점 뒤에 둡니다.

Bedrock에서는 이 조립을 두 가지 방식으로 할 수 있습니다. RetrieveAndGenerate API는 검색과 입력 구성, 답변 생성을 한 번에 처리합니다. Retrieve API는 검색 결과만 돌려주고, 입력 구성과 모델 호출은 애플리케이션이 직접 합니다. 두 API의 차이와 인용 처리는 29편에서 다룹니다.

04

RAG·프롬프트·미세 조정의 차이

모델의 답을 바꾸는 방법은 RAG만이 아닙니다. 프롬프트를 고치는 방법과 모델을 추가로 학습시키는 미세 조정(fine-tuning)도 있습니다. 세 방법은 바꾸는 대상이 다르므로, 해결하려는 문제가 무엇인지에 따라 고릅니다.

세 방법이 바꾸는 대상
방법바꾸는 것맞는 문제정보 변경 반영
프롬프트 수정지시·형식·예시답변 형식, 어조, 판단 기준프롬프트를 고치면 즉시
RAG질문 시점에 넣는 근거모델이 모르는 정보, 자주 바뀌는 정보문서를 고치고 다시 수집하면 반영
미세 조정모델 가중치반복되는 작업 방식·출력 행동다시 학습해야 반영

흔한 오해는 사내 지식을 넣으려면 미세 조정이 필요하다는 생각입니다. 운영 시간이나 신청 절차처럼 자주 바뀌는 사실은 학습시킨 뒤에도 또 바뀌므로, 바뀔 때마다 다시 학습해야 합니다. 이런 정보는 RAG로 넣고, 미세 조정은 특정 형식으로 분류하거나 일정한 방식으로 요약하는 것처럼 작업 방식 자체를 바꿀 때 검토합니다. 세 방법은 함께 쓸 수 있으며, Bedrock의 맞춤화 방식별 선택 기준은 34편에서 다룹니다. 모델 맞춤화 가이드라인

05

RAG로 해결할 수 없는 문제

RAG는 검색된 근거의 품질을 넘어서지 못합니다. 다음과 같은 문제는 RAG를 붙여도 그대로 남거나 다른 방법이 필요합니다.

  • 문서에 없는 정보: 근거가 없으면 정확한 답도 없습니다. 이때는 "확인이 필요하다"고 답하게 하는 것이 목표입니다.
  • 검색 실패: 질문 표현이 문서와 다르거나 필요한 내용이 조각 경계에서 잘리면 관련 조각을 찾지 못합니다. 분할과 검색 설정은 26·28편에서 다룹니다.
  • 문서 간 충돌: 오래된 안내문과 새 공지가 함께 검색되면 모델이 어느 쪽을 따를지 흔들립니다. 문서 정리와 메타데이터가 필요합니다.
  • 집계·계산 질문: "신청이 가장 많은 달은?"처럼 전체 데이터를 세야 하는 질문은 몇 개 조각을 찾는 방식으로 답하기 어렵습니다. 정형 데이터 조회는 30편에서 다룹니다.
  • 권한 통제: 원본 시스템에서 문서별로 접근 권한을 걸어 두었더라도, 검색 결과에 그 권한이 그대로 적용된다고 가정하면 안 됩니다. 원본 권한과 검색 권한의 차이는 25편에서 다룹니다.
  • 근거를 벗어난 답변: 근거를 넣어도 모델이 그 밖의 내용을 덧붙일 수 있습니다. 답변이 근거와 맞는지 검사하는 방법은 42편에서 다룹니다.

또 RAG는 질문마다 검색 단계가 추가되므로 응답 지연과 비용이 늘어납니다. 문서가 적고 거의 바뀌지 않는다면 RAG 없이 안내문을 직접 넣는 쪽이 더 단순할 수 있습니다. RAG는 문서가 많거나 자주 바뀌어 전체를 넣기 어려울 때 선택하는 구조입니다.

반응형