Attu 连接本地 Milvus 双向 TLS(mTLS)实战指南:Docker 证书生成、Compose 配置与连接验证
Attu 连接本地 Milvus 双向 TLS(mTLS)实战指南:Docker 证书生成、Compose 配置与连接验证
本指南基于仓库官方文档 docs/milvus-mtls-local-docker.md 展开,完整演示如何在本地 Docker 环境中为 Milvus Standalone 启用双向 TLS(mutual TLS,简称 mTLS),并让 Attu 通过客户端证书安全接入 Milvus。读完本文,你将掌握自签名 CA 与证书的生成流程、Milvus Docker Compose 的 TLS 配置、Attu 容器的 mTLS 连接预配置方法,以及容器内基于 Milvus Node.js SDK 的端到端验证手段。
适用场景:本地开发与联调验证。文中的自签名证书命令仅适用于本地测试环境,严禁直接照搬到生产环境——生产环境应使用企业 CA 或密钥管理系统签发并轮换的证书。
mTLS 基础:双向 TLS 的信任模型与 Attu 的 TLS 连接配置
Milvus 的 gRPC 服务监听在 19530 端口(另暴露 9091 端口用于健康检查与指标)。在单向 TLS 下,只有客户端校验服务端证书;而 mTLS 是双向校验:
- Milvus(服务端)向 Attu(客户端)出示服务端证书
server.pem,Attu 用 CA 证书ca.pem校验其合法性; - Attu 同时出示客户端证书
client.pem与私钥client.key,Milvus 用同一个 CA 反向校验客户端身份; - 双方共同信任同一个 CA,实现"双向握手、互相验明正身"。
在 Attu 侧,TLS 连接由一组 MILVUS_TLS_* 环境变量控制,这与仓库根目录 README.md 中 "TLS / SSL" 小节(README.md#L177-L194)给出的配置完全一致:
| 环境变量 | 示例值 | 作用 |
|---|---|---|
MILVUS_SSL |
true |
开启 TLS 加密连接 |
MILVUS_TLS_ROOT_CERT_PATH |
/etc/attu/certs/ca.pem |
CA 根证书路径,用于校验 Milvus 服务端 |
MILVUS_TLS_PRIVATE_KEY_PATH |
/etc/attu/certs/client.key |
客户端私钥路径 |
MILVUS_TLS_CERT_CHAIN_PATH |
/etc/attu/certs/client.pem |
客户端证书链路径 |
MILVUS_TLS_SERVER_NAME |
standalone |
主机名校验(SNI),必须匹配服务端证书 SAN 中的 DNS 名称 |
在 Milvus 侧,TLS 强度由 COMMON_SECURITY_TLSMODE 控制。按 Milvus 的安全配置约定:0 表示关闭 TLS,1 表示单向 TLS(仅校验服务端证书),2 表示双向 TLS(服务端、客户端证书都校验)。本指南使用的 COMMON_SECURITY_TLSMODE: 2 正是开启外部 gRPC 双向 TLS 的关键开关。
版本差异提醒:仓库中的 README_CN.md 在 SSL 示例(README_CN.md#L157-L168)中展示的是 Attu v2.6 时代的配置方式,使用 ROOT_CERT_PATH、PRIVATE_KEY_PATH、CERT_CHAIN_PATH、SERVER_NAME 等不带 MILVUS_ 前缀的变量,且证书挂载目录为 /app/tls。本文基于 Attu v3(zilliz/attu:v3.0.0-beta.4),变量名与挂载路径(/etc/attu/certs)均已更新,请勿混用。
1. 生成本地 TLS 证书:CA、Milvus 服务端证书与 Attu 客户端证书
证书将统一生成在 ~/workspace/volumes/milvus/tls 目录下,后续 Milvus 与 Attu 容器都从这个目录挂载证书。
1.1 核心设计:openssl.cnf 与 SAN
证书生成依赖一份 openssl.cnf 配置文件,它同时定义了 CA、服务端、客户端三类证书的扩展属性,关键设计如下:
[ v3_ca ]:CA 证书扩展,声明basicConstraints = critical, CA:true与keyCertSign,使该证书具备签发其他证书的资格;[ v3_req ]:服务端证书扩展,extendedKeyUsage = serverAuth限定用途为服务器认证,并通过subjectAltName = @alt_names引用 SAN 列表;[ v3_client ]:客户端证书扩展,extendedKeyUsage = clientAuth限定用途为客户端认证(mTLS 中服务端校验客户端身份的关键依据);- SAN 列表(
[ alt_names ])包含milvus-standalone、standalone、localhost、milvus、host.docker.internal及回环 IP。其中standalone是必须项:本指南中 Attu 容器通过 Docker 网络 DNS 以standalone:19530访问 Milvus,服务端证书必须覆盖该主机名,否则 TLS 握手阶段的主机名校验会失败。
1.2 完整生成命令
CERT_DIR="$HOME/workspace/volumes/milvus/tls"
mkdir -p "$CERT_DIR"
cat > "$CERT_DIR/openssl.cnf" <<'EOF'
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
req_extensions = v3_req
[ dn ]
C = US
ST = CA
L = Local
O = Milvus Local Test
OU = Attu TLS Test
CN = milvus-standalone
[ v3_ca ]
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
basicConstraints = critical, CA:true
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
[ v3_req ]
basicConstraints = CA:false
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[ alt_names ]
DNS.1 = milvus-standalone
DNS.2 = standalone
DNS.3 = localhost
DNS.4 = milvus
DNS.5 = host.docker.internal
IP.1 = 127.0.0.1
IP.2 = ::1
[ v3_client ]
basicConstraints = CA:false
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = clientAuth
EOF
openssl genrsa -out "$CERT_DIR/ca.key" 4096
openssl req -x509 -new -nodes -key "$CERT_DIR/ca.key" -sha256 -days 3650 \
-subj "/C=US/ST=CA/L=Local/O=Milvus Local Test/OU=Attu TLS Test/CN=Milvus Local Test CA" \
-out "$CERT_DIR/ca.pem" \
-extensions v3_ca -config "$CERT_DIR/openssl.cnf"
openssl genrsa -out "$CERT_DIR/server.key" 2048
openssl req -new -key "$CERT_DIR/server.key" -out "$CERT_DIR/server.csr" \
-subj "/C=US/ST=CA/L=Local/O=Milvus Local Test/OU=Attu TLS Test/CN=milvus-standalone" \
-config "$CERT_DIR/openssl.cnf"
openssl x509 -req -in "$CERT_DIR/server.csr" -CA "$CERT_DIR/ca.pem" -CAkey "$CERT_DIR/ca.key" -CAcreateserial \
-out "$CERT_DIR/server.pem" -days 3650 -sha256 -extensions v3_req -extfile "$CERT_DIR/openssl.cnf"
openssl genrsa -out "$CERT_DIR/client.key" 2048
openssl req -new -key "$CERT_DIR/client.key" -out "$CERT_DIR/client.csr" \
-subj "/C=US/ST=CA/L=Local/O=Milvus Local Test/OU=Attu TLS Test/CN=attu-client" \
-config "$CERT_DIR/openssl.cnf"
openssl x509 -req -in "$CERT_DIR/client.csr" -CA "$CERT_DIR/ca.pem" -CAkey "$CERT_DIR/ca.key" -CAcreateserial \
-out "$CERT_DIR/client.pem" -days 3650 -sha256 -extensions v3_client -extfile "$CERT_DIR/openssl.cnf"
chmod 755 "$CERT_DIR"
chmod 644 "$CERT_DIR"/*.pem "$CERT_DIR"/*.key
ls -l "$CERT_DIR"
命令要点(对应 docs/milvus-mtls-local-docker.md#L19-L89):
- CA 私钥与自签名证书:
genrsa生成 4096 位 CA 私钥,req -x509以v3_ca扩展自签 CA 证书,有效期 3650 天; - 服务端证书:
genrsa生成 2048 位私钥 →req -new产出 CSR →x509 -req用 CA 签发,签发时通过-extensions v3_req -extfile注入 SAN 与serverAuth用途; - 客户端证书:流程与服务端相同,仅
CN=attu-client且签发时使用-extensions v3_client,携带clientAuth用途; - 权限收尾:目录设为
755,证书与私钥设为644,保证容器只读挂载时各用户可读。
1.3 产出文件说明
| 文件 | 角色 |
|---|---|
ca.pem |
CA 证书,Attu 用它校验 Milvus 服务端证书 |
server.pem / server.key |
Milvus 服务端证书与私钥 |
client.pem / client.key |
Attu 客户端证书与私钥(mTLS 双向认证用) |
2. 在 Milvus Docker Compose 中启用 mTLS
2.1 standalone 服务配置
编辑 Milvus 的 docker-compose.yml(本指南使用 ~/workspace/docker-compose.yml),为 standalone 服务开启双向 TLS,并让 Milvus 从挂载在 /var/lib/milvus/tls 下的证书文件读取密钥(完整片段见 docs/milvus-mtls-local-docker.md#L103-L132):
services:
standalone:
container_name: milvus-standalone
image: milvusdb/milvus:v3.0-beta
command: ["milvus", "run", "standalone"]
security_opt:
- seccomp:unconfined
environment:
ETCD_ENDPOINTS: etcd:2379
MINIO_ADDRESS: minio:9000
MQ_TYPE: woodpecker
COMMON_SECURITY_TLSMODE: 2
PROXY_HTTP_ENABLED: "false"
TLS_SERVERPEMPATH: /var/lib/milvus/tls/server.pem
TLS_SERVERKEYPATH: /var/lib/milvus/tls/server.key
TLS_CAPEMPATH: /var/lib/milvus/tls/ca.pem
volumes:
- ${DOCKER_VOLUME_DIRECTORY:-.}/volumes/milvus:/var/lib/milvus
ports:
- "19530:19530"
- "9091:9091"
depends_on:
- etcd
- minio
networks:
default:
name: milvus
2.2 关键配置项说明
| 配置项 | 值 | 说明 |
|---|---|---|
COMMON_SECURITY_TLSMODE |
2 |
开启 Milvus 外部 gRPC 双向 TLS(mTLS) |
TLS_SERVERPEMPATH |
/var/lib/milvus/tls/server.pem |
服务端证书路径 |
TLS_SERVERKEYPATH |
/var/lib/milvus/tls/server.key |
服务端私钥路径 |
TLS_CAPEMPATH |
/var/lib/milvus/tls/ca.pem |
CA 证书路径,用于校验客户端证书 |
PROXY_HTTP_ENABLED |
"false" |
避免开启 TLS 时 Milvus 试图让 proxy HTTP 与外部 gRPC 共用同一端口 |
ETCD_ENDPOINTS / MINIO_ADDRESS / MQ_TYPE |
etcd:2379 / minio:9000 / woodpecker |
Milvus Standalone 的元数据、存储与消息队列依赖(与同 Compose 文件中的 etcd、minio 服务配合) |
networks.default.name |
milvus |
将 Compose 网络命名为 milvus,供后续 Attu 容器加入 |
上述片段是
standalone服务的局部示例,实际使用时请保留原 Compose 文件中的etcd、minio服务定义与其余配置。
2.3 重启并确认状态
docker compose -f ~/workspace/docker-compose.yml up -d
docker compose -f ~/workspace/docker-compose.yml ps
等待 milvus-standalone 进入 healthy 状态后再进行下一步。重启后,Milvus 的 19530 端口对外仅接受 mTLS 握手——没有客户端证书的任何连接都将被拒绝。
3. 拉取 Attu 镜像
docker pull zilliz/attu:v3.0.0-beta.4
本指南经 milvusdb/milvus:v3.0-beta 与 zilliz/attu:v3.0.0-beta.4 组合验证。若需在其他 Milvus 版本上部署,可参考仓库 README.md 的兼容性矩阵(README.md#L210-L219)选择对应 Attu 版本。
4. 启动 Attu 并预配置 mTLS 连接
4.1 docker run 命令
以下命令启动 Attu、以只读方式挂载刚生成的证书,并在首次启动时自动创建一条指向 mTLS Milvus 的默认连接(完整命令见 docs/milvus-mtls-local-docker.md#L160-L175):
docker rm -f attu-tls-test >/dev/null 2>&1 || true && \
docker run -d \
--name attu-tls-test \
--network milvus \
-p 3000:3000 \
-v "$HOME/workspace/volumes/milvus/tls:/etc/attu/certs:ro" \
-e ATTU_DB_PATH=/tmp/attu-tls-test.db \
-e MILVUS_NAME=milvus-mtls-local \
-e MILVUS_ADDRESS=standalone:19530 \
-e MILVUS_SSL=true \
-e MILVUS_TLS_ROOT_CERT_PATH=/etc/attu/certs/ca.pem \
-e MILVUS_TLS_PRIVATE_KEY_PATH=/etc/attu/certs/client.key \
-e MILVUS_TLS_CERT_CHAIN_PATH=/etc/attu/certs/client.pem \
-e MILVUS_TLS_SERVER_NAME=standalone \
zilliz/attu:v3.0.0-beta.4
启动后,浏览器访问 http://localhost:3000 即可打开 Attu。
4.2 参数逐项说明
| 参数 / 变量 | 值 | 说明 |
|---|---|---|
--network milvus |
— | Attu 容器加入 Compose 创建的 milvus 网络,从而可通过 Docker DNS 访问 standalone:19530 |
-p 3000:3000 |
— | 将 Attu Web 界面映射到宿主机 3000 端口 |
-v ...:/etc/attu/certs:ro |
— | 只读挂载证书目录,容器内路径 /etc/attu/certs 与各 MILVUS_TLS_* 变量一一对应 |
ATTU_DB_PATH |
/tmp/attu-tls-test.db |
指定 Attu 连接与偏好数据的 SQLite 数据库位置(测试用临时路径) |
MILVUS_NAME |
milvus-mtls-local |
连接列表中的显示名称 |
MILVUS_ADDRESS |
standalone:19530 |
Milvus gRPC 端点,必须是容器可达的地址(不能用 localhost) |
MILVUS_SSL |
true |
开启 TLS 连接 |
MILVUS_TLS_ROOT_CERT_PATH |
/etc/attu/certs/ca.pem |
CA 根证书 |
MILVUS_TLS_PRIVATE_KEY_PATH |
/etc/attu/certs/client.key |
客户端私钥 |
MILVUS_TLS_CERT_CHAIN_PATH |
/etc/attu/certs/client.pem |
客户端证书链 |
MILVUS_TLS_SERVER_NAME |
standalone |
主机名校验名称,必须与 SAN 中的 DNS 名一致 |
首行 docker rm -f attu-tls-test 用于清理同名旧容器,保证脚本可重复执行。
4.3 持久化 Attu 数据库
默认情况下 Attu 镜像将 SQLite 数据库存放在 /data/attu.db(见 README.md#L46)。如需持久化保存已配置的连接、AI Agent 会话与偏好,可把临时 DB 路径替换为命名卷:
-v attu-data:/data \
-e ATTU_DB_PATH=/data/attu.db \
4.4 理解"默认连接只写入一次"的行为
由 MILVUS_ADDRESS 等变量自动创建的默认连接,每个 Attu SQLite 数据库只会写入一次。如果修改了 TLS 相关环境变量却复用了同一个 ATTU_DB_PATH,Attu 启动时不会自动更新已存在的连接记录。此时有两种处理方式:
- 在 Attu Web 界面中手动编辑已保存的连接(更新证书路径、
SERVER_NAME等字段); - 使用全新的
ATTU_DB_PATH(或删除旧数据库文件)重新启动,让新环境变量生效。
这也是本地反复调试 mTLS 配置时最容易踩的"坑",建议调试期间使用独立、可丢弃的 DB 路径。
5. 可选:在容器内使用 Milvus Node.js SDK 验证 mTLS 连接
attu-tls-test 启动后,可以从容器内部,用镜像自带的 Milvus Node.js SDK 复现完全相同的 mTLS 参数做端到端验证(命令见 docs/milvus-mtls-local-docker.md#L193-L195):
docker exec attu-tls-test sh -lc 'cd /app/.output/server && node -e "import(\"@zilliz/milvus2-sdk-node\").then(async ({ MilvusClient }) => { const client = new MilvusClient({ address: \"standalone:19530\", ssl: true, tls: { rootCertPath: \"/etc/attu/certs/ca.pem\", privateKeyPath: \"/etc/attu/certs/client.key\", certChainPath: \"/etc/attu/certs/client.pem\", serverName: \"standalone\" } }); const result = await client.listCollections(); console.log(JSON.stringify(result, null, 2)); await client.closeConnection(); }).catch((error) => { console.error(error); process.exit(1); })"'
5.1 命令原理拆解
docker exec attu-tls-test sh -lc '...':进入 Attu 容器执行一段 shell 命令;cd /app/.output/server:切入 Attu v3 服务端输出目录。从镜像结构可以推断,该目录是 Attu 服务端构建产物所在位置,@zilliz/milvus2-sdk-node依赖被打包在此,可直接import使用;new MilvusClient({ address, ssl: true, tls: { rootCertPath, privateKeyPath, certChainPath, serverName } }):以与 Attu 容器启动时完全一致的地址、证书三件套与serverName创建客户端;client.listCollections():发起一次真实的 gRPC 请求,触发完整 mTLS 握手;- 失败时
process.exit(1),保证验证结果可被脚本感知。
5.2 期望输出
验证成功的响应中应包含 status.error_code: "Success",并返回当前 Milvus 中的集合列表(含 data 字段与各集合名称)。这一结果同时证明了:
- Milvus 端 mTLS 已生效,且接受了该客户端证书;
- Attu 镜像内挂载的 CA / 客户端证书路径正确、权限可读;
- 容器间
standalone:19530的 Docker DNS 解析与 SAN 主机名校验通过。
故障排查
以下问题与解法均来自文档 docs/milvus-mtls-local-docker.md#L199-L228。
proxy server(http) and external grpc server share the same port, tls mode must be 0
Milvus 在开启 TLS 时检测到 proxy HTTP 与外部 gRPC 共用端口而拒绝启动。解决办法:
- 在 Milvus
standalone服务中设置PROXY_HTTP_ENABLED: "false"; - 或为 proxy HTTP 单独配置一个不同的端口。
主机名校验失败(Hostname verification fails)
确保 MILVUS_TLS_SERVER_NAME 的值与服务端证书 SAN 列表中的某个 DNS 名称完全一致。本指南中 Attu 通过 standalone:19530 连接 Milvus,因此使用 MILVUS_TLS_SERVER_NAME=standalone——它与 openssl.cnf 中 DNS.2 = standalone 对应。若改动连接地址(如换成 milvus-standalone:19530),需同步修改该变量。
mTLS 握手失败
按以下顺序排查:
- 确认 Milvus 以
COMMON_SECURITY_TLSMODE: 2运行(单向 TLS 模式下 Milvus 不会要求客户端证书); - 确认 Attu 三个 mTLS 路径均已设置:
MILVUS_TLS_ROOT_CERT_PATH、MILVUS_TLS_PRIVATE_KEY_PATH、MILVUS_TLS_CERT_CHAIN_PATH; - 确认证书挂载在容器内可读:
docker exec attu-tls-test ls -l /etc/attu/certs
若挂载目录为空或文件缺失,请检查第 1 节证书生成是否成功、docker run 的 -v 源路径是否正确。
Attu 启动但默认连接未变化
这是第 4.4 节所述行为的直接表现:MILVUS_ADDRESS 创建的默认连接仅在每个 Attu 数据库中插入一次。处理方式:
- 删除测试容器并使用全新的
ATTU_DB_PATH(例如删除/tmp/attu-tls-test.db); - 或在 Attu 界面中直接编辑已保存的连接配置。
附:Attu TLS 环境变量速查与延伸阅读
Attu 侧(容器启动时设置):
| 环境变量 | 本指南取值 |
|---|---|
MILVUS_SSL |
true |
MILVUS_ADDRESS |
standalone:19530 |
MILVUS_TLS_ROOT_CERT_PATH |
/etc/attu/certs/ca.pem |
MILVUS_TLS_PRIVATE_KEY_PATH |
/etc/attu/certs/client.key |
MILVUS_TLS_CERT_CHAIN_PATH |
/etc/attu/certs/client.pem |
MILVUS_TLS_SERVER_NAME |
standalone |
Milvus 侧(docker-compose 环境变量): COMMON_SECURITY_TLSMODE=2、TLS_SERVERPEMPATH、TLS_SERVERKEYPATH、TLS_CAPEMPATH、PROXY_HTTP_ENABLED="false"。
延伸阅读(仓库内):
- docs/milvus-mtls-local-docker.md:本文所依据的原始官方指南;
- README.md:Attu v3 功能总览、Docker/K8s 部署与环境变量总表(含 TLS 小节);
- README_CN.md:中文版说明,其中包含 v2.6 时代的 SSL 环境变量示例(注意与 v3 命名差异);
- deploy/attu-k8s-deploy.yaml:Kubernetes 部署清单,展示
MILVUS_ADDRESS在 K8s 场景下的写法; - deploy/nginx/README.md:如需在反向代理后部署 Attu 的参考配置。
以上步骤组合起来,即构成一套完整可复现的"本地 Milvus + mTLS + Attu"验证链路:证书生成 → Compose 开启双向 TLS → Attu 预配置连接 → SDK 端到端验证,适用于开发环境下的安全连接调试与回归测试。