mirror of
https://github.com/danswer-ai/danswer.git
synced 2025-09-20 13:05:49 +02:00
DAN-120 Kubernetes (#98)
Sample Kubernetes deployment with Auth default on Also includes a bugfix for credentialed connectors with Auth turned on
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
FROM python:3.11-slim-bullseye
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler \
|
||||
&& apt-get install -y git cmake pkg-config libprotobuf-c-dev protobuf-compiler \
|
||||
libprotobuf-dev libgoogle-perftools-dev libpq-dev build-essential cron curl \
|
||||
supervisor \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import abc
|
||||
from typing import Generic
|
||||
from typing import TypeVar
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.chunking.models import BaseChunk
|
||||
from danswer.chunking.models import EmbeddedIndexChunk
|
||||
@@ -14,7 +15,7 @@ IndexFilter = dict[str, str | list[str] | None]
|
||||
|
||||
class DocumentIndex(Generic[T], abc.ABC):
|
||||
@abc.abstractmethod
|
||||
def index(self, chunks: list[T], user_id: int | None) -> bool:
|
||||
def index(self, chunks: list[T], user_id: UUID | None) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@@ -23,7 +24,7 @@ class VectorIndex(DocumentIndex[EmbeddedIndexChunk], abc.ABC):
|
||||
def semantic_retrieval(
|
||||
self,
|
||||
query: str,
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
filters: list[IndexFilter] | None,
|
||||
num_to_retrieve: int,
|
||||
) -> list[InferenceChunk]:
|
||||
@@ -35,7 +36,7 @@ class KeywordIndex(DocumentIndex[IndexChunk], abc.ABC):
|
||||
def keyword_search(
|
||||
self,
|
||||
query: str,
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
filters: list[IndexFilter] | None,
|
||||
num_to_retrieve: int,
|
||||
) -> list[InferenceChunk]:
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from functools import partial
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.chunking.models import EmbeddedIndexChunk
|
||||
from danswer.configs.constants import ALLOWED_GROUPS
|
||||
@@ -86,7 +87,7 @@ def delete_qdrant_doc_chunks(
|
||||
|
||||
def index_qdrant_chunks(
|
||||
chunks: list[EmbeddedIndexChunk],
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
collection: str,
|
||||
client: QdrantClient | None = None,
|
||||
batch_upsert: bool = True,
|
||||
|
@@ -1,8 +1,7 @@
|
||||
import uuid
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.chunking.models import EmbeddedIndexChunk
|
||||
from danswer.chunking.models import InferenceChunk
|
||||
from danswer.configs.app_configs import NUM_RERANKED_RESULTS
|
||||
from danswer.configs.app_configs import NUM_RETURNED_HITS
|
||||
from danswer.configs.app_configs import QDRANT_DEFAULT_COLLECTION
|
||||
from danswer.configs.constants import ALLOWED_USERS
|
||||
@@ -27,7 +26,7 @@ logger = setup_logger()
|
||||
|
||||
|
||||
def _build_qdrant_filters(
|
||||
user_id: int | None, filters: list[IndexFilter] | None
|
||||
user_id: UUID | None, filters: list[IndexFilter] | None
|
||||
) -> list[FieldCondition]:
|
||||
filter_conditions: list[FieldCondition] = []
|
||||
# Permissions filter
|
||||
@@ -78,7 +77,7 @@ class QdrantIndex(VectorIndex):
|
||||
self.collection = collection
|
||||
self.client = get_qdrant_client()
|
||||
|
||||
def index(self, chunks: list[EmbeddedIndexChunk], user_id: int | None) -> bool:
|
||||
def index(self, chunks: list[EmbeddedIndexChunk], user_id: UUID | None) -> bool:
|
||||
return index_qdrant_chunks(
|
||||
chunks=chunks,
|
||||
user_id=user_id,
|
||||
@@ -90,7 +89,7 @@ class QdrantIndex(VectorIndex):
|
||||
def semantic_retrieval(
|
||||
self,
|
||||
query: str,
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
filters: list[IndexFilter] | None,
|
||||
num_to_retrieve: int = NUM_RETURNED_HITS,
|
||||
page_size: int = NUM_RETURNED_HITS,
|
||||
@@ -106,7 +105,7 @@ class QdrantIndex(VectorIndex):
|
||||
|
||||
page_offset = 0
|
||||
found_inference_chunks: list[InferenceChunk] = []
|
||||
found_chunk_uuids: set[uuid.UUID] = set()
|
||||
found_chunk_uuids: set[UUID] = set()
|
||||
while len(found_inference_chunks) < num_to_retrieve:
|
||||
try:
|
||||
hits = self.client.search(
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
from functools import partial
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
|
||||
import typesense # type: ignore
|
||||
from danswer.chunking.models import EmbeddedIndexChunk
|
||||
@@ -101,7 +102,7 @@ def delete_typesense_doc_chunks(
|
||||
|
||||
def index_typesense_chunks(
|
||||
chunks: list[IndexChunk | EmbeddedIndexChunk],
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
collection: str,
|
||||
client: typesense.Client | None = None,
|
||||
batch_upsert: bool = True,
|
||||
@@ -171,7 +172,7 @@ def index_typesense_chunks(
|
||||
|
||||
|
||||
def _build_typesense_filters(
|
||||
user_id: int | None, filters: list[IndexFilter] | None
|
||||
user_id: UUID | None, filters: list[IndexFilter] | None
|
||||
) -> str:
|
||||
filter_str = ""
|
||||
|
||||
@@ -205,7 +206,7 @@ class TypesenseIndex(KeywordIndex):
|
||||
self.collection = collection
|
||||
self.ts_client = get_typesense_client()
|
||||
|
||||
def index(self, chunks: list[IndexChunk], user_id: int | None) -> bool:
|
||||
def index(self, chunks: list[IndexChunk], user_id: UUID | None) -> bool:
|
||||
return index_typesense_chunks(
|
||||
chunks=chunks,
|
||||
user_id=user_id,
|
||||
@@ -216,7 +217,7 @@ class TypesenseIndex(KeywordIndex):
|
||||
def keyword_search(
|
||||
self,
|
||||
query: str,
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
filters: list[IndexFilter] | None,
|
||||
num_to_retrieve: int,
|
||||
) -> list[InferenceChunk]:
|
||||
|
@@ -63,7 +63,7 @@ def create_credential(
|
||||
) -> ObjectCreationIdResponse:
|
||||
credential = Credential(
|
||||
credential_json=credential_data.credential_json,
|
||||
user_id=int(user.id) if user else None,
|
||||
user_id=user.id if user else None,
|
||||
public_doc=credential_data.public_doc,
|
||||
)
|
||||
db_session.add(credential)
|
||||
@@ -83,7 +83,7 @@ def update_credential(
|
||||
return None
|
||||
|
||||
credential.credential_json = credential_data.credential_json
|
||||
credential.user_id = int(user.id) if user is not None else None
|
||||
credential.user_id = user.id if user is not None else None
|
||||
credential.public_doc = credential_data.public_doc
|
||||
|
||||
db_session.commit()
|
||||
|
@@ -2,6 +2,7 @@ import datetime
|
||||
from enum import Enum as PyEnum
|
||||
from typing import Any
|
||||
from typing import List
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.auth.schemas import UserRole
|
||||
from danswer.configs.constants import DocumentSource
|
||||
@@ -105,7 +106,7 @@ class Credential(Base):
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True)
|
||||
credential_json: Mapped[dict[str, Any]] = mapped_column(postgresql.JSONB())
|
||||
user_id: Mapped[int | None] = mapped_column(ForeignKey("user.id"), nullable=True)
|
||||
user_id: Mapped[UUID | None] = mapped_column(ForeignKey("user.id"), nullable=True)
|
||||
public_doc: Mapped[bool] = mapped_column(Boolean, default=False)
|
||||
time_created: Mapped[datetime.datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.chunking.models import InferenceChunk
|
||||
from danswer.configs.app_configs import NUM_RETURNED_HITS
|
||||
@@ -34,7 +35,7 @@ def query_processing(query: str) -> str:
|
||||
@log_function_time()
|
||||
def retrieve_keyword_documents(
|
||||
query: str,
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
filters: list[IndexFilter] | None,
|
||||
datastore: KeywordIndex,
|
||||
num_hits: int = NUM_RETURNED_HITS,
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from uuid import UUID
|
||||
|
||||
import numpy
|
||||
from danswer.chunking.models import EmbeddedIndexChunk
|
||||
@@ -58,7 +59,7 @@ def semantic_reranking(
|
||||
@log_function_time()
|
||||
def retrieve_ranked_documents(
|
||||
query: str,
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
filters: list[IndexFilter] | None,
|
||||
datastore: VectorIndex,
|
||||
num_hits: int = NUM_RETURNED_HITS,
|
||||
|
@@ -4,6 +4,7 @@ from typing import Generic
|
||||
from typing import Literal
|
||||
from typing import Optional
|
||||
from typing import TypeVar
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.configs.constants import DocumentSource
|
||||
from danswer.connectors.models import InputType
|
||||
@@ -171,7 +172,7 @@ class CredentialBase(BaseModel):
|
||||
|
||||
class CredentialSnapshot(CredentialBase):
|
||||
id: int
|
||||
user_id: int | None
|
||||
user_id: UUID | None
|
||||
time_created: datetime
|
||||
time_updated: datetime
|
||||
|
||||
|
@@ -49,7 +49,7 @@ def semantic_search(
|
||||
filters = question.filters
|
||||
logger.info(f"Received semantic search query: {query}")
|
||||
|
||||
user_id = None if user is None else int(user.id)
|
||||
user_id = None if user is None else user.id
|
||||
ranked_chunks, unranked_chunks = retrieve_ranked_documents(
|
||||
query, user_id, filters, QdrantIndex(collection)
|
||||
)
|
||||
@@ -71,7 +71,7 @@ def keyword_search(
|
||||
filters = question.filters
|
||||
logger.info(f"Received keyword search query: {query}")
|
||||
|
||||
user_id = None if user is None else int(user.id)
|
||||
user_id = None if user is None else user.id
|
||||
ranked_chunks = retrieve_keyword_documents(
|
||||
query, user_id, filters, TypesenseIndex(collection)
|
||||
)
|
||||
@@ -99,7 +99,7 @@ def direct_qa(
|
||||
if use_keyword is None:
|
||||
use_keyword = predicted_search == SearchType.KEYWORD
|
||||
|
||||
user_id = None if user is None else int(user.id)
|
||||
user_id = None if user is None else user.id
|
||||
if use_keyword:
|
||||
ranked_chunks: list[InferenceChunk] | None = retrieve_keyword_documents(
|
||||
query, user_id, filters, TypesenseIndex(collection)
|
||||
@@ -165,7 +165,7 @@ def stream_direct_qa(
|
||||
if use_keyword is None:
|
||||
use_keyword = predicted_search == SearchType.KEYWORD
|
||||
|
||||
user_id = None if user is None else int(user.id)
|
||||
user_id = None if user is None else user.id
|
||||
if use_keyword:
|
||||
ranked_chunks: list[InferenceChunk] | None = retrieve_keyword_documents(
|
||||
query, user_id, filters, TypesenseIndex(collection)
|
||||
|
@@ -1,6 +1,7 @@
|
||||
from functools import partial
|
||||
from itertools import chain
|
||||
from typing import Protocol
|
||||
from uuid import UUID
|
||||
|
||||
from danswer.chunking.chunk import Chunker
|
||||
from danswer.chunking.chunk import DefaultChunker
|
||||
@@ -16,7 +17,7 @@ from danswer.search.semantic_search import DefaultEmbedder
|
||||
|
||||
class IndexingPipelineProtocol(Protocol):
|
||||
def __call__(
|
||||
self, documents: list[Document], user_id: int | None
|
||||
self, documents: list[Document], user_id: UUID | None
|
||||
) -> list[EmbeddedIndexChunk]:
|
||||
...
|
||||
|
||||
@@ -28,7 +29,7 @@ def _indexing_pipeline(
|
||||
vector_index: VectorIndex,
|
||||
keyword_index: KeywordIndex,
|
||||
documents: list[Document],
|
||||
user_id: int | None,
|
||||
user_id: UUID | None,
|
||||
) -> list[EmbeddedIndexChunk]:
|
||||
# TODO: make entire indexing pipeline async to not block the entire process
|
||||
# when running on async endpoints
|
||||
|
@@ -1,16 +1,18 @@
|
||||
[supervisord]
|
||||
nodaemon=true
|
||||
logfile=/dev/null
|
||||
logfile=/dev/stdout
|
||||
logfile_maxbytes=0
|
||||
|
||||
[program:indexing]
|
||||
command=python danswer/background/update.py
|
||||
stdout_logfile=/var/log/supervisor/update.log
|
||||
stdout_logfile=/var/log/update.log
|
||||
redirect_stderr=true
|
||||
stdout_logfile_maxbytes=52428800
|
||||
autorestart=true
|
||||
|
||||
[program:file_deletion]
|
||||
command=python danswer/background/file_deletion.py
|
||||
stdout_logfile=/var/log/supervisor/file_deletion.log
|
||||
stdout_logfile=/var/log/file_deletion.log
|
||||
redirect_stderr=true
|
||||
stdout_logfile_maxbytes=52428800
|
||||
autorestart=true
|
||||
|
1
deployment/.gitignore
vendored
1
deployment/.gitignore
vendored
@@ -1 +1,2 @@
|
||||
.env*
|
||||
secrets.yaml
|
||||
|
@@ -1,32 +1,77 @@
|
||||
This serves as an example for how to deploy everything on a single machine. This is
|
||||
not optimal, but can get you started easily and cheaply. To run:
|
||||
# Deploying Danswer
|
||||
The two options provided here are the easiest ways to get Danswer up and running.
|
||||
|
||||
- Docker Compose is simpler and default values are already preset to run right out of the box with a single command.
|
||||
As everything is running on a single machine, this may not be as scalable depending on your hardware, traffic and needs.
|
||||
|
||||
- Kubernetes deployment is also provided. Depending on your existing infrastructure, this may be more suitable for
|
||||
production deployment but there are a few caveats.
|
||||
- User auth is turned on by default for Kubernetes (with the assumption this is for production use)
|
||||
so you must either update the deployments to turn off user auth or provide the values shown in the example
|
||||
secrets.yaml file.
|
||||
- The example provided assumes a blank slate for existing Kubernetes deployments/services. You may need to adjust the
|
||||
deployments or services according to your setup. This may require existing Kubernetes knowledge or additional
|
||||
setup time.
|
||||
|
||||
All the features of Danswer are fully available regardless of the deployment option.
|
||||
|
||||
For information on setting up connectors, check out https://docs.danswer.dev/connectors/overview
|
||||
|
||||
|
||||
1. Run one of the docker compose commands below depending on your environment:
|
||||
- For Local:
|
||||
- `docker compose -f docker-compose.dev.yml -p danswer-stack up -d --build`
|
||||
- This will start Web/API servers, Postgres (backend DB), Qdrant (vector DB), and the background indexing job.
|
||||
- Downloading packages/requirements may take 20+ minutes depending on your internet connection.
|
||||
## Docker Compose:
|
||||
Docker Compose provides the easiest way to get Danswer up and running.
|
||||
|
||||
Requirements: Docker and docker compose
|
||||
|
||||
1. To run Danswer, navigate to `docker_compose` directory and run the following:
|
||||
- `docker compose -f docker-compose.dev.yml -p danswer-stack up -d --build`
|
||||
- This will start Web/API servers, Postgres (backend DB), Qdrant (vector DB), and the background indexing job.
|
||||
- Downloading packages/requirements may take 20+ minutes depending on your internet connection and whether it needs
|
||||
to install packages for GPU.
|
||||
|
||||
|
||||
2. To shut down the deployment run (use stop to stop containers, down to remove containers):
|
||||
- For Local:
|
||||
- `docker compose -f docker-compose.dev.yml -p danswer-stack stop`
|
||||
2. To shut down the deployment, run:
|
||||
- To stop the containers: `docker compose -f docker-compose.dev.yml -p danswer-stack stop`
|
||||
- To delete the containers: `docker compose -f docker-compose.dev.yml -p danswer-stack down`
|
||||
|
||||
|
||||
3. To completely remove Danswer (**WARNING, this will also erase your indexed data and all users**) run:
|
||||
- For Local:
|
||||
- `docker compose -f docker-compose.dev.yml -p danswer-stack down -v`
|
||||
3. To completely remove Danswer run:
|
||||
- **WARNING, this will also erase your indexed data and users**
|
||||
- `docker compose -f docker-compose.dev.yml -p danswer-stack down -v`
|
||||
|
||||
|
||||
Additional steps for setting up for Prod:
|
||||
Additional steps for user auth and https if you do want to use Docker Compose for production:
|
||||
|
||||
1. Set up a `.env` file in this directory with relevant environment variables.
|
||||
- Refer to env.dev.template and env.prod.template
|
||||
|
||||
- To turn on user auth, set:
|
||||
- ENABLE_OAUTH=True
|
||||
- GOOGLE_OAUTH_CLIENT_ID=\<your GCP API client ID\>
|
||||
- GOOGLE_OAUTH_CLIENT_SECRET=\<associated client secret\>
|
||||
- Refer to https://developers.google.com/identity/gsi/web/guides/get-google-api-clientid
|
||||
|
||||
2. Set up https:
|
||||
- Set up a `.env.nginx` file in this directory based on `env.nginx.template`.
|
||||
- `chmod +x init-letsencrypt.sh` + `./init-letsencrypt.sh` to set up https certificate.
|
||||
|
||||
3. Follow the above steps but replacing dev with prod.
|
||||
|
||||
|
||||
## Kubernetes:
|
||||
Depending on your deployment needs Kubernetes may be more suitable. The yamls provided will work out of the box but the
|
||||
intent is for you to customize the deployment to fit your own needs. There is no data replication or auto-scaling built
|
||||
in for the provided example.
|
||||
|
||||
Requirements: a Kubernetes cluster and kubectl
|
||||
|
||||
**NOTE: This setup does not explicitly enable https, the assumption is you would have this already set up for your
|
||||
prod cluster**
|
||||
|
||||
1. To run Danswer, navigate to `kubernetes` directory and run the following:
|
||||
- `kubectl apply -f .`
|
||||
|
||||
2. To remove Danswer, run:
|
||||
- **WARNING, this will also erase your indexed data and users**'
|
||||
- `kubectl delete -f .`
|
||||
- To not delete the persistent volumes (Document indexes and Users), specify the specific `.yaml` files instead of
|
||||
`.` without specifying delete on persistent-volumes.yaml.
|
@@ -1,4 +1,4 @@
|
||||
upstream app_server {
|
||||
upstream api_server {
|
||||
# fail_timeout=0 means we always retry an upstream even if it failed
|
||||
# to return a good HTTP response
|
||||
|
||||
@@ -35,7 +35,7 @@ server {
|
||||
# we don't want nginx trying to do something clever with
|
||||
# redirects, we set the Host: header above already.
|
||||
proxy_redirect off;
|
||||
proxy_pass http://app_server;
|
||||
proxy_pass http://api_server;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
@@ -1,4 +1,4 @@
|
||||
upstream app_server {
|
||||
upstream api_server {
|
||||
# fail_timeout=0 means we always retry an upstream even if it failed
|
||||
# to return a good HTTP response
|
||||
|
||||
@@ -35,7 +35,7 @@ server {
|
||||
# we don't want nginx trying to do something clever with
|
||||
# redirects, we set the Host: header above already.
|
||||
proxy_redirect off;
|
||||
proxy_pass http://app_server;
|
||||
proxy_pass http://api_server;
|
||||
}
|
||||
|
||||
location / {
|
||||
|
@@ -2,7 +2,7 @@ version: '3'
|
||||
services:
|
||||
api_server:
|
||||
build:
|
||||
context: ../backend
|
||||
context: ../../backend
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- relational_db
|
||||
@@ -24,7 +24,7 @@ services:
|
||||
- file_connector_tmp_storage:/home/file_connector_storage
|
||||
background:
|
||||
build:
|
||||
context: ../backend
|
||||
context: ../../backend
|
||||
dockerfile: Dockerfile.background
|
||||
depends_on:
|
||||
- relational_db
|
||||
@@ -42,7 +42,7 @@ services:
|
||||
- file_connector_tmp_storage:/home/file_connector_storage
|
||||
web_server:
|
||||
build:
|
||||
context: ../web
|
||||
context: ../../web
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- api_server
|
||||
@@ -97,7 +97,7 @@ services:
|
||||
- "80:80"
|
||||
- "3000:80" # allow for localhost:3000 usage, since that is the norm
|
||||
volumes:
|
||||
- ./data/nginx:/etc/nginx/conf.d
|
||||
- ../data/nginx:/etc/nginx/conf.d
|
||||
command: >
|
||||
/bin/sh -c "envsubst '$$\{DOMAIN\}' < /etc/nginx/conf.d/app.conf.template.dev > /etc/nginx/conf.d/app.conf
|
||||
&& while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\""
|
@@ -2,7 +2,7 @@ version: '3'
|
||||
services:
|
||||
api_server:
|
||||
build:
|
||||
context: ../backend
|
||||
context: ../../backend
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- relational_db
|
||||
@@ -21,7 +21,7 @@ services:
|
||||
- file_connector_tmp_storage:/home/file_connector_storage
|
||||
background:
|
||||
build:
|
||||
context: ../backend
|
||||
context: ../../backend
|
||||
dockerfile: Dockerfile.background
|
||||
depends_on:
|
||||
- relational_db
|
||||
@@ -37,7 +37,7 @@ services:
|
||||
- file_connector_tmp_storage:/home/file_connector_storage
|
||||
web_server:
|
||||
build:
|
||||
context: ../web
|
||||
context: ../../web
|
||||
dockerfile: Dockerfile
|
||||
depends_on:
|
||||
- api_server
|
||||
@@ -82,9 +82,9 @@ services:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./data/nginx:/etc/nginx/conf.d
|
||||
- ./data/certbot/conf:/etc/letsencrypt
|
||||
- ./data/certbot/www:/var/www/certbot
|
||||
- ../data/nginx:/etc/nginx/conf.d
|
||||
- ../data/certbot/conf:/etc/letsencrypt
|
||||
- ../data/certbot/www:/var/www/certbot
|
||||
command: >
|
||||
/bin/sh -c "envsubst '$$\{DOMAIN\}' < /etc/nginx/conf.d/app.conf.template > /etc/nginx/conf.d/app.conf
|
||||
&& while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\""
|
||||
@@ -95,8 +95,8 @@ services:
|
||||
image: certbot/certbot
|
||||
restart: always
|
||||
volumes:
|
||||
- ./data/certbot/conf:/etc/letsencrypt
|
||||
- ./data/certbot/www:/var/www/certbot
|
||||
- ../data/certbot/conf:/etc/letsencrypt
|
||||
- ../data/certbot/www:/var/www/certbot
|
||||
entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'"
|
||||
volumes:
|
||||
local_dynamic_storage:
|
68
deployment/kubernetes/api_server-service-deployment.yaml
Normal file
68
deployment/kubernetes/api_server-service-deployment.yaml
Normal file
@@ -0,0 +1,68 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: api-server-service
|
||||
spec:
|
||||
selector:
|
||||
app: api-server
|
||||
ports:
|
||||
- name: api-server-port
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: api-server-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: api-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: api-server
|
||||
spec:
|
||||
containers:
|
||||
- name: api-server
|
||||
image: danswer/danswer-api-server:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
env:
|
||||
- name: POSTGRES_HOST
|
||||
value: relational-db-service
|
||||
- name: QDRANT_HOST
|
||||
value: vector-db-service
|
||||
- name: TYPESENSE_HOST
|
||||
value: search-engine-service
|
||||
- name: TYPESENSE_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: typesense_api_key
|
||||
- name: GOOGLE_OAUTH_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: google_oauth_client_id
|
||||
- name: GOOGLE_OAUTH_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: google_oauth_client_secret
|
||||
volumeMounts:
|
||||
- name: dynamic-storage
|
||||
mountPath: /home/storage
|
||||
- name: file-connector-storage
|
||||
mountPath: /home/file_connector_storage
|
||||
volumes:
|
||||
- name: dynamic-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: dynamic-pvc
|
||||
- name: file-connector-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: file-connector-pvc
|
42
deployment/kubernetes/background-deployment.yaml
Normal file
42
deployment/kubernetes/background-deployment.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: background-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: background
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: background
|
||||
spec:
|
||||
containers:
|
||||
- name: background
|
||||
image: danswer/danswer-background:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: POSTGRES_HOST
|
||||
value: relational-db-service
|
||||
- name: QDRANT_HOST
|
||||
value: vector-db-service
|
||||
- name: TYPESENSE_HOST
|
||||
value: search-engine-service
|
||||
- name: TYPESENSE_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: typesense_api_key
|
||||
volumeMounts:
|
||||
- name: dynamic-storage
|
||||
mountPath: /home/storage
|
||||
- name: file-connector-storage
|
||||
mountPath: /home/file_connector_storage
|
||||
volumes:
|
||||
- name: dynamic-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: dynamic-pvc
|
||||
- name: file-connector-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: file-connector-pvc
|
42
deployment/kubernetes/nginx-configmap.yaml
Normal file
42
deployment/kubernetes/nginx-configmap.yaml
Normal file
@@ -0,0 +1,42 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: nginx-configmap
|
||||
data:
|
||||
nginx.conf: |
|
||||
upstream api_server {
|
||||
server api-server-service:80 fail_timeout=0;
|
||||
}
|
||||
|
||||
upstream web_server {
|
||||
server web-server-service:80 fail_timeout=0;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name $$DOMAIN;
|
||||
|
||||
location ~ ^/api(.*)$ {
|
||||
rewrite ^/api(/.*)$ $1 break;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_http_version 1.1;
|
||||
proxy_buffering off;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://api_server;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
proxy_set_header X-Forwarded-Host $host;
|
||||
proxy_set_header Host $host;
|
||||
proxy_http_version 1.1;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://web_server;
|
||||
}
|
||||
}
|
52
deployment/kubernetes/nginx-service-deployment.yaml
Normal file
52
deployment/kubernetes/nginx-service-deployment.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
- name: danswer
|
||||
protocol: TCP
|
||||
port: 3000
|
||||
targetPort: 80
|
||||
type: LoadBalancer
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.23.4-alpine
|
||||
ports:
|
||||
- containerPort: 80
|
||||
env:
|
||||
- name: DOMAIN
|
||||
value: localhost
|
||||
volumeMounts:
|
||||
- name: nginx-conf
|
||||
mountPath: /etc/nginx/conf.d
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
while :; do sleep 6h & wait $$!; nginx -s reload; done & nginx -g "daemon off;"
|
||||
volumes:
|
||||
- name: nginx-conf
|
||||
configMap:
|
||||
name: nginx-configmap
|
54
deployment/kubernetes/persistent-volumes.yaml
Normal file
54
deployment/kubernetes/persistent-volumes.yaml
Normal file
@@ -0,0 +1,54 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: dynamic-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: file-connector-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: db-volume-claim
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: qdrant-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: typesense-pvc
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
50
deployment/kubernetes/postgres-service-deployment.yaml
Normal file
50
deployment/kubernetes/postgres-service-deployment.yaml
Normal file
@@ -0,0 +1,50 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: relational-db-service
|
||||
spec:
|
||||
selector:
|
||||
app: relational-db
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 5432
|
||||
targetPort: 5432
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: relational-db-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: relational-db
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: relational-db
|
||||
spec:
|
||||
containers:
|
||||
- name: relational-db
|
||||
image: postgres:15.2-alpine
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: postgres_user
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: postgres_password
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
volumeMounts:
|
||||
- mountPath: /var/lib/postgresql/data
|
||||
name: db-volume
|
||||
volumes:
|
||||
- name: db-volume
|
||||
persistentVolumeClaim:
|
||||
claimName: db-volume-claim
|
40
deployment/kubernetes/qdrant-service-deployment.yaml
Normal file
40
deployment/kubernetes/qdrant-service-deployment.yaml
Normal file
@@ -0,0 +1,40 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: vector-db-service
|
||||
spec:
|
||||
selector:
|
||||
app: qdrant
|
||||
ports:
|
||||
- name: qdrant-port
|
||||
protocol: TCP
|
||||
port: 6333
|
||||
targetPort: 6333
|
||||
type: LoadBalancer
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: qdrant-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: qdrant
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: qdrant
|
||||
spec:
|
||||
containers:
|
||||
- name: qdrant
|
||||
image: qdrant/qdrant:v1.1.3
|
||||
ports:
|
||||
- containerPort: 6333
|
||||
volumeMounts:
|
||||
- name: qdrant-storage
|
||||
mountPath: /qdrant/storage
|
||||
volumes:
|
||||
- name: qdrant-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: qdrant-pvc
|
12
deployment/kubernetes/secrets.yaml
Normal file
12
deployment/kubernetes/secrets.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
# The values in this file should be changed
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: danswer-secrets
|
||||
type: Opaque
|
||||
data:
|
||||
postgres_user: cG9zdGdyZXM= # "postgres" base64 encoded
|
||||
postgres_password: cGFzc3dvcmQ= # "password" base64 encoded
|
||||
typesense_api_key: bG9jYWxfZGV2X3R5cGVzZW5zZQ== # "local_dev_typesense" base64 encoded
|
||||
google_oauth_client_id: REPLACE-THIS # You will need to provide this, use echo -n "your-client-id" | base64
|
||||
google_oauth_client_secret: REPLACE-THIS # You will need to provide this, use echo -n "your-client-id" | base64
|
48
deployment/kubernetes/typesense-service-deployment.yaml
Normal file
48
deployment/kubernetes/typesense-service-deployment.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: search-engine-service
|
||||
spec:
|
||||
selector:
|
||||
app: typesense
|
||||
ports:
|
||||
- name: typesense-port
|
||||
protocol: TCP
|
||||
port: 8108
|
||||
targetPort: 8108
|
||||
type: LoadBalancer
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: typesense-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: typesense
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: typesense
|
||||
spec:
|
||||
containers:
|
||||
- name: typesense
|
||||
image: typesense/typesense:0.24.1
|
||||
ports:
|
||||
- containerPort: 8108
|
||||
env:
|
||||
- name: TYPESENSE_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: danswer-secrets
|
||||
key: typesense_api_key
|
||||
- name: TYPESENSE_DATA_DIR
|
||||
value: /typesense/storage
|
||||
volumeMounts:
|
||||
- name: typesense-storage
|
||||
mountPath: /typesense/storage
|
||||
volumes:
|
||||
- name: typesense-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: typesense-pvc
|
36
deployment/kubernetes/web_server-service-deployment.yaml
Normal file
36
deployment/kubernetes/web_server-service-deployment.yaml
Normal file
@@ -0,0 +1,36 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: web-server-service
|
||||
spec:
|
||||
selector:
|
||||
app: web-server
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 3000
|
||||
type: ClusterIP
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: web-server-deployment
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: web-server
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: web-server
|
||||
spec:
|
||||
containers:
|
||||
- name: web-server
|
||||
image: danswer/danswer-web-server:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: INTERNAL_URL
|
||||
value: "http://api-server-service:80"
|
Reference in New Issue
Block a user