我目前正在构建一个将 Docker 容器连接到 websocket 的系统,然后客户端可以通过 websocket 与该容器交互。我想让客户端能够使用键盘中断来中断 Docker 容器内运行的进程。
到目前为止我的尝试如下:
.........
pty, tty = pypty.openpty()
async def interact_docker(pty, websocket):
while True:
data = os.read(pty, 10240)
print(data)
# using python's pty as I/O, everytime client gives an input,
# the pty reflects this input and duplicates the input into the output.
# ex: command "ls" inputted by client will output "ls\r\n" and the result of "ls".
# to prevent duplicated output from input, save the length of the input string
# and add 2 to account for \r\n. Then, for each output from pty, check the first
# (len(input) + 2) string, and check if the string ends in \r\n. If it does, it means
# the output is a duplicate of the input, so emit this duplicate.
check_duplicate = data[:offset]
if check_duplicate[(offset - 2):] == "\r\n".encode():
data = data[offset:]
try:
await websocket.send_text(data.decode())
except UnicodeDecodeError:
print("decoding error")
await websocket.send_text(data.decode("latin-1"))
container = subprocess.Popen(['docker', 'run', '--name', str(instance.id), '-it', '--rm', '--privileged', '-e', 'DISPLAY', '-v', '/tmp/.X11-unix:/tmp/.X11-unix', '-v', '/lib/modules:/lib/modules', 'iwaseyusuke/mininet'], stdin=tty, stdout=tty, stderr=tty)
loop = asyncio.get_running_loop()
loop.run_in_executor(None, lambda: asyncio.run(interact_docker(pty, websocket)))
while True:
data = await websocket.receive_text()
if data == "^C":
print("keyboard interrupt")
container.send_signal(signal.SIGINT)
correct_inp = process_input(db, instance, data, expected_inp)
if correct_inp and len(expected_inp) == 0 and not finished_lab:
message = "\x1b[1;92m \nSelamat! Anda telah menyelesaikan lab ini.\nAnda bisa melanjutkan penggunaan lab atau anda bisa kembali ke menu utama untuk menghentikan pengerjaan lab. \x1b[0m\n\n"
await websocket.send_text(message)
user.finished_labs.append(lab)
db.commit()
offset = len(data) + 2
data = data + "\n"
os.write(pty, data.encode())
为了清晰起见,省略了一些代码行,但脚本的主要思想就在那里。目前,使用
container.send_signal(signal.SIGINT)
的尝试只会杀死容器。向 pty 发送“^C”不会向容器内的进程发送 SIGINT,而只是发送文字字符串。
如果您以某种方式在容器内运行服务器,请向该服务器发送特定消息,并在服务器端添加代码以触发 SIGINT。
如果您想向容器内的进程发送信号,请使用
docker exec
:
docker exec <container_name> kill -INT [pid]
如果该进程是容器的主进程,则它的 pid 为 1:
# don't run this at home ;)
docker exec <container_name> kill -INT 1