Moby Remote API v1.5 全解:Docker 引擎早期 HTTP 接口的设计、端点与 Hijack 机制
本篇以 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 风格,但对
attach、pull这类复杂命令,会劫持(hijack)HTTP 连接来传输 stdout、stdin 和 stderr 的原始字节流。
当前 Moby 仓库中的 Dockerfile 注释里仍能看到 --host=tcp://0.0.0.0:2375 这一默认端口的用法示例,说明 2375 作为明文 API 端口的约定一直延续至今。
1.1 与当前仓库 API 版本的对比
需要说明的适用前提:v1.5 是 API 版本演进序列中的早期版本,仓库中保留了从 v1.0 到 v1.55 的完整版本文档。现代客户端(本仓库 client 模块)在发起请求时会与服务端进行 API 版本协商——从 client/client.go 的 negotiateAPIVersion 可以看到,客户端会解析 /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,包含 Driver、Platform、RestartCount、Mounts 等 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 中的 Summary,SizeRw/SizeRootFs、Ports、Created 等字段一脉相承,此外还新增了 Names、Labels、Health、NetworkSettings 等字段——这正是 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 时代的配置字段是扁平化的单对象(Memory、PortSpecs、Volumes 混在一起)。当前仓库中容器配置已按语义拆分为 api/types/container/config.go(容器自身配置 Config)与 api/types/container/hostconfig.go(宿主侧配置 HostConfig),创建请求则封装在 api/types/container/create_request.go 中——这解释了为什么现代 API 的 create 请求同时包含 config 与 hostConfig 两个键。
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 中的 State:Ghost 字段被更精细的 Status 字符串(created/running/paused/restarting/removing/exited/dead)、Paused、OOMKilled、FinishedAt、Health 取代。
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.go 与 api/types/container/change_types.go,v1.5 示例中的 0/1 对应“修改/新增”类变更,现代版本将其规范化为 ChangeModify、ChangeAdd、ChangeDelete 常量。
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
状态码: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
查询参数(均可取 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
状态码: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 处的文件插入镜像 name 的 path 路径下:
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
HTTP/1.1 200 OK
请求体要求(原文档完整保留):
- 流必须是使用以下算法之一压缩的 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.go(CommitConfig)。
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 调用”的关键:
- 创建容器(
POST /containers/create); - 若返回状态码 404,说明镜像不存在:先尝试 pull(
POST /images/create),然后重试创建容器; - 启动容器(
POST /containers/(id)/start); - 若不在 detached 模式:附加到容器(
POST /containers/(id)/attach),参数logs=1(以获取容器启动时的 stdout 和 stderr)且stream=1; - 若处于 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):
- 设置
Connection: Upgrade与Upgrade: <proto>请求头; - 建立 TCP 连接并对
net.TCPConn启用 30 秒 TCP KeepAlive,防止长命令无输出期间被中间网络设备以ECONNTIMEOUT切断; - 用自定义
hijackedConn的RoundTrip手动写出请求并解析响应; - 服务端劫持连接后返回
101 Switching Protocols,客户端检查该状态码,否则报unable to upgrade to <proto>; - 若 HTTP 层已有缓冲数据,则用
hijackedConn/hijackedConnCloseWriter包装连接(后者实现CloseWriter以便单向关闭 stdin); - 最终返回
HijackedResponse,其中的MediaType()告知调用方拿到的是 raw 流还是多路复用流——这正对应 v1.5 响应头里的Content-Type: application/vnd.docker.raw-stream。
服务端一侧的附加逻辑见 daemon/attach.go:ContainerAttach 按 stream.AttachConfig(UseStdin、日志/流式开关等)把客户端写入的 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.md 至 api/docs/v1.55.yaml);
- 对现代 Moby 引擎发起实际请求时,应以最新版本文档为准:v1.5 中的
attach/ws、images/viz、POST /commit顶层路径等端点在后续版本已被重新组织(例如 commit 迁移到/images/(id)/commit,build 迁移到/build?t=...并新增 Dockerfile 命名、memory、cpu等构建参数); - 源码级对照可重点阅读:api/types/container/container.go(list/inspect 响应结构)、api/types/container/hostconfig.go(宿主配置)、client/hijack.go(连接劫持)、client/client.go(API 版本协商)、daemon/attach.go(服务端 attach 实现)。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00