In a Make rule, the body of the rule is a series of ordinary Bourne shell commands, with the exception that $ is used by Make so if you need it in a shell context you need to double it.
If, within the context of a single rule, you need the output of some subcommand, you should use ordinary command substitution. You should not need the GNU-Make-specific $(shell ...) function within the body of a rule.
build:
DOCKER_IMAGE_ID=$$(docker build -q -t myimage .); \
OUT=$$(docker run --rm $$DOCKER_IMAGE_ID);
Make is more suited as a build tool than as a general-purpose scripting engine, so it may make more sense to split this into separate rules.
IMAGE_NAME := myimage
.PHONY: build
build: .container-output
.image-build: Dockerfile
docker build -q -t $(IMAGE_NAME) .
touch "$@"
.container-output: .image-build
docker run --rm $(IMAGE_NAME) > "$@"
If you just want to run a sequence of commands, a plain shell script might work better.
#!/bin/sh
DOCKER_IMAGE_ID=$(docker build -q -t myimage .)
OUT=$(docker run --rm "$DOCKER_IMAGE_ID")
The problem with the fragment you show is that the $(shell ...) expressions are all evaluated when the rule is read in and before the actual rule command is executed. In particular, docker run --rm $DOCKER_IMAGE_ID is run directly by Make, before the shell rule executes to set the shell variable. You'd get the value of that environment variable from the containing environment or a previous (GNU) Make export statement, but not from a shell statement in the same rule.