Kubernetes
Kubernetes
Haystack 파이프라인을 Kubernetes로 배포하는 방법을 살펴볼게요. 컨테이너 오케스트레이터(Kubernetes 같은)에서 Haystack을 워크로드로 돌리는 가장 좋은 방법은, 한 개 이상의 Hayhooks 인스턴스를 노출하는 서비스를 만드는 거예요.
출처: Kubernetes
본문
Hayhooks로 Haystack Kubernetes 서비스 만들기
첫 단계로 로컬 KinD 또는 Minikube Kubernetes 클러스터를 만드는 걸 권장해요. 클러스터는 CLI로 관리할 수도 있지만, k9s나 Lens 같은 도구를 쓰면 더 편해요.
준비가 끝나면, Hayhooks Pod 한 개를 돌리는 아주 단순한 Kubernetes Service부터 시작해 볼게요.
kind: Pod
apiVersion: v1
metadata:
name: hayhooks
labels:
app: haystack
spec:
containers:
- image: deepset/hayhooks:v1.23.0
name: hayhooks
imagePullPolicy: IfNotPresent
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
---
kind: Service
apiVersion: v1
metadata:
name: haystack-service
spec:
selector:
app: haystack
type: ClusterIP
ports:
# Default port used by the Hayhooks Docker image
- port: 1416
위 구성을 기존 Kubernetes 클러스터에 적용하면, hayhooks Pod가 haystack-service라는 이름의 Service로 나타나요.
여기서 정의한 Service는 ClusterIP 타입이에요. 즉 클러스터 내부에서만 노출돼요. Hayhooks API를 외부에까지 노출하려면 NodePort나 Ingress 리소스가 필요해요. 대안으로 Port Forwarding을 써서 Service에 로컬로 접근할 수도 있어요.
그러려면 KinD 클러스터의 Host-To-Node Mapping에 포트 30080을 추가해야 해요. 즉 클러스터를 다음 비슷한 노드 설정으로 만들어야 한다는 뜻이에요.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
# ...
extraPortMappings:
- containerPort: 30080
hostPort: 30080
protocol: TCP
그다음 Hayhooks Pod가 올바르게 동작하는지 확인하기 위해 간단한 NodePort를 만들어요.
apiVersion: v1
kind: Service
metadata:
name: haystack-nodeport
spec:
selector:
app: haystack
type: NodePort
ports:
- port: 1416
targetPort: 1416
nodePort: 30080
name: http
이 설정을 적용하면 hayhooks Pod가 localhost:30080에서 접근 가능해져요.
여기부터는 파이프라인을 관리할 수 있어요. 단일 Hayhooks 인스턴스에 여러 개의 서로 다른 파이프라인을 배포하는 것도 가능하다는 걸 기억해 두세요. 자세한 내용은 Hayhooks 개요나 공식 Hayhooks 문서를 확인하세요.
Pod 시작 시 파이프라인 자동 실행
Hayhooks는 시작 시점에 Haystack 파이프라인을 로드해서, 서버가 시작되면 바로 사용할 수 있게 만들 수 있어요. 이 메커니즘을 활용하면 Pod가 시작하자마자 한 개 이상의 파이프라인을 즉시 서빙할 수 있어요.
시작할 때 HAYHOOKS_PIPELINES_DIR에 지정된 경로에서 배포된 파이프라인을 찾아 로드해요.
배포된 파이프라인은 기본적으로 pipeline_wrapper.py 파일과 (필요하면) 다른 파일들을 포함하는 디렉토리예요. 예시 파이프라인을 미리 로드하려면, 로컬 폴더를 클러스터 노드에 마운트한 뒤 Hayhooks Pod에서도 그 폴더를 사용할 수 있게 해야 해요.
먼저 로컬 폴더가 KinD 클러스터 노드의 /data에 올바르게 마운트됐는지 확인해요.
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
# ...
extraMounts:
- hostPath: /path/to/local/pipelines/folder
containerPath: /data
다음으로 /data를 볼륨으로 사용할 수 있게 만들고 Hayhooks Pod에 마운트해요. 그러려면 이전 Pod 구성을 아래처럼 업데이트해요.
kind: Pod
apiVersion: v1
metadata:
name: hayhooks
labels:
app: haystack
spec:
containers:
- image: deepset/hayhooks:v1.23.0
name: hayhooks
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args:
- |
pip install trafilatura && \
hayhooks run --host 0.0.0.0
volumeMounts:
- name: local-data
mountPath: /mnt/data
env:
- name: HAYHOOKS_PIPELINES_DIR
value: /mnt/data
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: openai-secret
key: api-key
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
volumes:
- name: local-data
hostPath:
path: /data
type: Directory
여기서 주의할 점이 몇 가지 있어요.
- Hayhooks 컨테이너의
command를 바꿔서, 시작 전에trafilatura의존성을 설치하게 했어요. 우리의 chat_with_website 예시 파이프라인에 필요하기 때문이에요. 실제 프로덕션 환경에서는 여기에 설명된 대로 커스텀 Hayhooks 이미지를 만드는 걸 권장해요. - Hayhooks 컨테이너가
OPENAI_API_KEY를 Kubernetes Secret에서 읽도록 했어요.
새 구성을 적용하기 전에 openai-secret을 만들어요.
apiVersion: v1
kind: Secret
metadata:
name: openai-secret
type: Opaque
data:
# Replace the placeholder below with the base64 encoded value of your API key
# Generate it using: echo -n $OPENAI_API_KEY | base64
api-key: YOUR_B...HERE
이 설정을 적용한 뒤 Hayhooks Pod 로그를 확인하면 chat_with_website 파이프라인이 이미 배포된 것을 볼 수 있어요.
여러 Pod로 확장
Haystack 파이프라인은 보통 무상태(stateless)라서, 같은 파이프라인 세트를 실행하는 여러 Pod에 요청을 분산하기에 완벽한 경우예요. 단일-Pod 구성을 실제 Kubernetes Deployment로 바꿔 볼게요.
apiVersion: apps/v1
kind: Deployment
metadata:
name: haystack-deployment
spec:
replicas: 3
selector:
matchLabels:
app: haystack
template:
metadata:
labels:
app: haystack
spec:
initContainers:
- name: install-dependencies
image: python:3.12-slim
workingDir: /mnt/data
command: ["/bin/bash", "-c"]
args:
- |
echo "Installing dependencies..."
pip install trafilatura
echo "Dependencies installed successfully!"
touch /mnt/data/init-complete
volumeMounts:
- name: local-data
mountPath: /mnt/data
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "250m"
containers:
- image: deepset/hayhooks:v1.23.0
name: hayhooks
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args:
- |
pip install trafilatura && \
hayhooks run --host 0.0.0.0
ports:
- containerPort: 1416
name: http
volumeMounts:
- name: local-data
mountPath: /mnt/data
env:
- name: HAYHOOKS_PIPELINES_DIR
value: /mnt/data
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: openai-secret
key: api-key
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
volumes:
- name: local-data
hostPath:
path: /data
type: Directory
이 구성을 적용하면 Pod 세 개가 만들어져요. 각 Pod는 서로 다른 Hayhooks 인스턴스를 실행하지만, 모두 이전 예시에서 마운트한 볼륨이 제공하는 같은 예시 파이프라인을 서빙해요.
앞서 만든 NodePort는 이제 로드 밸런서 역할을 해서, 들어오는 요청을 세 개의 Hayhooks Pod에 분산할 거예요.