How to 'docker exec' a container built from scratch?
Asked Answered
A

6

13

I am trying to docker exec a container that is built from scratch (say, a NATS container). Seems pretty straight-forward, but since it is built from scratch, I am unable to access /bin/bash, /bin/sh and literally any such command.

I get the error: oci runtime error (command not found, file not found, etc. depending upon the command that I enter).

I tried some commands like:

docker exec -it <container name> /bin/bash
docker exec -it <container name> /bin/sh
docker exec -it <container name> ls

My question is, how do I docker exec a container that is built from scratch and consisting only of binaries? By doing a docker exec, I wish to find out if the files have been successfully copied from my host to the container (I have a COPY in the Dockerfile).

Antakiya answered 16/2, 2019 at 7:23 Comment(1)
Having said that, docker run --entrypoint would do, too, since I just wish to check if the files have been copied.Antakiya
C
18

If your scratch container is running you can copy a shell (and other needed utils) into its filesystem and then exec it. The shell would need to be a static binary. Busybox is a great choice here because it can double as so many other binaries.

Full example:

# Assumes scratch container is last launched one, else replace with container ID of
# scratch image, e.g. from `docker ps`, for example:
# scratch_container_id=401b31621b36
scratch_container_id=$(docker ps -ql)

docker run -d busybox:latest sleep 100
busybox_container_id=$(docker ps -ql)
docker cp "$busybox_container_id":/bin/busybox .

# The busybox binary will become whatever you name it (or the first arg you pass to it), for more info run:
# docker run busybox:latest /bin/busybox
# The `busybox --install` command copies the binary with different names into a directory.

docker cp ./busybox "$scratch_container_id":/busybox

docker exec -it "$scratch_container_id" /busybox sh -c '
export PATH="/busybin:$PATH"
/busybox mkdir /busybin
/busybox --install /busybin
sh'

For Kubernetes I think Ephemeral Containers provide or will provide equivalent functionality.

References: distroless java docker image error https://github.com/GoogleContainerTools/distroless/issues/168#issuecomment-371077961

Cassaba answered 7/8, 2019 at 13:9 Comment(2)
Unfortunately, I still get OCI runtime exec failed: exec failed: unable to start container process: exec /busybox: no such file or directory: unknown Not sure why. I can even copy that busybox back locally and it's the same file.Tryptophan
@Tryptophan when I see that error it's usually either because the file wasn't executable, or because it's a binary for a different architecture (e.g. ARM vs x86, or 32-bit vs 64-bit)Cassaba
S
6

There are several options.

  1. You can do docker container cp ${CONTAINER}:/path/to/file/on/container /path/to/temp/dir/on/host. This will copy the files to your host where you can inspect things using host tools.
  2. You can add an appropriate VOLUME to your Dockerfile. Then you can docker container inspect ${CONTAINER}. This will expose the volume name where the files should be. You can then inspect those in another container (based off an image with all the tools you need).
  3. You can at runtime bind the container to a volume or host directory at the appropriate place.
  4. You can add those binaries that you feel you need to the image. If you need /bin/ls or /bin/sh, then you can add them.
  5. You can bind mount the necessary binaries to the container - so the container has them for verification purposes but the image is not bloated by them.
Seducer answered 16/2, 2019 at 12:43 Comment(1)
Thank you for the answer, but it does not precisely answer my question about being able to ‘docker exec’ a minimal container.Antakiya
B
2

You can only use docker exec to run commands that actually exist in a container. If those commands don't exist, you can't run them. As you've noted, the scratch base image contains nothing – no shells, no libraries, no system files, nothing.

If all you're trying to check is if a Dockerfile COPY command actually copied the files you said it would, I'd generally assume the tooling works and just reference the copied files in my application.

Since it sounds like you control the Dockerfile, one workaround could be to change the base image to something lightweight but non-empty, like FROM busybox. That would give you a minimal set of tools that you could work with without blowing up the image size too much.

Byran answered 16/2, 2019 at 12:10 Comment(1)
Thanks for your answer. So just to clarify, I have cloned a production repo and trying to copy files from host to container by modifying the local Dockerfile. That’s why modifying the Dockerfile to include something like busybox would not be the exact thing that would happen in production. Sure, I can use it for testing purposes, but if there is some command that I can use to test the transfer, then that would be closer to what happens in production and would be more helpful.Antakiya
D
0

I am trying to do the same files check for my needs. I ended up with docker cp copy this file from container. In my case I am using nats container, but you can use any other container running scratch-based-image

sudo docker cp nats_nats_1:/nats-server.conf ./nats-server.conf
Dalenedalenna answered 28/6, 2021 at 16:59 Comment(0)
B
0

You can just grab the container identifier and throw it into a variable. For example, let's say the (truncated) output of docker ps -a is listed with your running container:

CONTAINER ID  IMAGE

111111111111   neo4j-migrator

To further the example, you can docker exec -t using the variable you created. For example:

CONTAINER_ID=`docker ps -aqf "ancestor=neo4j-migrator"`
docker exec -it $CONAINER_ID \
 sh -c "/usr/bin/neo4j-migrations \
 --password $NEO4J_PASSWORD \
 --username $NEO4J_USERNAME \
 --address $NEO4J_URI \
 migrate"
Butyrin answered 13/1, 2023 at 21:11 Comment(0)
U
0

You can copy the busybox into a running container and then run it there.


[[TLDR]]

docker run -d --rm --name busybox busybox:musl sleep 100
docker cp busybox:/bin/busybox ./
chmod +x busybox
docker cp busybox mycontainer1:/busybox
docker exec -it mycontainer1 /busybox mkdir -p /usr/bin /bin /usr/sbin
docker exec -it mycontainer1 /busybox --install
docker exec -it mycontainer1 /busybox sh

(make sure busybox is statically linked with file busybox)


[[EXPLAINED]]

To run a shell in a Docker container built FROM scratch using BusyBox, follow these steps:

1. Extract BusyBox from a Docker Image

  1. Run a temporary BusyBox container:
docker run -d --rm --name busybox busybox:musl sleep 100
  1. Copy the BusyBox binary from the temporary container and make it executable:
docker cp busybox:/bin/busybox ./
chmod +x busybox
  1. Verify that BusyBox is statically linked:
file busybox

The output should indicate that BusyBox is statically linked:

busybox: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, BuildID[sha1]=...

If it is dynamically linked, you might encounter the following errors when running it inside other containers.

OCI runtime exec failed: exec failed: unable to start container process:
exec /busybox: no such file or directory: unknown

If you still experience the issue while running other images, make sure BusyBox platform matches yours

FROM --platform=$BUILDPLATFORM golang
<...>
docker build --build-arg BUILDPLATFORM=linux/arm64 --build-arg opts="CGO_ENABLED=0 GOOS=linux GOARCH=arm64" -t myuser/example:arm64 .

Note:

  • Earlier, extracting BusyBox from the busybox:latest image provided a static binary. However, recent versions of busybox:latest might give you a dynamically linked binary instead. Therefore, it's crucial to ensure you get a statically linked version (check image descriptions at https://hub.docker.com/_/busybox). At the time of writing both busybox:uclibc and busybox:musl images contained statically linked BusyBox.
  • Alternatively, you can download a statically linked BusyBox binary directly from https://busybox.net

2. Copy BusyBox to Your Target Container

  1. Copy the BusyBox binary into your running container (e.g., mycontainer1):
docker cp busybox mycontainer1:/busybox

3. (Optional) Install BusyBox Functions

  1. Create necessary directories and install BusyBox tools:
docker exec -it mycontainer1 /busybox mkdir -p /usr/bin /bin /usr/sbin
docker exec -it mycontainer1 /busybox --install

This command installs BusyBox functions (such as ls, mkdir, etc.) into the appropriate directories within the container, allowing you to use various utilities provided by BusyBox.

4. Run a Shell in the Container

  1. Execute a shell using BusyBox in your target container:
docker exec -it mycontainer1 /busybox sh
Undersurface answered 23/8 at 12:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.