Skip to content

Commit 3660c11

Browse files
chatbot-rag-app: adds Kubernetes manifest and instructions
Signed-off-by: Adrian Cole <[email protected]>
1 parent 48d829f commit 3660c11

File tree

5 files changed

+346
-3
lines changed

5 files changed

+346
-3
lines changed

example-apps/chatbot-rag-app/README.md

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ Copy [env.example](env.example) to `.env` and fill in values noted inside.
2222
## Installing and connecting to Elasticsearch
2323

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

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

@@ -71,6 +71,58 @@ Clean up when finished, like this:
7171
docker compose down
7272
```
7373

74+
### Run with Kubernetes
75+
76+
Kubernetes is more complicated than Docker, but closer to the production
77+
experience for many users. [k8s-manifest.yml](k8s-manifest.yml) creates the
78+
same services, but needs additional configuration first.
79+
80+
First step is to setup your environment. [env.example](env.example) must be
81+
copied to a file name `.env` and updated with `ELASTICSEARCH_URL` and
82+
`OTEL_EXPORTER_OTLP_ENDPOINT` values visible to you Kubernetes deployment.
83+
84+
For example, if you started your Elastic Stack with [k8s-manifest-elastic.yml][k8s-manifest-elastic],
85+
you would update these values:
86+
```
87+
ELASTICSEARCH_URL=http://elasticsearch:9200
88+
OTEL_EXPORTER_OTLP_ENDPOINT=http://apm-server:8200
89+
```
90+
91+
Then, import your `.env` file as a configmap like this:
92+
```bash
93+
kubectl create configmap chatbot-rag-app-env --from-env-file=.env
94+
```
95+
96+
If you are using Vertex AI, make a secret for authentication:
97+
```bash
98+
kubectl create secret generic gcloud-credentials \
99+
--from-file=application_default_credentials.json=$HOME/.config/gcloud/application_default_credentials.json
100+
```
101+
102+
Now that your configuration is applied, create the chatbot-rag-app deployment
103+
and service by applying this manifest:
104+
```bash
105+
kubectl apply -f k8s-manifest.yml
106+
```
107+
108+
Next, block until chatbot-rag-app is available.
109+
```bash
110+
kubectl wait --for=condition=available --timeout=10m deployment/chatbot-rag-app
111+
```
112+
113+
*Note*: The first run may take several minutes to become available.
114+
115+
Next, forward the kibana port:
116+
```bash
117+
kubectl port-forward service/kibana 5601:5601 &
118+
```
119+
120+
Clean up when finished, like this:
121+
122+
```bash
123+
kubectl delete -f k8s-manifest.yml
124+
```
125+
74126
### Run with Python
75127

76128
If you want to run this example with Python, you need to do a few things listed
@@ -198,3 +250,5 @@ docker compose up --build --force-recreate
198250
---
199251
[loader-docs]: https://python.langchain.com/docs/how_to/#document-loaders
200252
[install-es]: https://www.elastic.co/search-labs/tutorials/install-elasticsearch
253+
[docker-compose-elastic]: ../../docker/docker-compose-elastic.yml
254+
[k8s-manifest-elastic]: ../../k8s/k8s-manifest-elastic.yml

example-apps/chatbot-rag-app/env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ FLASK_APP=api/app.py
66
PYTHONUNBUFFERED=1
77

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

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

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
---
2+
apiVersion: apps/v1
3+
kind: Deployment
4+
metadata:
5+
name: chatbot-rag-app
6+
spec:
7+
replicas: 1
8+
selector:
9+
matchLabels:
10+
app: chatbot-rag-app
11+
template:
12+
metadata:
13+
labels:
14+
app: chatbot-rag-app
15+
spec:
16+
# The below will recreate your secret based on the gcloud credentials file
17+
# kubectl create secret generic gcloud-credentials \
18+
# --from-file=application_default_credentials.json=$HOME/.config/gcloud/application_default_credentials.json
19+
volumes:
20+
- name: gcloud-credentials
21+
secret:
22+
secretName: gcloud-credentials
23+
initContainers:
24+
- name: ingest-data
25+
image: &image ghcr.io/elastic/elasticsearch-labs/chatbot-rag-app:latest
26+
imagePullPolicy: &imagePullPolicy IfNotPresent
27+
args: ["flask", "create-index"]
28+
env:
29+
- name: FLASK_APP
30+
value: api/app.py
31+
# This recreates your configmap based on your .env file:
32+
# kubectl create configmap chatbot-rag-app-env --from-env-file=.env
33+
envFrom: &envFrom
34+
- configMapRef:
35+
name: chatbot-rag-app-env
36+
volumeMounts: &volumeMounts
37+
- name: gcloud-credentials
38+
mountPath: /root/.config/application_default_credentials.json
39+
readOnly: true
40+
containers:
41+
- name: api-frontend
42+
image: *image
43+
imagePullPolicy: *imagePullPolicy
44+
ports:
45+
- containerPort: 4000
46+
envFrom: *envFrom
47+
volumeMounts: *volumeMounts
48+
---
49+
apiVersion: v1
50+
kind: Service
51+
metadata:
52+
name: api
53+
spec:
54+
selector:
55+
app: chatbot-rag-app
56+
ports:
57+
- protocol: TCP
58+
port: 4000
59+
targetPort: 4000

k8s/README.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Running your own Elastic Stack with Kubernetes
2+
3+
If you'd like to start Elastic with Kubernetes, you can use the provided
4+
[manifest-elastic.yml](manifest-elastic.yml) file. This starts
5+
Elasticsearch, Kibana, and APM Server and only requires Docker installed.
6+
7+
First, configure the Elastic stack:
8+
```bash
9+
kubectl apply -f manifest-elastic.yml
10+
```
11+
12+
**Note**: For simplicity, this adds an Elastic Stack to the default namespace.
13+
Commands after here are simpler due to this. If you want to choose a different
14+
one, use `kubectl`'s `--namespace` flag!
15+
16+
Next, block until the whole stack is available. First install or changing the
17+
Elastic Stack version can take a long time due to image pulling.
18+
```bash
19+
kubectl wait --for=condition=available --timeout=10m \
20+
deployment/elasticsearch \
21+
deployment/kibana \
22+
deployment/apm-server
23+
```
24+
25+
Next, forward the kibana port:
26+
```bash
27+
kubectl port-forward service/kibana 5601:5601 &
28+
```
29+
30+
Finally, you can view Kibana at http://localhost:5601/app/home#/
31+
32+
If asked for a username and password, use username: elastic and password: elastic.
33+
34+
Clean up when finished, like this:
35+
36+
```bash
37+
kubectl delete -f manifest-elastic.yml
38+
```

k8s/k8s-manifest-elastic.yml

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
apiVersion: v1
2+
kind: Service
3+
metadata:
4+
name: elasticsearch
5+
spec:
6+
ports:
7+
- port: 9200
8+
targetPort: 9200
9+
selector:
10+
app: elasticsearch
11+
12+
---
13+
apiVersion: apps/v1
14+
kind: Deployment
15+
metadata:
16+
name: elasticsearch
17+
spec:
18+
replicas: 1
19+
selector:
20+
matchLabels:
21+
app: elasticsearch
22+
template:
23+
metadata:
24+
labels:
25+
app: elasticsearch
26+
spec:
27+
containers:
28+
- name: elasticsearch
29+
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
30+
ports:
31+
- containerPort: 9200
32+
env:
33+
- name: node.name
34+
value: elasticsearch
35+
- name: cluster.name
36+
value: docker-cluster
37+
- name: discovery.type
38+
value: single-node
39+
- name: ELASTIC_PASSWORD
40+
value: elastic
41+
- name: bootstrap.memory_lock
42+
value: "true"
43+
- name: xpack.security.enabled
44+
value: "true"
45+
- name: xpack.security.http.ssl.enabled
46+
value: "false"
47+
- name: xpack.security.transport.ssl.enabled
48+
value: "false"
49+
- name: xpack.license.self_generated.type
50+
value: trial
51+
# Note that ELSER is recommended to have 2GB, but it is JNI (PyTorch).
52+
# So, ELSER's memory is in addition to the heap and other overhead.
53+
- name: ES_JAVA_OPTS
54+
value: "-Xms2g -Xmx2g"
55+
resources:
56+
limits:
57+
memory: "4.5Gi" # max heap plus overhead and 2GB for ELSER
58+
securityContext:
59+
capabilities:
60+
add: ["CHOWN", "DAC_OVERRIDE", "SETGID", "SETUID"]
61+
drop: ["ALL"]
62+
readinessProbe:
63+
exec:
64+
command: ["sh", "-c", "curl -s http://localhost:9200 | grep -q 'missing authentication credentials'"]
65+
initialDelaySeconds: 5
66+
periodSeconds: 1
67+
timeoutSeconds: 10
68+
failureThreshold: 120
69+
70+
---
71+
apiVersion: v1
72+
kind: Service
73+
metadata:
74+
name: kibana
75+
spec:
76+
ports:
77+
- port: 5601
78+
targetPort: 5601
79+
selector:
80+
app: kibana
81+
82+
---
83+
apiVersion: apps/v1
84+
kind: Deployment
85+
metadata:
86+
name: kibana
87+
spec:
88+
replicas: 1
89+
selector:
90+
matchLabels:
91+
app: kibana
92+
template:
93+
metadata:
94+
labels:
95+
app: kibana
96+
spec:
97+
initContainers:
98+
- name: setup-kibana-system-user
99+
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.2
100+
command:
101+
- bash
102+
- -xc
103+
- |
104+
echo "Setup the kibana_system password";
105+
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;
106+
containers:
107+
- name: kibana
108+
image: docker.elastic.co/kibana/kibana:8.17.2
109+
ports:
110+
- containerPort: 5601
111+
env:
112+
- name: SERVERNAME
113+
value: kibana
114+
- name: ELASTICSEARCH_HOSTS
115+
value: http://elasticsearch:9200
116+
- name: ELASTICSEARCH_USERNAME
117+
value: kibana_system
118+
- name: ELASTICSEARCH_PASSWORD
119+
value: elastic
120+
- name: MONITORING_UI_CONTAINER_ELASTICSEARCH_ENABLED
121+
value: "true"
122+
- name: XPACK_SECURITY_ENCRYPTIONKEY
123+
value: fhjskloppd678ehkdfdlliverpoolfcr
124+
- name: XPACK_ENCRYPTEDSAVEDOBJECTS_ENCRYPTIONKEY
125+
value: fhjskloppd678ehkdfdlliverpoolfcr
126+
- name: SERVER_PUBLICBASEURL
127+
value: http://127.0.0.1:5601
128+
readinessProbe:
129+
httpGet:
130+
path: /api/status
131+
port: 5601
132+
initialDelaySeconds: 10
133+
periodSeconds: 1
134+
failureThreshold: 300
135+
136+
---
137+
apiVersion: v1
138+
kind: Service
139+
metadata:
140+
name: apm-server
141+
spec:
142+
ports:
143+
- port: 8200
144+
targetPort: 8200
145+
selector:
146+
app: apm-server
147+
148+
---
149+
apiVersion: apps/v1
150+
kind: Deployment
151+
metadata:
152+
name: apm-server
153+
spec:
154+
replicas: 1
155+
selector:
156+
matchLabels:
157+
app: apm-server
158+
template:
159+
metadata:
160+
labels:
161+
app: apm-server
162+
spec:
163+
containers:
164+
- name: apm-server
165+
image: docker.elastic.co/apm/apm-server:8.17.2
166+
ports:
167+
- containerPort: 8200
168+
env:
169+
- name: apm-server.kibana.enabled
170+
value: "true"
171+
- name: apm-server.kibana.host
172+
value: http://kibana:5601
173+
- name: apm-server.kibana.username
174+
value: elastic
175+
- name: apm-server.kibana.password
176+
value: elastic
177+
- name: output.elasticsearch.hosts
178+
value: http://elasticsearch:9200
179+
- name: output.elasticsearch.username
180+
value: elastic
181+
- name: output.elasticsearch.password
182+
value: elastic
183+
readinessProbe:
184+
tcpSocket:
185+
port: 8200
186+
initialDelaySeconds: 10
187+
periodSeconds: 1
188+
failureThreshold: 300

0 commit comments

Comments
 (0)