Skip to content

Instantly share code, notes, and snippets.

@aanand
Last active December 24, 2015 13:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aanand/6809058 to your computer and use it in GitHub Desktop.
Save aanand/6809058 to your computer and use it in GitHub Desktop.
Docker attach timing bug

When starting and attaching to a container with a pseudo-tty, the initial bash prompt will only appear if the 'attach' request comes very quickly after the 'start' request (<0.1 seconds, in my tests).

Note how in the first run (attach immediately after starting), a chunk is received containing the prompt, but in the second run (start, then sleep 1s, then attach), it hangs after receiving the response headers.

This is an issue when trying to run a bash container from a Docker client that is geographically distant from the Docker server - the prompt doesn't appear, and you have to hit Enter to get one. From a UX standpoint, it appears as if Docker is simply hanging, which is pretty frustrating!

root@docker:~# python test.py localhost 4000 0
curl --verbose http://localhost:4000/v1.5/containers/create -d '{"Hostname":"","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"PortSpecs":null,"Tty":true,"OpenStdin":true,"StdinOnce":true,"Env":null,"Cmd":["bash"],"Dns":null,"Image":"ubuntu","Volumes":{},"VolumesFrom":"","WorkingDir":"","Entrypoint":[],"NetworkDisabled":false,"Privileged":false}' -H 'Content-Type: application/json'
Created container 668b00739999
curl --verbose http://localhost:4000/v1.5/containers/668b00739999/start -d '{"Binds":null,"ContainerIDFile":"","LxcConf":[]}'
Sending u'POST /v1.5/containers/668b00739999/attach?logs=1&stderr=1&stdin=1&stdout=1&stream=1 HTTP/1.1\r\n\r\n'
Got chunk: 'HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n'
Got chunk: 'root@668b00739999:/# '
^CTraceback (most recent call last):
File "test.py", line 42, in <module>
chunk = s.recv(4096)
KeyboardInterrupt
root@docker:~# python test.py localhost 4000 1
curl --verbose http://localhost:4000/v1.5/containers/create -d '{"Hostname":"","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"PortSpecs":null,"Tty":true,"OpenStdin":true,"StdinOnce":true,"Env":null,"Cmd":["bash"],"Dns":null,"Image":"ubuntu","Volumes":{},"VolumesFrom":"","WorkingDir":"","Entrypoint":[],"NetworkDisabled":false,"Privileged":false}' -H 'Content-Type: application/json'
Created container d61c1660a7d2
curl --verbose http://localhost:4000/v1.5/containers/d61c1660a7d2/start -d '{"Binds":null,"ContainerIDFile":"","LxcConf":[]}'
Sending u'POST /v1.5/containers/d61c1660a7d2/attach?logs=1&stderr=1&stdin=1&stdout=1&stream=1 HTTP/1.1\r\n\r\n'
Got chunk: 'HTTP/1.1 200 OK\r\nContent-Type: application/vnd.docker.raw-stream\r\n\r\n'
^CTraceback (most recent call last):
File "test.py", line 42, in <module>
chunk = s.recv(4096)
KeyboardInterrupt
import sys
import subprocess
import json
import time
import socket
from pipes import quote
argv = sys.argv[1:]
if len(argv) != 3:
print "Usage: test.py DOCKER_HOST DOCKER_PORT SLEEP_TIME"
exit(1)
host, port, sleep_time = argv[0], argv[1], float(argv[2])
def curl(*args):
print " ".join(map(quote, ["curl", "--verbose"] + list(args)))
return ["curl", "--fail", "--silent"] + list(args)
post_data = '{"Hostname":"","Domainname":"","User":"","Memory":0,"MemorySwap":0,"CpuShares":0,"AttachStdin":true,"AttachStdout":true,"AttachStderr":true,"PortSpecs":null,"Tty":true,"OpenStdin":true,"StdinOnce":true,"Env":null,"Cmd":["bash"],"Dns":null,"Image":"ubuntu","Volumes":{},"VolumesFrom":"","WorkingDir":"","Entrypoint":[],"NetworkDisabled":false,"Privileged":false}'
container_response = subprocess.check_output(curl("http://%s:%s/v1.5/containers/create" % (host, port), "-d", post_data, "-H", "Content-Type: application/json"))
print
container_id = json.loads(container_response)['Id']
print "Created container", container_id
print
subprocess.check_call(curl("http://%s:%s/v1.5/containers/%s/start" % (host, port, container_id), "-d", '{"Binds":null,"ContainerIDFile":"","LxcConf":[]}'))
print
time.sleep(sleep_time)
attach_request = "POST /v1.5/containers/%s/attach?logs=1&stderr=1&stdin=1&stdout=1&stream=1 HTTP/1.1\r\n\r\n" % container_id
print "Sending", repr(attach_request)
print
s = socket.create_connection((host, port))
s.send(attach_request)
while True:
chunk = s.recv(4096)
if not chunk:
break
print 'Got chunk:', repr(chunk)
print
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment