docker:镜像命令
数组堆如何构建一个大根堆https://blog.csdn.net/zhizhengguan/article/details/106826270有一个需求:有千亿个数据,需要求得最大值,应该怎么做打: 这当然不能用数组,如果用数组的话,需要先把所有数据读到内存中,然后在比较。内存没有那么大。也不能一个个从磁盘中读取数据,因为非常耗费磁盘IO。 我们可以使用链式堆来做链式堆package maini
简介
- 镜像相当于一个程序,容器相当于一个进程。
- 容器就是运行着的镜像。
- 镜像由多个层组成,每层叠加之后,从外部看来就是一个独立的镜像。镜像内部是一个精简的操作系统,同时内部还包含应用运行所必须的文件和依赖。
- 这些层都是只读的
镜像仓库服务
- 刚开始是本地docker是没有镜像的,一般需要先从镜像仓库中拉取镜像到本地,然后就可以使用这个镜像启动一个或者多个容器
- 一旦容器从镜像中启动之后,在容器全部停止之前,这个镜像是无法被删除的
远程镜像仓库
- docker镜像存储在镜像仓库服务中。docker客户端的镜像仓库服务是可配置的,默认使用Docker Hub
- 一个镜像仓库服务包含多个仓库(就是镜像名),一个仓库中包含多个版本
Docker Hub分为官方仓库和非官方仓库
- 官方仓库是由docker公司审查的,安全
- 非官方仓库没有人审查,可能会不安全
本地镜像仓库
命令
搜索(远程)镜像
// 搜索带 nginx 关键字的镜像
$ docker search nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 16924 [OK]
linuxserver/nginx An Nginx container,
// 搜索官方提供的带 nginx 关键字的镜像
$ docker search --filter=is-official=true nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 16924 [OK]
// 搜索所有收藏数超过4的带 nginx 关键字的镜像
$ docker search --filter=stars=4 nginx
// 搜索自动创建的仓库(镜像)
$ docker search nginx --filter "is-automated=true"
可以看到返回了很多包含关键字的镜像,其中
- NAME :仓库名称,包括docker ID,或者非官方仓库的组织名称
- DESCRIPTION :描述
- STARS :收藏数(表示该镜像的受欢迎程度)
- OFFICIAL: 是否官方创建
- AUTOMATED: 是否自动创建
默认的输出结果将按照星级评价进行排序。
默认值只返回前25行,可以通过--limit
参数来增加内容行数,最多100行
查看(本地)镜像
使用 images 命令列出镜像
使用 docker images 或 docker image ls 命令可以列出本地主机上已有镜像的基
本信息。
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
gb28181 latest e2bb49fceade 6 hours ago 880MB
- 同一个仓库源可以有多个TAG,代表这个仓库源的不同版本,我们使用镜像仓库源:镜像标签来定义不同的镜像。
- 这些镜像都是存储在Docker宿主机的/var/lib/docker目录下
images 子命令主要支持如下选项:
–format 格式化输出内容
可以通过–format参数来过滤docker image ls
的输出内容。
(1)仅显示镜像的大小属性:
$ docker image ls --format "{{.Size}}"
880MB
880MB
(2)仅显示镜像的仓库和标签属性:
$ docker image ls --format "{{.Repository}}:{{.Tag}}"
gb28181:latest
–filter过滤镜像
可以通过–filter参数来过滤docker image ls
的输出内容。其支持的过滤器如下
(1)仅显示标签为latest的镜像
docker image ls --filter=reference="*:latest"
REPOSITORY TAG IMAGE ID CREATED SIZE
gb28181 latest e2bb49fceade 6 hours ago 880MB
(2)仅会显示悬虚镜像
$ docker image ls --filter dangling=true
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 5d1686b1730f 6 months ago 880MB
那什么叫做悬虚镜像呢?
- 没有标签的镜像叫做悬虚镜像,在列表中展示为
<none:none>
- 为什么出现这种情况,
- 当构建新镜像时,为该镜像打了一个已经存在的标签
- 此时,docker在构建新镜像时,会移除旧镜像上的标签,然后将标签移到新镜像上。旧镜像就变成了悬虚镜像
移除全部的悬虚镜像(这个命令没有起作用):
$ docker image prune
WARNING! This will remove all dangling images.
Are you sure you want to continue? [y/N] y
Total reclaimed space: 0B
使用 inspect 命令查看详细信息
使用docker image inspect命令可以获取该镜像的详细信息,包括制作者、适应
架构、各层的数字摘要等:
$ docker image inspect ubuntu
[
{
"Id": "sha256:27941809078cc9b2802deb2b0bb6feed6c236cde01e487f200e24653533701ee",
"RepoTags": [
"myubuntu:lastest",
"ubuntu:latest"
],
"RepoDigests": [
"ubuntu@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac"
上面代码返回的是一个 JSON 格式的消息,如果我们只要其中一项内容时,可以使用f 来指定,例如:
docker image inspect -f {{".Metadata"}} ubuntu
使用history命令查看镜像历史
既然镜像文件由多个层组成,那么怎么知道各个层的内容具体是什么呢?这时候可以使
用 history 子命令,该命令将列出各层的创建信息。
$ docker image history ubuntu
IMAGE CREATED CREATED BY SIZE COMMENT
27941809078c 34 hours ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 34 hours ago /bin/sh -c #(nop) ADD file:11157b07dde10107f… 77.8MB
注意,过长的命令被自动截断了,可以使用–no-trunc选项来输出完整命令
docker image history ubuntu --no-trunc
拉取镜像
- Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在, Docker 会尝试先从镜像仓库服务下载。
- 那怎么知道要下载那个镜像呢?只要给出镜像的名字和标签,就能在官方仓库中定位一个镜像(采用
:
分隔)
根据标签拉取镜像
从拉取镜像的命令格式如下:
docker image pull <repository>:<tag>
- repository 是镜像仓库名称(用来区分镜像)
- tag 是镜像的标签(往往用来表示版本信息)
(1)从官方仓库拉取镜像实例:
$ docker image pull ubuntu:18.04 // 从官方ubuntu库中拉取标签为18.04的镜像
$ docker image pull ubuntu:latest // 从官方ubuntu库中拉取标签为latest的镜像
$ docker image pull ubuntu // 从官方ubuntu库中拉取标签为latest的镜像
注意:
- 如果没有指定标签,则默认拉取latest镜像
- 标有latest标签的镜像不保证这是仓库中最新的镜像
下载过程中可以看出,镜像文件一般由若干层 (layer) 组成, 6c953ac5d795 这样的
串是层的唯一 id (实际上完整的 id 包括 256 比特, 64 个十六进制字符组成)。使用 docker pull 命令下载中会获取并输出镜像的各层信息。当不同的镜像包括相同的层时,本地仅存储了层的一份内容,减小了存储空间。
(2) 从非官方仓库拉取镜像
docker pull docker hub账户名/仓库名:镜像名
(3) 从第三方镜像仓库服务中拉取镜像
docker pull 第三方镜像仓库服务地址/docker hub账户名/仓库名:镜像名
pull 子命令支持的选项主要包括:
关于-a
选项:一个镜像可能有多个标签,我们可以用docker image pull -a
参数来拉取仓库中的全部镜像
docker image pull -a <repository>
另外,有时需要使用镜像代理服务来加速 Docker镜像获取过程,可以在 Docker 服务
启动配置中增加 --registry-mirror==proxy_URL 来指定镜像代理服务地址
下载镜像到本地后,即可随时使用该镜像了,例如利用该镜像创建一个容器,在其中运
行 bash 应用,执行打印 “Hello World" 命令:
根据摘要拉取镜像
问题:如果有两个镜像,它们的标签和名字都是一样的,但是内容不一样,怎么区分它们呢?
答案
通过摘要区别,每一个镜像都有一个摘要,只要镜像内容变更了散列值一定会改变。
每次拉取镜像时会有有一个摘要作为返回值。
也可以通过如下命令在本地查看摘要:
$ docker image ls --digests ubuntu
$ docker image pull ubuntu@sha256:b6b83d3c331794420340093eb706a6f152d9c1fa51b262d9bf34594887c2c7ac
添加镜像标签
为了方便在后续工作中使用特定镜像,还可以使用 docker tag 命令来为本地镜像任
意添加新的标签。例如,添加一个新的 myubun七u:la七 es 七镜像标签:
docker tag ubuntu:latest myubuntu:latest
再次使用 docker images 列出本地主机上镜像信息,可以看到多了一个 myubuntu:latest的镜像:
可以看到,这些镜像的 ID是一样的,它们实际上指向了同一个镜像文件,只是别名不同而已。 docker tag命令添加的标签实际上起到了类似链接的作用。
删除和清理镜像
使用标签删除镜像
删除操作会在当前主机上删除该镜像以及相关的镜像层,这意味着无法通过docker image ls命令查看删除后的镜像(它只能看本机仓库的),并且对应的包含镜像层数据的目录会被删除
- 当同一个镜像拥有多个标签的时候, docker rmi 命令只是删除了该镜像多个标签中的指定标签而已,并不影响镜像文件
$ docker rmi myubuntu:latest
Untagged: myubuntu:latest
- 当镜像只剩下一个标签的时候就要小心了,此时再使用 docker rmi 命令会彻底删除镜像(如下可以看出,删除了这个镜像的所有文件层)
使用镜像 ID 来删除镜像
当使用 docker rmi 命令,并且后面跟上镜像的 ID (也可以是能进行区分的部分 ID 串前缀)时,会先尝试删除所有指向该镜像的标签,然后删除该镜像文件本身。
注意,当有该镜像创建的容器存在时,镜像文件默认是无法被删除的,看个例子:
(1) 先启动一个容器
$ docker run ubuntu:latest echo 'hello'
hello
(2)使用 docker ps -a 命令可以看到本机上存在的所有容器:
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
1cc612923b2a ubuntu:latest "echo hello" 6 seconds ago Exited (0) 5 seconds ago
可以看到,后台存在一个退出状态的容器,是刚基于ubuntu:latest镜像创建的。
(3)试图删除该镜像, Docker 会提示有容器正在运行,无法删除:
$ docker rmi ubuntu
Error response from daemon: conflict: unable to remove repository reference "ubuntu" (must force) - container 1cc612923b2a is using its referenced image 27941809078c
如果要想强行删除镜像,可以使用-f
参数:
$ docker rmi -f ubuntu
注意,通常并不推荐使用-f参数来强制删除一个存在容器依赖的镜像。正确的做法是,先删除依赖该镜像的所有容器,再来删除镜像。
(1)先删除容器
$ docker rm 1cc612923b2a
1cc612923b2a
(2)再删除镜像
$ docker rm 27941809078c
批量删除镜像
删除全部镜像
可以在docker image rm
命令中传入当前系统的全部镜像ID,可以通过docker image ls -q
获取全部镜像ID
# 停止所有容器
docker ps -a | grep "Exited" | awk '{print $1 }'|xargs docker stop
# 删除所有容器
docker ps -a | grep "Exited" | awk '{print $1 }'|xargs docker rm
# 删除异常停止的docker容器
docker rm `docker ps -a | grep Exited | awk '{print $1}'`
# 可以删除所有名字中带 “none” 关键字的镜像
docker rmi $(docker images | grep "none" | awk '{print $3}')
# 删除所有镜像
docker image rm $(docker image ls -q) -f
# 删除所有镜像
docker rmi `docker images -q`
遇到过的错误:
image has dependent child images
在 Docker 中删除 image 时有时会遇到类似
Error response from daemon: conflict: unable to delete 6ec9a5a0fc9f (cannot be forced) - image has dependent child images
这样的错误,是因为镜像有从属关系,因为当前镜像是其他镜像的创建底层。而且这个镜像在容器里面还在运行。
解决:
- 先查询依赖
docker image inspect --format='{{.RepoTags}} {{.Id}} {{.Parent}}' $(docker image ls -q --filter since=xxxxxx)
其中 xxxxxx 是报错 image 的 id,在文章开头的例子中就是 6ec9a5a0fc9f。从列表中查找到之后就可以核对并删除这些 image。
- 然后根据根据TAG删除容器
docker rm REPOSITORY:TAG
image is referenced in multiple repositories
原因:IMAGE ID为7042885a156a的nginx指向两个repository,所以删除失败了
解决:指定repository和tag删除
docker rmi xxx:rag
Error response from daemon: No command specified.
一般来说产生这个报错是因为启动的容器是被docker export出来的镜像
[root@dockertest container]# docker run --name nginx-import-test -d -p 4000:80 nginx-import-test:1.0
docker: Error response from daemon: No command specified.
See 'docker run --help'.
被docker export出来的镜像在启动的时候需要指定command,例如我启动上述nginx容器,需要指定Command
Command可以通过下面命令查询到:
$ docker ps -a --no-trunc
查询到Command之后,运行容器:
docker run --name nginx-import-test -d -p 4000:80 nginx-import-test:1.0 /docker-entrypoint.sh nginx -g 'daemon off;
容器就正常启动了
清理镜像
比如,如下命令会自动删除临时的遗留镜像文件层,最高提示是否的存储空间:
$ docker image prune -f
....
Total reclaimed space: ...
创建镜像
镜像是容器的基础,每次执行 docker run 的时候都会指定哪个镜像作为容器运行的基础。当我们使用Docker Hub的镜像无法满足我们的需求时,我们就需要自己定制镜像来满足我们的需求。
创建镜像的方法主要有三种:
- 基于已有镜像的容器创建,对应commit命令
- 基于本地模板导入,对应import命令
- 基于dockerfile创建,对应build命令
基于已有容器创建
docker commit 语法格式:
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
实例
(1)启动一个镜像,并在其中做一些修改:
$ docker run -it ubuntu /bin/bash
root@944848df736f:/$ touch test
root@944848df736f:/$ exit
注意,容器的ID为944848df736f
(2)此时容器已经和原来的镜像由一些改变,可以使用docker commit将之提交为一个新的镜像:
$ docker commit --author "oceanstar" --message "add a new file" 944848df736f test:0.1
sha256:b33d05e3bc50856da633dda7982067b5934e8eb96298129daeb50bb88b4cc5c7
顺利的话,会返回新创建镜像的 ID 信息
(3)此时查看本地镜像列表,会发现新创建的镜像已经存在了:
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test 0.1 b33d05e3bc50 About a minute ago 77.8MB
实例二
如下命令会用 nginx:1.11 镜像启动一个容器,命名为 webServer,并且映射了80 端口,这样便可以去访问这个 nginx 服务器。然后我们直接访问宿主机的IP可以看到Nginx的欢迎页面
[root@server ~]# docker run --name webServer -d -p 80:80 nginx:1.11 #启动一个容器,基于docker hub上面的nginx:1.11镜像
现在,假设我们不喜欢这个欢迎页面,我们喜欢改成别的文字,我们可以使用docker exec 命令进入到容器,修改其内容给
root@714830c04e5e:/# echo '<h1>Hello Docker Nginx Server</h1>' >/usr/share/nginx/html/index.html #修改默认首页的内容
root@714830c04e5e:/# exit
exit
已交互式终端方式进入 webServer 容器,并执行了 bash命令, 也就是获得了一个可操作的shell。然后覆盖了index.html内容,再次刷新浏览器,会发现内容被改变了。
修改了容器的文件,也就是改动了容器的存储器,可以通过 docker diff 命令查看具体的改动
[root@server ~]# docker diff webServer #查看webServer容器改动的内容
现在我们定制好了变化,我们希望能将其保存下来形成镜像。要知道,当我们运行一个容器的时候(如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储器里。而Docker提供了一个 docker commit 命令,可以将容器的存储层保存下来称为镜像。也就是说在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们在运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
示例将上面更改首页的webServer 容器保存为镜像:
[root@server ~]# docker commit \
--author "Bu Ji <381347268@qq.com>" \
--message "修复了默认首页" \
webServer \
nginx:v1
........
[root@server ~]# docker images nginx #查看制作完成的nginx镜像
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v1 b639fbcc5ec4 2 minutes ago 183MB
nginx 1.11 5766334bdaa0 21 months ago 183MB
其中 --author 是指定修改的作者,而 --message 则是记录本次修改的内容。这点和 git 版本控制器相似。还可以使用 docker history 具体查看镜像内的历史记录
[root@server ~]# docker history nginx:v1 #查看nginx:v1镜像的历史记录
新的镜像定制好后,我们来运行这个镜像
[root@server ~]# docker run --name web1 -d -p 81:80 nginx:v1 #基于上面新建的nginx:v1启动一个名字为web1的容器
当我们访问宿主机IP:81时候,其内容和之前修改后的 webServer一样
至此,完成了一个定制镜像,使用的是 docker commit 命令,手动给旧的镜像添加了新的一层,形成了新的镜像,对镜像多层存储应该有了很直观的感受。
慎用 docker commit
使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中很少这样使用。
- 首先, 从上面的 docker diff webServer 的结果中,可以发现除了真正想要修改的 /usr/share/nginx/html/index.html 文件外,由于命令的执行,还有很多文件被改动或添加了。这还只是最简单的操作,如果安装软件包、编译构建,那么有大量的无关内容被添加进来,如果不小心清理,将会导致镜像为臃肿。
- 此外,使用docker commit 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为黑箱镜像,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无法从知。虽热docker diff 或许可以告诉得到一些线索,但是远远不到可以确保生成一致镜像的地步。这种黑箱镜像的维护工作是非常痛苦的。
- 而且,除当前层外,之前的每一层都是不会发生改变的,也就是说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 docker commit 制作镜像,以及后期修改的话,每次一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。
基于本地模板导入
要直接导入一个镜像,可以使用 OpenVZ 提供的模板来创建,或者用其他已导出的镜像模板来创建。 OPENVZ 模板的下载地址为http://openvz.org/Download/templates/pr,ecr~ated。
例如,下载了 ubuntu-18.04 的模板压缩包,之后使用以下命令导入即可:
基于 Dockerfile 创建
基于 Dockerfile 创建是最常见的方式。 Dockerfile 是一个文本文件,利用给定的指令描述基于某个父镜像创建新镜像的过程。
步骤:
- 先写一个dockerfile脚本
- 使用docker build命令构建
导出和载入镜像
docker save
如果要导出镜像到本地文件,可以使用 docker [image] save 命令。该命令支持-o 、 -output string 参数,导出镜像到指定的文件中。
docker load
可以使用 docker [image] load 将导出的七 ar 文件再导入到本地镜像库。支持-i、一input string
选项,从指定文件中读入镜像内容。
这将导入镜像以及相关的元数据信息(包括标签)。导入成功后,可以使用docker images命令进行查看,和原镜像一致。
上传镜像
可以使用 docker [image] push 命令上
传镜像到仓库,默认上传到 Docker Hub 官方仓库(需要登录)
理论
镜像是分层的
镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储是在以镜像为基础层,在基础层上加一层作为容器运行时的存储层。
怎么验证
docker镜像由一些只读镜像层组成,如下图:
怎么查看某个镜像的分层呢?可以通过docker image pull
可以看出这个镜像包括5个镜像层,如下图:
也可以可以通过使用docker image inspect来查看分层信息
这些"24efXXXXXX"是镜像摘要
什么叫做镜像摘要:
- 镜像是由一层层独立的层组合起来的
- 镜像本身是一个配置对象,其中包含了镜像层的列表以及一些元数据信息
- 镜像层才是实际数据存储的地方,各个精选层之间是完全独立的,没有从属于某个镜像集合的概念
- 镜像的唯一标识是一个加密ID,即配置对象的摘要(散列值)。每个镜像层也由一个加密ID区分,其值为镜像层本身内容的摘要
- 只要镜像内容有一点点变化,都会改变这个摘要就会改变。
问题:
- 在推送和拉取镜像的时候,都会对镜像层进行压缩以节省网络带宽和仓库存储空间
- 但是压缩会改变镜像内容,这就意味着镜像的摘要在推送或者拉取之后,会与镜像内容不相符!
怎么构建
- 所有docker镜像都起始于一个基础镜像层,当进行修改或者增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
-
在额外添加精选层的同时,镜像时钟是当前所有镜像的组合
-
所有镜像堆叠合并,对外提供统一的视图
-
docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保障多镜像层对外展示为统一的文件系统。linux上可用的存储引擎用AUFS、Device Mapper等。每种存储引擎都基于linux中对应的文件系统或者块设备技术。
共享镜像层
- 多个镜像之间会共享镜像层,以便节省空间提升性能。
- docker在linux上支持很多存储引擎,每个存储引擎都有自己的镜像分层、镜像层共享以及写时复制技术的具体实现
怎么映射
docker在拉取镜像时,会先看看这一层是否在本地已经存在,如果存在,就不去拉取了。
更多推荐
所有评论(0)