You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

openjdk:8 Docker容器中Java应用UnknownHostException问题求助

问题描述

我有一个自定义Java应用,使用org.apache.commons.io.FileUtils.copyURLToFile从网络下载文件。本地直接运行完全正常,但在Docker容器中运行时抛出UnknownHostException异常,栈跟踪如下:

Exception in thread "main" java.net.UnknownHostException: <MY HOST>
 at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:184)
 at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
 at java.net.Socket.connect(Socket.java:589)
 at sun.net.NetworkClient.doConnect(NetworkClient.java:175)
 at sun.net.www.http.HttpClient.openServer(HttpClient.java:463)
 at sun.net.www.http.HttpClient.openServer(HttpClient.java:558)
 at sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
 at sun.net.www.http.HttpClient.New(HttpClient.java:339)
 at sun.net.www.http.HttpClient.New(HttpClient.java:357)
 at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1220)
 at sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1156)
 at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1050)
 at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:984)
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564)
 at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
 at org.apache.commons.io.FileUtils.copyURLToFile(FileUtils.java:1506)

我已经尝试过以下操作,但都没解决问题:

  • 在Dockerfile中添加EXPOSE 8080EXPOSE 8443并使用-P参数运行容器
  • 设置--hostname=127.0.0.1启动容器
  • 在Dockerfile中添加RUN echo 'hosts: files mdns4_minimal [NOTFOUND=return] dns mdns4' >> /etc/nsswitch.conf配置

环境信息:

  • Docker版本:17.10.0-ce,build f4ffd25
  • Dockerfile基础镜像:openjdk:8
  • 容器内/etc/hosts内容:
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
172.17.0.2 afa4800849e0
可行解决方案

咱们先从核心问题入手——这个UnknownHostException本质是容器里没法解析目标主机的域名,下面是一步步排查和解决的方法:

1. 先确认容器内的DNS能不能正常解析目标主机

先进入容器内部,手动测试一下,排除代码层面的问题:

# 替换成你的容器ID或名称,进入容器
docker exec -it <your-container-id> bash
# 用nslookup解析目标主机
nslookup <MY HOST>
# 或者用ping测试连通性
ping <MY HOST>

如果这俩命令都失败,那问题肯定出在容器的网络/DNS配置上,和Java代码无关。

2. 手动指定Docker容器的DNS服务器

Docker默认会用宿主机的DNS,但有时候宿主机的DNS没法解析你的目标主机。你可以在运行容器时手动指定靠谱的DNS,比如谷歌的8.8.8.8或者国内的114.114.114.114:

docker run --dns 8.8.8.8 --dns 114.114.114.114 <your-image-name>

这一招对大多数DNS解析失败的问题都管用。

3. 检查目标主机是否允许容器网段访问

如果DNS解析正常,但还是连不上,那可能是目标主机的防火墙或者安全组把Docker容器所在的网段给拦了。Docker默认的网桥网段是172.17.0.0/16,你可以在容器里用telnet <MY HOST> <目标端口>测试端口是否开放,或者联系目标主机的管理员确认是否允许这个网段的流量。

4. 改掉错误的hostname设置

你之前用--hostname=127.0.0.1启动容器是个错误操作——127.0.0.1是回环地址,把它设成容器的hostname会导致容器自身的DNS逻辑混乱,直接去掉这个参数就行。

5. 排查Java应用的代理配置

有时候Java会自动读取系统的代理设置,如果容器里有错误的代理配置,就会导致无法连接目标主机。你可以在启动Java应用时加上参数禁用代理:

java -Dhttp.proxyHost= -Dhttp.proxyPort= -Dhttps.proxyHost= -Dhttps.proxyPort= -jar your-app.jar

或者在代码里显式清空代理设置:

System.setProperty("http.proxyHost", "");
System.setProperty("http.proxyPort", "");
System.setProperty("https.proxyHost", "");
System.setProperty("https.proxyPort", "");

6. 考虑升级Docker版本(可选)

你用的Docker 17.10.0-ce版本比较老了,旧版本可能存在一些DNS相关的已知bug,升级到较新的稳定版本(比如20.10.x系列)说不定能解决一些潜在的网络问题。

内容的提问来源于stack exchange,提问作者CGH Tompkins

火山引擎 最新活动