TIL:你可以使用Bash /dev/TCP在不使用curl的情况下发起HTTP请求
我需要检查一个容器是否能够通过内部Docker网络访问另一个容器:对共享网络上的服务进行简单的GET /health请求。显而易见的做法是使用curl http://service:8642/health。但这个应用程序镜像被简化了,没有curl或wget,也没有其他可以用来打开套接字的工具。事实证明,bash可以独立进行HTTP通信。打开到主机和端口的连接,并手动写入请求,完全不需要除了已经存在的shell: bash exec 3<>/dev/tcp/service/8642 printf 'GET /health HTTP/1.1 Host: service Connection: close ' > & 3 cat < & 3 这里的service只是你要连接的主机名。它必须从你运行此命令的地方解析并可达,因此需要首先设置:在你配置的Docker网络上的容器或服务名称,或任何可以解析的DNS名称。将你自己的主机和端口替换进去。这将打印整个响应:状态行、头部、空行和主体。要添加一个头部,比如Authorization: Bearer令牌,在结束请求的空行之前放入另一个以 结束的行: bash exec 3<>/dev/tcp/service/8642 printf 'GET /v1/models HTTP/1.1 Host: service Authorization: Bearer %s Connection: close ' "$API_KEY" > & 3 cat < & 3 让我第一次感到困惑的是,/dev/tcp并不是一个真实的设备文件。在磁盘上没有这样的路径;ls /dev/tcp什么都找不到,从另一个shell中执行cat /dev/tcp/...也只是出现错误。这是一个bash内部处理的重定向。从Bash手册中:/dev/tcp/host/port — 如果host是一个有效的主机名或互联网地址,而port是一个整数端口号或服务名称,bash会尝试打开相应的TCP套接字。之所以选择这些名称,是因为没有真实的Unix系统有/dev/tcp或/dev/udp层次结构,因此不会发生冲突。Bash为你处理DNS查找和connect(2),exec 3<>将套接字的文件描述符(3)交给你以便像其他任何描述符一样进行读写。有一些值得了解的事项:Connection: close头部很重要。没有它服务器在响应后会保持连接打开,这是HTTP/1.1的默认行为,而cat <&3会永远等待未到达的字节。请求服务器关闭连接意味着cat会达到EOF并返回。将调用封装在timeout 6 bash -c '...'中可以保障你的安全。没有TLS。/dev/tcp打开一个原始套接字,因此这仅适用于明文HTTP。对于https,你需要使用openssl s_client,到了那时你可能还不如使用合适的工具。这是一个bash特性,而不是POSIX。dash(Debian的/bin/sh)和zsh没有此功能,因此#!/bin/sh脚本无法使用它。请直接调用bash。这是一个编译时选项,在使用--enable-net-redirections构建bash时开启。大多数主流构建都启用了它,在我所处的基于Debian的镜像中没问题,但Debian曾经禁用它多年,因此在老旧或非常简化的系统上最好先检查一下。对于日常工作,curl仍然是正确的工具。但是在一个故意简小的容器中,无法安装任何东西,这样的方式可以快速完成检查,而不需要添加包。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡