2023-08-23:Docker镜像

学习自:03 镜像使用:Docker 环境下如何配置你的镜像?

1、镜像概念

在之前曾经说过,镜像是一个只读的Docker容器模板,包含了容器所需的所有文件系统结构和内容

简单来说,镜像是一个特殊的文件系统,它提供了容器运行所需的程序、软件库、资源、配置静态数据。即镜像不包含任何动态数据,镜像内容在构建后不会被改变

2、镜像操作

上图给出了镜像的操作方法:

操作

命令

docker xxx

说明

拉取镜像 pull 拉取远程仓库的镜像到本地
重命名镜像 tag 重命名

查看镜像

image ls 

images

查看本地已经存在的镜像
删除镜像 rmi 删除无用镜像
构建镜像

build

commit

基于Dockerfile构建镜像

基于已运行的容器提交为镜像

1)拉取镜像

用法:docker pull [Registry]/[Repository]/[Image:[Tag]

说明:

  • Registry:注册服务器,拉取镜像的来源。Docker默认从docker.io拉取镜像,如果你有自己的镜像仓库,可以把Registry替换为自己的注册服务器;
  • Repository:镜像仓库,通常将一组相关联的镜像归为一个镜像仓库library是Docker默认的镜像仓库;
  • Image:镜像名称
  • Tag:镜像标签,默认latest

例子:

获取一个busybox镜像,可以执行以下命令:

1
docker pull busybox

实际上在执行docker pull busybox命令,都是先从本地搜索,如果本地搜索不到busybox,镜像就从Docker Hub下载镜像。

拉取完镜像,如果要查看镜像,该怎么做?

2)查看镜像

用法:

  • docker image

  • docker image ls

  • docker image ls 镜像名

例子:

列出本地所有的镜像

1
docker images  

查询指定的镜像

1
2
docker image ls busybox
docker images | grep busybox

3)重命名镜像

 

用法:docker tag [Source_Image][:Tag] [Target_Image][:Tag]

说明:把镜像名:标签从Source_Image:Tag重命名为Target_Image:Tag

例子

1
docker tag busybox:latest mybusybox:latest

可以看到,镜像列表中多了一个mybusybox镜像,它与busyboxIMAGE ID是完全一样的,因为它们实际上指向了同一个镜像文件,只是别名不同而已。

如果我不需要mybusybox镜像了,要怎么删除呢?

4)删除镜像

用法:

  • docker rmi 镜像

  • docker image rm

例子

删除mybusybox镜像

1
docker rmi mybusybox

此时再用docker images命令查看一下我们机器上的镜像列表:

如果我们要构建属于自己的镜像,应该怎么做?

5)构建镜像

方式:

  • docker commit:从运行中的容器提交为镜像

  • docker build:从Dockerfile构建镜像

例子

1.如何从运行中的容器提交为镜像——commit

使用如下命令创建一个名为busybox的容器并进入busybox容器:

1
docker run --rm --name=busybox -it busybox sh

执行完该命令后,当前窗口会启动一个busybox并进入容器中

在容器中,执行以下命令创建一个文件并写入内容

1
2
/ # touch hello.txt && echo "I love docker. " > hello.txt
/ #

此时在容器的根目录下,就创建了一个hello.txt文件,并写入了内容I love Docker

再打开另一个命令行窗口,运行以下命令提交镜像:

1
docker commit busybox busybox:hello

再用docker image ls busybox查看镜像:

可以看到,主机上新生成了busybox:hello这个镜像。

2.用Dockerfile构建镜像——build(最常用)

使用Dockerfile构建镜像具有以下特性:

  • Dockerfile的每行命令都会生成一个独立的镜像层,并且拥有唯一ID
  • Dockerfile的命令是完全透明的,通过查看Dockerfile的内容,就能知道镜像是如何一步步创建的;
  • Dockerfile是纯文本的,方便跟随代码一起存放在代码仓库并做版本管理

Dockerfile常用指令

指令

说明

FROM 除了注释之外,第一行必须是FROM,后跟容器名称,代表我们要基于哪个基础镜像构建我们的容器
RUN 后跟具体指令,类似Linux命令行执行命令
ADD 拷贝本机文件远程文件镜像
COPY 拷贝本机文件到镜像
USER 容器启动的用户
ENTRYPOINT 容器的启动命令
CMD CMD为ENTRYPOINT指令提供默认参数,也可以单独使用CMD指定容器启动参数
ENV 指定容器运行时的环境变量,格式KEY=VALUE
ARG 定义外部变量,构建镜像时可以使用build-arg = xxx的格式传递参数用于构建
EXPOSE 指定容器监听的端口,格式为[port]/tcp[port]/udp
WORKDIR 为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY、ADD设置工作目录

下面借助一个Dockerfile的例子来解释说明:

1
2
3
4
5
6
FROM centos:7
COPY nginx.repo /etc/yum.repos.d/nginx.repo
RUN yum install -y nginx
EXPOSE 80
ENV HOST=mynginx
CMD ["nginx","-g","daemon off;"]
  • 第一行:基于centos:7这个镜像来构建自定义镜像。每个Dockerfile的第一行除了注释都必须以FROM开头
  • 第二行:拷贝本地文件nginx.repo容器的/etc/yum.repos.d目录下。这里拷贝nginx.repo文件是为了添加nginx安装源
  • 第三行:在容器内运行yum install -y nginx命令,安装nginx服务到容器内,执行完该行命令,容器内已经将nginx安装完毕;
  • 第四行:声明容器业务(nginx)使用80端口对外提供服务
  • 第五行:容器启动时的环境变量HOST=mynginx,容器启动后可以获取到环境变量HOST的值为mynginx
  • 第六行:容器的启动命令,格式为json数组。这里设置了容器的启动命令为nginx,并且添加了nginx启动参数-g 'daemon off;',使nginx以前台方式启动

这个Dockerfile的例子涵盖了常用的镜像构建指令

下面深入了解一下镜像的实现原理。

3、镜像的实现原理

其实Docker镜像是由一系列镜像层(layer)组成的,每层代表了镜像构建过程中的一次提交(Dockerfile中的一行)。

下面以一个镜像构建的Dockerfile来说明镜像是如何分层的:

1
2
3
FROM busybox
COPY test /tmp/test
RUN mkdir /tmp/testdir

将该文件保存为Dockerfile,注意Dockerfile就是文件名,不能修改,大小写敏感,无后缀

该Dockerfile由3步组成:

  1. 基于busybox创建一个镜像层;
  2. 拷贝本机test文件到镜像内;
  3. 在/mp目录下创建一个目录testdir。

为了验证镜像的存储,可以用docker build命令在上边的Dockerfile目录构建一个镜像(不要忘记最后的.,代表构建目录):

1
docker build -t mybusybox .

这里Docker使用的是overlay2文件驱动,进入到/var/lib/docker/overlay2目录下使用tree .命令查看产生的镜像文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
tree .
# 以下为 tree . 命令输出内容
|-- 3e89b959f921227acab94f5ab4524252ae0a829ff8a3687178e3aca56d605679
|   |-- diff  # 这一层为基础层,对应上述 Dockerfile 第一行,包含 busybox 镜像所有文件内容,例如 /etc,/bin,/var 等目录
... 此次省略部分原始镜像文件内容
|   `-- link
|-- 6591d4e47eb2488e6297a0a07a2439f550cdb22845b6d2ddb1be2466ae7a9391
|   |-- diff   # 这一层对应上述 Dockerfile 第二行,拷贝 test 文件到 /tmp 文件夹下,因此 diff 文件夹下有了 /tmp/test 文件
|   |   `-- tmp
|   |       `-- test
|   |-- link
|   |-- lower
|   `-- work
|-- backingFsBlockDev
|-- bec6a018080f7b808565728dee8447b9e86b3093b16ad5e6a1ac3976528a8bb1
|   |-- diff  # 这一层对应上述 Dockerfile 第三行,在 /tmp 文件夹下创建 testdir 文件夹,因此 diff 文件夹下有了 /tmp/testdir 文件夹
|   |   `-- tmp
|   |       `-- testdir
|   |-- link
|   |-- lower
|   `-- work
...

如果这里查起来比较困难的话,可以先把指令结果输出到文件,再用vim去文件中查找

1
tree . > tree.txt

 

通过这一目录结构可以看到,Dockerfile的每行命令,都生成了一个镜像层,每层的diff文件夹下都只存放了增量数据,如下图所示:

 分层的结构使得Docker镜像非常轻量,每层根据镜像内容都有一个唯一的ID值,当不同的镜像之间有相同的镜像层时,便可以实现不同镜像之间共享镜像层的效果。

小结

Docker镜像是静态、分层管理的文件组合,镜像底层实现依赖于联合文件系统(UnionFS)充分掌握镜像原理,可以帮助我们在生产实践中构建出最优的镜像,同时也可以帮助我们更好地理解容器和镜像的关系。

镜像的实现原理:镜像是由一系列镜像层(layer)组成,每层代表了镜像构建过程中的一次提交(Dockerfile中的一行),当我们需要修改镜像内某个文件时,只需要在当前镜像层的基础上新建一个镜像层,并且只存放修改过的文件内容。分层使得镜像共享镜像层变得非常简单方便。

 

回顾

1、镜像

  • 镜像是只读的Docker容器模板,包含了容器所需的文件系统结构和内容;
  • 它是静态的,其内容在构建之后就无法改变;
  • 镜像底层实现依赖于联合文件系统(UnionFs),这是一种文件分层机制;
  • 镜像由一系列的镜像层组成,每次代表了Dockerfile中的一次提交;
  • 当我们要修改镜像内的某个文件时,只需要在当前镜像层的基础上新建一个镜像层,并且只存放修改的内容
  • 分层使镜像共享镜像层非常方便简单。

2、镜像操作:构建、拉取、重命名、查看、删除

3、拉取镜像:pull

拉取时,先从本地搜索,如果搜索不到,就从仓库下载。

4、查看镜像:image

5、重命名:tag

6、删除:rmi

7、构建:commit(将运行的容器保存为镜像文件)、build(从Dockerfile构建镜像,最常用)通过buid从Dockerfile构建镜像是最常用的方式

8、Dockerfile特性:

  • 每行都会执行一个命令,每行命令都会生成一个独立镜像层,并具有唯一ID
  • 这些命令的执行对容器外透明,只需要看这些命令就知道这个镜像是怎么创建起来的;
  • Dockerfile是纯文本的,方便与代码一起存放在代码仓库并做版本管理

9、一个Dockerfile文件会用到的指令(常用,非全部)

指令

说明

FROM 除了注释之外,第一行必须是FROM,后跟容器名称,代表我们要基于哪个基础镜像构建我们的容器
RUN 后跟具体指令,类似Linux命令行执行命令
ADD 拷贝本机文件远程文件镜像
COPY 拷贝本机文件到镜像
USER 容器启动的用户
ENTRYPOINT 容器的启动命令
CMD CMD为ENTRYPOINT指令提供默认参数,也可以单独使用CMD指定容器启动参数
ENV 指定容器运行时的环境变量,格式KEY=VALUE
ARG 定义外部变量,构建镜像时可以使用build-arg = xxx的格式传递参数用于构建
EXPOSE 指定容器监听的端口,格式为[port]/tcp[port]/udp
WORKDIR 为Dockerfile中所有的RUN、CMD、ENTRYPOINT、COPY、ADD设置工作目录

10、镜像的实现过程及原理

Docker镜像由一系列镜像层构成,每层代表了Docker文件中的一次提交(按行)

1
2
3
FROM busybox
COPY test /tmp/test
RUN mkdir /tmp/testdir

该Dockerfile有3步:

  • 构建的镜像来自基础镜像busybox;
  • copy本机本目录test文件至镜像中的/tmp/test文件
  • 在镜像内创建目录/tmp/testdir

用docker build依据该Dockerfile构建为镜像:

1
docker build -t mybusybox .

进入到/var/lib/docker/overlay2目录下使用tree .命令查看产生的镜像文件:

1
tree .

通过这一目录结构可以看出,Dockerfile每行命令都会产生一个镜像层(上图的2aq和nyt开头的ID),每层的diff文件下都只存放了增量数据

11、分层的优点

使Docker镜像非常轻量;

每层根据镜像内容都有一个唯一ID,当不同镜像之间有相同镜像层时,可以实现不同镜像之间共享镜像层的效果。

 

posted @   ShineLe  阅读(323)  评论(0编辑  收藏  举报
编辑推荐:
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· [.NET]调用本地 Deepseek 模型
· 一个费力不讨好的项目,让我损失了近一半的绩效!
阅读排行:
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
点击右上角即可分享
微信分享提示