Skip to content

Commit

Permalink
chatbot-rag-app: adds Kubernetes manifest and instructions
Browse files Browse the repository at this point in the history
Signed-off-by: Adrian Cole <[email protected]>
  • Loading branch information
codefromthecrypt committed Feb 21, 2025
1 parent 48d829f commit 3660c11
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 3 deletions.
58 changes: 56 additions & 2 deletions example-apps/chatbot-rag-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ Copy [env.example](env.example) to `.env` and fill in values noted inside.
## Installing and connecting to Elasticsearch

There are a number of ways to install Elasticsearch. Cloud is best for most
use-cases. We also have [docker-compose-elastic.yml](../../docker), that starts
Elasticsearch, Kibana, and APM Server on your laptop with one command.
use-cases. We also have [docker-compose-elastic.yml][docker-compose-elastic],
that starts Elasticsearch, Kibana, and APM Server on your laptop in one step.

Once you decided your approach, edit your `.env` file accordingly.

Expand Down Expand Up @@ -71,6 +71,58 @@ Clean up when finished, like this:
docker compose down
```

### Run with Kubernetes

Kubernetes is more complicated than Docker, but closer to the production
experience for many users. [k8s-manifest.yml](k8s-manifest.yml) creates the
same services, but needs additional configuration first.

First step is to setup your environment. [env.example](env.example) must be
copied to a file name `.env` and updated with `ELASTICSEARCH_URL` and
`OTEL_EXPORTER_OTLP_ENDPOINT` values visible to you Kubernetes deployment.

For example, if you started your Elastic Stack with [k8s-manifest-elastic.yml][k8s-manifest-elastic],
you would update these values:
```
ELASTICSEARCH_URL=http://elasticsearch:9200
OTEL_EXPORTER_OTLP_ENDPOINT=http://apm-server:8200
```

Then, import your `.env` file as a configmap like this:
```bash
kubectl create configmap chatbot-rag-app-env --from-env-file=.env
```

If you are using Vertex AI, make a secret for authentication:
```bash
kubectl create secret generic gcloud-credentials \
--from-file=application_default_credentials.json=$HOME/.config/gcloud/application_default_credentials.json
```

Now that your configuration is applied, create the chatbot-rag-app deployment
and service by applying this manifest:
```bash
kubectl apply -f k8s-manifest.yml
```

Next, block until chatbot-rag-app is available.
```bash
kubectl wait --for=condition=available --timeout=10m deployment/chatbot-rag-app
```

*Note*: The first run may take several minutes to become available.

Next, forward the kibana port:
```bash
kubectl port-forward service/kibana 5601:5601 &
```

Clean up when finished, like this:

```bash
kubectl delete -f k8s-manifest.yml
```

### Run with Python

If you want to run this example with Python, you need to do a few things listed
Expand Down Expand Up @@ -198,3 +250,5 @@ docker compose up --build --force-recreate
---
[loader-docs]: https://python.langchain.com/docs/how_to/#document-loaders
[install-es]: https://www.elastic.co/search-labs/tutorials/install-elasticsearch
[docker-compose-elastic]: ../../docker/docker-compose-elastic.yml
[k8s-manifest-elastic]: ../../k8s/k8s-manifest-elastic.yml
6 changes: 5 additions & 1 deletion example-apps/chatbot-rag-app/env.example
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ FLASK_APP=api/app.py
PYTHONUNBUFFERED=1

# How you connect to Elasticsearch: change details to your instance
# This defaults to a Elastic Stack accessible via localhost. When this is
# running inside Kubernetes, update to http://elasticsearch:9200 or similar.
ELASTICSEARCH_URL=http://localhost:9200
ELASTICSEARCH_USER=elastic
ELASTICSEARCH_PASSWORD=elastic
Expand Down Expand Up @@ -68,7 +70,9 @@ OTEL_SDK_DISABLED=true
# Assign the service name that shows up in Kibana
OTEL_SERVICE_NAME=chatbot-rag-app

# Default to send traces to the Elastic APM server
# Default to send logs, traces and metrics to an Elastic APM server accessible
# via localhost. If using running inside k8s, update to http://apm-server:8200
# or similar.
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:8200
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf

Expand Down
59 changes: 59 additions & 0 deletions example-apps/chatbot-rag-app/k8s-manifest.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: chatbot-rag-app
spec:
replicas: 1
selector:
matchLabels:
app: chatbot-rag-app
template:
metadata:
labels:
app: chatbot-rag-app
spec:
# The below will recreate your secret based on the gcloud credentials file
# kubectl create secret generic gcloud-credentials \
# --from-file=application_default_credentials.json=$HOME/.config/gcloud/application_default_credentials.json
volumes:
- name: gcloud-credentials
secret:
secretName: gcloud-credentials
initContainers:
- name: ingest-data
image: &image ghcr.io/elastic/elasticsearch-labs/chatbot-rag-app:latest
imagePullPolicy: &imagePullPolicy IfNotPresent
args: ["flask", "create-index"]
env:
- name: FLASK_APP
value: api/app.py
# This recreates your configmap based on your .env file:
# kubectl create configmap chatbot-rag-app-env --from-env-file=.env
envFrom: &envFrom
- configMapRef:
name: chatbot-rag-app-env
volumeMounts: &volumeMounts
- name: gcloud-credentials
mountPath: /root/.config/application_default_credentials.json
readOnly: true
containers:
- name: api-frontend
image: *image
imagePullPolicy: *imagePullPolicy
ports:
- containerPort: 4000
envFrom: *envFrom
volumeMounts: *volumeMounts
---
apiVersion: v1
kind: Service
metadata:
name: api
spec:
selector:
app: chatbot-rag-app
ports:
- protocol: TCP
port: 4000
targetPort: 4000
38 changes: 38 additions & 0 deletions k8s/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Running your own Elastic Stack with Kubernetes

If you'd like to start Elastic with Kubernetes, you can use the provided
[manifest-elastic.yml](manifest-elastic.yml) file. This starts
Elasticsearch, Kibana, and APM Server and only requires Docker installed.

First, configure the Elastic stack:
```bash
kubectl apply -f manifest-elastic.yml
```

**Note**: For simplicity, this adds an Elastic Stack to the default namespace.
Commands after here are simpler due to this. If you want to choose a different
one, use `kubectl`'s `--namespace` flag!

Next, block until the whole stack is available. First install or changing the
Elastic Stack version can take a long time due to image pulling.
```bash
kubectl wait --for=condition=available --timeout=10m \
deployment/elasticsearch \
deployment/kibana \
deployment/apm-server
```

Next, forward the kibana port:
```bash
kubectl port-forward service/kibana 5601:5601 &
```

Finally, you can view Kibana at http://localhost:5601/app/home#/

If asked for a username and password, use username: elastic and password: elastic.

Clean up when finished, like this:

```bash
kubectl delete -f manifest-elastic.yml
```
188 changes: 188 additions & 0 deletions k8s/k8s-manifest-elastic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
apiVersion: v1
kind: Service
metadata:
name: elasticsearch
spec:
ports:
- port: 9200
targetPort: 9200
selector:
app: elasticsearch

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: elasticsearch
spec:
replicas: 1
selector:
matchLabels:
app: elasticsearch
template:
metadata:
labels:
app: elasticsearch
spec:
containers:
- name: elasticsearch
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
ports:
- containerPort: 9200
env:
- name: node.name
value: elasticsearch
- name: cluster.name
value: docker-cluster
- name: discovery.type
value: single-node
- name: ELASTIC_PASSWORD
value: elastic
- name: bootstrap.memory_lock
value: "true"
- name: xpack.security.enabled
value: "true"
- name: xpack.security.http.ssl.enabled
value: "false"
- name: xpack.security.transport.ssl.enabled
value: "false"
- name: xpack.license.self_generated.type
value: trial
# Note that ELSER is recommended to have 2GB, but it is JNI (PyTorch).
# So, ELSER's memory is in addition to the heap and other overhead.
- name: ES_JAVA_OPTS
value: "-Xms2g -Xmx2g"
resources:
limits:
memory: "4.5Gi" # max heap plus overhead and 2GB for ELSER
securityContext:
capabilities:
add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"]
drop: ["ALL"]
readinessProbe:
exec:
command: ["sh", "-c", "curl -s http://localhost:9200 | grep -q 'missing authentication credentials'"]
initialDelaySeconds: 5
periodSeconds: 1
timeoutSeconds: 10
failureThreshold: 120

---
apiVersion: v1
kind: Service
metadata:
name: kibana
spec:
ports:
- port: 5601
targetPort: 5601
selector:
app: kibana

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: kibana
spec:
replicas: 1
selector:
matchLabels:
app: kibana
template:
metadata:
labels:
app: kibana
spec:
initContainers:
- name: setup-kibana-system-user
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
command:
- bash
- -xc
- |
echo "Setup the kibana_system password";
until curl -vs -u "elastic:elastic" -X POST http://elasticsearch:9200/_security/user/kibana_system/_password -d '{"password":"elastic"}' -H "Content-Type: application/json" | grep -q "^{}"; do sleep 5; done;
containers:
- name: kibana
image: docker.elastic.co/kibana/kibana:8.17.2
ports:
- containerPort: 5601
env:
- name: SERVERNAME
value: kibana
- name: ELASTICSEARCH_HOSTS
value: http://elasticsearch:9200
- name: ELASTICSEARCH_USERNAME
value: kibana_system
- name: ELASTICSEARCH_PASSWORD
value: elastic
- name: MONITORING_UI_CONTAINER_ELASTICSEARCH_ENABLED
value: "true"
- name: XPACK_SECURITY_ENCRYPTIONKEY
value: fhjskloppd678ehkdfdlliverpoolfcr
- name: XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY
value: fhjskloppd678ehkdfdlliverpoolfcr
- name: SERVER_PUBLICBASEURL
value: http://127.0.0.1:5601
readinessProbe:
httpGet:
path: /api/status
port: 5601
initialDelaySeconds: 10
periodSeconds: 1
failureThreshold: 300

---
apiVersion: v1
kind: Service
metadata:
name: apm-server
spec:
ports:
- port: 8200
targetPort: 8200
selector:
app: apm-server

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: apm-server
spec:
replicas: 1
selector:
matchLabels:
app: apm-server
template:
metadata:
labels:
app: apm-server
spec:
containers:
- name: apm-server
image: docker.elastic.co/apm/apm-server:8.17.2
ports:
- containerPort: 8200
env:
- name: apm-server.kibana.enabled
value: "true"
- name: apm-server.kibana.host
value: http://kibana:5601
- name: apm-server.kibana.username
value: elastic
- name: apm-server.kibana.password
value: elastic
- name: output.elasticsearch.hosts
value: http://elasticsearch:9200
- name: output.elasticsearch.username
value: elastic
- name: output.elasticsearch.password
value: elastic
readinessProbe:
tcpSocket:
port: 8200
initialDelaySeconds: 10
periodSeconds: 1
failureThreshold: 300

0 comments on commit 3660c11

Please sign in to comment.