Docker – Systemd Service Start With Dynamic Port Value From Docker

dockersystemd

Using CoreOS, Docker and systemd to manage my services I want to properly perform service discovery. Since CoreOS utilizes etcd (distributed key-value) there is a very convenient way to do this. On systemd's ExecStartPost I can just insert the started service into etcd without problems. My usecase needs something like this:

ExecStartPost=/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": 5555 }'

which works like a charm.

But this is where my idea popped up. Docker has the power to randomly assign a port if I just run docker run -p 5555 which is awesome since I don't have to set it statically in the *.service file and I could possibly run multiple instances on the same host. What if I could get the randomly assigned port and insert instead of the static 5555?

Turns out I can use the docker port command to get the port and with some formatting we can get just the port with

$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)

which works if I set it (using bash) like this:

/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": '$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)' }'

but using systemd I just can't get it to work. This is the code I'm using:

ExecStartPost=/usr/bin/etcdctl set /services/myServiceName '{ \"host\": \"%H\", \"port\": '$(echo $(/usr/bin/docker port my-container-name 5555) | cut -d':' -f2)'}'

Somehow I got something wrong but it's hard to debug since it works when typed in the terminal.

Best Answer

The systemd ExecStart option doesn't do any shell interpolation and thus doesn't understand stuff like $(echo 'Hello'). But, you can start a shell that executes the command to get behavior like this. Try something like:

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)'}'"