镜像相关操作
镜像从使用上来看,有如下12个指令:
1 | docker image build Build an image from a Dockerfile |
在一般情况下,可以省略images,即docker image pull可以简写为docker pull,但也有一些意外,如docker image ls的简写为docker images,docker image rm的简写为docker rmi等。
获取镜像docker pull
从 Docker 镜像仓库获取镜像的命令是 docker pull。其命令格式为1
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
具体的选项可以通过 docker pull —help 命令看到,这里我们说一下镜像名称的格式。
- Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
- 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub,如果不给出用户名,则默认为 library,也就是官方镜像。
- 如果要下载其他hub上面的镜像,就需要指定域名了,如下载华为的:
docker pull swr.cn-north-1.myhuaweicloud.com/fang141x/tank:v1.1
如下为获取一个centos的镜像:
1 | [root@master ~]# docker pull ubuntu |
从下载过程中可以看到我们之前提及的分层存储的概念,镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的 ID 的前 12 位。并且下载结束后,给出该镜像完整的 sha256 的摘要,以确保下载一致性。
查看镜像docker images
使用docker images 或者 docker image ls来查看,如果要查看特定的镜像,后面直接加镜像名即可。使用-q参数,只显示image id。
1 | [root@master ~]# docker images centos |
另外我们应该知道镜像ID是镜像的唯一标识,一个镜像可以对应多个标签。
可以使用--format参数来指定输出的格式。
1 | [root@master ~]# docker image ls --format "table {{.ID}}\t{{.Repository}}\t{{.Tag}}" |
其他具体详细的用法,请参数官方文档:https://docs.docker.com/engine/reference/commandline/images/ ,或者使用man docker-images来查看帮助信息。
另外一个需要注意的问题是,docker image ls 列表中的镜像体积总和并非是所有镜像实际硬盘消耗。由于 Docker 镜像是多层存储结构,并且可以继承、复用,因此不同镜像可能会因为使用相同的基础镜像,从而拥有共同的层。由于 Docker 使用 Union FS,相同的层只需要保存一份即可,因此实际镜像硬盘占用空间很可能要比这个列表镜像大小的总和要小的多。
你可以通过docker system df命令来便捷的查看镜像、容器、数据卷所占用的空间。
1 | [root@master ~]# docker images |
上面可以看到images占用了267M大小,但是docker images显示超过了380M了,这个就说明了docker分层的,同样又是可以复用的。另外Containers一共有2个活跃的,就是有2个在运行,但是占用的大小才130B。
搜索镜像docker search
搜索官方提供的带nginx关键字的镜像。official是否是官方的1
2
3[root@linjx ~]# docker search --filter is-official=true nginx
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
nginx Official build of Nginx. 10881 [OK]
STARS为收藏数,--filter stars=4是指过滤出收藏数量在4以上的image
1 | [root@master ~]# docker search busybox --filter stars=4 --limit 3 |
Automated是 Docker Hub 提供的功能,他可以在你的 Github repo 有更新时马上根据 Dockerfile 帮你 build 一個新的 image,要使用这個功能首先到 Docker Hub 右上角: Create Automated Build。
删除镜像docker rmi
使用docker image rm或者docker rmi来执行。删除image之前必须先删除container。如下,即使容器没有运行。
1 | [root@master ~]# docker ps |
如果观察上面这几个命令的运行输出信息的话,你会注意到删除行为分为两类,一类是 Untagged,另一类是 Deleted。我们之前介绍过,镜像的唯一标识是其 ID 和摘要,而一个镜像可以有多个标签。
因此当我们使用上面命令删除镜像的时候,实际上是在要求删除某个标签的镜像。所以首先需要做的是将满足我们要求的所有镜像标签都取消,这就是我们看到的 Untagged 的信息。因为一个镜像可以对应多个标签,因此当我们删除了所指定的标签后,可能还有别的标签指向了这个镜像,如果是这种情况,那么 Delete 行为就不会发生。所以并非所有的 docker image rm 都会产生删除镜像的行为,有可能仅仅是取消了某个标签而已。
当该镜像所有的标签都被取消了,该镜像很可能会失去了存在的意义,因此会触发删除行为。镜像是多层存储结构,因此在删除的时候也是从上层向基础层方向依次进行判断删除。镜像的多层结构让镜像复用变动非常容易,因此很有可能某个其它镜像正依赖于当前镜像的某一层。这种情况,依旧不会触发删除该层的行为。直到没有任何层依赖当前层时,才会真实的删除当前层。
删除虚悬镜像docker image prune
有时会出现仓库名、标签均为 docker image prune删除,前提是容器必须完全退出才能删除。注意,容器处于exited状态也是不能删除虚悬镜像的。
1 | [root@master ~]# docker image ls -f dangling=true |
添加标签docker tag
使用docker [image] tag来重命名一个标签,非常有用。完整用法是docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG],这跟 cp 命令是一样,先源后位置。
1 | [root@master ~]# docker images *alpine |
使用tag有点类似做了一个软链接,但是跟软链接还是有区别的,删除myalpine:latest只是解除了tag名字。如果再去删除alpine,就是真正删除了这个images了。
查看详细信息docker inspect
使用docker [image] inspect命令可以获取该镜像的详细信息,包括制作者、适应架构、各层的数字摘要等。此命令也同样适用于容器、网络、数据卷等。
1 | # 原始内容 |
其他用法可以参考:奇妙的 Docker Inspect 模版
查看历史docker history
使用docker [image] history查看,显示的是镜像的构建时使用了哪些操作。
1 | [root@master ~]# docker history nginx:latest |
如上所示,CREATED BY是有截断的,看不出来完整的命令,可以使用如下指令显示完整信息:
1 | [root@master ~]# docker history nginx:latest --no-trunc=true --format "{{.ID}}: {{.CreatedBy}}" |
保存镜像docker save
使用docker [image] save image_name -o tar_name来保存已有的镜像。这个就可以将镜像分享给其他人。
1 | [root@master ~]# docker images nginx |
导入镜像docker load
使用docker load -i tar_name或者docker load < tar_name导入镜像。导人成功后,可以使用docker images 命令进行查看, 与原镜像一致。
1 | [root@master ~]# docker save busybox -o busybox.tar.gz |
创建镜像
创建镜像的方法主要有三种: 基于已有镜像的容器创建、基千本地模板导入、基于Dockerfile 创建。
基于容器创建
使用docker [container] commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]来创建,-m表示message,-a作者信息,-p提交时暂停容器运行。
1 | [root@master ~]# docker ps |
使用 docker commit 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,如果不小心清理,将会导致镜像极为臃肿。
其他用法,请参考https://docs.docker.com/engine/reference/commandline/commit/
基于容器导入
这个意思是说将容器导出之后,形成一下没压缩包,将这个压缩包通过docker import来导入,具体的用法为:docker import [OPTIONS] file|URL|- [REPOSITORY[:TAG]]
1 | [root@master ~]# docker export alpine >alpine.tar |
镜像保存/载入:docker load/docker save;将一个镜像导出为文件,再使用docker load命令将文件导入为一个镜像,会保存该镜像的的所有历史记录。比docker export命令导出的文件大,很好理解,因为会保存镜像的所有历史记录。容器导入/导出:docker import/docker export;将一个容器导出为文件,再使用docker import命令将容器导入成为一个新的镜像,但是相比docker save命令,容器文件会丢失所有元数据和历史记录,仅保存容器当时的状态,相当于虚拟机快照。
如下是docker save与docker import的区别,可以看到import丢失了所有的历史记录
1 | [root@master ~]# docker history test:v1 |
Dockerfile创建
这是镜像创建最常见的方法。Dockerfile是一个文本文件,利用给定的指令描述基于某个父镜像创建新镜像的过程。创建一个Dockerfile文件,然后使用docker build -t Name:version即可。此部分以后再详细介绍 。
上传镜像docker push
可以使用docker [image] push命令上传镜像到仓库,默认上传到Docker Hub 官方仓库(需要登录)。
官方登陆方法
先打开 https://hub.docker.com 进行注册,完成之后,直接使用docker logint进行登陆。
1 | [root@master ~]# docker login |
登录成功之后,根据提示信息可以知道,它把用户名密码存放在/root/.docker/config.json中,进入查看:
1 | [root@master ~]# cat /root/.docker/config.json |
这个auth是通过base64进行加密的,可以使用echo 'auth中的内容'|base64 --decode进行解密,解码后即是username:password。
推送镜像至官方
使用docker push [OPTIONS] NAME[:TAG]推送,但是要注意的是要加上刚刚注册的用户名,如果不加上,就默认是推送到官方的仓库中,即library,是没有权限的,直接会报denied错误。需要使用docker tag image username/repository:tag来重命名。
1 | fang2000:sghje141x[root@master ~]# docker push test:v1 |
推送成功之后,登陆hub就可以看到上传好的镜像了。

详细的用法,请参考:Docker镜像推送(push)到Docker Hub
容器相关操作
容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统(提供了运行态环境和其他系统环境)和跑在上面的应用。跟容器有关的操作指令有很多,可以查看官方文档: https://docs.docker.com/engine/reference/commandline/container/ ,主要的指令如下:
- docker run 运行container
- docker stop 停止运行的container
- docker start 运行已经停止运行的container
- docker rm 删除container
- docker attach 进入container
- docker exec 也是进入container,区别下面介绍
- docker export 导出container
- docker import 导入container,这在镜像时有介绍
- docker inspect 查看container信息
- docker top 查看container内的进程
- docker logs 查看container的日志
- docker port 查看container内的端口映射
- docker stats 查看container内的统计信息
- docker cp 复制文件,支持container与主机之后相互复件文件
- docker diff 查看container内有变更过的文件
- docker update 更新container运行时的配置,主要是资源限制份额的更新
运行容器
docker run介绍
使用docker run命令来启动容器,运行后,docker会做以下操作::
- 检查本地是否存在指定的镜像,不存在则从公有仓库下载
- 使用镜像创建并启动容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读可写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口道容器中去
- 从地址池分配一个ip地址给容器
- 执行用户指定的应用程序
docker run参数
完整的命令为docker run [OPTIONS] IMAGE [COMMAND] [ARG...],其中OPTIONS为参数,有很多,下面会介绍;IMAGE就是镜像的名字,后面接的是[COMMAND],说明这个参数可有可无,如果不指定,会默认运行Dockerfile指定的命令。
以下是常用参数:
- -d: 后台运行容器,并返回容器ID
- -i:以交互模式运行容器,通常与 -t 同时使用
- -t:为容器重新分配一个伪输入终端
- -p:端口映射,格式为:主机(宿主)端口:容器端口
- -m:设置容器使用内存最大值
- -v:给容器挂载存储卷,挂载到容器的某个目录
- —name=”nginx-lb”: 为容器指定一个名称,后续可以通过名字进行容器管理,links特性需要使用名字
- —rm:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm。
- —net: 指定容器的网络连接类型,支持 bridge/host/none/container四种类型;
- —net=bridge: 使用docker daemon指定的网桥
- —net=host: 容器使用主机的网络
- —net=container:NAME_or_ID:使用其他容器的网路,共享IP和PORT等网络资源
- —net=none:容器使用自己的网络(类似—net=bridge),但是不进行配置
简单实例
运行ubuntu镜像,这里使用了--rm表示可以使用exit直接退出运行,当容器退出后随之会删除。
1 | [root@master ~]# docker run -it --rm --name ubuntu ubuntu |
但是可以使用ctrl+p,ctrl+q返回宿主机。
1 | [root@master ~]# docker run -it --rm --name ubuntu ubuntu |
以下命令是运行tank镜像,使用本地的default.conf配置文件,将宿主机的端口8081映射到容器的8080端口上。
1 | docker run -d --name tank1 -p 8081:8080 -v $PWD/default.conf://etc/nginx/conf.d/default.conf swr.cn-north-1.myhuaweicloud.com/fang141x/tank:v1.1 |
挂载目录
Docker容器启动的时候,如果要挂载宿主机的一个目录,可以用-v参数指定。如下,使用-v /test:/soft就可以实现,冒号”:”前面的目录是宿主机目录,后面的目录是容器内目录。如果目录不存在,则会自动创建目录。
1 | [root@master ~]# ll /test |
两个目录是有一些的区别的,容器目录不可以为相对路径,但是宿主机是可以使用相对路径的。不过挂载时不是当前的目录了。如下,使用-v test:/soft之后,test目录是挂载到/var/lib/docker/volumes/test这里面去了。
1 | [root@master ~]# docker run -itd --rm -v test:/soft alpine |
如果只是-v指定一个目录,那表示绑定宿主机的一个随机目录至容器的指定的目录下。
使用-v绑定之后,在容器里面修改目录的权限以及属主,是会同步至宿主机中的。
1 | [root@master ~]# chmod 755 /test |
从这个例子中,可以看出在容器里面修改目录的权限以及属主,是会同步至宿主机中的。同时,容器销毁之后,目录权限还是保持原样。来源:关于Docker目录挂载的总结
容器的重启策略
Docker容器的重启都是由Docker守护进程完成的,因此与守护进程息息相关。
Docker容器的重启策略如下:
- no,默认策略,在容器退出时不重启容器
- on-failure,在容器非正常退出时(退出状态非0),才会重启容器
- on-failure:3,在容器非正常退出时重启容器,最多重启3次
- always,在容器退出时总是重启容器
- unless-stopped,在容器退出时总是重启容器,但是不考虑在Docker守护进程启动时就已经停止了的容器
docker run的退出状态码如下:
- 0,表示正常退出
- 非0,表示异常退出(退出状态码采用chroot标准)
- 125,Docker守护进程本身的错误
- 126,容器启动后,要执行的默认命令无法调用
- 127,容器启动后,要执行的默认命令不存在
- 其他命令状态码,容器启动后正常执行命令,退出命令时该命令的返回状态码作为容器的退出状态码
通过—restart选项,可以设置容器的重启策略,以决定在容器退出时Docker守护进程是否重启刚刚退出的容器。-restart选项通常只用于detached模式的容器。
1 | 启动一个容器,没有加--restart选项 |
如上所述,直接kill掉进程后之后,进程的ID是有发现变化的,所以策略有生效了。但是如果直接使用docker stop的话,容器就不会起来。这点要特别注意。官方还有另外一个实例:https://docs.docker.com/engine/reference/run/#restart-policies---restart
其他参数
1 | Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...] |
容器在使用的时候,基本上都是在跟docker run打交道,比较熟记,官网简介:https://docs.docker.com/engine/reference/commandline/run/
复制文件docker cp
此命令的作用是在容器和本地文件系统之间复制文件/文件夹。从帮助信息可以看出,docker cp后接container的名字就是从容器复制文件到宿主机。也就是说第一个参数是源位置,指从哪里复制文件或目录;第二个参数为目的位置,要把文件或目录复制到哪里。并且不管容器有没有启动,拷贝命令都会生效。
1 | docker cp --help |
docker cp命令类似于UNIX中的cp -a命令,递归复制目录下的所有子目录和文件,如下是一个从容器里面复制目录到宿主机的当前目录。
1 | [root@master ~]# docker cp tank:/usr/share/nginx/html . |
拓展一下,其实容器里面的目录和文件都是在保存在Docker Root Dir这里面的,在使用overlay2存储结构下,可以在MergedDir下面找到所有的目录和文件:
1 | [root@master html]# docker info |grep 'Docker Root Dir' |
进入容器方法
在运行容器之后,我们有时需要进入容器去查看文件、日志、调试、启动其他进程等等一些信息,所以就有了以下方法。
docker attach
通过 docker attach 可以 attach 到容器启动命令的终端。允许多个用户同时attach,但是每个人操作的内容都会同步显示,如下图,左边是我操作的内容,右边就会同时显示这些内容。同时还有一个特点,如果使用exit退出之后,容器也相应地停止运行了。所以其选项基本上不会用于生产环境。

注:可通过 Ctrl+p 然后 Ctrl+q 组合键退出 attach 终端。这样容器就还会继续 运行。
docker exec
生产环境下一般是使用这个命令来进入。完整的命令是docker exec [OPTIONS] CONTAINER COMMAND [ARG...],这跟docker run的用法差不多,要进入容器,就是要运行容器里面的bash命令。主要是需要理解 COMMAND [ARG...],这个ARG就是command的参数。
1 | 以下命令仅是使用容器里面的bash命令运行echo 123 |
attach 与 exec 主要区别如下:
- attach 直接进入容器 启动命令 的终端,不会启动新的进程。
- exec 则是在容器中打开新的终端,并且可以启动新的进程。
- 如果想直接在终端中查看启动命令的输出,用 attach;其他情况使用 exec。当然,如果只是为了查看启动命令的输出,可以使用
docker logs命令。
nsenter
nsenter跟docker exec类似,但nsenter不进入cgroups,因此可以避免资源限制。这样做的潜在好处是调试和外部审计。具体请参考:https://github.com/jpetazzo/nsenter
安装
在了解了什么是 nsenter 之后,我们需要把 nsenter 安装到主机中(注意:是主机而非容器或镜像)
1 | [root@master ~]# docker pull jpetazzo/nsenter |
这样运行完成之后,nsenter就安装到了/usr/local/bin下了。他是怎么样实现的是呢?使用docker history --no-trunc jpetazzo/nsenter:latest |grep CMD可以看到,CMD ["/bin/sh" "-c" "/installer"]运行的是/installer
1 | [root@master ~]# docker run --rm -it jpetazzo/nsenter bash |
就是复制nsenter至/target目录下,这样就完成了命令的安装了。
还有另外一个安装方法:先下载包:https://mirrors.edge.kernel.org/pub/linux/utils/util-linux/ ,然后使用./configure --without-ncurses ; cp nsenter /usr/local/bin也是可以的。
使用方法
在使用nsenter命令之前需要获取到docker容器的进程,然后再使用nsenter工具进去到docker容器中,具体的使用方法如下:
1 | docker inspect -f {{.State.Pid}} 容器名或者容器id #每一个容器都有.State.Pid,所以这个命令除了容器的id需要我们根据docker ps -a去查找,其他的全部为固定的格式 |
在安装完nsenter,还会附加docker-enter,这个命令只是一个shell脚本,将以上2步操作合并为一个。如下,docker-enter后面容器名或者ID即可。
1 | [root@master ~]# docker-enter tank |
另外,有些情况下还可以调用宿主机的命令:
1 | [root@master share]# docker-enter tank |
以上内容有大量参考了网上博文,同时对每个命令都有实际的操作记录,如有侵权,请及时联系我。万里长征,这只是第一步,仅仅是到image和container,就写了近一周时间,还需要继续努力 。