MacOS + PHP + proc_open('docker-compose (v2)') = `read /dev/stderr: bad file descriptor`
Description Docker-compose v2 does something to stderr on macOS. We're using Monterey / Darwin / Intel, 12.1 (21C52), kernel 21.2.0 as our operating system, and Docker Desktop with a project with some Docker containers, tied together through (during development) multiple docker-compose.yml files. It repros with one compose file as well.
Steps to reproduce the issue:
- Have an Intel Mac
- Have a docker-compose orchestration running, name it for example "mysql". Minimal repro (but triggered on various containers/images):
services:
mysql:
image: mysql:8.0
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
- Run this PHP code on your Mac, targeting said container named "mysql":
$command = 'docker-compose exec mysql ls';
$descriptors = [
['file', '/dev/tty', 'r'], // stdin
['file', '/dev/tty', 'w'], // stdout
['file', '/dev/tty', 'w'], // stderr
];
$handle = proc_open($command, $descriptors, $pipes);
echo proc_close($handle);
This is, very simplified, what Symfony's Process component does when you start a process. This Process in turn is instantiated by Deployer, used to execute commands locally and in a container.
Describe the results you received: When the checkbox for "Use Docker Compose V2" is off, it works fine. Always has. You'll get a nice directory listing of the entrypoint of the mysql container (or the output of whatever other command you executed on whatever container), followed by the exit code of "0%".
However, when you do check the V2 checkbox in Docker Desktop and apply, you will still get the same output, but now it'll be followed by:
read /dev/stderr: bad file descriptor 1%
This trips the Process component into thinking the execution of the command failed. It didn't, I can see long-running commands being started within the container, but the caller doesn't know that.
Describe the results you expected:
I would expect docker-compose in V2 mode not to give errors about /dev/stderr being a bad file descriptor.
Additional information you deem important (e.g. issue happens only occasionally): This was ultimately discovered by our use of a filesystem syncing tool called Mutagen, which recently released v0.13.0 out of beta, which now exclusively uses Compose V2.
Output of docker compose version:
$ docker compose version
Docker Compose version v2.2.3
With V2 checkbox off (and docker-compose, not docker compose):
$ docker-compose version
docker-compose version 1.29.2, build 5becea4c
docker-py version: 5.0.0
CPython version: 3.9.0
OpenSSL version: OpenSSL 1.1.1h 22 Sep 2020
Output of 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: 39
Running: 19
Paused: 0
Stopped: 20
Images: 100
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: x86_64
Workarounds
- Use docker-compose V1, but we can't, we use
mutagen-composewhich is now irrevocably a V2 wrapper. - Change
['file', '/dev/tty', 'w'], // stderrto .../dev/null, but then we won't get error output, and also, the code that builds that array is outside of our control.
Does invoking docker-compose exec with -T have any effect on the outcome here?
Also, does invoking Compose V2 as a CLI plugin (i.e. docker compose exec) alter the behavior?
@xenoscopic docker-compose exec -T, V1:
➜ ~/.../ComposeV2 $ php dc2test.php
bin
boot
dev
[...]
tmp
usr
var
0%
docker-compose exec -T, V2:
➜ ~/.../ComposeV2 $ php dc2test.php
bin
boot
dev
[...]
usr
var
0%
docker compose exec displays the original behavior, ending with an exit code of 1 and the error about stderr.
I see "interesting" behavior like this from docker-compose v2.2.2 or docker-compose v2.2.3 with calling code like this:
proc := exec.Command(path, arg...)
proc.Stdout = stdout
proc.Stdin = stdin
proc.Stderr = stderr
err = proc.Run()
It's calling docker-compose exec and docker-compose exits with an exitErr and "read /dev/stderr: bad file descriptor"
Related Stack Overflow link: https://stackoverflow.com/questions/20134095/why-do-i-get-bad-file-descriptor-in-this-go-program-using-stderr-and-ioutil-re
I seem to be past this. I note now that it was happening when debugging in Goland, so could be possible that Goland or delve does something funky to stderr.
But I do suspect that since this has been discovered in various contexts that it's misbehavior from composer when something has happened to stderr, or perhaps the issue in the stack overflow posting.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically closed because it had not recent activity during the stale period.