十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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.7Docker 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但允许劫持。对于attach、pull这类需要双向传输stdin/stdout/stderr的复杂命令HTTP 连接会被 hijack劫持后续流量不再受 HTTP 报文边界约束。第一条原则在今天的仓库中依然成立守护进程的默认 socket 路径定义于 daemon/pkg/opts/hosts.go 的DefaultUnixSocket /var/run/docker.sockdaemon/command/daemon.go 中的defaultAPISocketPath函数在 rootlessrootlessKit场景下则改为监听$XDG_RUNTIME_DIR/docker.sock——这正是对文档可以绑定到另一个 Unix socket的落地。第三条原则在 client/hijack.go 中能看到完整继承postHijacked发起 POST 后调用setupHijackConn后者设置Connection: Upgrade与Upgrade: 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?all1before8dfafdbc3a40size1 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/true或0/False/false显示全部容器默认只显示运行中的默认 falselimit只显示最近创建的limit个容器包括非运行中的since只显示 Id 之后创建的容器包括非运行中的before只显示 Id 之前创建的容器包括非运行中的size1/True/true或0/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服务端错误。对照现代 APIv1.7 把创建配置与主机配置拆在 create/start 两个端点里这正是后来ContainerCreateConfigHostConfig分离结构的雏形。2.3 检查容器GET /containers/(id)/json返回容器的低层信息。示例请求GET /containers/4fa6e0f0c678/json HTTP/1.1示例响应节选关键字段{ Id: 4fa6e0f0c6786287e131c3852c58a2e01cc697a68231826813597e4994f1d6e2, Created: 2013-05-07T14:51:42.04184702: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.08765802: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运行态Running、Pid、ExitCode、StartedAt、Ghost、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的参数例如auxTitles与Processes的每一行即按该格式对齐。状态码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 可附带主机配置hostConfigPOST /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)/stopPOST /containers/e90e34656806/stop?t5 HTTP/1.1重启POST /containers/(id)/restartPOST /containers/e90e34656806/restart?t5 HTTP/1.1stop 与 restart 共享查询参数t—— 超时秒数到时未退出则 kill。响应均为HTTP/1.1 204 No Content。强制终止POST /containers/(id)/killPOST /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?logs1stream0stdout1 HTTP/1.1示例响应HTTP/1.1 200 OK Content-Type: application/vnd.docker.raw-stream {{ STREAM }}查询参数均为1/True/true或0/False/false默认 false参数说明logs返回日志stream返回流stdinstreamtrue时附加到 stdinstdoutlogstrue时返回 stdout 日志streamtrue时附加到 stdoutstderrlogstrue时返回 stderr 日志streamtrue时附加到 stderr状态码200成功400参数错误404容器不存在500服务端错误。流细节本规范的核心算法必须完整掌握创建容器时若启用了 TTY流就是进程 PTY 与客户端 stdin 的原始数据raw stream若未启用 TTY流会做多路复用multiplexed把 stdout 与 stderr 交织在同一条字节流里格式为帧头Header 载荷Payloadheader : [8]byte{STREAM_TYPE, 0, 0, 0, SIZE1, SIZE2, SIZE3, SIZE4}其中STREAM_TYPE0 stdin读取时写入 stdout、1 stdout、2 stderrSIZE1..SIZE4为 big-endian 编码的 uint32 帧长。文档给出的最简实现循环读 8 字节帧头根据第 1 个字节选择 stdout 或 stderr从后 4 字节解析帧大小读取该大小的载荷并输出到对应流回到第 1 步。源码印证这段 2013 年的协议描述与今天仓库中的 api/pkg/stdcopy/stdcopy.go 完全同构。该包定义Stdin0、Stdout1、Stderr2三个流类型常量并新增Systemerr3用于守护进程侧错误帧头长度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/wsGET /containers/e90e34656806/attach/ws?logs0stream1stdin1stdout1stderr1 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.1HTTP/1.1 200 OK Content-Type: application/json {StatusCode: 0}状态码200/404/500。2.11 删除容器DELETE /containers/(id)DELETE /containers/16253994b7c4?v1 HTTP/1.1查询参数v——1/True/true或0/False/false是否同时移除容器关联的卷默认 false。响应HTTP/1.1 204 No Content。状态码204成功400参数错误404容器不存在500服务端错误。2.12 从容器复制文件POST /containers/(id)/copyPOST /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 cp与GET /containers/(id)/archive的前身。状态码200/404/500。3. 镜像端点3.1 列出镜像GET /images/jsonGET /images/json?all0 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 或 importPOST /images/createPOST /images/create?fromImagebase 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要导入的来源-表示 stdinrepo目标仓库tag目标标签registry要拉取的 registry请求头X-Registry-Auth—— base64 编码的 AuthConfig 对象。状态码200成功500服务端错误。源码印证这个请求头的编解码规则在今天的仓库中由 api/pkg/authconfig/authconfig.go 承担——Encode将registry.AuthConfig序列化为 base64urlRFC 4648 第 5 节编码的 JSON 字符串用于X-Registry-Auth头Decode反向解码且即使出错也返回空 AuthConfig的兼容性策略注释明确写着是为了兼容旧客户端与旧 API 版本。v1.7 文档确立的认证走请求头、正文走进度流模式即为今日规范。3.3 向镜像插入文件POST /images/(name)/insert从url取文件插入到镜像name的path路径POST /images/test/insert?path/usrurlmyurl HTTP/1.1响应同样是逐行 JSON 进度流{status:Inserting...}等。查询参数url文件来源、path存储路径。状态码200/500。该端点是早期不构建、直接改镜像的能力后由 Dockerfile build 流程取代并从 API 中移除。3.4 检查镜像GET /images/(name)/jsonGET /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:true、OpenStdin:true与 2.2 节 create 示例的差异。状态码200/404/500。3.5 镜像历史GET /images/(name)/historyGET /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)/pushPOST /images/test/push HTTP/1.1响应为逐行 JSON 进度流{status:Pushing...}等请求头同样支持X-Registry-Auth携带 base64 编码 AuthConfig。状态码200成功404镜像不存在500服务端错误。3.7 打标签POST /images/(name)/tagPOST /images/test/tag?repomyrepoforce0tagv42 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.1HTTP/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?termsshd 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)/get与POST /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 /buildPOST /build HTTP/1.1 {{ TAR STREAM }}响应HTTP/1.1 200 OKContent-Type: application/json响应体为{{ STREAM }}构建进度流。约束tar 流必须使用 identity不压缩、gzip、bzip2、xz 之一压缩原文如此表述实际指这几种编码之一且归档根部必须包含名为Dockerfile的文件归档中的其他文件都可作为构建上下文在ADD指令中使用。查询参数参数说明t构建成功后应用到镜像的仓库名可含标签remote构建来源 URIgit 或 HTTPS/HTTPq抑制详细构建输出nocache构建时不使用缓存请求头Content-type应设为application/tar。状态码200/500。4.2 校验认证配置POST /authPOST /auth HTTP/1.1 Content-Type: application/json { username: hannibal, password: xxxx, email: hannibala-team.com, serveraddress: https://index.docker.io/v1/ }响应为HTTP/1.1 200 OK或204均为成功200 时返回校验后的 username/email 信息。状态码200/204成功500服务端错误。该端点是docker login校验凭据的通道请求体即后来registry.AuthConfig的前身对照 api/types/registry 下今天的 AuthConfig 类型可看到字段演化。4.3 系统信息GET /infoGET /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 /versionGET /version HTTP/1.1{ Version:0.2.2, GitCommit:5a2a5ccCHANGES, GoVersion:go1.0.3 }状态码200/500。这个端点同时是 API 版本协商的载体现代客户端正是通过/versionPing拿到守护进程的ApiVersion后再进行协商见 client/client.go 的negotiateAPIVersion逻辑。4.5 从容器变更创建镜像POST /commitPOST /commit?container44c004db4b17mmessagerepomyrepo HTTP/1.1HTTP/1.1 201 OK Content-Type: application/vnd.docker.raw-stream {Id: 596069db4bf5}查询参数参数说明container源容器repo仓库tag标签m提交说明author作者例如 John Hannibal Smith hannibala-team.comrun镜像运行时自动应用的配置如{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?since1374067924HTTP/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 调用的关键创建容器POST /containers/create若返回404说明镜像不存在尝试拉取POST /images/create然后重试创建容器启动容器POST /containers/(id)/start非分离detached模式下附加到容器使用logs1以获得容器启动以来的 stdout 与 stderr和stream1分离模式或仅附加 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 KeepAlive30 秒周期注释里说明这是为了规避某些网络环境下 ECONNTIMEOUT 导致的客户端状态不确定——这正是 v1.7 时代 hijack 流在弱网络上踩过的坑的工程化修复响应头Content-Type被HijackedResponse.MediaType()暴露给上层用于判定拿到的是原始流还是多路复用流进而决定是否走 api/pkg/stdcopy 拆帧。5.2 跨域请求CORS文档第 3.3 节允许对远程 API 的跨域请求需要在守护进程模式启动时加--api-enable-cors标志$ docker -d -H192.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 并结合仓库源码可以清晰地看到三条延续至今的主线端点资源模型/containers/*、/images/*、/build、/events的资源划分与逐行 JSON 进度流响应风格直接演化为今天 api/swagger.yaml 与 api/docs/v1.25.yaml 等现代版本的 OpenAPI 规范流协议attach 的 8 字节帧头多路复用格式[STREAM_TYPE,0,0,0,SIZE1..SIZE4]big-endian uint32被 api/pkg/stdcopy 逐字节实现并沿用至今是该文档最有长期价值的算法资产认证与版本协商X-Registry-Authbase64 头由 api/pkg/authconfig 承接/version端点则是客户端协商 API 版本当前客户端支持 1.401.56的入口见 client/client.go。同时应注意适用边界v1.7 中的insert、WebSocket attachattach/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),仅供参考
返回列表