手动构建 Docker 镜像
本文最后更新于11 天前,其中的信息可能已经过时,如有错误请发送邮件到big_fw@foxmail.com

1. 目标与背景

目标:不使用docker build/ Dockerfile,而是手动组装一个 Docker 镜像 tar 包,再通过docker load 导入 Docker 引擎,使其可以运行容器。

最终要得到:

  • 镜像名:basic:v1
  • 容器可运行/bin/bash,并能执行命令: docker run --rm basic:v1 -c "echo hello"

2. 手动构建镜像的原理(必须理解)

Docker 镜像导入(docker load)本质上需要 3 个组成部分:

  1. Layers(层):一个或多个 tar 文件(这里是demo-image.tar),包含 rootfs 文件系统内容
  2. config.json:镜像配置/元数据(Entrypoint、Env、WorkingDir、rootfs.diff_ids 等)
  3. manifest.json:镜像清单(RepoTags、使用哪些 Layers、对应哪个 config)

最后把它们一起打成一个basic.tar,交给docker load 导入。


3. Step-by-step 手动构建流程

Step 1:构建 rootfs 目录结构

创建最小文件系统目录:

mkdir -p rootfs/{dev,bin,lib64}

说明:

  • bin:放可执行文件(bash、echo)
  • lib64:放动态库依赖(so)和动态链接器
  • dev:设备目录(最小化镜像里通常先预留)

企业里通常还会补齐/proc /sys /tmp /etc 等目录,但你这个最小案例可不强制。


Step 2:拷贝可执行程序到 rootfs

先确认依赖(查看动态库):

ldd /bin/echo ldd /bin/bash

把程序放进 rootfs:

cp /bin/echo rootfs/bin/ cp /bin/bash rootfs/bin/


Step 3:拷贝动态库依赖与动态链接器(关键点)

/bin/bash/bin/echo 是动态链接程序,需要系统动态库支持,否则容器启动会报:

  • exec /bin/bash: no such file or directory

这个错误很多时候不是 bash 不存在,而是动态链接器缺失导致 ELF 无法加载。

拷贝核心依赖: cp /lib64/libtinfo.so.6 rootfs/lib64/ cp /lib64/libdl.so.2 rootfs/lib64/ cp /lib64/libc.so.6 rootfs/lib64/ cp /lib64/ld-linux-x86-64.so.2 rootfs/lib64/

快速拷贝某程序依赖库(for 循环)

适用于像/bin/echo 这种依赖较少的程序:

for i in $(ldd /bin/echo | awk '{print $3}' | grep -v '^$'); do cp $i rootfs/lib64/ done

建议:对 bash 也可以做同样处理,但要注意ldd输出里有些行不是库路径,需要过滤(比如linux-vdso.so.1)。


Step 4:打包 rootfs 成为镜像 layer(demo-image.tar)

打包命令:

tar -cf demo-image.tar -C rootfs/ .

参数解释:

  • -c:create 创建 tar 包
  • -f demo-image.tar:指定包名
  • -C rootfs/:切换到 rootfs 目录下再打包
  • .:打包 rootfs 目录下所有内容(包括隐藏文件)

⚠️ 关键点: 必须用**-C rootfs .**,让 tar 解包后直接落到/bin /lib64 ...,不能把 rootfs 目录本身打进去,否则会变成/rootfs/bin/bash,容器会找不到/bin/bash


Step 5:计算 layer 的 sha256(写入 config.json)

sha256sum demo-image.tar

得到一串 hash,例如:

3653d761aea229215c2fb5a8049dae704b699b28ac00c3bbbecff092c05f4a5d


Step 6:编写 config.json(镜像元数据)

创建config.json,内容如下(你示例):

{
  "architecture": "amd64",
  "os": "linux",
  "config": {
    "Entrypoint": ["/bin/bash"],
    "Env": ["PATH=/bin"],
    "WorkingDir": "/"
  },
  "rootfs": {
    "type": "layers",
    "diff_ids": [
      "sha256:3653d761aea229215c2fb5a8049dae704b699b28ac00c3bbbecff092c05f4a5d"
    ]
  },
  "history": [
    {
      "created": "2024-06-18T00:00:00Z",
      "created_by": "yai_yang"
    }
  ]
}

字段解释(笔记重点):

  • architecture/os:镜像平台(amd64/linux)
  • Entrypoint:容器默认启动程序,这里指定/bin/bash
  • Env:环境变量(这里只放 PATH=/bin)
  • WorkingDir:默认工作目录/
  • rootfs.diff_ids:镜像层的 hash 列表(这里是 demo-image.tar 的 sha)

Step 7:编写 manifest.json(镜像清单)

创建manifest.json

[
  {
    "Config": "config.json",
    "RepoTags": ["basic:v1"],
    "Layers": ["demo-image.tar"]
  }
]

字段解释:

  • Config:镜像配置文件名
  • RepoTags:镜像名和 tag(导入后docker images 可见)
  • Layers:镜像包含的层 tar(这里仅一层 demo-image.tar)

Step 8:打包最终镜像 tar(basic.tar)

把 layer + config + manifest 打包成 Docker 可 load 的 tar:

tar -cf basic.tar demo-image.tar config.json manifest.json


Step 9:导入镜像

docker load -i basic.tar

导入后可用:

docker images | grep basic


Step 10:运行验证

因为 Entrypoint 是/bin/bash,所以可以直接执行 bash 的-c 参数:

docker run --rm basic:v1 -c "echo hello"

输出:

hello

说明镜像构建成功 ✅

文末附加内容
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇