首页
/ Moby Remote API v1.16 深度解析:容器与镜像端点全解及多路复用流协议

Moby Remote API v1.16 深度解析:容器与镜像端点全解及多路复用流协议

2026-09-05 11:34:26作者:凌朦慧Richard

本文以 Moby 仓库中的历史 API 规范文档 api/docs/v1.16.md 为主体,完整还原 Docker Remote API v1.16 的端点定义:容器生命周期管理、镜像仓库交互、构建与事件流、以及 attach/exec 背后基于 HTTP 连接劫持的 stdout/stderr 多路复用协议。读完本文,你将掌握 v1.16 版本 API 的完整请求/响应格式、查询参数与状态码语义,并对照当前仓库源码(版本协商中间件、流拆包器、hijack 客户端)验证这套协议的实现依据与后续演进。

1. v1.16 Remote API 的定位与传输形态

v1.16 规范开篇给出三条基本事实:

  • Remote API 取代了 rcli:早期 Docker 的远程客户端 rcli 被统一的 HTTP REST API 取代;
  • 默认监听 Unix socket:daemon 监听 unix:///var/run/docker.sock,也可绑定到其它 host/port 或 Unix socket;
  • 以 REST 为主,流式场景使用 hijack:API 总体倾向于 REST 风格,但对于 attachpull 等复杂命令,HTTP 连接会被"劫持"(hijack),在同一条连接上双向传输 STDOUTSTDINSTDERR

从源码结构看,当前仓库中这套 API 的入口组织在 daemon/server/router/ 下按资源划分(容器、镜像、exec、系统等),版本协商则由 daemon/server/middleware/version.go 完成:每个请求携带的 API 版本若低于 minAPIVersion 或高于 daemon 的默认版本,会返回 versionUnsupportedError,同时每个响应都会带上 Api-Version 头(见 version.go)。

需要注意适用前提:当前仓库 daemon/config/config.go 中定义的版本范围是 MaxAPIVersion = "1.56"MinAPIVersion = "1.24",即 v1.16 属于历史规范版本,已低于当前 daemon 支持的下限,但它的端点划分、查询参数与流协议仍是理解整个 Remote API 体系的基线,本文按原文档完整继承其内容。

2. 容器端点(2.1 Containers)

2.1 列出容器:GET /containers/json

示例请求

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

示例响应(节选,字段与原文档一致):

[
    {
        "Id": "8dfafdbc3a40",
        "Names": ["/boring_feynman"],
        "Image": "ubuntu:latest",
        "Command": "echo 1",
        "Created": 1367854155,
        "Status": "Exit 0",
        "Ports": [{"PrivatePort": 2222, "PublicPort": 3333, "Type": "tcp"}],
        "SizeRw": 12288,
        "SizeRootFs": 0
    },
    {
        "Id": "9cd87474be90",
        "Names": ["/coolName"],
        "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,返回容器大小信息(即响应中的 SizeRw/SizeRootFs 字段)
filters JSON 编码的 map[string][]string 过滤器。可用:exited=<int>(按退出码过滤)、`status=(restarting

状态码:200 无错误;400 参数错误;500 服务器错误。

演进佐证:在当前仓库的 client/container_list.go 中,all/limit/size/filters 仍然以相同语义映射为查询参数,但 SinceBefore 选项已被标记为废弃(自 docker 1.12 / API 1.24 起改用 since/before filter 代替)——v1.16 文档中的 since/before 查询参数正是这次演变的上一代形态。

2.2 创建容器:POST /containers/create

示例请求

POST /containers/create HTTP/1.1
Content-Type: application/json
{
    "Hostname": "",
    "Domainname": "",
    "User": "",
    "Memory": 0,
    "MemorySwap": 0,
    "CpuShares": 512,
    "Cpuset": "0,1",
    "AttachStdin": false,
    "AttachStdout": true,
    "AttachStderr": true,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": ["FOO=bar", "BAZ=quux"],
    "Cmd": ["date"],
    "Entrypoint": "",
    "Image": "ubuntu",
    "Volumes": {"/tmp": {}},
    "WorkingDir": "",
    "NetworkDisabled": false,
    "MacAddress": "12:34:56:78:9a:bc",
    "ExposedPorts": {"22/tcp": {}},
    "SecurityOpt": [],
    "HostConfig": {
        "Binds": ["/tmp:/tmp"],
        "Links": ["redis3:redis"],
        "LxcConf": {"lxc.utsname": "docker"},
        "PortBindings": {"22/tcp": [{"HostPort": "11022"}]},
        "PublishAllPorts": false,
        "Privileged": false,
        "Dns": ["8.8.8.8"],
        "DnsSearch": [""],
        "ExtraHosts": null,
        "VolumesFrom": ["parent", "other:ro"],
        "CapAdd": ["NET_ADMIN"],
        "CapDrop": ["MKNOD"],
        "RestartPolicy": {"Name": "", "MaximumRetryCount": 0},
        "NetworkMode": "bridge",
        "Devices": []
    }
}

示例响应

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

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

顶层 JSON 参数(完整继承原文档):

  • Hostname:容器内使用的主机名;
  • Domainname:容器内使用的域名;
  • User:容器内运行命令的用户;
  • Memory:内存限制(字节);
  • MemorySwap:总内存限制(内存 + swap);设为 -1 启用无限 swap;
  • CpuShares:CPU 相对权重(相对于其它容器的权重);
  • Cpuset:cgroups cpuset,如 "0,1"
  • AttachStdin / AttachStdout / AttachStderr:布尔值,是否附加对应标准流;
  • Tty:是否将标准流附加到 TTY(含未关闭的 stdin);
  • OpenStdin:是否打开 stdin;
  • StdinOnce:一个 attach 客户端断开后关闭 stdin;
  • Env["VAR=value", "VAR2=value2"] 形式的环境变量列表;
  • Cmd:要运行的命令(字符串或字符串数组);
  • Entrypoint:入口点(字符串或字符串数组);
  • Image:镜像名;
  • Volumes:挂载点路径到空对象的映射;
  • WorkingDir:工作目录;
  • NetworkDisabled:为 true 时禁用容器网络;
  • ExposedPorts{"<port>/<tcp|udp>: {}}" 形式的端口暴露映射;
  • SecurityOpt:用于 SELinux 等 MLS 系统的标签定制列表。

HostConfig 参数

  • Binds:卷绑定列表,三种形态——container_path(为容器新建卷)、host_path:container_path(绑定挂载宿主路径)、host_path:container_path:ro(只读挂载);
  • Links:链接列表,形如 "container_name:alias"
  • LxcConf:LXC 专属配置(仅在 lxc 执行驱动下生效,该字段后来在 v1.22 中被移除);
  • PortBindings{ "<port>/<protocol>": [{"HostPort": "<port>"}] },注意 port 是字符串而非整数;
  • PublishAllPorts:为所有暴露端口随机分配宿主端口(布尔值);
  • Privileged:授予容器对宿主机的完全访问权限(布尔值);
  • Dns / DnsSearch:DNS 服务器 / DNS 搜索域列表;
  • ExtraHosts:追加到容器 /etc/hosts 的映射,形式 ["hostname:IP"]
  • VolumesFrom:从其它容器继承卷,形式 <container name>[:<ro|rw>]
  • CapAdd / CapDrop:添加/移除的内核 capability 列表;
  • RestartPolicyName"always"(总是重启)或 "on-failure"(退出码非零才重启);on-failureMaximumRetryCount 控制重试上限;默认不重启。每次重启前会加入递增延迟(上次延迟的两倍,起始 100ms),防止"重启风暴"打垮服务端;
  • NetworkMode:网络模式,支持 bridgehostnonecontainer:<name|id>
  • Devices:设备映射,形如 {"PathOnHost": "/dev/deviceName", "PathInContainer": "/dev/deviceName", "CgroupPermissions": "mrw"}

查询参数name —— 为容器指定名称,必须匹配 /?[a-zA-Z0-9_-]+

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

2.3 查看容器详情:GET /containers/(id or name)/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": {},
    "HostConfig": {
        "Binds": null,
        "ContainerIDFile": "",
        "LxcConf": [],
        "Privileged": false,
        "PortBindings": {
            "80/tcp": [{"HostIp": "0.0.0.0", "HostPort": "49153"}]
        },
        "Links": ["/name:alias"],
        "PublishAllPorts": false,
        "CapAdd": ["NET_ADMIN"],
        "CapDrop": ["MKNOD"]
    }
}

状态码:200 无错误;404 无此容器;500 服务器错误。

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

在 Unix 系统上通过执行 ps 命令实现;Windows 不支持。默认 ps_args-ef,可用 ?ps_args=aux 切换:

GET /containers/4fa6e0f0c678/top HTTP/1.1
{
    "Titles": ["UID", "PID", "PPID", "C", "STIME", "TTY", "TIME", "CMD"],
    "Processes": [
        ["root", "13642", "882", "0", "17:03", "pts/0", "00:00:00", "/bin/bash"],
        ["root", "13735", "13642", "0", "17:06", "pts/0", "00:00:00", "sleep 10"]
    ]
}
GET /containers/4fa6e0f0c678/top?ps_args=aux HTTP/1.1
{
    "Titles": ["USER", "PID", "%CPU", "%MEM", "VSZ", "RSS", "TTY", "STAT", "START", "TIME", "COMMAND"],
    "Processes": [
        ["root", "13642", "0.0", "0.1", "18172", "3184", "pts/0", "Ss", "17:03", "0:00", "/bin/bash"],
        ["root", "13895", "0.0", "0.0", "4348", "692", "pts/0", "S+", "17:15", "0:00", "sleep 10"]
    ]
}

状态码:200 无错误;404 无此容器;500 服务器错误。

2.5 获取容器日志:GET /containers/(id or name)/logs

GET /containers/4fa6e0f0c678/logs?stderr=1&stdout=1&timestamps=1&follow=1&tail=10 HTTP/1.1

响应 Content-Type: application/vnd.docker.raw-stream,正文为 {{ STREAM }}

查询参数(全部默认 false,除 tail 默认 all):

  • follow – 是否返回流式日志;
  • stdout / stderr – 是否分别返回 stdout / stderr 日志;
  • timestamps – 是否为每行日志打印时间戳;
  • tail – 只输出末尾指定行数:all<number>

状态码:200 / 404 / 500。

2.6 文件系统变更、导出与 TTY 调整

查看变更:GET /containers/(id or name)/changes —— 返回容器文件系统的变更列表,Kind 字段表示变更类型(原文示例中 0 为未变更、1 为修改):

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

导出容器:GET /containers/(id or name)/export —— 以 application/octet-stream 返回 {{ TAR STREAM }}(tar 流)。

调整 TTY:POST /containers/(id or name)/resize?h=<height>&w=<width> —— v1.16 版本中 resize 需要重启容器才生效

POST /containers/4fa6e0f0c678/resize?h=40&w=80 HTTP/1.1

状态码 200;404 无此容器;500 无法 resize。

这三个端点的状态码均为 200 / 404 / 500。

2.7 生命周期操作:start / stop / restart / kill / pause / unpause

  • 启动:POST /containers/(id or name)/start。注意:为向后兼容,该端点在 v1.16 中接受 JSON 编码的 HostConfig 作为请求体(见创建容器一节);此后不再接受(v1.24 起移除)。成功 204,已启动 304,无此容器 404,服务器错误 500。
  • 停止:POST /containers/(id or name)/stop,查询参数 t 为强杀前等待的秒数。成功 204,已停止 304,404,500。
  • 重启:POST /containers/(id or name)/restart,同样支持 t 参数。204 / 404 / 500。
  • 强杀:POST /containers/(id or name)/kill,查询参数 signal 可以是信号整数或字符串(如 SIGINT);未指定时默认 SIGKILL,且调用会阻塞等待容器退出。204 / 404 / 500。
  • 暂停 / 恢复:POST /containers/(id or name)/pause/unpause。204 / 404 / 500。

2.8 附加到容器:POST /containers/(id or name)/attach

POST /containers/16253994b7c4/attach?logs=1&stream=0&stdout=1 HTTP/1.1

响应 Content-Type: application/vnd.docker.raw-stream

查询参数(均默认 false):

  • logs – 返回历史日志;
  • stream – 返回实时流;
  • stdin – 若 stream=true,附加到 stdin;
  • stdout – 若 logs=true 返回 stdout 日志;若 stream=true 附加到 stdout;
  • stderr – 对 stderr 同理。

状态码:200;400 参数错误;404;500。

流协议(Stream details) —— 这是 v1.16 文档中最关键的技术细节之一:

  • 创建容器时若启用了 Tty,流就是进程 PTY 与客户端 stdin 的原始数据
  • 若 TTY 关闭,流是多路复用的,用帧格式区分 stdout 与 stderr。

帧 = Header(8 字节)+ Payload

header := [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}
  • STREAM_TYPE0 = stdin(写到 stdout 侧)、1 = stdout、2 = stderr;
  • SIZE1..SIZE4:载荷长度(uint32,大端编码)。

文档给出的最简实现循环:

  1. 读 8 字节;
  2. 按第 1 个字节选择 stdout 或 stderr;
  3. 从最后 4 字节解出帧长;
  4. 读取该长度字节并写到对应输出;
  5. 回到第 1 步。

WebSocket 版本:GET /containers/(id or name)/attach/ws —— 查询参数与 attach 相同,按 RFC 6455 完成 WebSocket 握手:

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

源码印证:这套 8 字节帧协议在当前仓库中仍然原样存在。api/pkg/stdcopy/stdcopy.go 定义了 Stdin = 0Stdout = 1Stderr = 2,以及 stdWriterPrefixLen = 8stdWriterFdIndex = 0stdWriterSizeIndex = 4StdCopy() 函数(stdcopy.go)逐帧读取、按首字节分流到 destOut/destErr、用 binary.BigEndian.Uint32 解析帧长——与 v1.16 文档中的五步实现一一对应。

2.9 等待 / 删除 / 拷贝

  • 等待:POST /containers/(id or name)/wait —— 阻塞到容器停止并返回退出码:响应体 {"StatusCode": 0}。200 / 404 / 500。
  • 删除:DELETE /containers/(id or name) —— 参数 v(同时删除关联卷)、force(先 kill 再删)。204;400 参数错误;404;500。
  • 从容器拷贝文件:POST /containers/(id or name)/copy —— 请求体 {"Resource": "test.txt"},响应 application/x-tar{{ TAR STREAM }}。200 / 404 / 500。该端点后来在 API v1.24 中被移除(见 api/docs/CHANGELOG.md 中 v1.24 条目)。

2.10 Exec 四端点

  • Exec Create:POST /containers/(id or name)/exec —— 在运行中的容器里建立 exec 实例。JSON 参数:AttachStdinAttachStdoutAttachStderrTtyCmd。响应 201,返回 {"Id": "f90e34656806"}。404 表示无此容器。
  • Exec Start:POST /exec/(id)/start —— JSON 参数 Detach(分离模式)、TtyDetach=true 时启动后立即返回;否则建立交互式会话,响应流行为与 attach 完全一致(8 字节帧多路复用)。
  • Exec Resize:POST /exec/(id)/resize?h=&w= —— 仅在 exec 创建与启动时指定了 tty 才有效。201 / 404。
  • Exec Inspect:GET /exec/(id)/json —— 返回 IDRunningExitCodeProcessConfig(含 privilegeduserttyentrypointarguments)以及完整的 Container 快照(State、Config、NetworkSettings、Volume 路径等)。200 / 404 / 500。

客户端侧对应实现可参见 client/container_exec.go

3. 镜像端点(2.2 Images)

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
    }
]

查询参数all(默认 false);filters(JSON 编码,可用 dangling=true);filter(仅返回指定名称的镜像,该参数后被 filters 取代)。

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

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

响应是 JSON 进度流

{"status": "Pulling..."}
{"status": "Pulling", "progress": "1 B/ 100 B", "progressDetail": {"current": 1, "total": 100}}
{"error": "Invalid..."}

查询参数

  • fromImage – 要拉取的镜像名;
  • fromSrc – 导入来源,可以是 URL,也可以是 -(从请求体读取镜像);
  • repo – 仓库名;
  • tag – 标签。

请求头X-Registry-Auth – base64 编码的 AuthConfig 对象(拉取私有仓库时携带认证)。状态码:200 / 500。

3.3 其余镜像端点

  • 查看:GET /images/(name)/json —— 返回 CreatedContainerContainerConfig(含 Hostname/User/Memory/Tty/Cmd/Image/Volumes 等)、IdParentSize。200 / 404 / 500。(ContainerContainerConfig 字段后来在 v1.45 被移除。)
  • 历史:GET /images/(name)/history —— 返回 IdCreatedCreatedBy 数组(/bin/bash 等层创建命令)。200 / 404 / 500。
  • 推送:POST /images/(name)/push —— 返回 {"status": "Pushing..."} 式进度流。推私有仓库时,镜像必须先 tag 到引用该 registry 主机名的仓库,URL 中即使用该仓库名(与 CLI 流程一致),例如 POST /images/registry.acme.com:5000/test/push。查询参数 tag;请求头 X-Registry-Auth。200 / 404 / 500。
  • 打标签:POST /images/(name)/tag?repo=myrepo&force=0&tag=v42 —— 201 成功;400 参数错误;404;409 冲突;500。
  • 删除:DELETE /images/(name) —— 参数 forcenoprune。响应为数组,如 [{"Untagged": "3e2f21a89f"}, {"Deleted": "3e2f21a89f"}, {"Deleted": "53b4f83ac9"}]。200 / 404 / 409 / 500。
  • 搜索:GET /images/search?term=sshd —— 在 Docker Hub 上搜索;注意 v1.6 起响应键名已改为 registry 服务返回的原始 JSON(descriptionis_officialis_automatednamestar_count)。200 / 500。

3.4 镜像 tarball:save / load 与格式

  • 单仓库导出:GET /images/(name)/get —— 返回 application/x-tar 二进制流。name 为具体 仓库:tag 时只导出该镜像(含父层);为镜像 ID 时同样只导出该镜像,且 tarball 中不包含 repositories 文件(因为没有名称可引用)。
  • 多仓库导出:GET /images/get?names=myname%2Fmyapp%3Alatest&names=busybox —— names 可重复,语义同上。
  • 导入:POST /images/load —— tarball 放在请求体中,200 表示成功。

镜像 tarball 格式(原文档完整继承):每个镜像层一个以长 ID 命名的目录,内含三个文件:

  1. VERSION:格式版本,当前为 1.0
  2. json:层的详细信息,类似 docker inspect layer_id
  3. layer.tar:该层文件系统变更的 tar 文件,其中使用 aufs 风格的 .wh..wh.aufs 文件与目录保存属性变更与删除信息。

若 tarball 定义了一个仓库,根目录还会有 repositories 文件,列出仓库与标签到层 ID 的映射:

{"hello-world":
    {"latest": "565a9d68a73f6706862bfe8409a7f659776d4d60a8d096eb4a3cbce6999cc2a1"}
}

4. 其它端点(2.3 Misc)

4.1 构建:POST /build

POST /build HTTP/1.1

{{ TAR STREAM }}

响应是构建输出流:

{"stream": "Step 1..."}
{"stream": "..."}
{"error": "Error...", "errorDetail": {"code": 123, "message": "Error..."}}

约束与参数(完整继承原文档):

  • 流必须是 tar 归档,压缩算法限 identity(不压缩)、gzip、bzip2、xz 之一;
  • 归档根目录必须包含 Dockerfile,其余文件都进入构建上下文(对应 Dockerfile 的 ADD 指令);
  • 查询参数:t(成功时给结果镜像打的仓库名/标签)、remote(git 或 HTTP/HTTPS 构建源)、q(静默输出)、nocache(不用缓存)、pull(即使本地存在旧镜像也尝试拉取)、rm(成功后删除中间容器,默认行为)、forcerm(总是删除中间容器,含 rm 的语义);
  • 请求头:Content-type 应为 "application/tar"X-Registry-Config – base64 编码的 ConfigFile 对象。

状态码:200 / 500。

4.2 认证检查:POST /auth

请求体(原文档示例):

{
    "username": "hannibal",
    "password": "xxxx",
    "email": "hannibal@a-team.com",
    "serveraddress": "https://index.docker.io/v1/"
}

响应 200 或 204 表示无错误;500 服务器错误。

4.3 系统信息:GET /info

响应示例(v1.16 时代的字段集合):

{
    "Containers": 11,
    "Images": 16,
    "Driver": "btrfs",
    "DriverStatus": [[""]],
    "ExecutionDriver": "native-0.1",
    "KernelVersion": "3.12.0-1-amd64",
    "NCPU": 1,
    "MemTotal": 2099236864,
    "Name": "prod-server-42",
    "ID": "7TRN:IPZB:QYBB:VPBQ:UMPP:KARE:6ZNR:XE6T:7EWV:PKF4:ZOJD:TPYS",
    "Debug": false,
    "NFd": 11,
    "NGoroutines": 21,
    "NEventsListener": 0,
    "InitPath": "/usr/bin/docker",
    "InitSha1": "",
    "IndexServerAddress": ["https://index.docker.io/v1/"],
    "MemoryLimit": true,
    "SwapLimit": false,
    "IPv4Forwarding": true,
    "Labels": ["storage=ssd"],
    "DockerRootDir": "/var/lib/docker",
    "OperatingSystem": "Boot2Docker"
}

其中 Driver 为存储驱动名,ID 为产品密钥标识,DockerRootDir 为数据根目录。200 / 500。

4.4 版本、心跳与提交

  • 版本:GET /version —— 返回 ApiVersionVersionGitCommitGoVersion(示例响应为 {"ApiVersion": "1.12", "Version": "0.2.2", ...})。
  • 心跳:GET /_ping —— 返回 200 OK,正文 OKtext/plain)。
  • 提交:POST /commit —— 基于容器变更创建新镜像:
POST /commit?container=44c004db4b17&comment=message&repo=myrepo HTTP/1.1

JSON 体是容器的 config(Hostname/User/CpuShares/Tty/Env/Cmd/Volumes/ExposedPorts 等,见创建容器一节)。响应 201,返回 {"Id": "596069db4bf5"}。查询参数:container(源容器)、repotagcomment(提交信息)、author。状态码 201 / 404 / 500。

4.5 事件流:GET /events

支持实时流式或按 since 轮询。容器事件:create, destroy, die, export, kill, pause, restart, start, stop, unpause;镜像事件:untag, delete

GET /events?since=1374067924
{"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(轮询起点时间戳)、until(轮询终点时间戳)、filters(JSON 编码,可用 event=<string>image=<string>container=<string>)。200 / 500。

5. 深入:v1.16 的三个机制(Going further)

5.1 docker run 背后的 API 调用序列

原文档给出的 docker run 调用链(完整继承):

  1. 创建容器POST /containers/create);
  2. 若返回 404,说明镜像不存在:先尝试 pullPOST /images/create),然后重试创建容器;
  3. 启动容器POST /containers/(id)/start);
  4. 分离模式:attach 到容器,使用 logs=1(拿到从容器启动起的 stdout/stderr)与 stream=1
  5. 若是分离模式或仅附加 stdin:直接显示容器 ID。

5.2 Hijacking(连接劫持)

v1.16 中 /attach 使用 hijacking 在同一条 socket 上传输 stdin、stdout、stderr。当前仓库中该机制的客户端实现在 client/hijack.gopostHijacked() 发送 POST 后接管底层连接,setupHijackConn() 负责处理服务器 hijack 时的握手(并处理 HTTP 层已缓冲数据的兼容路径);服务器侧则由 daemon/server/ 下的路由在 attach/exec start 等端点直接接管 http.Hijacker。文档同时注明该设计"未来可能变化"——事实上后续版本新增了 WebSocket 通道与会话端点,但 8 字节帧协议沿用至今(见 api/pkg/stdcopy/stdcopy.go)。

5.3 CORS 请求

要允许跨域请求访问 Remote API,以 daemon 模式运行 docker 时加上 --api-enable-cors 标志:

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

6. 历史坐标:从 v1.16 看后续演进

结合 api/docs/CHANGELOG.md 与当前源码,v1.16 文档中的几处设计在后来的版本中有明确去向,阅读旧文档时需注意:

  1. 容器列表的 since/before 查询参数:当前 client/container_list.goSince/Before 选项标注"自 docker 1.12(API 1.24)起不再支持,请改用 since/before filter"——v1.16 时代的独立查询参数被过滤器机制统一取代;
  2. POST /containers/{id}/copy(本文 2.9 节):CHANGELOG 记录该端点在 API v1.24 起移除并返回错误;
  3. POST /containers/start 接受 HostConfig 体(本文 2.7 节):v1.24 起不再接受;
  4. LxcConf:随 LXC 执行驱动退出,在 v1.22 从 create/inspect 中移除;
  5. 版本协商:v1.25 起 API 版本必须出现在 URL 路径中(/v1.25/containers/json),且每个响应携带 Api-VersionDocker-Experimental 头。

这些演进的版本边界由 daemon/server/middleware/version.go 的中间件统一执行:客户端请求的版本低于 minAPIVersion 会得到明确的 versionUnsupportedError,而不是静默降级——这正是 v1.16 这类历史文档仍能精确复现旧行为的前提。

小结:v1.16 文档的价值在于它以最小的端点集合完整定义了"REST + 连接劫持流"的 Docker 远程控制范式——容器 CRUD 与生命周期(2.1 节)、镜像仓库交互与 tarball 格式(2.2 节)、构建/认证/事件(2.3 节)、以及 8 字节多路复用帧协议。对照当前仓库的 api/pkg/stdcopy/stdcopy.goclient/hijack.goapi/docs/CHANGELOG.md,可以确认这套协议的核心帧格式与调用序列延续至今,而查询参数、废弃字段等外围细节则沿着 CHANGELOG 逐版本收敛。

登录后查看全文
热门项目推荐
相关项目推荐