just-containers / s6-overlay

s6 overlay for containers (includes execline, s6-linux-utils & a custom init)
Other
3.77k stars 212 forks source link

CTRL-C doesn't stop the container #399

Closed ngalaiko closed 2 years ago

ngalaiko commented 2 years ago

I have this small container which contains several services managed by s6. I would like to be able to stop it with CTRL-C when running in interactive mode (SIGINT should bring the container down)

Here is the s6 part of the Dockerfile I have:

Dockerfile ```dockerfile FROM alpine:3.15 as oneliner # s6-overlay ARG S6_OVERLAY_VERSION="3.0.0.2" \ S6_OVERLAY_NOARCH_SHA256_SUM="17880e4bfaf6499cd1804ac3a6e245fd62bc2234deadf8ff4262f4e01e3ee521" \ S6_OVERLAY_SYMLINKS_ARCH_SHA256_SUM="6ee2b8580b23c0993b1e8c66b58777f32f6ff031ba0192cccd53a31e62942c70" \ S6_OVERLAY_SYMLINKS_NOARCH_SHA256_SUM="d67c9b436ef59ffefd4f083f07b2869662af40b2ea79a069b147dd0c926db2d3" SHELL ["/bin/ash", "-o", "pipefail", "-c"] RUN ARCH="$(uname -m)" \ && if [[ "$ARCH" == 'x86_64' ]]; then \ S6_OVERLAY_ARCH_SHA256_SUM="a4c039d1515812ac266c24fe3fe3c00c48e3401563f7f11d09ac8e8b4c2d0b0c"; \ elif [[ "$ARCH" == 'aarch64' ]]; then \ S6_OVERLAY_ARCH_SHA256_SUM="e6c15e22dde00af4912d1f237392ac43a1777633b9639e003ba3b78f2d30eb33"; \ fi \ && wget --quiet --output-document "/tmp/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz" "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ && sha256sum "/tmp/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ && echo "${S6_OVERLAY_NOARCH_SHA256_SUM} /tmp/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz" | sha256sum -c \ && tar -C / -Jxpf "/tmp/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ && rm "/tmp/s6-overlay-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ \ && wget --quiet --output-document "/tmp/s6-overlay-${ARCH}-${S6_OVERLAY_VERSION}.tar.xz" "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-${ARCH}-${S6_OVERLAY_VERSION}.tar.xz" \ && sha256sum "/tmp/s6-overlay-${ARCH}-${S6_OVERLAY_VERSION}.tar.xz" \ && echo "${S6_OVERLAY_ARCH_SHA256_SUM} /tmp/s6-overlay-${ARCH}-${S6_OVERLAY_VERSION}.tar.xz" | sha256sum -c \ && tar -C / -Jxpf "/tmp/s6-overlay-${ARCH}-${S6_OVERLAY_VERSION}.tar.xz" \ && rm "/tmp/s6-overlay-${ARCH}-${S6_OVERLAY_VERSION}.tar.xz" \ \ && wget --quiet --output-document "/tmp/s6-overlay-symlinks-noarch-${S6_OVERLAY_VERSION}.tar.xz" "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ && sha256sum "/tmp/s6-overlay-symlinks-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ && echo "${S6_OVERLAY_SYMLINKS_NOARCH_SHA256_SUM} /tmp/s6-overlay-symlinks-noarch-${S6_OVERLAY_VERSION}.tar.xz" | sha256sum -c \ && tar -C / -Jxpf "/tmp/s6-overlay-symlinks-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ && rm "/tmp/s6-overlay-symlinks-noarch-${S6_OVERLAY_VERSION}.tar.xz" \ \ && wget --quiet --output-document "/tmp/s6-overlay-symlinks-arch-${S6_OVERLAY_VERSION}.tar.xz" "https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-symlinks-arch-${S6_OVERLAY_VERSION}.tar.xz" \ && sha256sum "/tmp/s6-overlay-symlinks-arch-${S6_OVERLAY_VERSION}.tar.xz" \ && echo "${S6_OVERLAY_SYMLINKS_ARCH_SHA256_SUM} /tmp/s6-overlay-symlinks-arch-${S6_OVERLAY_VERSION}.tar.xz" | sha256sum -c \ && tar -C / -Jxpf "/tmp/s6-overlay-symlinks-arch-${S6_OVERLAY_VERSION}.tar.xz" \ && rm "/tmp/s6-overlay-symlinks-arch-${S6_OVERLAY_VERSION}.tar.xz" ENV LANG="en_US.UTF-8" \ LANGUAGE="en_US.UTF-8" \ LC_ALL="C" \ S6_KILL_GRACETIME=0 \ S6_SERVICES_GRACETIME=0 \ S6_CMD_WAIT_FOR_SERVICES_MAXTIME=30000 ENTRYPOINT [ "/init" ] ```

Example of me running the container:

at 12:50:32 ❯ docker build --tag test -f Dockerfile . && docker run --interactive --ttytest
[+] Building 0.1s (6/6) FINISHED
 => [internal] load build definition from Dockerfile.test                                 0.0s
 => => transferring dockerfile: 3.39kB                                                    0.0s
 => [internal] load .dockerignore                                                         0.0s
 => => transferring context: 34B                                                          0.0s
 => [internal] load metadata for docker.io/library/alpine:3.15                            0.0s
 => [1/2] FROM docker.io/library/alpine:3.15                                              0.0s
 => CACHED [2/2] RUN ARCH="$(uname -m)"     && if [[ "$ARCH" == 'x86_64' ]]; then     S6  0.0s
 => exporting to image                                                                    0.0s
 => => exporting layers                                                                   0.0s
 => => writing image sha256:7eac2b1d7d9c906d92ea1465a70dd850951fc3d2e5d4cbdb92cfd8ea06a5  0.0s
 => => naming to docker.io/library/test                                                   0.0s
s6-rc: info: service s6rc-oneshot-runner: starting
s6-rc: info: service s6rc-oneshot-runner successfully started
s6-rc: info: service fix-attrs: starting
s6-rc: info: service fix-attrs successfully started
s6-rc: info: service legacy-cont-init: starting
s6-rc: info: service legacy-cont-init successfully started
s6-rc: info: service legacy-services: starting
s6-rc: info: service legacy-services successfully started
^C^C^C^C

Note that ^C didn't have any effect here.

After I ran docker stop in a separate terminal:

s6-rc: info: service legacy-services successfully stopped
s6-rc: info: service legacy-cont-init: stopping
s6-rc: info: service legacy-cont-init successfully stopped
s6-rc: info: service fix-attrs: stopping
s6-rc: info: service fix-attrs successfully stopped
s6-rc: info: service s6rc-oneshot-runner: stopping
s6-rc: info: service s6rc-oneshot-runner successfully stopped

What am I missing? Do I have to have the main process that stops on SIGINT?

docker info ``` Client: Context: default Debug Mode: false Plugins: buildx: Docker Buildx (Docker Inc., v0.7.1) compose: Docker Compose (Docker Inc., v2.2.3) scan: Docker Scan (Docker Inc., v0.16.0) Server: Containers: 186 Running: 4 Paused: 0 Stopped: 182 Images: 169 Server Version: 20.10.12 Storage Driver: overlay2 Backing Filesystem: extfs Supports d_type: true Native Overlay Diff: true userxattr: false Logging Driver: json-file Cgroup Driver: cgroupfs Cgroup Version: 2 Plugins: Volume: local Network: bridge host ipvlan macvlan null overlay Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog Swarm: inactive Runtimes: io.containerd.runc.v2 io.containerd.runtime.v1.linux runc Default Runtime: runc Init Binary: docker-init containerd version: 7b11cfaabd73bb80907dd23182b9347b4245eb5d runc version: v1.0.2-0-g52b36a2 init version: de40ad0 Security Options: seccomp Profile: default cgroupns Kernel Version: 5.10.76-linuxkit Operating System: Docker Desktop OSType: linux Architecture: aarch64 CPUs: 4 Total Memory: 1.942GiB Name: docker-desktop ID: GAJ5:G2EK:YWI4:DZEU:SREW:HGQ7:RXDM:5QDF:SSRR:XCGV:D5WT:EVAG Docker Root Dir: /var/lib/docker Debug Mode: false Registry: https://index.docker.io/v1/ Labels: Experimental: false Insecure Registries: 127.0.0.0/8 Live Restore Enabled: false ```
skarnet commented 2 years ago

You're the second person with this issue so I should add it to a FAQ. ;-)

The short answer is: don't use -it when you don't run your container with a CMD, and ^C will work.

A summary of the long answer is that when the container is run with its own terminal, s6-overlay passes it to the CMD, so job control will work on the CMD, which is what people want most of the time. But when there is no CMD, the terminal is lost; there's no way to avoid that. But when the container is not run with its own terminal, ^C is caught by the docker command, which then sends a SIGINT to pid 1 in the container, which triggers the shutdown procedure.

It's an unfortunate side-effect of Docker's terminal management. It appeared to work in previous versions of s6-overlay, but it didn't (^C killed the whole supervision tree, which is not a clean container shutdown, and was one of the causes of various timeouts that people experienced at shutdown). The current behaviour is the best we can get, as in, there is always a way for people to achieve what they want; here, you can achieve what you want by not running your container in a terminal.

ngalaiko commented 2 years ago

@skarnet Thanks! That works.

Dropping --tty only was sufficient.