Docker容器操作

前面总结了docker的镜像的基本操作,有了镜像之后我们就可以用这个镜像来运行容器了。

docker的容器技术是什么?

以我极度不专业的话来讲,docker的容器技术就是一种数据虚拟化和数据隔离的技术,通过某个镜像,比如ubuntu,就可以在你的电脑上虚拟出来一台装了ubuntu系统的计算机,它里面有文件系统,有用户管理,文件的权限管理等等,你可以在里面下载软件,敲实体ubuntu中能够敲的绝大多数命令,并且他可以与外界的环境进行有效的隔离。虽然他无法完全模拟出物理机的所有功能,但是对于应用的部署,集群的构建,分布式系统的构建来说,他的功能已经够用。灵活高效,对物理机的低消耗是他的特点。

Container VS Virtual Machine

与虚拟机相比Docker实现了轻量级虚拟化。
docker vs VM

为什么不是虚拟机,而是Container呢?大家可能会讲,虚拟机的功能比Container的功能全,比如虚拟机可以虚拟出图形化操作,但是Container就不行。没错,同样作为一种数据虚拟化和数据隔离的技术,虚拟机在功能上比Container强的多,但是虚拟机的资源消耗,安装过程,存储空间的占用等都是惊人的。虚拟机中部署的应用,是依赖于虚拟机所安装的操作系统,虚拟机的操作系统又依赖于虚拟机引擎,而虚拟机引擎则依赖于其底层包和物理机的操作系统。而Container则不一样,在其中的应用直接依赖于docker引擎及其底层包,然后docker引擎则依赖于操作系统。两相对比,就可以发现,运行虚拟机比运行container要多占用一个操作系统的空间。更不用说物理机的操作系统要付出多大的代价了。最为重要的是,虚拟机的安装过程是及其繁琐和耗时的,这对于集群的构建来说是致命的。而对Container,只要一条命令即可安装docker,然后几条命令就可以跑起来一个容器,并且这个容器可以非常方便的移植到其他的主机上。

容器操作的基本命令

启动容器

1
$ docker run IMAGE [COMMAND] [ARG…]

IMAGE-----镜像的名称,可以是镜像的唯一ID,也可以是repository:tag的形式
COMMAND-----启动容器后运行的命令
ARG-----命令的参数
比如下面以ubuntu为镜像,运行起来一个容器,并且在容器中运行/bin/bash(即打开容器的bash):

1
$ docker run ubuntu /bin/bash

运行完了之后我们发现容器直接退出了!?容器退出了,但是我们的命令确实执行了,容器运行了起来,bash也打开了,然后容器就退出了。可是这有什么用呢?我们需要容器一直打开,然后进行其他操作。请往下看。

启动交互式容器

1
$ docker run –i –t IMAGE /bin/bash

-i –interactive=ture|false 默认为false 告诉docker的守护进程,为容器始终打开标准输入
-t –tty=ture|false 默认为false,告诉docker为容器分配一个伪tty终端

上面的命令让新创建的容器提供了一个交互式的shell 当然IMAGE里必须有bash才行。这条命令运行完了之后,我们就直接进入了这个容器的交互式运行环境,我们会发现我们终端中输入命令的前缀已经发生了变化。也就是说,现在你所输入的命令已经不是在你的物理机上执行的,而是在容器中执行的。你可以输入ls,ps 等你在物理机上常使用的命令,你会发现你就好像进入了另一台计算机的终端一样。最后你要想直接退出当前容器,请直接在容器中输入exit,这样你就退出了容器,回到了你的主机bash中。同时刚才的容器也就停止运行了

查看容器

1
2
3
$ docker ps  查看当前还在运行的容器
$ docker ps –a 查看系统中所有的容器
$ docker ps –l 查看最近一次使用的容器

查看容器的详细信息

1
$ docker inspect NAME

NAME既可以是docker ps 命令返回内容中的容器的CONTAINER ID也可以是最后一列中的NAME,该命令会返回该容器的详细信息。在将镜像的时候也提到过docker的inspect命令,inspect命令既可以查看容器也可以查看镜像,并且用法还相同。

自定义容器名

我们在上面查看容器的一些操作中会发现,有一列数据NAME,每一个容器都会对应一个NAME,这里的NAME非常没有规律,不便于记忆,就是一串随机的字符串。那是因为我们在运行这个容器的时候没有指定容器的名字,然后docker就分配了一个随机的名字给我们的容器。那么怎样制定容器的名字呢?

1
$ docker run -it --name 容器名 ubuntu /bin/bash

看到没,就是这么简单,在刚才运行容器的命令上面再加一个–name的参数就可以指定容器名了。

重新启动已经停止的容器

我们刚才用exit直接退出了容器,容器就停止运行了,假如我们又想进入这个容器,怎么办呢?

1
$ docker start [-i] 容器名

-i 表示是否用交互的方式启动已经停止的容器

删除已经停止的容器

又假如我们不想要这个容器了,想要删除他,可以下面这样做:

1
$ docker rm 容器名或ID

这样这个容器就删除了,注意这个命令只能删除已经停止运行的容器

导入和导出容器

有时,需要将容器从一个系统迁移到另外一个系统,此时就可以使用docker的导入和导出功能。

导入容器

导出容器是指导出一个已经创建的容器到一个文件,不管此时这个容器是否处于运行状态,都可以使用docker export命令,示例:

1
2
3
$ docker export -o test.tar CONTAINER_ID
或者:
$ docker export CONTAINER_ID >test.tar

之后可以将导出的tar文件传输到其他的机器上,然后通过导入命令导入到系统中,从而实现了容器的迁移。

导出容器

导出的文件可以通过docker import命令导入变成镜像,下面将上面导出的test.tar文件导入到系统中:

1
$ docker import test.tar - test/ubuntu:v1.0

完成后通过docker images命令就可以看到导入的test/ubuntu:v1.0镜像了。

导入导出容器与导入导出镜像的区别

在docker image and repostory中讲过docker load命令来导入一个镜像文件,这与docker import命令十分相似。事实上既可以使用docker load命令来导入镜像存储文件到本地镜像库,也可以使用docker import命令来导入一个容器快照到本地镜像库。这两者之间的区别在于容器快照文件将丢失所有的历史记录和元数据信息(仅保存容器运行时的快照状态),而镜像存储文件将保存完整记录,体积也更大。

docker的守护式容器

我们在交互式容器中不要以exit 命令来退出,而是用Ctrl+P,Ctrl+Q来退出交互式容器,这时我们就得到了一个守护容器。这种方式退出与exit不同的是,用exit退出之后容器就停止了,而这种方式容器就会移入到后台运行,容器并未停止。
如果我们想要再次进入此在后台运行的容器的话,我们可以使用:updated:

1
$ docker attach 容器名或ID

1
$ docker exec -it 容器名或ID /bin/bash

这样我们就又可以进入其交互式环境中了。上面这两种进入守护式容器的命令稍有不同,后面会讲到。

除了通过交互式环境得到守护容器外,我们还通过run命令来启动一个守护式容器。

1
$ docker run –d IMAGE [COMMAND] [ARG…]

在run命令中加入-d这个选项之后,命令的执行被放在了后台。这样同样可以得到一个守护式容器。

Log命令查看容器的日志

1
$ docker log [-f ] [-t ] [--tail] 容器名

-f –follow=ture|false 默认为 false 跟踪日志的变化,并返回结果
-t –timestamps=ture|false 默认为false 给每条日志加上一个时间戳,及日志产生的时间
–tail 选择结尾处多少数量的日志,如果不指定,返回所有的日志

查看运行中容器内的进程

1
$ docker top 容器名

在已经运行的容器中启动新的进程

1
$ docker exec [-i] [-t] [-d] 容器名 [COMMAND] [ARG…]

虽然docker的理念是一个容器运行一种服务,但我们有时任然需要做一些监控,日志记录等服务,所以有时候我们还会在已经运行的容器中开启新的进程,上面的命令可以做到这一点,可以发现他与run命令非常像。

停止守护式容器

1
2
$ docker stop 容器名
$ docker kill 容器名

attach和exec的区别

刚刚我们讲到进入守护式容器有两种方法:

1
$ docker attach 容器名或ID

1
$ docker exec -it 容器名或ID /bin/bash

他们之间有什么区别呢?他们有很大的区别,attach是进入前面的那个进程,并且进入你离开时的状态,而exec则是在容器内开启一个新的进程。虽然两条命令都可以进入到一个容器中去操作容器,但是我们还是有必要学会在什么时候用什么命令。如果我们一直以exec进入容器,并且在退出的时候是以ctrl+p,ctrl+q的方式退出的话,那么容器中开启的进程会逐渐的增多,这对资源是一个巨大的消耗,如果以exit退出当前的进程则可以避免这个问题。attach呢?貌似我们可以一直使用attach,其实不然。由于attach总是进入到同一个进程,如果那个进程由于运行个什么东西而被阻塞(比如uwsgi)那么你就无法再次进入到这个进程了,这个时候就该使用exec开启一个新线程。总而言之,明白两者之间的区别,根据不同的情况使用不同的命令,才是最好的。
对了,关于attach还有一个小问题。就是当使用attach进入时,容器中的命令提示符总是迟迟不出来,好像就是卡在哪儿似的。其实他已经进入了容器,只是不显示前面的命令提示符而已。这个时候我们可以敲一个ls或者是个其他的什么命令,就可以让命令行回归正常了。不知道这是不是docker的一个小bug呢?(我的系统环境是:4.10.0-32-generic,docker版本:Docker version 17.05.0-ce, build 89658be)