首页
/ Moby Remote API v1.5 全解:Docker 引擎早期 HTTP 接口的设计、端点与 Hijack 机制

Moby Remote API v1.5 全解:Docker 引擎早期 HTTP 接口的设计、端点与 Hijack 机制

2026-09-04 22:52:52作者:段琳惟

本篇以 Moby 仓库中保存的 Remote API v1.5 文档 为核心,完整还原 Docker 引擎最早期远程 API 的容器、镜像与杂项端点、请求/响应格式与状态码语义,并结合当前仓库的客户端与服务端源码,剖析 hijacking(连接劫持)传输 stdin/stdout/stderr 的实现原理,以及 API 版本协商机制。读完后,你可以基于端口 2375 直接对 dockerd 发起原始 HTTP 调用,并理解这套老接口与现代 Moby API 之间的演化关系。

1. 背景:v1.5 时代的 Remote API

Docker Remote API v1.5 是 Docker 引擎在 CLI 客户端 rcli 被 HTTP API 取代时期的接口规范。文档原文明确了三个基本设计决策:

  • Remote API 取代 rcli:客户端不再通过自定义协议与守护进程通信,而是走标准 HTTP;
  • 守护进程默认端口为 2375(明文 TCP,未启用 TLS 时的默认监听端口);
  • API 大体遵循 REST 风格,但对 attachpull 这类复杂命令,会劫持(hijack)HTTP 连接来传输 stdout、stdin 和 stderr 的原始字节流。

当前 Moby 仓库中的 Dockerfile 注释里仍能看到 --host=tcp://0.0.0.0:2375 这一默认端口的用法示例,说明 2375 作为明文 API 端口的约定一直延续至今。

1.1 与当前仓库 API 版本的对比

需要说明的适用前提:v1.5 是 API 版本演进序列中的早期版本,仓库中保留了从 v1.0v1.55 的完整版本文档。现代客户端(本仓库 client 模块)在发起请求时会与服务端进行 API 版本协商——从 client/client.gonegotiateAPIVersion 可以看到,客户端会解析 /ping 返回的 APIVersion,若服务端版本低于客户端最低支持版本则直接报错,若低于客户端当前版本则自动降级:

func (cli *Client) negotiateAPIVersion(pingVersion string) error {
	...
	if versions.LessThan(pingVersion, MinAPIVersion) {
		return cerrdefs.ErrInvalidArgument.WithMessage(...)
	}
	// if server version is lower than the client version, downgrade
	if versions.LessThan(pingVersion, negotiatedVersion) {
		negotiatedVersion = pingVersion
	}
	...
}

因此,v1.5 文档中的端点路径与字段在新一代引擎上大多已演进(例如 /containers/(id)/json 现在对应 GET /containers/{id}/json,但 inspect 响应的结构体已扩展为 api/types/container/container.go 中的 InspectResponse,包含 DriverPlatformRestartCountMounts 等 v1.5 时代没有的字段)。本文以 v1.5 原始规范为准,仅在必要时标注与当前实现的对应关系。

2. 容器端点(Containers)

2.1 列出容器:GET /containers/json

请求与响应示例(原文档完整保留):

GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "Id": "8dfafdbc3a40",
    "Image": "ubuntu:latest",
    "Command": "echo 1",
    "Created": 1367854155,
    "Status": "Exit 0",
    "Ports":[{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
    "SizeRw":12288,
    "SizeRootFs":0
  },
  {
    "Id": "9cd87474be90",
    "Image": "ubuntu:latest",
    "Command": "echo 222222",
    "Created": 1367854155,
    "Status": "Exit 0",
    "Ports":[],
    "SizeRw":12288,
    "SizeRootFs":0
  }
]

查询参数:

参数 说明
all 1/True/true 或 0/False/false,是否显示全部容器。默认只显示运行中的容器(默认 false)
limit 只显示最近创建的 limit 个容器,包含非运行中的
since 只显示该 Id 之后创建的容器,包含非运行中的
before 只显示该 Id 之前创建的容器,包含非运行中的
size 1/True/true 或 0/False/false,是否显示容器占用大小

状态码:200 成功;400 参数错误;500 服务端错误。

对照当前实现,该端点的响应结构体是 api/types/container/container.go 中的 SummarySizeRw/SizeRootFsPortsCreated 等字段一脉相承,此外还新增了 NamesLabelsHealthNetworkSettings 等字段——这正是 v1.5 响应样例中未出现的部分。

2.2 创建容器:POST /containers/create

请求体为容器配置 JSON(v1.5 时代的字段集,完整继承原文档):

POST /containers/create HTTP/1.1
Content-Type: application/json

{
  "Hostname":"",
  "User":"",
  "Memory":0,
  "MemorySwap":0,
  "AttachStdin":false,
  "AttachStdout":true,
  "AttachStderr":true,
  "PortSpecs":null,
  "Privileged": false,
  "Tty":false,
  "OpenStdin":false,
  "StdinOnce":false,
  "Env":null,
  "Cmd":[
    "date"
  ],
  "Dns":null,
  "Image":"ubuntu",
  "Volumes":{},
  "VolumesFrom":"",
  "WorkingDir":""
}

响应:

HTTP/1.1 201 Created
Content-Type: application/json

{
  "Id":"e90e34656806"
  "Warnings":[]
}

参数说明:config —— 容器配置(即请求体本身)。

状态码:201 成功;404 容器不存在;406 无法附加(容器未在运行);500 服务端错误。

值得注意的是,v1.5 时代的配置字段是扁平化的单对象(MemoryPortSpecsVolumes 混在一起)。当前仓库中容器配置已按语义拆分为 api/types/container/config.go(容器自身配置 Config)与 api/types/container/hostconfig.go(宿主侧配置 HostConfig),创建请求则封装在 api/types/container/create_request.go 中——这解释了为什么现代 API 的 create 请求同时包含 confighostConfig 两个键。

2.3 查看容器详细信息:GET /containers/(id)/json

返回容器 id 的底层信息。

GET /containers/4fa6e0f0c678/json HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{
  "Id": "4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2",
  "Created": "2013-05-07T14:51:42.041847+02:00",
  "Path": "date",
  "Args": [],
  "Config": {
    "Hostname": "4fa6e0f0c678",
    "User": "",
    "Memory": 0,
    "MemorySwap": 0,
    "AttachStdin": false,
    "AttachStdout": true,
    "AttachStderr": true,
    "PortSpecs": null,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": null,
    "Cmd": ["date"],
    "Dns": null,
    "Image": "ubuntu",
    "Volumes": {},
    "VolumesFrom": "",
    "WorkingDir":""
  },
  "State": {
    "Running": false,
    "Pid": 0,
    "ExitCode": 0,
    "StartedAt": "2013-05-07T14:51:42.087658+02:01360",
    "Ghost": false
  },
  "Image": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
  "NetworkSettings": {
    "IpAddress": "",
    "IpPrefixLen": 0,
    "Gateway": "",
    "Bridge": "",
    "PortMapping": null
  },
  "SysInitPath": "/home/kitty/go/src/github.com/docker/docker/bin/docker",
  "ResolvConfPath": "/etc/resolv.conf",
  "Volumes": {}
}

状态码:200 成功;404 容器不存在;500 服务端错误。

对照当前源码,v1.5 的 State 对象(Running/Pid/ExitCode/Ghost)已演进为 api/types/container/container.go 中的 StateGhost 字段被更精细的 Status 字符串(created/running/paused/restarting/removing/exited/dead)、PausedOOMKilledFinishedAtHealth 取代。

2.4 列出容器内进程:GET /containers/(id)/top

GET /containers/4fa6e0f0c678/top HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{
  "Titles":[
    "USER","PID","%CPU","%MEM","VSZ","RSS","TTY","STAT","START","TIME","COMMAND"
  ],
  "Processes":[
    ["root","20147","0.0","0.1","18060","1864","pts/4","S","10:06","0:00","bash"],
    ["root","20271","0.0","0.0","4312","352","pts/4","S+","10:07","0:00","sleep","10"]
  ]
}

查询参数:ps_args —— 传给 ps 的参数(例如 aux)。

状态码:200 成功;404 容器不存在;500 服务端错误。

2.5 查看容器文件系统变更:GET /containers/(id)/changes

GET /containers/4fa6e0f0c678/changes HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

[
  { "Path":"/dev", "Kind":0 },
  { "Path":"/dev/kmsg", "Kind":1 },
  { "Path":"/test", "Kind":1 }
]

状态码:200 成功;404 容器不存在;500 服务端错误。

当前实现中该端点返回的 Kind 语义定义在 api/types/container/change_type.goapi/types/container/change_types.go,v1.5 示例中的 0/1 对应“修改/新增”类变更,现代版本将其规范化为 ChangeModifyChangeAddChangeDelete 常量。

2.6 导出容器:GET /containers/(id)/export

导出容器 id 的全部内容,响应为原始 tar 流:

GET /containers/4fa6e0f0c678/export HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/octet-stream

{{ TAR STREAM }}

状态码:200 成功;404 容器不存在;500 服务端错误。

2.7 启动容器:POST /containers/(id)/start

POST /containers/(id)/start HTTP/1.1
Content-Type: application/json

{
  "Binds":["/tmp:/tmp"],
  "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}]
}
HTTP/1.1 204 No Content
Content-Type: text/plain

JSON 参数:hostConfig —— 容器宿主配置(可选)。v1.5 示例中的 LxcConf 反映了该时代容器运行时仍由 LXC 驱动承载;当前 Moby 已全面迁移至 OCI 运行时,HostConfig(见 api/types/container/hostconfig.go)不再包含 LXC 相关字段。

状态码:204 成功;404 容器不存在;500 服务端错误。

2.8 停止 / 重启 / 强制结束容器

三个端点共享相同的参数与状态码模式:

停止 POST /containers/(id)/stop

POST /containers/e90e34656806/stop?t=5 HTTP/1.1

查询参数:t —— 强制结束前等待的秒数。响应 204

重启 POST /containers/(id)/restart

POST /containers/e90e34656806/restart?t=5 HTTP/1.1

查询参数:t —— 强制结束前等待的秒数。响应 204 No Content

强制结束 POST /containers/(id)/kill

POST /containers/e90e34656806/kill HTTP/1.1

响应 204 No Content,无查询参数。

三者状态码均为:204 成功;404 容器不存在;500 服务端错误。

2.9 附加到容器:POST /containers/(id)/attach

这是 v1.5 中最具特色的端点之一,通过连接劫持传输原始 stdio 流:

POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/vnd.docker.raw-stream

{{ STREAM }}

查询参数(均可取 1/True/true 或 0/False/false,默认均为 false):

参数 说明
logs 是否返回日志,默认 false
stream 是否返回实时流,默认 false
stdin 当 stream=true 时是否附加 stdin,默认 false
stdout 当 logs=true 时返回 stdout 日志;当 stream=true 时附加 stdout,默认 false
stderr 当 logs=true 时返回 stderr 日志;当 stream=true 时附加 stderr,默认 false

状态码:200 成功;400 参数错误;404 容器不存在;500 服务端错误。

附:WebSocket 版本 GET /containers/(id)/attach/ws

v1.5 同时提供了按 RFC 6455 规范实现 WebSocket 握手的附加方式:

GET /containers/e90e34656806/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1 HTTP/1.1
{{ STREAM }}

查询参数与状态码同上表。200 成功;400 参数错误;404 容器不存在;500 服务端错误。

2.10 等待容器:POST /containers/(id)/wait

阻塞直到容器 id 停止,然后返回退出码:

POST /containers/16253994b7c4/wait HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{"StatusCode": 0}

状态码:200 成功;404 容器不存在;500 服务端错误。

2.11 删除容器:DELETE /containers/(id)

DELETE /containers/16253994b7c4?v=1 HTTP/1.1
HTTP/1.1 204 No Content

查询参数:v —— 1/True/true 或 0/False/false,是否同时删除容器关联的卷,默认 false。

状态码:204 成功;400 参数错误;404 容器不存在;500 服务端错误。

2.12 从容器复制文件:POST /containers/(id)/copy

POST /containers/4fa6e0f0c678/copy HTTP/1.1
Content-Type: application/json

{
  "Resource":"test.txt"
}
HTTP/1.1 200 OK
Content-Type: application/octet-stream

{{ TAR STREAM }}

状态码:200 成功;404 容器不存在;500 服务端错误。

3. 镜像端点(Images)

3.1 列出镜像:GET /images/(format)

format 可以是 json(默认)或 viz

JSON 格式:

GET /images/json?all=0 HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "Repository":"ubuntu",
    "Tag":"precise",
    "Id":"b750fe79269d",
    "Created":1364102658,
    "Size":24653,
    "VirtualSize":180116135
  },
  {
    "Repository":"ubuntu",
    "Tag":"12.04",
    "Id":"b750fe79269d",
    "Created":1364102658,
    "Size":24653,
    "VirtualSize":180116135
  }
]

viz 格式(graphviz 有向图,可直接用 dot 渲染镜像父子关系):

GET /images/viz HTTP/1.1
HTTP/1.1 200 OK
Content-Type: text/plain

digraph docker {
"d82cbacda43a" -> "074be284591f"
"1496068ca813" -> "08306dc45919"
"08306dc45919" -> "0e7893146ac2"
"b750fe79269d" -> "1496068ca813"
base -> "27cf78414709" [style=invis]
"f71189fff3de" -> "9a33b36209ed"
"27cf78414709" -> "b750fe79269d"
"0e7893146ac2" -> "d6434d954665"
"d6434d954665" -> "d82cbacda43a"
base -> "e9aa60c60128" [style=invis]
"074be284591f" -> "f71189fff3de"
"b750fe79269d" [label="b750fe79269d\nubuntu",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
"e9aa60c60128" [label="e9aa60c60128\ncentos",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
"9a33b36209ed" [label="9a33b36209ed\nfedora",shape=box,fillcolor="paleturquoise",style="filled,rounded"];
base [style=invisible]
}

查询参数:all —— 1/True/true 或 0/False/false,是否显示全部镜像(原文档此处笔误写作 containers,语义应为镜像;默认 false)。

状态码:200 成功;400 参数错误;500 服务端错误。

3.2 创建镜像(拉取或导入):POST /images/create

从 registry 拉取,或从源导入创建镜像:

POST /images/create?fromImage=ubuntu HTTP/1.1

响应为逐行 JSON 进度流

HTTP/1.1 200 OK
Content-Type: application/json

{"status":"Pulling..."}
{"status":"Pulling", "progress":"1/? (n/a)"}
{"error":"Invalid..."}
...

使用本端点从 registry 拉取镜像时,可用 X-Registry-Auth 请求头携带 base64 编码的 AuthConfig 对象(这一机制延续至今,仓库 client/image_pull.go 中仍以 X-Registry-Auth 传递认证信息)。

查询参数:

参数 说明
fromImage 要拉取的镜像名
fromSrc 要导入的源;- 表示 stdin
repo 仓库名
tag 标签
registry 要从中拉取的 registry

状态码:200 成功;500 服务端错误。

3.3 向镜像中插入文件:POST /images/(name)/insert

url 处的文件插入镜像 namepath 路径下:

POST /images/test/insert?path=/usr&url=myurl HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{"status":"Inserting..."}
{"status":"Inserting", "progress":"1/? (n/a)"}
{"error":"Invalid..."}
...

查询参数:url —— 文件来源 URL;path —— 文件存放路径。

状态码:200 成功;500 服务端错误。

3.4 查看镜像详情:GET /images/(name)/json

GET /images/centos/json HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{
  "id":"b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc",
  "parent":"27cf784147099545",
  "created":"2013-03-23T22:24:18.818426-07:00",
  "container":"3d67245a8d72ecf13f33dffac9f79dcdf70f75acb84d308770391510e0c23ad0",
  "container_config":
    {
      "Hostname":"",
      "User":"",
      "Memory":0,
      "MemorySwap":0,
      "AttachStdin":false,
      "AttachStdout":false,
      "AttachStderr":false,
      "PortSpecs":null,
      "Tty":true,
      "OpenStdin":true,
      "StdinOnce":false,
      "Env":null,
      "Cmd": ["/bin/bash"],
      "Dns":null,
      "Image":"centos",
      "Volumes":null,
      "VolumesFrom":"",
      "WorkingDir":""
    },
  "Size": 6824592
}

状态码:200 成功;404 镜像不存在;500 服务端错误。

3.5 获取镜像历史:GET /images/(name)/history

GET /images/fedora/history HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

[
  {
    "Id":"b750fe79269d",
    "Created":1364102658,
    "CreatedBy":"/bin/bash"
  },
  {
    "Id":"27cf78414709",
    "Created":1364068391,
    "CreatedBy":""
  }
]

状态码:200 成功;404 镜像不存在;500 服务端错误。

3.6 推送镜像:POST /images/(name)/push

POST /images/test/push HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{"status":"Pushing..."}
{"status":"Pushing", "progress":"1/? (n/a)"}
{"error":"Invalid..."}
...

同样支持通过 X-Registry-Auth 头携带 base64 编码的 AuthConfig 对象。

状态码:200 成功;404 镜像不存在;500 服务端错误。

3.7 给镜像打标签:POST /images/(name)/tag

POST /images/test/tag?repo=myrepo&force=0&tag=v42 HTTP/1.1
HTTP/1.1 201 OK

查询参数:repo —— 目标仓库;force —— 1/True/true 或 0/False/false,默认 false;tag —— 新标签名。

状态码:201 成功;400 参数错误;404 镜像不存在;409 冲突;500 服务端错误。

3.8 删除镜像:DELETE /images/(name)

DELETE /images/test HTTP/1.1
HTTP/1.1 200 OK
Content-type: application/json

[
  {"Untagged":"3e2f21a89f"},
  {"Deleted":"3e2f21a89f"},
  {"Deleted":"53b4f83ac9"}
]

响应逐条报告哪些标签被移除(Untagged)、哪些层被删除(Deleted)。

状态码:200 成功;404 镜像不存在;409 冲突;500 服务端错误。

3.9 搜索镜像:GET /images/search

在 Docker Hub 上搜索镜像:

GET /images/search?term=sshd HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

[
  { "Name":"cespare/sshd", "Description":"" },
  { "Name":"johnfuller/sshd", "Description":"" },
  { "Name":"dhrp/mongodb-sshd", "Description":"" }
]

查询参数:term —— 搜索词。

状态码:200 成功;500 服务端错误。

4. 杂项端点(Misc)

4.1 通过 stdin 构建镜像:POST /build

POST /build HTTP/1.1

{{ TAR STREAM }}
HTTP/1.1 200 OK

{{ STREAM }}

请求体要求(原文档完整保留):

  • 流必须是使用以下算法之一压缩的 tar 归档:identity(无压缩)、gzip、bzip2、xz;
  • 归档根部必须包含名为 Dockerfile 的文件;
  • 可以包含任意数量的其他文件,它们都会出现在构建上下文中(供 ADD 构建指令使用);
  • Content-Type 头应设置为 application/tar

查询参数:

参数 说明
t 构建成功后应用于结果镜像的仓库名(可带标签)
remote 构建源 URI(git 或 HTTPS/HTTP)
q 抑制冗长的构建输出
nocache 构建时不使用缓存
rm 构建成功后删除中间容器

状态码:200 成功;500 服务端错误。

4.2 校验认证配置:POST /auth

POST /auth HTTP/1.1
Content-Type: application/json

{
  "username":"hannibal",
  "password:"xxxx",
  "email":"hannibal@a-team.com",
  "serveraddress":"https://index.docker.io/v1/"
}
HTTP/1.1 200 OK
Content-Type: text/plain

状态码:200 成功;204 无错误(无内容);500 服务端错误。

4.3 系统级信息:GET /info

GET /info HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{
  "Containers":11,
  "Images":16,
  "Debug":false,
  "NFd": 11,
  "NGoroutines":21,
  "MemoryLimit":true,
  "SwapLimit":false,
  "IPv4Forwarding":true
}

v1.5 的 /info 返回内容非常精简(容器数、镜像数、调试开关、文件描述符数、goroutine 数、内核特性开关)。现代 Moby 的 /info 响应已扩展出 OS、架构、存储驱动、安全配置、CDI 等大量字段,参见 client/system_info.go

状态码:200 成功;500 服务端错误。

4.4 版本信息:GET /version

GET /version HTTP/1.1
HTTP/1.1 200 OK
Content-Type: application/json

{
  "Version":"0.2.2",
  "GitCommit":"5a2a5cc+CHANGES",
  "GoVersion":"go1.0.3"
}

状态码:200 成功;500 服务端错误。

4.5 从容器变更创建新镜像:POST /commit

POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
Content-Type: application/json

{
    "Cmd": ["cat", "/world"],
    "PortSpecs":["22"]
}
HTTP/1.1 201 OK
Content-Type: application/vnd.docker.raw-stream

{"Id": "596069db4bf5"}

查询参数:container —— 源容器;repo —— 仓库;tag —— 标签;m —— 提交说明;author —— 作者(例如 "John Hannibal Smith hannibal@a-team.com")。

状态码:201 成功;404 容器不存在;500 服务端错误。

请求体结构在仓库中有对应类型 api/types/container/commit.goCommitConfig)。

4.6 监控 Docker 事件:GET /events

以实时流式或基于 since 的轮询方式获取事件。容器会报告以下事件:

create, destroy, die, export, kill, pause, restart, start, stop, unpause

镜像会报告:

untag, delete
GET /events?since=1374067924
HTTP/1.1 200 OK
Content-Type: application/json

{"status":"create","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924}
{"status":"start","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067924}
{"status":"stop","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067966}
{"status":"destroy","id":"dfdf82bd3881","from":"ubuntu:latest","time":1374067970}

查询参数:since —— 用于轮询的时间戳。

状态码:200 成功;500 服务端错误。

现代实现中事件类型与载荷定义在 api/types/events 包中,事件类别已从容器/镜像扩展到 network、volume、daemon、plugin 等,并支持按类型过滤(filter 参数)。

5. docker run 的底层调用序列

原文档第 3 节给出了 docker run 在 v1.5 下的完整客户端编排步骤,这也是理解“一条 CLI 命令对应哪些 API 调用”的关键:

  1. 创建容器POST /containers/create);
  2. 若返回状态码 404,说明镜像不存在:先尝试 pullPOST /images/create),然后重试创建容器
  3. 启动容器POST /containers/(id)/start);
  4. 若不在 detached 模式:附加到容器POST /containers/(id)/attach),参数 logs=1(以获取容器启动时的 stdout 和 stderr)且 stream=1
  5. 若处于 detached 模式或仅附加了 stdin:打印容器 Id

5.1 Hijacking:v1.5 的 stdio 传输机制

v1.5 文档明确指出:该版本 API 中 /attach 使用 hijacking 将 stdin、stdout、stderr 复用在同一个 socket 上(“This might change in the future”——事实上这个设计延续到了现代 Moby)。

当前仓库客户端的实现在 client/hijack.go,核心流程(见 setupHijackConn):

  1. 设置 Connection: UpgradeUpgrade: <proto> 请求头;
  2. 建立 TCP 连接并对 net.TCPConn 启用 30 秒 TCP KeepAlive,防止长命令无输出期间被中间网络设备以 ECONNTIMEOUT 切断;
  3. 用自定义 hijackedConnRoundTrip 手动写出请求并解析响应;
  4. 服务端劫持连接后返回 101 Switching Protocols,客户端检查该状态码,否则报 unable to upgrade to <proto>
  5. 若 HTTP 层已有缓冲数据,则用 hijackedConn/hijackedConnCloseWriter 包装连接(后者实现 CloseWriter 以便单向关闭 stdin);
  6. 最终返回 HijackedResponse,其中的 MediaType() 告知调用方拿到的是 raw 流还是多路复用流——这正对应 v1.5 响应头里的 Content-Type: application/vnd.docker.raw-stream

服务端一侧的附加逻辑见 daemon/attach.goContainerAttachstream.AttachConfigUseStdin、日志/流式开关等)把客户端写入的 stdin 通过 goroutine io.Copy 拷入容器,同时把容器 stdout/stderr 反向拷给客户端。这解释了 v1.5 文档中 attach 的 logs/stream/stdin/stdout/stderr 五个参数为何语义互相耦合(例如 stdin 只在 stream=true 时生效)。

5.2 启用 CORS

v1.5 文档还记录了跨域请求的开启方式:在守护进程模式下添加 --api-enable-cors 标志:

$ docker -d -H="192.168.1.9:2375" --api-enable-cors

6. 阅读建议与适用边界

  • 本文所有端点、参数、状态码均取自 api/docs/v1.5.md 原文,适用于理解 Docker 引擎 API 的设计起点,以及阅读早期 API 版本演进文档(api/docs/v1.6.mdapi/docs/v1.55.yaml);
  • 对现代 Moby 引擎发起实际请求时,应以最新版本文档为准:v1.5 中的 attach/wsimages/vizPOST /commit 顶层路径等端点在后续版本已被重新组织(例如 commit 迁移到 /images/(id)/commit,build 迁移到 /build?t=... 并新增 Dockerfile 命名、memorycpu 等构建参数);
  • 源码级对照可重点阅读:api/types/container/container.go(list/inspect 响应结构)、api/types/container/hostconfig.go(宿主配置)、client/hijack.go(连接劫持)、client/client.go(API 版本协商)、daemon/attach.go(服务端 attach 实现)。
登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
982
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384