为何认证后调用OpenStack SDK方法会出现连接超时?
看起来你遇到的问题挺典型的——用Java SDK带项目域认证后调用compute或image服务超时,但直接用curl调用API却正常,不带项目认证又能调用但结果不对。我来帮你分析下可能的原因和解决办法:
核心原因推测
最可能的问题是OpenStack Java SDK自动从Keystone服务目录获取的服务端点(Endpoint)不可访问。当你通过SDK完成带项目的认证后,它会自动查询Keystone的服务目录,获取Nova(compute)、Glance(image)等服务的endpoint地址,但如果这些地址是Packstack默认配置的内部IP/localhost,或者在GCP环境下存在网络解析/访问限制,就会导致SDK调用超时;而你手动用curl调用时指定了可访问的endpoint,所以能正常工作。
不带项目认证时,SDK虽然能完成认证,但没有项目上下文,所以无法读取对应项目下的资源(比如规格、镜像),自然返回异常结果。
具体解决步骤
1. 验证Keystone服务目录中的端点是否可访问
先通过curl获取Keystone的服务目录,确认里面的compute和image服务端点是不是你实际能访问的地址:
# 先获取token(用你之前的认证命令) TOKEN=$(curl -s -i -H "Content-Type: application/json" -d '{ "auth": { "identity": { "methods": ["password"], "password": { "user": { "name": "admin", "domain": {"id": "default"}, "password": "pass" } } }, "scope": { "project": { "name": "admin", "domain": {"id": "default"} } } } }' http://host:5000/v3/auth/tokens | grep -i X-Subject-Token | awk '{print $2}') # 调用服务目录接口 curl -H "X-Auth-Token: $TOKEN" http://host:5000/v3/catalog
查看返回结果中compute和image服务的endpoints字段,如果里面的url是http://localhost:8774或者GCP虚拟机的私有IP(而你需要用公网IP/可访问的hostname),那就是SDK获取了错误的端点导致超时。
2. 手动指定SDK的服务端点
既然知道问题出在自动获取的端点上,那我们可以在初始化OSClient时手动指定可访问的服务端点,跳过自动查询目录的步骤:
OSClient.OSClientV3 os = OSFactory.builderV3() .endpoint("http://host:5000/v3") .credentials("admin", "pass", domain) .scopeToProject(project) // 手动指定Nova(compute)服务端点,和你curl时用的地址一致 .computeEndpoint("http://host:8774/v2") // 手动指定Glance(image)服务端点 .imageEndpoint("http://host:9292/v2") .authenticate(); // 现在再调用试试 os.compute().flavors().list();
这样SDK就会直接使用你指定的、已经验证过可访问的端点,不会再去服务目录拿可能不可用的地址。
3. 额外排查点
如果手动指定端点后还是有问题,可以检查这几个地方:
- DNS解析:在运行Java代码的环境下,执行
ping host看看解析的IP是不是正确的。如果解析到错误的地址,需要修改/etc/hosts或者调整DNS配置。 - 服务监听地址:确认Nova-api和Glance-api服务是不是监听在
0.0.0.0(允许所有IP访问),而不是仅localhost。可以用以下命令检查:ss -tulpn | grep 8774 # 检查Nova端口 ss -tulpn | grep 9292 # 检查Glance端口 - 超时时间配置:尝试增加SDK的连接和读取超时时间,避免因网络延迟导致超时:
OSClient.OSClientV3 os = OSFactory.builderV3() .endpoint("http://host:5000/v3") .credentials("admin", "pass", domain) .scopeToProject(project) .computeEndpoint("http://host:8774/v2") .imageEndpoint("http://host:9292/v2") .withConfig(Config.newConfig() .withConnectionTimeout(30000) // 30秒连接超时 .withReadTimeout(30000)) // 30秒读取超时 .authenticate();
内容的提问来源于stack exchange,提问作者Kilmer Luiz Aleluia

