60 通过容器名称访问不到服务 但是通过ip能够访问服务

前言

最近在 弄环境的时候出现了这样的一件事情 

同一个 docker 网络下面有 两个容器, 容器A, 容器B 

容器B 依赖于 容器A, 容器A 开放了一个端口 1234 给其他服务 

然后 容器B 通过 容器A的ip能够访问到 容器A:1234 的服务, 但是通过 容器名称:1234 访问不到服务, 报的是一个 400 吧 ?

当然 最后发现, 这个问题 并不是 docker 容器的问题, 而是 应用服务器上面的校验的问题, 又或者说 http 协议约定如此(但是找了一下规范, 似乎是没得关于 host 的具体的约束, 可能是没找对吧, 或者怎么的), 不过 刚好我踩到雷区了而已 

呵呵 这里就 特此记录一下 

 

 

环境信息 : jdk1.8 + Apache Tomcat/9.0.35

 

 

现象如下

复现 我们就直接使用 普通的服务器了, 不使用 docker 了, 我这里只是在 docker 容器上面碰到这个问题而已 

服务开在 本地, 1234 端口, 我们这里测试 使用域名 "config_server", "config-server"

master:~ jerry$ ping config_server
PING config_server (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.039 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.099 ms
^C
--- config_server ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.039/0.069/0.099/0.030 ms


master:~ jerry$ ping config-server
PING config-server (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=64 time=0.046 ms
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.096 ms
^C
--- config-server ping statistics ---
2 packets transmitted, 2 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 0.046/0.071/0.096/0.025 ms

 

使用 ip + port 访问如下 

 

使用 "config_server" + port 访问如下 

 

使用 "config-server" + port 访问如下 

 

 

第一次错误请求打印日志如下, 后面的错误请求 "没有打印日志" 

 Note: further occurrences of request parsing errors will be logged at DEBUG level.

java.lang.IllegalArgumentException: The character [_] is never valid in a domain name.
	at org.apache.tomcat.util.http.parser.HttpParser$DomainParseState.next(HttpParser.java:974) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.http.parser.HttpParser.readHostDomainName(HttpParser.java:870) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.http.parser.Host.parse(Host.java:71) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.http.parser.Host.parse(Host.java:45) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.AbstractProcessor.parseHost(AbstractProcessor.java:295) ~[tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.http11.Http11Processor.prepareRequest(Http11Processor.java:776) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:349) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_211]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_211]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.35.jar:9.0.35]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_211]

 

 

tomcat解析请求头的Host部分

如下图 解析域的时候, 将要读取的数据是 config_server:1234 

 

然后在校验的时候, 发现了一个 不是 "ALPHA"字符, 不是 "NUMERIC", 不是 ".", 不是 ":", 不是 "-" 的字符 "_" 

校验抛出了异常 

 

我们再来看一下 "ALPHA", "NUMERIC" 的定义 

 

 

tomcat有错误日志, 但是只出现了一次

另外还有一件事情就是, 在问题解决的最后, 发现了一件事情, 对于 这一次 400 的请求, tomcat 服务器 是打印了日志的 

但是 后面的 这种 400 请求就没有再打印日志了 

呵呵 我们在这里 也看一下  

发现了吗, 打印日志也有这么多技巧(花样) 

 

config 的配置是一个 组合的配置, "一定的条件"下以 INFO 打印日志, 后面以 DEBUG 打印日志 

 

我们再来看一下 logAtInfo 的这个 "一定的条件" 是什么 

哈哈哈 一招奇淫技巧, 记录了一个 lastInfoTime, "第一次"(不完全线程安全) 打印 info 打印, 后面的 返回 false 

这里作者写的注释 也非常有意思, 很棒, 但是 感觉方法名字 不能更好体现出在这个场景下的意义吧 

 

然后 因为我这里默认是 没有启动 DEBUG 级别的日志, 所以 一次 INFO 的异常打印了之后, 后面对于 这个解析参数的问题, 就没有再 打印出日志了  

 

另外看一下 这种日志技巧 还在那里使用了 

在解析 域, cookie, 请求参数的时候, 都是用到了 

 

 

对于 ALPHA, NUMERIC, 规范中的介绍 

对于主机名的介绍, 似乎是 没有太细的限定 

refer : http://web-sniffer.net/rfc/rfc2616.html#section-3.2.2

2.2 Basic Rules

The following rules are used throughout this specification to
describe basic parsing constructs. The US-ASCII coded character set
is defined by ANSI X3.4-1986 [21].

OCTET          = <any 8-bit sequence of data>

CHAR           = <any US-ASCII character (octets 0 - 127)>
UPALPHA        = <any US-ASCII uppercase letter "A".."Z">
LOALPHA        = <any US-ASCII lowercase letter "a".."z">

ALPHA          = UPALPHA | LOALPHA
DIGIT          = <any US-ASCII digit "0".."9">
CTL            = <any US-ASCII control character
(octets 0 - 31) and DEL (127)>
CR             = <US-ASCII CR, carriage return (13)>
LF             = <US-ASCII LF, linefeed (10)>

SP             = <US-ASCII SP, space (32)>
HT             = <US-ASCII HT, horizontal-tab (9)>
<">            = <US-ASCII double-quote mark (34)>

 

 

问题排查花絮

1. 尝试访问 docker_node_1 上面的 8080 服务, ip 能够访问, 域名访问不了 

# 通过 容器名称 访问不到, 但是通过 ip 能够访问
~ # java -cp . DockerNetworkConsume http://docker_node_1:8080/api/users/
Exception in thread "main" java.io.IOException: Server returned HTTP response code: 400 for URL: http://docker_node_1:8080/api/users/
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1894)
	at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
	at DockerNetworkConsume.main(DockerNetworkConsume.java:26)
~ # java -cp . DockerNetworkConsume http://172.25.0.3:8080/api/users/
{"status":401,"body":null,"message":"无效的访问令牌","developerMessage":"com.spring.framework.common.exception.ExceptionHelper.lambda$invalidTokenSupplier$3(ExceptionHelper.java:54)","errorFields":null}

 

2. 访问 independent 是通的, 但是同样的配置 docker_project_independent, 就是 400 

## docker_project_module01 访问 independent, docker_project_independent, docker_config_server 的不同
[root@t3420 independent]# docker exec -it docker_project_module01 /bin/sh
/ # wget http://independent:9999/
Connecting to independent:9999 (172.25.0.9:9999)
wget: server returned error: HTTP/1.1 404
/ # wget http://docker_project_independent:9999/
Connecting to docker_project_independent:9999 (172.25.0.17:9999)
wget: server returned error: HTTP/1.1 400
/ # wget http://docker_config_server:9999/
Connecting to docker_config_server:9999 (172.25.0.22:9999)
wget: can't connect to remote host (172.25.0.22): Connection refused
/ # wget http://docker_config_server:1234
Connecting to docker_config_server:1234 (172.25.0.22:1234)
wget: server returned error: HTTP/1.1 400

 

3. 查看 config_server 上面的具体的日志  

-- 通过 容器名称 访问不到, 但是通过 ip 能够访问 的问题始末
[root@t3420 bak]# docker logs -f docker_project_module01

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.3.0.RELEASE)

2020-07-08 15:00:36.419  INFO 1 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Fetching config from server at : http://docker_config_server:1234
2020-07-08 15:00:36.628  WARN 1 --- [           main] c.c.c.ConfigServicePropertySourceLocator : Could not locate PropertySource: 400 : [<!doctype html><html lang="en"><head><title>HTTP Status 400 – Bad Request</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;... (435 bytes)]

-- 访问的服务 docker_config_server 中的日志信息, 只出现了一次, 呵呵 最开始没看到, 我的天
java.lang.IllegalArgumentException: The character [_] is never valid in a domain name.
	at org.apache.tomcat.util.http.parser.HttpParser$DomainParseState.next(HttpParser.java:974) ~[tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.tomcat.util.http.parser.HttpParser.readHostDomainName(HttpParser.java:870) ~[tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.tomcat.util.http.parser.Host.parse(Host.java:71) ~[tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.tomcat.util.http.parser.Host.parse(Host.java:45) ~[tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.coyote.AbstractProcessor.parseHost(AbstractProcessor.java:295) ~[tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.coyote.http11.Http11Processor.prepareRequest(Http11Processor.java:776) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:349) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1590) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) [na:1.8.0_201]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [na:1.8.0_201]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-9.0.35.jar!/:9.0.35]
	at java.lang.Thread.run(Thread.java:748) [na:1.8.0_201]

 

4. 还有一些其他的网络测试这里就不贴出来了, 不过写了一个 get 访问网络的脚本 

/**
 * DockerNetworkConsume
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2020-07-08 11:08
 */
public class DockerNetworkConsume {

  // DockerNetworkConsume
  // eg : java -cp . DockerNetworkConsume http://localhost:1234/independent/dev
  // eg : java -cp . DockerNetworkConsume http://10.0.0.129:1234/independent/dev
  // eg : java -cp . DockerNetworkConsume http://docker_config_server:1234/independent/dev
  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      throw new RuntimeException(" please input url you want to visit ");
    }

//    args = new String[]{"http://localhost:1234/independent/dev"};
    URL url = new URL(args[0]);

    URLConnection connection = url.openConnection();
    InputStream is = connection.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));

    String line = null;
    while ((line = reader.readLine()) != null) {
      System.out.println(line);
    }

  }

}

 

 

完 

 

 

参考

http://web-sniffer.net/rfc/rfc2616.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值