Skip to content

service: add 'docker service exec' command - #7283

Open
dlarochette wants to merge 1 commit into
docker:masterfrom
dlarochette:feat/service-exec
Open

service: add 'docker service exec' command#7283
dlarochette wants to merge 1 commit into
docker:masterfrom
dlarochette:feat/service-exec

Conversation

@dlarochette

Copy link
Copy Markdown

What I did

Added a docker service exec subcommand that executes a command in a running task of a service, on whichever node the task is currently scheduled — without having to manually resolve the node and container and SSH into it.

This addresses the long-standing request in moby/moby#27552 (open since 2016), at the CLI level: since the engine API has no cross-node exec and swarmkit has no exec RPC, the CLI reuses its own existing SSH connection helper (the DOCKER_HOST=ssh:// mechanism, docker system dial-stdio on the remote side) to reach the engine of the node running the task.

How I did it

  • Resolve the service, list its tasks filtered on desired-state=running, and pick the first running task — or the one given with --task-id.
  • If the task runs on the node the client is already connected to (compared via Info().Swarm.NodeID), perform a plain local exec.
  • Otherwise, inspect the node, build an ssh:// host from its advertised address (Status.Addr, falling back to the hostname), open a second API client through connhelper.GetConnectionHelperWithSSHOpts, and run the exec through it.
  • In both cases the existing container.RunExec plumbing is reused as-is, so -i, -t, detach keys, console resize, and exit-code propagation behave exactly like a local docker exec.
  • --ssh-user and --ssh-option allow adjusting the SSH connection (jump hosts, identity files), with the same semantics as DOCKER_HOST=ssh:// connections.

Requirements for the remote path (documented in the reference page): SSH access to the node and the docker CLI in the remote user's PATH with access to the local engine socket — the same requirements as DOCKER_HOST=ssh://.

How to verify it

Unit tests cover task selection (default, explicit, not-running, missing) and node address resolution:

go test ./cli/command/service/ -run 'TestPickTask|TestNodeSSHHost' -v

Verified manually on a 2-node swarm, local and remote paths:

$ docker service exec web-remote sh -c 'hostname; echo remote-exec-works'
executing on node node2 (uwrn9hfgx65c95wd4tcek3xiy)
4984efe363e7
remote-exec-works

$ echo hello-stdin | docker service exec -i web-remote sh -c 'cat; echo got-it'
hello-stdin
got-it

$ docker service exec web-remote sh -c 'exit 43'; echo $?
43

$ docker service exec -it web-remote sh -c 'tty'   # under a real terminal
/dev/pts/0

A note on the approach

I'm aware a fully native cross-node exec would live in swarmkit (a bidirectional sibling of the LogBroker) and the engine API; that's a much larger, 3-repo effort. This PR takes the pragmatic route with the transport the CLI already owns and documents its requirements explicitly. Happy to adjust the UX (flag names, error wording) or discuss whether this belongs behind an experimental gate.

Description for the changelog

Add `docker service exec` command to execute a command in a running task of a service, reaching the node through SSH when the task runs on another node.

Add a 'docker service exec' subcommand that executes a command in a
running task of a service, on whichever node the task is scheduled,
addressing a long-standing request (moby/moby#27552).

The service is resolved to its first running task (or the task given
with --task-id). When the task runs on the node the client is talking
to, this is a plain container exec. When it runs on another node, the
client connects to that node's engine through the existing SSH
connection helper (the DOCKER_HOST=ssh:// mechanism) and reuses the
regular exec plumbing, so interactive mode, TTY allocation, and
exit-code propagation behave exactly like a local 'docker exec'.

The node is reached at the address advertised in its status, falling
back to the node hostname; --ssh-user and --ssh-option allow adjusting
the SSH connection (e.g. jump hosts).

Signed-off-by: David LAROCHETTE <david.larochette@movesol.com>
@dlarochette
dlarochette requested review from a team and thaJeztah as code owners September 3, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant