[](https://res.cloudinary.com/practicaldev/image/fetch/s--8uLHvwmg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2Ao8EmyG8hOa0C7gi4zRifKQ.png)

来源

2016 年,我构建了一个依赖 Docker 提供 VPN 部署服务的应用程序。部分自动化使我能够自动生成并向将 VPN 容器部署到其服务器的用户提供服务,使他们能够通过一次性 UI 下载他们的配置,以确保只能下载一个副本。

我不会详细介绍应用程序本身,而只关注这一功能(它将通知容器的生命周期,从而通知服务),但是,对于好奇的,您可以在此处查看应用程序组件:

https://github.com/jmarhee/dockvpn

就像标题所暗示的那样,配置将主要依赖于打开和终止与配置提供程序服务的连接的 shell 脚本,它使用 socatLinux utility 来完成此操作。这是一个非常可配置的实用程序,并且通过 Socket 接口提供了很多选项:

http://man7.org/linux/man-pages/man7/socket.7.html

在这个例子中,与我们相关的是 SO_REUSEADDR 和 Linux fork() 系统调用来创建一个新的子进程(我们不会使用它,但我会解释为什么在其他情况下你可能会这样做——如果你'还不熟悉类 Unix 系统中的进程管理)。

通常,要使用 socat 中继数据,您可以使用如下命令:

socat -d -d TCP-LISTEN:8080,reuseaddr,fork EXEC:"cat config"

进入全屏模式 退出全屏模式

在我的示例中,config 是您要从磁盘读取到响应中的文件。

在您的 TCP-LISTEN 块中(您可以使用许多其他绑定选项,但是因为这将主要使用覆盖网络为 Docker 容器之间的流量提供服务,并且单一使用文件的入口本身将在前端应用程序上使用 TLS,我使用的 TCP 端口),您指定端口(8080),然后重用addr(这对我们来说是可选的,因为我们只打算使用该接口一次而无需重试——这主要确保我们可以为该服务绑定本地地址),以及然后 fork,在请求完成时创建一个新进程。

[](https://res.cloudinary.com/practicaldev/image/fetch/s--6_6QXifL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1. medium.com/max/1024/0%2AfvlorB-Ihh_U0Nwp.png)

然后我们可以发出请求,看到 socat 进程仍然绑定到端口 8080:

[](https://res.cloudinary.com/practicaldev/image/fetch/s--QdnwpEjG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/0%2AfruqrDJrYcjZvFot.png)

[](https://res.cloudinary.com/practicaldev/image/fetch/s--2iM1cIK2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1. medium.com/max/1024/0%2AExq0awD5mONqiVQM.png)

好的,很好,对吧?因此,在我们的例子中,我们_dont_希望能够再次请求此数据,正如您在下面的输出中看到的那样,我们的进程退出 0,创建了一个子进程,并再次绑定到端口 8080 以获得新的联系:

...
2018/07/07 04:35:22 socat[464] N exiting with status 0
2018/07/07 04:35:22 socat[461] N childdied(): handling signal 17
2018/07/07 04:35:23 socat[461] N accepting connection from AF=2 100.115.92.193:60238 on AF=2 100.115.92.197:8080
2018/07/07 04:35:23 socat[461] N forked off child process 466
2018/07/07 04:35:23 socat[461] N listening on AF=2 0.0.0.0:8080
...

进入全屏模式 退出全屏模式

所以让我们在服务器端修改我们的命令,如下所示:

TCP-LISTEN:8080,reuseaddr

进入全屏模式 退出全屏模式

不使用 fork,以防止新进程替换已完成的请求运行:

[](https://res.cloudinary.com/practicaldev/image/fetch/s--jnWOtYYA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/0%2Apk4Eq_nnF-G8JEOK.png)

你会看到这个进程退出后,每个后续请求都失败了:

[](https://res.cloudinary.com/practicaldev/image/fetch/s--2M1QZtoQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/0%2A5ieMc8-F9UZF99wK.png)

就像我说的,这个 TCP 端口只会暴露在 Docker 容器网络覆盖上,前端服务会发出这样的请求:

get "/config" do
 response = HTTParty.get("http://dockvpn_serveconfig_1:8080")
 tempfile = Tempfile.new('client.ovpn')
 File.open(tempfile.path,'w') do |f|
 f.write response.body
 end
 send_file(tempfile)
end

进入全屏模式 退出全屏模式

创建配置的可下载副本并将其提供给 UI,并使用我们的 socat 命令作为 _servconfig 中的后端,只有第一个请求会成功。

因此,回到我们的 serveconfig 服务,我们将创建一个脚本 entrypoint.sh,如下所示:

#!/bin/bash

socat -d -d \
 TCP-LISTEN:8080,reuseaddr \
 EXEC:'cat client.http' \
 2\>\> http8080.log

进入全屏模式 退出全屏模式

和一个像这样的 Dockerfile:

FROM ubuntu:trusty

RUN apt-get update ; \
 apt-get install -y socat

COPY entrypoint.sh serveconfig

ENTRYPOINT serveconfig

进入全屏模式 退出全屏模式

我们这里的 ENTRYPOINT 实际上只是我们的 socat 命令,因此一旦进程退出,容器就会停止,并且在 Composefile 的上下文中,例如:

https://raw.githubusercontent.com/jmarhee/dockvpn/master/docker-compose.yml

serveconfig 服务将停止可用(所有容器都已完成),并且就像在我们的手动 CLI 示例中一样,后续请求将无法连接!这意味着,如果服务启动,并且您发现该服务不可用,这是一个可靠的指标,表明该服务已失败,或者该资产已被请求,您应该重新配置该资源,这是 Docker 所做的使用上述 composefile 之类的声明,以程序方式复制和重现非常容易。

震惊的联机帮助页

Socket — Linux编程参考

绑定 — Linux编程参考

Logo

更多推荐