我正在尝试在同一设备上运行的两个容器之间打开 TCP 连接。
10.5.0.1
,容器 A 的地址为 10.5.0.5
network_mode: host
),是 TCP 通信的服务器。一些重要的注意事项:
我无法更改容器 B 的配置以在与容器 A 相同的桥接网络上运行。这是由于集成了在容器 B 上运行的第 3 方 SDK。该软件连接到主机网络上的雷达设备使用 SDK 并向雷达设备发送命令/读取状态。
之前,每当在我们的软件中调用 SDK 函数时,将容器 B 移动到桥接网络的尝试都会导致错误代码。 SDK 制造商的技术支持告诉我们,这是由于“SDK 试图阻止 MitM 攻击”,当他们尝试在桥接网络上运行的容器中使用他们的软件时,他们证实了我们的结果。他们的建议是运行到主机网络上的容器。
我也无法更改容器 A 的网络配置以在主机网络上运行,因为我们不想将此容器的全部内容公开到主机网络。这样做可以解决我的问题,但我的团队的其他成员宁愿避免此选项,因为此实施存在网络安全问题(此部署的一个有效问题)。
容器 A 的 Docker 撰写部分:
containerA:
build: ./containerA
restart: 'always'
privileged: true
environment:
// ....
labels:
// ....
volumes:
// ....
ports:
// ....
- 10299:10299
networks:
bridge:
ipv4_address: 10.5.0.5
容器 A 上的套接字打开:
// HOST = "10.5.0.1"
// PORT = 10299
int sd;
struct sockaddr_in server;
struct in_addr ipv4addr;
struct hostent *hp;
printf("Creating socket\r\n");
sd = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
printf("Getting host by addr\r\n");
inet_aton(HOST, &server.sin_addr);
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
printf("Connecting\r\n");
connect(sd, (const sockaddr *)&server, sizeof(server));
容器 B 的 Docker 撰写部分:
containerB:
build: ./containerB
restart: 'always'
privileged: true
environment:
// ....
network_mode: 'host'
ports:
- 10299:10299 // Author's note, fairly certain I don't need to call this when in host mode
容器B端Socket监听:
int opt = 1;
struct sockaddr_in addr;
socklen_t addrLen = sizeof(addr);
printf("Begin socket initialization\r\n");
int sockid = socket(AF_INET, SOCK_STREAM, 0);
if (sockid < 0)
{
// Failed to open a socket
printf("Failed to open socket; ret = %d\r\n", sockid);
return (ret = sockid);
}
int ret2 = setsockopt(sockid, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
if (ret2 < 0)
{
// Failed to configure socket
printf("Failed to configure socket; ret = %d\r\n", ret2);
return (ret = ret2);
}
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
//inet_aton(TCP_ADDRESS, &addr.sin_addr);
addr.sin_port = htons(targetPort);
ret2 = bind(sockid, (struct sockaddr*)&addr, sizeof(addr));
if (ret2 < 0)
{
printf("Failed to bind socket; ret = %d, errno = %d\r\n", ret2, errno);
return (ret = ret2);
}
// Wait for socket connection to be established
printf("Listening on socket\r\n");
ret2 = listen(sockid, 3);
if (ret2 < 0)
{
printf("Failed to listen for connection; ret = %d\r\n", ret2);
return (ret = ret2);
}
printf("Accepting connection\r\n");
int newsockid = accept(sockid, (struct sockaddr*)&addr, &addrLen);
if (newsockid < 0)
{
printf("Failed to accept socket connection; ret = %d\r\n", ret2);
return (ret = newsockid);
}
我尝试让客户端节点(容器 A)以套接字连接为目标
10.5.0.1
,但容器 B 从未继续侦听套接字。此外,我还尝试让服务器节点(容器 B)也侦听指定端口上的任何连接,但也无法看到来自容器 A 的传入连接。
我已经确认,当服务器节点在我的系统本地容器上运行并且两端都连接到本地主机时,套接字代码适用于两端,因此套接字初始化似乎是正确的。当我尝试让客户端来自“桥接网络”容器并且服务器在“主机网络”容器上运行时,问题就出现了。
问题: 是否可以打开从“桥接网络”容器到“主机网络”容器的套接字连接?
我发现这个问题导致我的套接字失败。显然,我在打开初始套接字时遇到了以前没有看到的 BlockingIOError 。 Python 套接字客户端使用
socket.create_connection()
并将超时值设置为 False,导致套接字以非阻塞模式打开。
我在从 ContainerA 运行的 python 解释器中尝试了这两个版本,这就是我得到的:
>>> node._socket.close()
>>> node._socket = socket.create_connection(('10.5.0.1', 10299), None)
>>> node._socket.sendall(message.encode())
>>> node._connect_timeout
False
>>> node._socket.close()
>>> node._socket = socket.create_connection(('10.5.0.1', 10299), False)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.8/socket.py", line 808, in create_connection
raise err
File "/usr/lib/python3.8/socket.py", line 796, in create_connection
sock.connect(sa)
BlockingIOError: [Errno 115] Operation now in progress
此外,我删除了 ContainerB 上的端口绑定,并将设置
pid: 'host'
添加到 docker-compose 文件中。 pid
设置是为了解决我在 ContainerB 中使用的第 3 方 SDK 的问题。我不清楚端口绑定是否也导致了该问题。
containerB:
build: ./containerB
restart: 'always'
privileged: true
environment:
// ....
network_mode: 'host'
pid: 'host'
将“pid: 'host'”添加到 docker compose 文件允许我使用的 SDK 与目标设备正确通信。