Skip to content

Commit 88fff16

Browse files
shanshanyingweicaoweiclaude
authored
Support/qdrant configurable auth and tls backups (#2949)
Co-authored-by: Wei Cao <cyg.cao@gmail.com> Co-authored-by: wei <wei@xiaobuou.local> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent e959910 commit 88fff16

16 files changed

Lines changed: 645 additions & 232 deletions

addons/qdrant/README.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,60 @@ spec:
107107
kubectl apply -f examples/qdrant/cluster.yaml
108108
```
109109

110+
#### Create with API key
111+
112+
Create a Qdrant cluster with `service.api_key` set through `QDRANT__SERVICE__API_KEY`:
113+
114+
```bash
115+
kubectl apply -f examples/qdrant/cluster-with-api-key.yaml
116+
```
117+
118+
You can configure the Qdrant API key in either of these ways:
119+
120+
```yaml
121+
# Qdrant environment variable. This has higher priority than config.yaml.
122+
env:
123+
- name: QDRANT__SERVICE__API_KEY
124+
value: my-key-123456
125+
```
126+
127+
```yaml
128+
# KubeBlocks configuration variable. This renders service.api_key into config.yaml.
129+
configs:
130+
- name: qdrant-config-template
131+
variables:
132+
service_api_key: my-key-123456
133+
```
134+
135+
Qdrant environment variables have higher priority than `config.yaml`. Backup and restore scripts follow the same order: they use `QDRANT__SERVICE__API_KEY` when that env is present in the action container, then fall back to `service.api_key` from the mounted `config.yaml`.
136+
137+
#### Configure telemetry
138+
139+
Qdrant telemetry is disabled by default in `config.yaml`. You can override it through the KubeBlocks configuration variables API:
140+
141+
```yaml
142+
configs:
143+
- name: qdrant-config-template
144+
variables:
145+
telemetry_disabled: "false"
146+
```
147+
148+
#### Create with TLS
149+
150+
Create a Qdrant cluster with TLS enabled by the KubeBlocks Cluster TLS API:
151+
152+
```bash
153+
kubectl apply -f examples/qdrant/cluster-with-tls.yaml
154+
```
155+
156+
#### Create with TLS and API key
157+
158+
Create a Qdrant cluster with TLS enabled and `service.api_key` set through `QDRANT__SERVICE__API_KEY`:
159+
160+
```bash
161+
kubectl apply -f examples/qdrant/cluster-with-tls-and-api-key.yaml
162+
```
163+
110164
### Horizontal scaling
111165

112166
> [!Important]
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
storage:
2+
# Where to store all the data
3+
storage_path: ./storage
4+
5+
# Where to store snapshots
6+
snapshots_path: ./storage/snapshots
7+
8+
# If true - point's payload will not be stored in memory.
9+
# It will be read from the disk every time it is requested.
10+
# This setting saves RAM by (slightly) increasing the response time.
11+
# Note: those payload values that are involved in filtering and are indexed - remain in RAM.
12+
on_disk_payload: true
13+
14+
# Write-ahead-log related configuration
15+
wal:
16+
# Size of a single WAL segment
17+
wal_capacity_mb: 32
18+
19+
# Number of WAL segments to create ahead of actual data requirement
20+
wal_segments_ahead: 0
21+
22+
# Normal node - receives all updates and answers all queries
23+
node_type: "Normal"
24+
25+
# Listener node - receives all updates, but does not answer search/read queries
26+
# Useful for setting up a dedicated backup node
27+
# node_type: "Listener"
28+
29+
performance:
30+
# Number of parallel threads used for search operations. If 0 - auto selection.
31+
max_search_threads: 0
32+
# Max total number of threads, which can be used for running optimization processes across all collections.
33+
# Note: Each optimization thread will also use `max_indexing_threads` for index building.
34+
# So total number of threads used for optimization will be `max_optimization_threads * max_indexing_threads`
35+
max_optimization_threads: 1
36+
37+
optimizers:
38+
# The minimal fraction of deleted vectors in a segment, required to perform segment optimization
39+
deleted_threshold: 0.2
40+
41+
# The minimal number of vectors in a segment, required to perform segment optimization
42+
vacuum_min_vector_number: 1000
43+
44+
# Target amount of segments optimizer will try to keep.
45+
# Real amount of segments may vary depending on multiple parameters:
46+
# - Amount of stored points
47+
# - Current write RPS
48+
#
49+
# It is recommended to select default number of segments as a factor of the number of search threads,
50+
# so that each segment would be handled evenly by one of the threads.
51+
# If `default_segment_number = 0`, will be automatically selected by the number of available CPUs
52+
default_segment_number: 0
53+
54+
# Do not create segments larger this size (in KiloBytes).
55+
# Large segments might require disproportionately long indexation times,
56+
# therefore it makes sense to limit the size of segments.
57+
#
58+
# If indexation speed have more priority for your - make this parameter lower.
59+
# If search speed is more important - make this parameter higher.
60+
# Note: 1Kb = 1 vector of size 256
61+
# If not set, will be automatically selected considering the number of available CPUs.
62+
max_segment_size_kb: null
63+
64+
# Maximum size (in KiloBytes) of vectors to store in-memory per segment.
65+
# Segments larger than this threshold will be stored as read-only memmaped file.
66+
# To enable memmap storage, lower the threshold
67+
# Note: 1Kb = 1 vector of size 256
68+
# If not set, mmap will not be used.
69+
memmap_threshold_kb: null
70+
71+
# Maximum size (in KiloBytes) of vectors allowed for plain index.
72+
# Default value based on https://github.com/google-research/google-research/blob/master/scann/docs/algorithms.md
73+
# Note: 1Kb = 1 vector of size 256
74+
indexing_threshold_kb: 20000
75+
76+
# Interval between forced flushes.
77+
flush_interval_sec: 5
78+
79+
# Max number of threads, which can be used for optimization per collection.
80+
# Note: Each optimization thread will also use `max_indexing_threads` for index building.
81+
# So total number of threads used for optimization will be `max_optimization_threads * max_indexing_threads`
82+
# If `max_optimization_threads = 0`, optimization will be disabled.
83+
max_optimization_threads: 1
84+
85+
# Default parameters of HNSW Index. Could be overridden for each collection individually
86+
hnsw_index:
87+
# Number of edges per node in the index graph. Larger the value - more accurate the search, more space required.
88+
m: 16
89+
# Number of neighbours to consider during the index building. Larger the value - more accurate the search, more time required to build index.
90+
ef_construct: 100
91+
# Minimal size (in KiloBytes) of vectors for additional payload-based indexing.
92+
# If payload chunk is smaller than `full_scan_threshold_kb` additional indexing won't be used -
93+
# in this case full-scan search should be preferred by query planner and additional indexing is not required.
94+
# Note: 1Kb = 1 vector of size 256
95+
full_scan_threshold_kb: 10000
96+
# Number of parallel threads used for background index building. If 0 - auto selection.
97+
max_indexing_threads: 0
98+
# Store HNSW index on disk. If set to false, index will be stored in RAM. Default: false
99+
on_disk: false
100+
# Custom M param for hnsw graph built for payload index. If not set, default M will be used.
101+
payload_m: null
102+
103+
service:
104+
105+
# Maximum size of POST data in a single request in megabytes
106+
max_request_size_mb: 32
107+
108+
# Number of parallel workers used for serving the api. If 0 - equal to the number of available cores.
109+
# If missing - Same as storage.max_search_threads
110+
max_workers: 0
111+
112+
# Host to bind the service on
113+
host: 0.0.0.0
114+
115+
# HTTP port to bind the service on
116+
http_port: 6333
117+
118+
# gRPC port to bind the service on.
119+
# If `null` - gRPC is disabled. Default: null
120+
grpc_port: 6334
121+
# Uncomment to enable gRPC:
122+
# grpc_port: 6334
123+
124+
# Enable CORS headers in REST API.
125+
# If enabled, browsers would be allowed to query REST endpoints regardless of query origin.
126+
# More info: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
127+
# Default: true
128+
enable_cors: true
129+
130+
{{- if eq (index . "TLS_ENABLED") "true" }}
131+
enable_tls: true
132+
{{- end }}
133+
134+
{{- if index . "service_api_key" }}
135+
api_key: {{ .service_api_key | quote }}
136+
{{- end }}
137+
138+
cluster:
139+
# Use `enabled: true` to run Qdrant in distributed deployment mode
140+
enabled: true
141+
142+
# Configuration of the inter-cluster communication
143+
p2p:
144+
# Port for internal communication between peers
145+
port: 6335
146+
{{- if eq (index . "TLS_ENABLED") "true" }}
147+
enable_tls: true
148+
{{- end }}
149+
150+
# Configuration related to distributed consensus algorithm
151+
consensus:
152+
# How frequently peers should ping each other.
153+
# Setting this parameter to lower value will allow consensus
154+
# to detect disconnected nodes earlier, but too frequent
155+
# tick period may create significant network and CPU overhead.
156+
# We encourage you NOT to change this parameter unless you know what you are doing.
157+
tick_period_ms: 100
158+
159+
{{- if eq (index . "TLS_ENABLED") "true" }}
160+
tls:
161+
cert: {{ .TLS_MOUNT_PATH }}/tls.crt
162+
key: {{ .TLS_MOUNT_PATH }}/tls.key
163+
ca_cert: {{ .TLS_MOUNT_PATH }}/ca.crt
164+
{{- end }}
165+
166+
# Set to true to prevent service from sending usage statistics to the developers.
167+
# Read more: https://qdrant.tech/documentation/telemetry
168+
{{- if hasKey . "telemetry_disabled" }}
169+
telemetry_disabled: {{ index . "telemetry_disabled" }}
170+
{{- else }}
171+
telemetry_disabled: true
172+
{{- end }}

addons/qdrant/scripts/qdrant-backup.sh

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ set -o pipefail
55
export PATH="$PATH:$DP_DATASAFED_BIN_PATH"
66
export DATASAFED_BACKEND_BASE_PATH="$DP_BACKUP_BASE_PATH"
77

8+
QDRANT_COMMON_FILE="${QDRANT_COMMON_FILE:-/qdrant/scripts/common.sh}"
9+
if [ -r "$QDRANT_COMMON_FILE" ]; then
10+
# shellcheck disable=SC1090
11+
. "$QDRANT_COMMON_FILE"
12+
fi
13+
814
# if the script exits with a non-zero exit code, touch a file to indicate that the backup failed,
915
# the sync progress container will check this file and exit if it exists
1016
function handle_exit() {
@@ -31,24 +37,33 @@ else
3137
CURL_TLS=""
3238
fi
3339

34-
collectionRes=$(curl $CURL_TLS ${endpoint}/collections)
35-
collections=$(echo ${collectionRes} | jq -r '.result.collections[].name')
36-
if [ -z $collections ]; then
40+
collectionRes=$(qdrant_curl -sS -f ${endpoint}/collections)
41+
if ! echo "${collectionRes}" | jq -e . >/dev/null 2>&1; then
42+
echo "failed to parse collections response: ${collectionRes}"
43+
exit 1
44+
fi
45+
collections=$(echo "${collectionRes}" | jq -r '.result.collections[].name // empty')
46+
if [ -z "$collections" ]; then
3747
save_backup_size
3848
exit 0
3949
fi
4050
# snapshot all collections
4151
for c in ${collections}; do
4252
echo "INFO: start to snapshot collection ${c}..."
43-
snapshot=$(curl $CURL_TLS -XPOST ${endpoint}/collections/${c}/snapshots)
44-
status=$(echo ${snapshot} | jq '.status')
45-
if [ "${status}" != "ok" ] && [ "${status}" != "\"ok\"" ]; then
53+
snapshot=$(qdrant_curl -sS -f -XPOST ${endpoint}/collections/${c}/snapshots)
54+
status=$(echo "${snapshot}" | jq -r '.status // empty')
55+
if [ "${status}" != "ok" ]; then
4656
echo "backup failed, status: ${status}"
57+
echo "response: ${snapshot}"
58+
exit 1
59+
fi
60+
name=$(echo "${snapshot}" | jq -r '.result.name // empty')
61+
if [ -z "$name" ]; then
62+
echo "backup failed, snapshot name is empty"
4763
exit 1
4864
fi
49-
name=$(echo ${snapshot} | jq -r '.result.name')
50-
curl $CURL_TLS -v --fail-with-body ${endpoint}/collections/${c}/snapshots/${name} | datasafed push - "/${c}.snapshot"
51-
curl $CURL_TLS -XDELETE ${endpoint}/collections/${c}/snapshots/${name}
65+
qdrant_curl -sS -f -L ${endpoint}/collections/${c}/snapshots/${name} | datasafed push - "/${c}.snapshot"
66+
qdrant_curl -sS -f -XDELETE ${endpoint}/collections/${c}/snapshots/${name} >/dev/null
5267
echo "INFO: snapshot collection ${c} successfully."
5368
done
5469
save_backup_size
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# shellcheck shell=sh
2+
3+
qdrant_config_service_api_key() {
4+
config_file="${QDRANT_CONFIG_FILE:-/qdrant/config/config.yaml}"
5+
[ -r "$config_file" ] || return 0
6+
7+
awk '
8+
/^[[:space:]]*#/ { next }
9+
/^[^[:space:]][^:]*:/ {
10+
section = $1
11+
sub(/:.*/, "", section)
12+
next
13+
}
14+
section == "service" {
15+
line = $0
16+
sub(/[[:space:]]+#.*/, "", line)
17+
if (line ~ /^[[:space:]]*api_key[[:space:]]*:/) {
18+
sub(/^[[:space:]]*api_key[[:space:]]*:[[:space:]]*/, "", line)
19+
gsub(/^[[:space:]]+|[[:space:]]+$/, "", line)
20+
gsub(/^["'\''"]|["'\''"]$/, "", line)
21+
if (line != "" && line != "null") {
22+
print line
23+
exit
24+
}
25+
}
26+
}
27+
' "$config_file"
28+
}
29+
30+
qdrant_effective_api_key() {
31+
if [ -n "${QDRANT__SERVICE__API_KEY:-}" ]; then
32+
printf "%s" "$QDRANT__SERVICE__API_KEY"
33+
return
34+
fi
35+
qdrant_config_service_api_key
36+
}
37+
38+
qdrant_curl() {
39+
qdrant_xtrace_enabled=0
40+
case "$-" in
41+
*x*)
42+
qdrant_xtrace_enabled=1
43+
set +x
44+
;;
45+
esac
46+
47+
api_key="$(qdrant_effective_api_key)"
48+
if [ -n "$api_key" ]; then
49+
"${QDRANT_CURL_BIN:-curl}" ${CURL_TLS:-} -H "api-key: ${api_key}" "$@"
50+
else
51+
"${QDRANT_CURL_BIN:-curl}" ${CURL_TLS:-} "$@"
52+
fi
53+
qdrant_curl_rc=$?
54+
55+
if [ "$qdrant_xtrace_enabled" = "1" ]; then
56+
set -x
57+
fi
58+
59+
return "$qdrant_curl_rc"
60+
}

0 commit comments

Comments
 (0)