news 2026/9/5 20:00:11

Moby Remote API v1.7 规格详解:容器/镜像端点全解与 Attach 多路复用流协议

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Moby Remote API v1.7 规格详解:容器/镜像端点全解与 Attach 多路复用流协议

Moby Remote API v1.7 规格详解:容器/镜像端点全解与 Attach 多路复用流协议

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

本文以 Moby 仓库中恢复归档的历史 API 规范 api/docs/v1.7.md 为主体,完整梳理 Docker Remote API v1.7 的全部端点(容器、镜像、杂项三大类共 24+ 个接口)及其请求/响应示例、查询参数与状态码,并结合当前仓库源码佐证其中仍存活的底层机制:/var/run/docker.sock默认监听、attach 流的 8 字节帧头多路复用协议(对应 api/pkg/stdcopy)、HTTP 连接劫持(对应 client/hijack.go)与X-Registry-Auth头部编解码(对应 api/pkg/authconfig),帮助读者既读懂这份早期 REST API 规格,又看清哪些设计延续到了今天的 Moby。

需要说明的历史定位:该文档是 API v1.7(Docker 0.x 时代)的规范快照,通过提交 "api/docs: restore API versions v1.0 - v1.13" 恢复进仓库,作为历史版本存档。当前仓库的 Go 客户端最低只支持 API 1.40、最高 1.56(见 client/client.go 中的MinAPIVersion/MaxAPIVersion常量),因此 v1.7 接口不可被现代客户端直接调用,但其端点结构、状态码语义和流协议是理解整个 Docker API 演进的起点。

1. 总体设计:REST + 连接劫持

v1.7 文档开篇给出三条总体设计原则:

  • Remote API 取代 rcli。rcli 是早期基于 RPC 的远程调用方案,Remote API 改用 HTTP 语义更通用的 REST 风格接口;
  • 默认监听 Unix socket。守护进程默认监听unix:///var/run/docker.sock,也可以绑定到其他 host/port 或另一个 Unix socket;
  • 倾向 REST,但允许劫持。对于attachpull这类需要双向传输stdin/stdout/stderr的复杂命令,HTTP 连接会被 hijack(劫持),后续流量不再受 HTTP 报文边界约束。

第一条原则在今天的仓库中依然成立:守护进程的默认 socket 路径定义于 daemon/pkg/opts/hosts.go 的DefaultUnixSocket = "/var/run/docker.sock",daemon/command/daemon.go 中的defaultAPISocketPath函数在 rootless(rootlessKit)场景下则改为监听$XDG_RUNTIME_DIR/docker.sock——这正是对文档"可以绑定到另一个 Unix socket"的落地。

第三条原则在 client/hijack.go 中能看到完整继承:postHijacked发起 POST 后调用setupHijackConn,后者设置Connection: UpgradeUpgrade: <proto>请求头完成协议升级,等待服务端返回101 Switching Protocols,随后把裸连接连同响应头里的Content-Type一并交给上层——这个Content-Type决定流是原始流还是多路复用流,与 v1.7 attach 端点返回的application/vnd.docker.raw-stream语义一脉相承。

2. 容器端点(v1.7 共 15 个)

2.1 列出容器:GET /containers/json

示例请求

GET /containers/json?all=1&before=8dfafdbc3a40&size=1 HTTP/1.1

示例响应Content-Type: application/json):

[ { "Id": "8dfafdbc3a40", "Image": "base:latest", "Command": "echo 1", "Created": 1367854155, "Status": "Exit 0", "Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}], "SizeRw": 12288, "SizeRootFs": 0 }, { "Id": "9cd87474be90", "Image": "base:latest", "Command": "echo 222222", "Created": 1367854155, "Status": "Exit 0", "Ports": [], "SizeRw": 12288, "SizeRootFs": 0 }, { "Id": "3176a2479c92", "Image": "base:latest", "Command": "echo 3333333333333333", "Created": 1367854154, "Status": "Exit 0", "Ports": [], "SizeRw": 12288, "SizeRootFs": 0 }, { "Id": "4cb07b47f9fb", "Image": "base:latest", "Command": "echo 444444444444444444444444444444444", "Created": 1367854152, "Status": "Exit 0", "Ports": [], "SizeRw": 12288, "SizeRootFs": 0 } ]

查询参数:

参数说明
all1/True/true0/False/false,显示全部容器;默认只显示运行中的(默认 false)
limit只显示最近创建的limit个容器,包括非运行中的
since只显示 Id 之后创建的容器,包括非运行中的
before只显示 Id 之前创建的容器,包括非运行中的
size1/True/true0/False/false,显示容器大小(对应响应中的SizeRw/SizeRootFs字段)

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

2.2 创建容器:POST /containers/create

示例请求

POST /containers/create HTTP/1.1 Content-Type: application/json { "Hostname":"", "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":"base", "Volumes":{"/tmp": {}}, "VolumesFrom":"", "WorkingDir":"", "ExposedPorts":{"22/tcp": {}} }

示例响应

HTTP/1.1 201 Created Content-Type: application/json { "Id":"e90e34656806" "Warnings":[] }

请求体是"容器配置(config)"对象:Memory/MemorySwap是内存与内存+交换上限(0 表示不限制),AttachStdin/Stdout/Stderr决定 attach 时可用的流,Tty决定是否分配伪终端(该字段直接影响后文 attach 流是原始流还是多路复用流),ExposedPorts声明镜像级端口暴露。

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

对照现代 API:v1.7 把"创建配置"与"主机配置"拆在 create/start 两个端点里,这正是后来ContainerCreateConfig+HostConfig分离结构的雏形。

2.3 检查容器:GET /containers/(id)/json

返回容器的低层信息。

示例请求

GET /containers/4fa6e0f0c678/json HTTP/1.1

示例响应(节选关键字段):

{ "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": "base", "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": {} }

响应结构是三层:Config(创建时的静态配置)、State(运行态:RunningPidExitCodeStartedAtGhost)、NetworkSettings(早期单体网络模型下的 IP 前缀、网关、桥接与端口映射)。状态码:200成功;404容器不存在;500服务端错误。

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

示例请求

GET /containers/4fa6e0f0c678/top HTTP/1.1

示例响应

{ "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),TitlesProcesses的每一行即按该格式对齐。状态码:200/404/500

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

示例请求

GET /containers/4fa6e0f0c678/changes HTTP/1.1

示例响应

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

Kind是变更类型编号(0/1/2 分别对应未变更/修改/删除的编码)。状态码:200/404/500

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

示例请求

GET /containers/4fa6e0f0c678/export HTTP/1.1

示例响应HTTP/1.1 200 OKContent-Type: application/octet-stream,响应体为{{ TAR STREAM }}(tar 数据流)。状态码:200/404/500

2.7 生命周期控制:start / stop / restart / kill

启动POST /containers/(id)/start。与创建不同,start 可附带"主机配置(hostConfig)":

POST /containers/(id)/start HTTP/1.1 Content-Type: application/json { "Binds":["/tmp:/tmp"], "LxcConf":[{"Key":"lxc.utsname","Value":"docker"}], "PortBindings":{ "22/tcp": [{ "HostPort": "11022" }] }, "Privileged":false, "PublishAllPorts":false }

注意文档特别强调:Binds必须引用容器创建时已定义的 Volumes。响应为HTTP/1.1 204 No Content

停止POST /containers/(id)/stop

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

重启POST /containers/(id)/restart

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

stop 与 restart 共享查询参数t—— 超时秒数,到时未退出则 kill。响应均为HTTP/1.1 204 No Content

强制终止POST /containers/(id)/kill

POST /containers/e90e34656806/kill HTTP/1.1

响应HTTP/1.1 204 No Content。四个端点状态码一致:204成功;404容器不存在;500服务端错误。

2.8 附加到容器:POST /containers/(id)/attach(本版本最复杂的端点)

示例请求

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/true0/False/false,默认 false):

参数说明
logs返回日志
stream返回流
stdinstream=true时附加到 stdin
stdoutlogs=true时返回 stdout 日志;stream=true时附加到 stdout
stderrlogs=true时返回 stderr 日志;stream=true时附加到 stderr

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

流细节(本规范的核心算法,必须完整掌握)

  • 创建容器时若启用了 TTY,流就是进程 PTY 与客户端 stdin 的原始数据(raw stream);
  • 若未启用 TTY,流会做多路复用(multiplexed),把 stdout 与 stderr 交织在同一条字节流里,格式为帧头(Header)+ 载荷(Payload)
header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}

其中STREAM_TYPE0= stdin(读取时写入 stdout)、1= stdout、2= stderr;SIZE1..SIZE4为 big-endian 编码的 uint32 帧长。文档给出的最简实现循环:

  1. 读 8 字节帧头;
  2. 根据第 1 个字节选择 stdout 或 stderr;
  3. 从后 4 字节解析帧大小;
  4. 读取该大小的载荷并输出到对应流;
  5. 回到第 1 步。

源码印证:这段 2013 年的协议描述与今天仓库中的 api/pkg/stdcopy/stdcopy.go 完全同构。该包定义Stdin=0Stdout=1Stderr=2三个流类型常量(并新增Systemerr=3用于守护进程侧错误),帧头长度stdWriterPrefixLen = 8、流类型位于偏移 0、帧长位于偏移 4 且按binary.BigEndian.Uint32解码;StdCopy函数实现的就是"读帧头 → 按第 1 字节分流 → 按后 4 字节读帧长 → 输出到 destOut/destErr"的循环。可以说 v1.7 文档里的手工解析步骤,就是现代 Go SDK 中stdcopy.StdCopy的算法原型,这套帧格式沿用至今未变。

2.9 通过 WebSocket 附加:GET /containers/(id)/attach/ws

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

响应体直接是{{ STREAM }}。该端点按 RFC 6455 完成 WebSocket 握手,查询参数与状态码同上一节 attach。它是早期浏览器端交互(如当年的 Web UI)的通道,后续版本被POST .../attach+ 原生 WebSocket 升级取代。

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

查询参数:v——1/True/true0/False/false,是否同时移除容器关联的卷,默认 false。响应HTTP/1.1 204 No Content。状态码: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 OKContent-Type: application/octet-stream,响应体为{{ TAR STREAM }}(即后来docker cpGET /containers/(id)/archive的前身)。状态码:200/404/500

3. 镜像端点

3.1 列出镜像:GET /images/json

GET /images/json?all=0 HTTP/1.1
[ { "RepoTags": ["ubuntu:12.04", "ubuntu:precise", "ubuntu:latest"], "Id": "8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c", "Created": 1365714795, "Size": 131506275, "VirtualSize": 131506275 }, { "RepoTags": ["ubuntu:12.10", "ubuntu:quantal"], "ParentId": "27cf784147099545", "Id": "b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc", "Created": 1364102658, "Size": 24653, "VirtualSize": 180116135 } ]

Size是镜像自身层大小,VirtualSize是含父层在内的总大小;RepoTags数组体现一个镜像 ID 可挂多个标签、无标签时RepoTags为空(即 dangling image)。

3.2 创建镜像(pull 或 import):POST /images/create

POST /images/create?fromImage=base 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 对象。

查询参数:

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

请求头:X-Registry-Auth—— base64 编码的 AuthConfig 对象。状态码:200成功;500服务端错误。

源码印证:这个请求头的编解码规则在今天的仓库中由 api/pkg/authconfig/authconfig.go 承担——Encoderegistry.AuthConfig序列化为 base64url(RFC 4648 第 5 节)编码的 JSON 字符串用于X-Registry-Auth头;Decode反向解码且"即使出错也返回空 AuthConfig"的兼容性策略,注释明确写着是为了兼容旧客户端与旧 API 版本。v1.7 文档确立的"认证走请求头、正文走进度流"模式即为今日规范。

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

url取文件插入到镜像namepath路径:

POST /images/test/insert?path=/usr&url=myurl HTTP/1.1

响应同样是逐行 JSON 进度流({"status":"Inserting..."}等)。查询参数:url(文件来源)、path(存储路径)。状态码:200/500。该端点是早期"不构建、直接改镜像"的能力,后由 Dockerfile + build 流程取代并从 API 中移除。

3.4 检查镜像:GET /images/(name)/json

GET /images/base/json HTTP/1.1
{ "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":"base", "Volumes":null, "VolumesFrom":"", "WorkingDir":"" }, "Size": 6824592 }

字段要点:parent构成镜像层链;container记录该层由哪个容器 commit 而来;container_config快照了构建该层时容器的运行配置(注意此层Tty:trueOpenStdin:true,与 2.2 节 create 示例的差异)。状态码:200/404/500

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

GET /images/base/history HTTP/1.1
[ {"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

响应为逐行 JSON 进度流({"status":"Pushing..."}等),请求头同样支持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(默认 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 中搜索镜像。文档特别注明:从 API v1.6 起响应键名已变更,以对齐 registry 服务端返回给守护进程的 JSON。

GET /images/search?term=sshd HTTP/1.1
[ { "description": "", "is_official": false, "is_trusted": false, "name": "wma55/u1210sshd", "star_count": 0 }, { "description": "", "is_official": false, "is_trusted": false, "name": "jdswinbank/sshd", "star_count": 0 }, { "description": "", "is_official": false, "is_trusted": false, "name": "vgauthier/sshd", "star_count": 0 } ]

查询参数:term(搜索词)。状态码:200/500

3.10 导出/加载镜像 tar 包:GET /images/(name)/getPOST /images/load

导出GET /images/ubuntu/get返回包含该仓库全部镜像与元数据的 tar 包,Content-Type: application/x-tar,状态码200/500

加载POST /images/load,请求体即 tar 包,把一组镜像与标签载入本地仓库,响应HTTP/1.1 200 OK,状态码200/500。二者即今天docker save/docker load的 API 原型。

4. 杂项端点

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

POST /build HTTP/1.1 {{ TAR STREAM }}

响应:HTTP/1.1 200 OKContent-Type: application/json,响应体为{{ STREAM }}(构建进度流)。

约束:tar 流必须使用 identity(不压缩)、gzip、bzip2、xz 之一压缩(原文如此表述,实际指这几种编码之一),且归档根部必须包含名为Dockerfile的文件;归档中的其他文件都可作为构建上下文在ADD指令中使用。

查询参数:

参数说明
t构建成功后应用到镜像的仓库名(可含标签)
remote构建来源 URI(git 或 HTTPS/HTTP)
q抑制详细构建输出
nocache构建时不使用缓存

请求头:Content-type应设为application/tar。状态码: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 OK204(均为成功,200 时返回校验后的 username/email 信息)。状态码:200/204成功;500服务端错误。该端点是docker login校验凭据的通道,请求体即后来registry.AuthConfig的前身(对照 api/types/registry 下今天的 AuthConfig 类型可看到字段演化)。

4.3 系统信息:GET /info

GET /info HTTP/1.1
{ "Containers":11, "Images":16, "Debug":false, "NFd": 11, "NGoroutines":21, "MemoryLimit":true, "SwapLimit":false, "IPv4Forwarding":true }

字段含容器/镜像计数、守护进程调试开关、打开的文件描述符数与 goroutine 数(典型的 Go 运行时自省指标)、以及内存/交换限制与 IPv4 转发能力。状态码:200/500

4.4 版本信息:GET /version

GET /version HTTP/1.1
{ "Version":"0.2.2", "GitCommit":"5a2a5cc+CHANGES", "GoVersion":"go1.0.3" }

状态码:200/500。这个端点同时是 API 版本协商的载体:现代客户端正是通过/version(Ping)拿到守护进程的ApiVersion后再进行协商,见 client/client.go 的negotiateAPIVersion逻辑。

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

POST /commit?container=44c004db4b17&m=message&repo=myrepo HTTP/1.1
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")
run镜像运行时自动应用的配置,如{"Cmd": ["cat", "/world"], "PortSpecs":["22"]}

状态码:201成功;404容器不存在;500服务端错误。响应中的新Id即 3.4 节 inspect 中container_config快照的来源机制(commit 把容器当前状态固化为新镜像层)。

4.6 监控事件: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": "base:latest", "time":1374067924} {"status": "start", "id": "dfdf82bd3881","from": "base:latest", "time":1374067924} {"status": "stop", "id": "dfdf82bd3881","from": "base:latest", "time":1374067966} {"status": "destroy", "id": "dfdf82bd3881","from": "base:latest", "time":1374067970}

查询参数:since—— 轮询用的时间戳。状态码:200/500

5. 深入理解:docker run背后的端点编排

v1.7 文档第 3.1 节给出了docker run客户端的完整编排步骤,这是理解"一条命令 = 一组 API 调用"的关键:

  1. 创建容器POST /containers/create);
  2. 若返回404,说明镜像不存在:
    • 尝试拉取(POST /images/create);
    • 然后重试创建容器;
  3. 启动容器POST /containers/(id)/start);
  4. 非分离(detached)模式下:
    • 附加到容器,使用logs=1(以获得容器启动以来的 stdout 与 stderr)和stream=1
  5. 分离模式或仅附加 stdin 时:
    • 打印容器 ID。

这套"create → 404 则 pull → 重试 → start → attach"的幂等重试模式,至今仍是各类 Docker 客户端 SDK 实现run语义的标准流程。

5.1 Hijacking 机制

v1.7 文档第 3.2 节明确指出:本版本 API 中/attach使用 hijacking 在同一 socket 上同时传输 stdin、stdout 与 stderr,"未来可能改变"。从当前仓库源码看,这个机制不但没有消失,反而被标准化了:

  • client/hijack.go 的setupHijackConn通过Connection: Upgrade/Upgrade头完成协议切换,并针对长空闲连接(长时间无输出的命令)设置 TCP KeepAlive(30 秒周期),注释里说明这是为了规避某些网络环境下 ECONNTIMEOUT 导致的客户端状态不确定——这正是 v1.7 时代 hijack 流在弱网络上踩过的坑的工程化修复;
  • 响应头Content-TypeHijackedResponse.MediaType()暴露给上层,用于判定拿到的是原始流还是多路复用流,进而决定是否走 api/pkg/stdcopy 拆帧。

5.2 跨域请求(CORS)

文档第 3.3 节:允许对远程 API 的跨域请求,需要在守护进程模式启动时加--api-enable-cors标志:

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

需要说明:--api-enable-cors是 v1.7 时代的守护进程标志,在当前仓库的 Go 源码中已检索不到该标志的实现(grepapi-enable-cors/APIEnableCORS无结果),它属于历史演进中被移除的选项;今天跨域支持由守护进程配置文件/--cors-header等形式承载。引用该段落时应以其历史语境为准。

6. 结语:从 v1.7 到现代 Moby API 的传承

通读 api/docs/v1.7.md 并结合仓库源码,可以清晰地看到三条延续至今的主线:

  1. 端点资源模型/containers/*/images/*/build/events的资源划分与"逐行 JSON 进度流"响应风格,直接演化为今天 api/swagger.yaml 与 api/docs/v1.25.yaml 等现代版本的 OpenAPI 规范;
  2. 流协议:attach 的 8 字节帧头多路复用格式([STREAM_TYPE,0,0,0,SIZE1..SIZE4],big-endian uint32)被 api/pkg/stdcopy 逐字节实现并沿用至今,是该文档最有长期价值的算法资产;
  3. 认证与版本协商X-Registry-Authbase64 头由 api/pkg/authconfig 承接,/version端点则是客户端协商 API 版本(当前客户端支持 1.40~1.56)的入口,见 client/client.go。

同时应注意适用边界:v1.7 中的insert、WebSocket attach(attach/ws)、--api-enable-cors等均为已被取代的历史特性;若要在当前 Moby 上编写客户端,应以 api/docs 下最高版本规范(如 api/docs/v1.55.yaml)为准,本文档的价值在于理解 API 演进的源头。

【免费下载链接】mobyThe Moby Project - a collaborative project for the container ecosystem to assemble container-based systems项目地址: https://gitcode.com/GitHub_Trending/mo/moby

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/5 19:57:41

Android在线教育App源码:从工程骨架到商用产品的深度实践指南

简介&#xff1a;这是一套功能完备、可商用的Android在线教育App源码&#xff0c;面向教育科技创业者、移动开发工程师及高校教学平台建设者&#xff0c;解决在线课堂实时互动、多端适配与高并发部署等核心难题。资源包含1257个文件&#xff0c;以377个Java业务逻辑文件、454个…

作者头像 李华
网站建设 2026/9/5 19:54:12

Flipper Zero 固件安装完整指南:DFU 刷写与 SD 卡更新 5 步完成

Flipper Zero 固件安装完整指南&#xff1a;DFU 刷写与 SD 卡更新 5 步完成 【免费下载链接】awesome-flipperzero &#x1f42c; A collection of awesome resources for the Flipper Zero device. 项目地址: https://gitcode.com/GitHub_Trending/aw/awesome-flipperzero …

作者头像 李华