20260509 docker项目总结

docker项目总结

一、base镜像

上一节我们介绍了最小的 Docker 镜像,本节讨论 base 镜像。
base 镜像有两层含义

  1. 不依赖其他镜像,从 scratch 构建。
  2. 其他镜像可以之为基础进行扩展。
    所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS
    等。

我们以 CentOS 为例考察 base 镜像包含哪些内容。

1.1 下载镜像:

bash 复制代码
[root@docker ~]# docker pull centos:7                     #下载centos 7

1.2 查看镜像信息:

bash 复制代码
[root@docker ~]# docker images centos:7
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
centos       7         eeb6ee3f44bd   3 years ago   204MB

镜像大小不到 300MB。

等一下!

一个 CentOS 才 204MB ?

平时我们安装一个 CentOS 至少都有几个 GB,怎么可能才 204MB !

相信这是几乎所有 Docker 初学者都会有的疑问,包括我自己。下面我们来解释这个问题。

Linux 操作系统由内核空间和用户空间组成。如下图所示

1.3 支持运行多种 Linux OS

不同 Linux 发行版的区别主要就是 rootfs。

比如 Ubuntu 14.04 使用 upstart 管理服务,apt 管理软件包;而 CentOS 7 使用 systemd 和 yum。这

些都是用户空间上的区别,Linux kernel 差别不大。

所以 Docker 可以同时支持多种 Linux 镜像,模拟出多种操作系统环境

这里需要说明的是:

  1. base 镜像只是在用户空间与发行版一致,kernel 版本与发型版是不同的。
    例如 ubuntu使用 3.x.x 的 kernel,如果 Docker Host 是 CentOS Stream 8(比如我们的实验环
    境),那么在 CentOS 容器中使用的实际是是 Host 4.18.0 的 kernel。
bash 复制代码
[root@docker ~]# uname -r
4.18.0-553.6.1.el8.x86_64
#Host OS kernel 为 4.18.0

1.4 启动一个ubuntu,ubuntu内核正常应该与host os(centos stream 8)不一致

bash 复制代码
[root@docker ~]# docker run -it ubuntu
root@4264749aa4af:/# uname -r
4.18.0-553.6.1.el8.x86_64         #容器ubuntu用的内核就是docker host内核

1.5 启动一个centos:7,centos7正常内核为3.10

bash 复制代码
[root@docker ~]# docker run -it centos:7
[root@72397b60bb10 /]# uname -r
4.18.0-553.6.1.el8.x86_64
  1. 容器只能使用 Host 的 kernel,并且不能修改。
    所有容器都共用 host 的 kernel,在容器中没办法对 kernel 升级。如果容器对 kernel 版本有要求
    (比如应用只能在某个 kernel 版本下运行),则不建议用容器,这种场景虚拟机可能更合适。
    下一节我们讨论镜像的分层结构。

二、构建镜像

对于 Docker 用户来说,最好的情况是不需要自己创建镜像。几乎所有常用的数据库、中间件、应用软件

等都有现成的 Docker 官方镜像或其他人和组织创建的镜像,我们只需要稍作配置就可以直接使用。

使用现成镜像的好处除了省去自己做镜像的工作量外,更重要的是可以利用前人的经验。特别是使用那

些官方镜像,因为 Docker 的工程师知道如何更好的在容器中运行软件。

当然,某些情况下我们也不得不自己构建镜像,比如:

  1. 找不到现成的镜像,比如自己开发的应用程序。
  2. 需要在镜像中加入特定的功能,比如官方镜像几乎都不提供 ssh。

所以本节我们将介绍构建镜像的方法。同时分析构建的过程也能够加深我们对前面镜像分层结构的理

解。

2.1 第一步, 运行容器

bash 复制代码
[root@docker ~ 09:46:53]# docker run -it ubuntu
root@397381d388d0:/#

-it 参数的作用是以交互模式进入容器,并打开终端。 d11014d4b667 是容器的内部 ID。

2.2 安装 vim

确认 vim 没有安装。

bash 复制代码
root@397381d388d0:/# vim
bash: vim: command not found
root@397381d388d0:/# apt-get update
Please select the geographic area in which you live. Subsequent configuration questions will narrow this
down by presenting a list of cities, representing the time zones in which they are located.

  1. Africa   3. Antarctica  5. Asia      7. Australia  9. Indian    11. Etc
  2. America  4. Arctic      6. Atlantic  8. Europe     10. Pacific
Geographic area: 5

Please select the city or region corresponding to your time zone.

  1. Aden      16. Brunei       31. Hovd          46. Kuching       61. Qatar          76. Thimphu
  2. Almaty    17. Chita        32. Irkutsk       47. Kuwait        62. Qostanay       77. Tokyo
  3. Amman     18. Chongqing    33. Istanbul      48. Macau         63. Qyzylorda      78. Tomsk
  4. Anadyr    19. Colombo      34. Jakarta       49. Magadan       64. Riyadh         79. Ulaanbaatar
  5. Aqtau     20. Damascus     35. Jayapura      50. Makassar      65. Sakhalin       80. Urumqi
  6. Aqtobe    21. Dhaka        36. Jerusalem     51. Manila        66. Samarkand      81. Ust-Nera
  7. Ashgabat  22. Dili         37. Kabul         52. Muscat        67. Seoul          82. Vientiane
  8. Atyrau    23. Dubai        38. Kamchatka     53. Nicosia       68. Shanghai       83. Vladivostok
  9. Baghdad   24. Dushanbe     39. Karachi       54. Novokuznetsk  69. Singapore      84. Yakutsk
  10. Bahrain  25. Famagusta    40. Kashgar       55. Novosibirsk   70. Srednekolymsk  85. Yangon
  11. Baku     26. Gaza         41. Kathmandu     56. Omsk          71. Taipei         86. Yekaterinburg
  12. Bangkok  27. Harbin       42. Khandyga      57. Oral          72. Tashkent       87. Yerevan
  13. Barnaul  28. Hebron       43. Kolkata       58. Phnom_Penh    73. Tbilisi
  14. Beirut   29. Ho_Chi_Minh  44. Krasnoyarsk   59. Pontianak     74. Tehran
  15. Bishkek  30. Hong_Kong    45. Kuala_Lumpur  60. Pyongyang     75. Tel_Aviv
Time zone: 68

3.3 保存为新镜像

打开一个新窗口中查看当前运行的容器。

bash 复制代码
[root@docker / 09:49:32]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED              STATUS              PORTS     NAMES
397381d388d0   ubuntu    "/bin/bash"   About a minute ago   Up About a minute             zen_robinson

上面查看结果的解释如下:

  • 397381d388d0 是新创建容器的ID
  • zen_robinson 是 Docker 为我们的容器随机分配的名字。
  • 执行 docker commit 命令将容器保存为镜像。
bash 复制代码
[root@docker ~ 09:51:19]# docker commit zen_robinson ubuntu-with-vim  #zen_robinson是容器名,ubuntu-with-vim是新建的镜像名
sha256:b6668a03b80d50d3cb04800e1407d7ce119322a24eb49f1029aed55529231055
  • 新镜像命名为 zen_robinson

3.4 查看新镜像的属性。

bash 复制代码
[root@docker ~ 09:51:23]# docker images
REPOSITORY        TAG       IMAGE ID       CREATED         SIZE
ubuntu-with-vim   latest    b6668a03b80d   9 seconds ago   213MB
ubuntu            latest    30ba44506a6d   2 weeks ago     100MB
centos            7         eeb6ee3f44bd   4 years ago     204MB
  • 从 size 上看到镜像因为安装了软件而变大了。
  • 从新镜像启动容器,验证 vim 已经可以使用。
bash 复制代码
[root@docker ~ 09:52:03]# docker run -it ubuntu-with-vim
root@4232ecd287dd:/# which vim
/usr/bin/vim
root@4232ecd287dd:/# vim file1
root@4232ecd287dd:/# 
exit

以上演示了如何用 docker commit 创建新镜像。然而,Docker 并不建议用户通过这种方式构建镜像

原因如下:

  1. 这是一种手工创建镜像的方式,容易出错,效率低且可重复性弱。比如要在 debian base 镜像中也
    加入 vim,还得重复前面的所有步骤。
  2. 更重要的:使用者并不知道镜像是如何创建出来的,里面是否有恶意程序。也就是说无法对镜像进
    行审计,存在安全隐患。

既然 docker commit 不是推荐的方法,我们干嘛还要花时间学习呢?

原因是:即便是用 Dockerfile(推荐方法)构建镜像,底层也 docker commit 一层一层构建新镜像的。

学习 docker commit 能够帮助我们更加深入地理解构建过程和镜像的分层结构。

下一节我们学习如何通过 Dockerfile 构建镜像。

三、Dockerfile构建镜像

Dockerfile 是一个文本文件,记录了镜像构建的所有步骤。
Dockerfile内容基础知识:

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交

3.1 第一个 Dockerfile

用 Dockerfile 创建上节的 ubuntu-with-vim,其内容则为:

bash 复制代码
FROM ubuntu
RUN apt-get update
RUN apt-get install -y vim

3.2 下面我们运行 docker build 命令构建镜像并详细分析每个细节。

bash 复制代码
[root@docker ~ 10:20:20]# cd /root
[root@docker ~ 10:20:26]# vim Dockerfile
[root@docker ~ 10:24:46]# docker build -t ubuntu-with-vim-dockerfile .
[+] Building 34.2s (7/7) FINISHED                                                          docker:default
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 155B                                                                 0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                     0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => [1/3] FROM docker.io/library/ubuntu:latest                                                       0.0s
 => [2/3] RUN apt-get update                                                                        19.3s
 => [3/3] RUN apt-get install -y vim                                                                14.7s
 => exporting to image                                                                               0.2s 
 => => exporting layers                                                                              0.2s 
 => => writing image sha256:509456095113268d839657f83113452e53f8fbe92d77ef51cab54db557b7bc3b         0.0s 
 => => naming to docker.io/library/ubuntu-with-vim-dockerfile 

① 当前目录为 /root。

② Dockerfile 准备就绪。

③ 运行 docker build 命令, -t 将新镜像命名为 ubuntu-with-vim-dockerfile ,命令末尾的 . 指明

build context 为当前目录。Docker 默认会从 build context 中查找 Dockerfile 文件,我们也可以通过 -

f 参数指定 Dockerfile 的位置。

④ 从这步开始就是镜像真正的构建过程。 首先 Docker 将 build context 中的所有文件发送给 Docker

daemon。build context 为镜像构建提供所需要的文件或目录。

Dockerfile 中的 ADD、COPY 等命令可以将 build context 中的文件添加到镜像。此例中,build context

为当前目录 /root ,该目录下的所有文件和子目录都会被发送给 Docker daemon。

所以,使用 build context 就得小心了,不要将多余文件放到 build context,特别不要把 / 、 /usr 作

为 build context,否则构建过程会相当缓慢甚至失败。

⑤ Step 1:执行 FROM ,将 ubuntu 作为 base 镜像。

⑥ Step 2:执行 RUN ,安装 vim

⑦ 镜像构建成功。

⑧ 镜像重命名为ubuntu-with-vim-dockerfile

通过 docker images 查看镜像信息。

bash 复制代码
[root@docker ~ 10:25:22]# docker images
REPOSITORY                   TAG       IMAGE ID       CREATED          SIZE                               
ubuntu-with-vim-dockerfile   latest    509456095113   27 seconds ago   213MB
ubuntu-with-vim              latest    b6668a03b80d   34 minutes ago   213MB
ubuntu                       latest    30ba44506a6d   2 weeks ago      100MB
centos                       7         eeb6ee3f44bd   4 years ago      204MB

四、镜像的缓存特性

上一节我们学习了镜像的分层结构,接下来讨论镜像的缓存特性。
Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创
建。

4.1 举例说明

在前面的 Dockerfile 中添加一点新内容,往镜像中复制一个文件:

bash 复制代码
[root@docker ~ 10:25:49]# pwd
/root
[root@docker ~ 10:26:00]# ls
anaconda-ks.cfg  Dockerfile
[root@docker ~ 10:26:01]# touch testfile
[root@docker ~ 10:26:18]# ls
anaconda-ks.cfg  Dockerfile  testfile
[root@docker ~ 10:26:20]# vim Dockerfile
FROM ubuntu
RUN apt-get update
RUN apt-get install -y vim
COPY testfile /

[root@docker ~ 10:26:56]# docker build -t ubuntu-with-vim-dockerfile-2 .
[+] Building 0.1s (9/9) FINISHED                                                           docker:default
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 171B                                                                 0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                     0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => [1/4] FROM docker.io/library/ubuntu:latest                                                       0.0s
 => [internal] load build context                                                                    0.0s
 => => transferring context: 89B                                                                     0.0s
 => CACHED [2/4] RUN apt-get update                                                                  0.0s
 => CACHED [3/4] RUN apt-get install -y vim                                                          0.0s
 => [4/4] COPY testfile /                                                                            0.0s
 => exporting to image                                                                               0.0s
 => => exporting layers                                                                              0.0s
 => => writing image sha256:4fa133503442fac8ff5e192d52ddb4aa637dc3ce716159b01f5f7b05387f1db8         0.0s
 => => naming to docker.io/library/ubuntu-with-vim-dockerfile-2                                      0.0s
  • Dockerfile 中每一个指令都会创建一个镜像层,上层是依赖于下层的。无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失效。
  • 也就是说,如果我们改变 Dockerfile 指令的执行顺序,或者修改或添加指令,都会使缓存失效。
  • 举例说明,比如交换前面 RUN 和 COPY 的顺序:
  • 虽然在逻辑上这种改动对镜像的内容没有影响,但由于分层的结构特性,Docker 必须重建受影响的镜像
    层。
bash 复制代码
[root@docker ~ 10:27:41]# vim Dockerfile
FROM ubuntu                                                                                       COPY testfile /
RUN apt-get update
RUN apt-get install -y vim

[root@docker ~ 10:28:13]# docker build -t ubuntu-with-vim-dockerfile-3 .
[+] Building 19.2s (9/9) FINISHED                                                          docker:default
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 171B                                                                 0.0s
 => [internal] load metadata for docker.io/library/ubuntu:latest                                     0.0s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => [internal] load build context                                                                    0.0s
 => => transferring context: 87B                                                                     0.0s
 => CACHED [1/4] FROM docker.io/library/ubuntu:latest                                                0.0s
 => [2/4] COPY testfile /                                                                            0.0s
 => [3/4] RUN apt-get update                                                                        10.0s
 => [4/4] RUN apt-get install -y vim                                                                 8.8s 
 => exporting to image                                                                               0.2s 
 => => exporting layers                                                                              0.2s 
 => => writing image sha256:efc854839d3de5fd7d62b15cff956af5508fa488edd4136046c55f06ab4d8e63         0.0s 
 => => naming to docker.io/library/ubuntu-with-vim-dockerfile-3                                      0.0s 

五、调试 Dockerfile

包括 Dockerfile 在内的任何脚本和程序都会出错。有错并不可怕,但必须有办法排查,所以本节讨论如
何 debug Dockerfile。

先回顾一下通过 Dockerfile 构建镜像的过程:

  • 从 base 镜像运行一个容器。
  • 执行一条指令,对容器做修改。
  • 执行类似 docker commit 的操作,生成一个新的镜像层。
  • Docker 再基于刚刚提交的镜像运行一个新容器。
  • 重复 2-4 步,直到 Dockerfile 中的所有指令执行完毕。

从这个过程可以看出,如果 Dockerfile 由于某种原因执行到某个指令失败了,我们也将能够得到前一个

指令成功执行构建出的镜像,这对调试 Dockerfile 非常有帮助。我们可以运行最新的这个镜像定位指令

失败的原因。

5.1 我们来看一个调试的例子。Dockerfile 内容如下:

bash 复制代码
[root@docker ~ 10:28:43]# ls   #查看下有没有Dockerfile和testfile
anaconda-ks.cfg  Dockerfile  testfile
[root@docker ~ 10:52:52]# vim Dockerfile       #编辑Dockerfile,写入上图的内容
FROM busybox
RUN touch tmpfile
RUN /bin/bash -c "echo continue to build..."
COPY testfile /

[root@docker ~ 10:55:12]# docker build -t  image-debug .  #基于刚才写的Dockerfile构建镜像image-debug
[+] Building 7.3s (7/8)                                                                    docker:default
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 189B                                                                 0.0s
 => [internal] load metadata for docker.io/library/busybox:latest                                    4.3s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => [1/4] FROM docker.io/library/busybox:latest@sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d63  2.5s
 => => resolve docker.io/library/busybox:latest@sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d63  0.0s
 => => sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d638e7827681e7cf6c83d5e 9.54kB / 9.54kB       0.0s
 => => sha256:b8d1827e38a1d49cd17217efd7b07d689e4ea1744e39c7dcbb95533d175bea65 610B / 610B           0.0s
 => => sha256:925ff61909aebae4bcc9bc04bb96a8bd15cd2271f13159fe95ce4338824531dd 459B / 459B           0.0s
 => => sha256:481282afbc4304ffee4792258ea114f09e423a4a082335b30695b50310394f47 2.21MB / 2.21MB       2.4s
 => => extracting sha256:481282afbc4304ffee4792258ea114f09e423a4a082335b30695b50310394f47            0.1s
 => [internal] load build context                                                                    0.0s
 => => transferring context: 87B                                                                     0.0s
 => [2/4] RUN touch tmpfile                                                                          0.2s
 => ERROR [3/4] RUN /bin/bash -c "echo continue to build..."                                         0.2s
------                                                                                                    
 > [3/4] RUN /bin/bash -c "echo continue to build...":
0.180 /bin/sh: /bin/bash: not found
------
Dockerfile:3
--------------------
   1 |     FROM busybox
   2 |     RUN touch tmpfile
   3 | >>> RUN /bin/bash -c "echo continue to build..."
   4 |     COPY testfile /
   5 |     
--------------------
ERROR: failed to solve: process "/bin/sh -c /bin/bash -c \"echo continue to build...\"" did not complete successfully: exit code: 127

# 找出错误原因,修改错误
[root@docker ~ 10:55:36]# vim Dockerfile 
FROM busybox
RUN touch tmpfile
RUN /bin/sh -c "echo continue to builld..."          #将错误的/bin/bash修改为正确
的/bin/sh
COPY testfile /

[root@docker ~ 10:56:38]# docker build -t  image-debug .  #基于刚才写的Dockerfile构建镜像image-debug
[+] Building 1.4s (9/9) FINISHED                                                           docker:default
 => [internal] load build definition from Dockerfile                                                 0.0s
 => => transferring dockerfile: 187B                                                                 0.0s
 => [internal] load metadata for docker.io/library/busybox:latest                                    1.0s
 => [internal] load .dockerignore                                                                    0.0s
 => => transferring context: 2B                                                                      0.0s
 => [1/4] FROM docker.io/library/busybox:latest@sha256:1487d0af5f52b4ba31c7e465126ee2123fe3f2305d63  0.0s
 => [internal] load build context                                                                    0.0s
 => => transferring context: 87B                                                                     0.0s
 => CACHED [2/4] RUN touch tmpfile                                                                   0.0s
 => [3/4] RUN /bin/sh -c "echo continue to build..."                                                 0.2s
 => [4/4] COPY testfile /                                                                            0.0s
 => exporting to image                                                                               0.0s
 => => exporting layers                                                                              0.0s
 => => writing image sha256:fd7f8ae70d22374f306c986470a6decff71b528527b3f831ee216b80268ad95e         0.0s
 => => naming to docker.io/library/image-debug      
相关推荐
玄尺1 小时前
jenkins安装和使用
运维·jenkins
剑神一笑1 小时前
Linux xargs 命令深度解析:从管道到命令构建的桥梁
linux·运维·chrome
谁在黄金彼岸2 小时前
Docker Compose删除服务后,docker compose down无法关闭的问题解决
docker·容器
数据智能老司机2 小时前
使用 Docker 构建可落地运行的 AI 系统——Docker Desktop:AI/ML 工作流的运行时基础
docker·容器
Cisyam^2 小时前
Bright Data Web Scraper 实战:构建 TikTok 与 LinkedIn Web Scraping 自动化 Skill(2026)
运维·前端·自动化
程序员大辉2 小时前
Rufus中文版(U盘引导盘制作工具)v4.14.2377,PE U盘启动工具,重装系统必备的软件工具
运维·windows
量子炒饭大师2 小时前
【Linux系统编程】Cyberpunk在霓虹丛林中构建堡垒 ——【关于shell命令及其运行原理】
linux·运维·服务器·shell
hjc_0420432 小时前
xtrabackup来备份恢复数据
运维·adb