Docker镜像

009 最小的镜像

镜像是 Docker 容器的基石,容器是镜像的运行实例,有了镜像才能启动容器。

本章内容安排如下:

  1. 首先通过研究几个典型的镜像,分析镜像的内部结构。
  2. 然后学习如何构建自己的镜像。
  3. 最后介绍怎样管理和分发镜像。

镜像的内部结构

为什么我们要讨论镜像的内部结构?

如果只是使用镜像,当然不需要了解,直接通过 docker 命令下载和运行就可以了。

但如果我们想创建自己的镜像,或者想理解 Docker 为什么是轻量级的,就非常有必要学习这部分知识

了。

我们从一个最小的镜像开始吧。

hello-world - 最小的镜像

hello-world 是 Docker 官方提供的一个镜像,通常用来验证 Docker 是否安装成功。

我们先通过 docker pull 从 Docker Hub 下载它。

[root@docker ~]# docker pull hello-world
Using default tag: latest
latest: Pulling from library/hello-world
c1ec31eb5944: Pull complete
Digest: sha256:91fb4b041da273d5a3273b6d587d62d518300a6ad268b28628f74997b93171b2
Status: Downloaded newer image for hello-world:latest
docker.io/library/hello-world:latest

查看镜像信息

[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest d2c94e258dcb 16 months ago 13.3kB

docker run运行

[root@docker ~]# docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/

010 base****镜像

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

base 镜像有两层含义:

  1. 不依赖其他镜像,从 scratch 构建。
  2. 其他镜像可以之为基础进行扩展。

所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS

等。

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

下载镜像:

[root@docker ~]# docker pull centos:7 #下载centos 7

查看镜像信息:

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

下载ubuntu镜像

[root@docker ~]# uname -r
4.18.0-553.6.1.el8.x86_64
#Host OS kernel 为 4.18.0
[root@docker ~]# docker run -it ubuntu
root@4264749aa4af:/# uname -r
4.18.0-553.6.1.el8.x86_64 #容器ubuntu用的内核就是docker host内核

启动centos:7镜像

[root@docker ~]# docker run -it centos:7
[root@72397b60bb10 /]# uname -r
4.18.0-553.6.1.el8.x86_64

011 镜像的分层结构

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

012 构建镜像

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

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

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

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

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

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

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

解。

Docker 容器文件系统

描述:从下面的图片可以看见出以下几点:

Docker 镜像代表了容器的文件系统里的内容,是容器的基础,镜像一般是通过 Dockerfile 生成

的;

Docker 的镜像是分层的,所有的镜像(除了基础镜像)都是在之前镜像的基础上加上自己这层的内

容生成的;Docker 中每一层镜像的元数据都是存在 json 文件中的,除了静态的文件系统之外,还会包含动态

的数据;

Docker 镜像生产容器后会在此基础之上加入挂载点到安装Docker宿主机文件系统之中,并提供一

个读写层(Read-Write Layer),所以容器进程的所有操作都在读写层进行;

Docker 提供了两种构建镜像的方法:

  1. docker commit 命令
  2. Dockerfile 构建文件

docker commit

docker commit 命令是创建新镜像最直观的方法,其过程包含三个步骤:

  1. 运行容器
  2. 修改容器
  3. 将容器保存为新的镜像

举个例子:在 ubuntu base 镜像中安装 vim并保存为新镜像。

  1. 第一步, 运行容器
[root@docker ~]# docker run -it ubuntu
root@8dbdff6d3d88:/#

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

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

打开一个新窗口查看容器

[root@docker ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS
NAMES
8dbdff6d3d88 ubuntu "/bin/bash" 2 minutes ago Up 2 minutes
cool_darwin

执行 docker commit 命令将容器保存为镜像。

[root@docker ~]# docker commit cool_darwin ubuntu-with-vim #cool_darwin是容器
名,ubuntu-with-vim是新建的镜像名
sha256:ba18ae460c068f9bdba060350e64dcec4bc4af05b9918602ee34e6350d0369a4
[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu-with-vim latest acefd029083b 27 minutes ago 189MB
ubuntu latest edbfe74c41f8 5 weeks ago 78.1MB
[root@docker ~]# docker run -it ubuntu-with-vim #ubuntu-with-vim是新创建的镜像名
root@4d071cf3014f:/# which vim
/usr/bin/vim
root@4d071cf3014f:/# vim file1

013 Dockerfile构建镜像

Dockerfile 是一个文本文件,记录了镜像构建的所有步骤。

Dockerfile****内容基础知识:

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交
[root@docker ~]# cd /root #生产环境一般新建一个目录,里面写
Dockerfile,一个项目
[root@docker ~]# vim Dockerfile
FROM ubuntu
RUN apt-get update && apt-get install -y vim
[root@docker ~]# docker build -t ubuntu-with-vim-dockerfile .

通过 docker images 查看镜像信息。

[root@docker ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu-with-vim-dockerfile latest bbc08145d011 10 minutes ago 189MB
ubuntu-with-vim latest acefd029083b 27 minutes ago 189MB
ubuntu latest edbfe74c41f8 5 weeks ago 78.1MB

015 调试Dockerfile

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

何 debug Dockerfile。

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

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

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

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

失败的原因。

我们来看一个调试的例子。Dockerfile 内容如下:执行 docker build :

[root@docker ~]# ls #查看下有没有Dockerfile和testfile
Dockerfile testfile
[root@docker ~]# vim Dockerfile #编辑Dockerfile,写入上图的内容
FROM busybox
RUN touch tmpfile
RUN /bin/bash -c "echo continue to build..."
COPY testfile /
[root@docker ~]# docker build -t image-debug . #基于刚才写的Dockerfile构建镜像
image-debug

Dockerfile 在执行第三步 RUN 指令时失败。我们可以利用busybox的镜像进行调试,方式是通过

docker run -it 启动镜像的一个容器。

手工执行 RUN 指令很容易定位失败的原因是 busybox 镜像中没有 bash,busybox中用的是sh。虽然这

是个极其简单的例子,但它很好地展示了调试 Dockerfile 的方法。

# 找出错误原因,修改错误
[root@docker ~]# vim Dockerfile
FROM busybox
RUN touch tmpfile
RUN /bin/sh -c "echo continue to builld..." #将错误的/bin/bash修改为正确
的/bin/sh
COPY testfile /
[root@docker ~]# docker build -t image-debug . #基于刚才写的Dockerfile构建镜像
image-debug
busybox中用的是sh。虽然这

是个极其简单的例子,但它很好地展示了调试 Dockerfile 的方法。

~~~bash
# 找出错误原因,修改错误
[root@docker ~]# vim Dockerfile
FROM busybox
RUN touch tmpfile
RUN /bin/sh -c "echo continue to builld..." #将错误的/bin/bash修改为正确
的/bin/sh
COPY testfile /
[root@docker ~]# docker build -t image-debug . #基于刚才写的Dockerfile构建镜像
image-debug

更多推荐