![Systemd 服务从 Docker 使用动态端口值启动](https://linux22.com/image/631127/Systemd%20%E6%9C%8D%E5%8A%A1%E4%BB%8E%20Docker%20%E4%BD%BF%E7%94%A8%E5%8A%A8%E6%80%81%E7%AB%AF%E5%8F%A3%E5%80%BC%E5%90%AF%E5%8A%A8.png)
使用 CoreOS、Docker 和 systemd 来管理我的服务,我希望正确执行服务发现。由于 CoreOS 使用 etcd(分布式键值),因此有一种非常方便的方法可以做到这一点。在 systemd 的 ExecStartPost 上,我可以将启动的服务插入到 etcd 中,不会出现任何问题。我的用例需要类似以下内容:
ExecStartPost=/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": 5555 }'
效果非常好。
但这正是我的想法出现的地方。如果我只是运行,Docker 就可以随机分配一个端口,docker run -p 5555
这很棒,因为我不必在 *.service 文件中静态设置它,而且我可能在同一主机上运行多个实例。如果我可以获取随机分配的端口并插入而不是静态的,会怎么样5555
?
事实证明,我可以使用docker port
命令来获取端口,并且通过一些格式化,我们可以只获取端口
$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)
如果我像这样设置它(使用 bash),它就会起作用:
/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": '$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)' }'
但使用 systemd 我无法让它工作。这是我使用的代码:
ExecStartPost=/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": '$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)'}'
不知怎的,我犯了个错误,但很难调试,因为它在终端中输入时可以工作。
答案1
systemdExecStart
选项不执行任何 shell 插值,因此无法理解诸如 之类的内容$(echo 'Hello')
。但是,您可以启动执行命令的 shell 来获得这样的行为。尝试以下操作:
ExecStartPost=/bin/sh -c "/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": '$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)'}'"
答案2
我最终使用了类似这样的东西:
ExecStart=/bin/sh -c "port=$(docker port [CONTAINER-NAME] [CONTAINER-EXPOSED-PORT] | cut -d ':' -f 2); echo Connected to $port, publishing to etcd...; while netstat -lnt | grep :$port >/dev/null; do etcdctl set /services/[CONTAINER-NAME]/[SOME-IDENTIFIER] $port --ttl 60 >/dev/null; sleep 45; done"
但我的问题的答案确实是我需要说明在哪个 shell 中运行该命令。
编辑:添加 Go 模板逻辑
[Service]
EnvironmentFile=/etc/environment
ExecStartPre=/bin/sh -c "until docker port {{.Name}} {{.Port}} >/dev/null 2>&1; do echo Waiting for {{.Name}}; sleep 2; done; port=$(docker port {{.Name}} {{.Port}} | cut -d ':' -f 2); echo Waiting for $port/tcp...; until netstat -lnt | grep :$port >/dev/null; do sleep 1; done"
ExecStart=/bin/sh -c "port=$(docker port {{.Name}} {{.Port}} | cut -d ':' -f 2); echo Connected to $COREOS_PRIVATE_IPV4:$port/tcp, publishing to etcd...; while netstat -lnt | grep :$port >/dev/null; do etcdctl set /services/{{.Service}}/{{.Name}} $COREOS_PRIVATE_IPV4:$port --ttl 60 >/dev/null; sleep 45; done"
ExecStop=/usr/bin/etcdctl rm --recursive /services/{{.Service}}/{{.Name}}