uniglot
Tech
Thoughts
Sign In
Cloud Native

[LLM / MLOps] Dify 자체 호스팅

U
uniglot
Jan 27, 20251y ago
Category
  1. Kubernetes
  2. Helm
  3. LLM
  4. Dify
제가 CTO로 있는 디비디랩에서는 좋은 유저 리서치를 할 수 있게 돕기 위해서 여러 LLM 프롬프트와 이를 종합한 에이전트를 개발해서 사용하고 있어요.
그 중에서 Dify라는 서비스를 활용해 두 개 이상의 프롬프트에 로직과 플로우 제어를 더해서 하나의 목표를 가진 워크플로우를 만드는 데 유용하게 사용하고 있어요.
디파이는 유료 플랜을 사용해서 사용할 수도 있지만, 태생적으로 오픈소스 솔루션(깃허브 리포지토리)이기 때문에 직접 호스팅해서 사용하는 것도 당연히 가능합니다.
이 아티클에서는 고유 요구사항을 따라서 디파이를 자체 호스팅한 과정을 소개합니다.

1. 요구사항

1.
헬름 차트를 통해서 쿠버네티스 클러스터 위에 배포할 수 있어야 합니다.
2.
PostgreSQL은 Amazon RDS를 사용합니다.
3.
Redis는 쿠버네티스 클러스터 위에 이미 존재하는 클러스터를 사용합니다.
4.
벡터 스토어는 Qdrant를 사용하고, 쿠버네티스 위에 자체 호스팅합니다.
5.
인그레스 자원을 사용하지 않고 Gateway API의 HTTPRoute 자원을 사용합니다.
6.
VPN을 통해 내부망에 접속한 다음에 디파이에 접속할 수 있습니다.

2. 디파이 헬름 차트

디파이는 공식 헬름 차트를 제공하지 않고, docker-compose를 사용한 설치 방법과 소스 코드로부터 설치하는 방법만 제공하고 있어요. 😢 (공식 문서 참조)
다행히 깃허브 리포지토리에 커뮤니티에서 관리하는 헬름 차트를 소개(깃허브 리포지토리 참조)하고 있어서, 아예 바닥부터 차트를 만들어야 하는 수고는 덜 수 있었습니다!

2.1. 차트 선정

공식 리포지토리에서 소개한 헬름 차트는 두 개입니다.
•
LeoQuote가 관리하는 차트 (douban/dify)
•
BorisPolonsky가 관리하는 차트 (BorisPolonsky/dify-helm)
여기서 첫 번째 차트는 두 번째 차트보다 나중에 시작된 프로젝트인데요. 패스워드와 API 키와 같은 민감정보를 평문으로 넘겨야 하는 한계때문에 만들어진 차트이지만, 현재는 둘 다 시크릿을 사용할 수 있게 되어 있어요.
솔직히 말해서, 어느 쪽도 완성되었거나 완벽하다고 할 수 없지만 첫 번째 차트의 경우가 사용자 입장에서 더 편리하다고 생각합니다.
우선 BorisPolonsky의 차트는 밸류가 너무 많아서 완전히 이해하고 값을 통제하기가 어려워요...
values.yaml 파일이 3004줄인데, 예시도 없고 밸류에 대한 문서화도 안 되어 있다
반면, LeoQuote의 차트는 약 400줄 정도의 간결한 밸류 정의를 가지고 있으면서, 예시도 비교적 많이 제공하고 있어요. 벡터 스토어를 헬름 차트 하나로 해결할 수 없다는 문제가 존재하는데, 이 부분은 벡터 스토어를 따로 배포한 다음 연결하는 식으로 해결하려 해요.
그런 이유로 LeoQuote의 차트를 사용합니다!

2.2. 차트 기본 밸류 설정

선정한 헬름 차트의 설치 예시를 기반으로 확장해 보도록 할까요? 아래는 설치 예시를 참조하여 확장한 것입니다. (헬름 차트 리포지토리 참조)
global:
  host: "dify.uniglot.com"
  enableTLS: true
  extraBackendEnvs:
  - name: SECRET_KEY
    value: "superconfidentialsecretkey"
•
host : 실제 디파이가 사용할 도메인을 적어 주시면 됩니다.
•
enableTLS : TLS를 사용할지 결정합니다.
◦
이 값이 true 로 설정되면, templates/_helpers.tpl의 dify.baseUrl 부분이 https 프로토콜을 사용하도록 변경됩니다. (리포지토리 참조)
◦
또한, dify.baseUrl 은 CONSOLE_API_URL , CONSOLE_WEB_URL , SERVICE_API_URL , APP_API_URL , APP_WEB_URL 의 값을 변경합니다. (리포지토리 참조, 공식 문서 환경 변수 참조)
◦
만약 TLS를 사용하는데 enableTLS 의 값이 false라면, HTTPS 백엔드에 HTTP로 요청을 하게 되어 Mixed Content 에러가 발생하게 됩니다.
•
image.tag : 디파이 컨테이너의 이미지를 지정합니다. 지정하지 않을 경우 차트에 지정된 기본값을 사용합니다.
◦
버전 피닝은 강하게 권장됩니다. 다만, 헬름 차트에서 이미지 버전을 피닝하기 때문에 ArgoCD를 사용하면서 헬름 차트의 버전을 피닝하는 것도 가능합니다.
◦
저의 경우 헬름 차트의 버전을 피닝하고, image.tag 를 생략하였습니다.
•
extraBackendEnvs : 디파이 컨테이너의 환경변수(공식 문서 참조)를 직접 넘기는 부분입니다. 이 설정값을 사용해서 이 헬름 차트에서 제공하지 않는 설정을 진행할 수 있어요.

2.3. 메타데이터를 위한 PostgreSQL 연결

디파이는 메타데이터의 저장을 위해서 PostgreSQL를 사용합니다.
아래는 이미 만들어서 사용하고 있던 테스트용 Amazon RDS를 연결하는 설정이에요!
필요한 환경변수는 공식 문서를 참조해 주세요. (환경 변수 공식 문서 링크)
global:
  # omit
  extraBackendEnvs:
  # omit
  - name: DB_HOST
    value: dify-uniglot.supersecret.ap-northeast-2.rds.amazonaws.com
  - name: DB_USERNAME
    valueFrom:
      secretKeyRef:
        name: dify-db-credentials
        key: username
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: dify-db-credentials
        key: password
  - name: DB_DATABASE
    value: dify

postgresql:
  embedded: false
postgresql.embedded 의 기본값은 true 인데, 그대로 두면 PostgreSQL을 클러스터 위에 배포하게 되니 비활성화해 줘요.
DB 인증 정보를 위한 시크릿은 적절히 생성해서 사용하시면 되겠습니다 허허헣

2.4. 레디스 연결

캐싱, pub/sub, 셀러리 브로커로 레디스를 사용합니다. (환경 변수 공식 문서 참조)
레디스의 경우 쿠버네티스 클러스터 위에 있던 레디스를 사용하기로 했어요. 물론 Elasticache를 사용할 수도 있다는 점!
⚠️
운영 환경에서는 레디스에 인증을 활성화하는 것을 추천합니다. REDIS_USERNAME 과 REDIS_PASSWORD 환경 변수에 인증 정보를 넘길 수 있습니다.
global:
  # omit
  extraBackendEnvs:
  # omit
  - name: REDIS_HOST
    value: test-cluster.redis.svc.cluster.local
  - name: CELERY_BROKER_URL
    value: redis://test-cluster.redis.svc.cluster.local:6379/1

redis:
  embedded: false
여기서 세션을 위한 레디스 데이터베이스와 셀러리 브로커로 사용하기 위한 데이터베이스는 서로 달라야 해요.
저의 경우 전자는 0(기본), 후자는 1로 설정하였어요.
PostgreSQL과 마찬가지로, 레디스도 redis.embedded 가 기본적으로 true 라서, 레디스를 새롭게 클러스터 위에 배포하게 됩니다. 필요 없으니 false 로 지정하도록 해 볼까요?

3. 벡터 스토어 설정

디파이는 다양한 종류의 벡터 스토어를 지원해요.
•
weaviate
•
qdrant
•
milvus
•
zilliz
•
myscale
•
analyticdb
•
couchbase
이 중에서 설치와 설정이 비교적 쉬운 Qdrant를 벡터 스토어로 선택했어요.

3.1. Qdrant 헬름 차트

이번 아티클에서는 공식 차트를 변경 없이 설치해 봅시다. ml 네임스페이스에 설치했어요!
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: qdrant
  namespace: argocd
spec:
  source:
    repoURL: https://qdrant.github.io/qdrant-helm
    targetRevision: '1.13.1'
    chart: qdrant
    helm:
      releaseName: qdrant
# omit

3.2. 디파이 환경 변수 설정

설치한 Qdrant의 도메인을 환경 변수로 넘겨줄게요. 디파이 또한 ml 네임스페이스에 설치될 예정이므로, 서비스 이름으로 충분해요.
global:
  # omit
  extraBackendEnvs:
  # omit
  - name: VECTOR_STORE
    value: qdrant
  - name: QDRANT_URL
    value: http://qdrant

4. 여기까지 완성된 헬름 밸류

이제 네트워크를 제외한 모든 기본적인 설정이 완료된 헬름 밸류가 나왔어요!
global:
  host: "dify.uniglot.com"
  enableTLS: true
  extraBackendEnvs:
  - name: SECRET_KEY
    value: "superconfidentialsecretkey"
  - name: DB_HOST
    value: dify-uniglot.supersecret.ap-northeast-2.rds.amazonaws.com
  - name: DB_USERNAME
    valueFrom:
      secretKeyRef:
        name: dify-db-credentials
        key: username
  - name: DB_PASSWORD
    valueFrom:
      secretKeyRef:
        name: dify-db-credentials
        key: password
  - name: DB_DATABASE
    value: dify
  - name: REDIS_HOST
    value: test-cluster.redis.svc.cluster.local
  - name: CELERY_BROKER_URL
    value: redis://test-cluster.redis.svc.cluster.local:6379/1
  - name: VECTOR_STORE
    value: qdrant
  - name: QDRANT_URL
    value: http://qdrant

postgresql:
  embedded: false
redis:
  embedded: false

5. HTTPRoute 설정을 위한 기존 차트 수정

저는 기본적으로 인그레스를 사용하지 않고 Envoy Gateway 기반의 Gateway API 자원을 사용하고 있습니다.
하지만 이번 디파이 차트에서는 인그레스만을 지원하므로, 제가 가지고 있는 차트 중 게이트웨이 차트를 약간 수정하여 HTTPRoute 자원을 별도로 배포해 주었어요.
참고로 디파이 헬름 차트의 ingress 는 기본적으로 비활성화되어 있어요.

5.1. 게이트웨이 자원 배포를 위한 커스텀 차트 수정

우선 제가 작성해서 사용하고 있던 envoy-gateway라는 차트는 게이트웨이 클래스, 게이트웨이, EnvoyProxy 자원을 배포하기 위해 만들었습니다.
여기에서 externalHelmRoutes 라는 밸류를 추가합니다.
# envoy-gateway/values.yaml

# omit

envoyProxy:
  # omit

gatewayClass:
  # omit

gateway:
  # omit

externalHelmRoutes: []
그리고 외부 헬름 차트가 인그레스만 지원할 경우에 여기에서 HTTPRoute를 정의할 수 있게 할 거예요.
실제 전달할 값의 형태는 아래와 같아요. 라우팅 규칙은 차트에 정의된 인그레스와 동일해요! (디파이 차트 인그레스 템플릿 참조)
externalHelmRoutes:
  - name: dify-route
    namespace: ml
    hostnames:
      - dify.uniglot.com
    rules:
      - path: /
        service: dify-frontend
        port: 80
      - path: /console/api
        service: dify-api-svc
        port: 80
      - path: /api
        service: dify-api-svc
        port: 80
      - path: /v1
        service: dify-api-svc
        port: 80
      - path: /files
        service: dify-api-svc
        port: 80
    internalAccess: true  # 내부망에서만 접근하도록 하기
    clientCIDRs:  # 접근 허용 대역 (내부망 대역)
      - 10.0.42.0/10
그러면 이 리스트를 통해 HTTPRoute와 Envoy Gateway에서 네트워크 보안 제어를 담당하는 CR인 SecurityPolicy를 생성하도록 해 봅시다.
# envoy-gateway/templates/httproute.yaml

{{- range .Values.externalHelmRoutes -}}
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: {{ .name }}
  namespace: {{ .namespace | default "default" }}
spec:
  parentRefs:  # 사정에 맞게 올바른 게이트웨이를 바라보도록 설정
    - name: {{ $.Values.gateway.name }}
      namespace: {{ $.Values.envoyProxy.namespace }}
  hostnames:
    {{- range .hostnames }}
    - {{ . | quote }}
    {{- end }}
  rules:
    {{- range .rules }}
    - matches:
      - path:
         type: PathPrefix
         value: {{ .path | quote }}
      backendRefs:
        - name: {{ .service | quote }}
          port: {{ .port }}
    {{- end }}
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: {{ .name }}-security-policy
  namespace: {{ .namespace | default "default" }}
spec:
  targetRef:
    group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: {{ .name }}
  {{- if .internalAccess }}  # 외부 접속 허용하지 않음
  authorization:
    defaultAction: Deny
    rules:
      - action: Allow
        principal:
          clientCIDRs:
            {{- range .clientCIDRs }}
            - {{ . | quote }}
            {{- end }}
  {{- end }}
---
{{- end }}

6. 결과

지금까지 나온 모든 차트를 다 설치해 줍니다.
ArgoCD 싱크가 모두 마쳐지면 VPN을 켜고, 지정한 도메인에 접속하면-
나만의 작은 디파이가 완성되었습니다!
당연히 실제로 배포에 사용한 값은 이 글과 다르고, 세부 보안 설정도 다르니 이 부분 유념해 주시면 좋겠고요!
감샤합니당 😍
'uniglot' 구독하기
사이트를 구독하면 새 포스트 등 최신 업데이트를 알림과 메일로 가장 먼저 받아보실 수 있습니다.
Slashpage에 가입하고 'uniglot'을 구독하세요!
구독
2
👍
2
Younghan
Feb 5, 2025
멋진 시도와 깔끔한 글입니다. 잘 봤습니다! 윤의님!
U
uniglot
Feb 5, 2025
@Younghan 감사합니다 영한님! 뤼튼 요즘 재밌는 거 많이 하는 것 같던데, 안목이 있으시군요!
See latest comments