玖叶教程网

前端编程开发入门

docker(3):容器中的进程(docker 进容器)

前文中,我们运行了一个名为nginx-demo的容器,如果我们想要进入这个容器内部,查看一下容器中的nginx配置文件内容,该怎么办呢?想进入容器很简单,我们只需要执行如下命令即可。

docker exec -it nginx-demo /bin/bash

执行上述命令后,命令提示符会从host主机的提示符变成了容器的提示符(host主机就是容器所在的主机),如下

[root@kvm32docker2 ~]# docker exec -it nginx-demo /bin/bashroot@55e0b2020fc0:/# root@55e0b2020fc0:/# 

容器的提示符是root@55e0b2020fc0:/#,细心如你肯定已经发现了,55e0b2020fc0正是容器的ID,也就是执行docker ps命令时看到的nginx-demo的容器ID。

提示符变化后,所有操作就是在容器中进行了,我们可以进入某个目录,查看一些配置文件的内容,如下

root@55e0b2020fc0:/# cd /etc/nginx/root@55e0b2020fc0:/etc/nginx# lsconf.d  fastcgi_params  mime.types  modules  nginx.conf  scgi_params  uwsgi_paramsroot@55e0b2020fc0:/etc/nginx# cat nginx.conf 

如果我们尝试在nginx-demo容器中使用vim或者vi命令去修改配置文件,则会报错

root@55e0b2020fc0:~# vim /etc/nginx/nginx.conf bash: vim: command not found

很明显,我们下载的nginx镜像中并没有包含vim和vi,所以,通过nginx镜像启动的容器中也不会有对应的命令,这种情况在容器内操作时是很常见的,因为在创建镜像时,制作者为了尽量减小镜像的体积,会只将必要的程序打包进镜像,显然,编辑器对于nginx来说,不是必须依赖的程序,没有编辑器,怎样修改文件内容呢?有两种方法,一种方法是先将容器内的文件拷贝出来,在host主机中修改完成后再拷贝回去,还有一种方法是通过docker卷的方式,将容器中的配置文件目录与host主机中的某个目录映射挂载,映射后,直接在host主机中即可修改对应的配置文件了。第二种方法是较为常用的,但是由于docker卷的话题后面会单独总结,所以此处咱们先使用第一种方法,直接把文件拷贝出来修改,然后再拷贝回去,操作如下

#注:拷贝操作均在host主机中完成#先查看一下当前目录,只有一个文件[root@kvm32docker2 ~]# lsanaconda-ks.cfg#通过docker cp命令,将nginx-demo容器中的/etc/nginx/nginx.conf文件拷贝到当前路径下[root@kvm32docker2 ~]# docker cp nginx-demo:/etc/nginx/nginx.conf ./#再次查看当前目录,已经多出了一个nginx.conf文件[root@kvm32docker2 ~]# lsanaconda-ks.cfg  nginx.conf#修改配置文件内容,在配置文件的最后一行添加一行注释[root@kvm32docker2 ~]# vim nginx.conf#通过docker cp命令,将当前目录中修改过的nginx.conf文件拷贝回容器中[root@kvm32docker2 ~]# docker cp ./nginx.conf nginx-demo:/etc/nginx/nginx.conf

完成上述操作后,再次回到容器中查看对应的文件,会发现文件内容已经是修改过的内容了。
如你所见,借助
docker cp命令,可以在host主机和容器间互相拷贝文件,即使容器停止了,也可以进行拷贝操作。

进入容器后,如果想要退出,在容器中执行exit命令即可,如下:

[root@kvm32docker2 ~]# docker exec -it nginx-demo /bin/bashroot@55e0b2020fc0:/# root@55e0b2020fc0:/# exitexit

在容器的shell中执行exit命令会结束当前shell,如果不想结束当前shell,只是想要从当前shell中剥离出来,可以使用组合键Ctrl + p + q

其实,docker exec命令不仅可以帮助我们进入容器,还能让我们在不进入容器的情况下,直接运行容器内的命令,我们先来看一个示例,然后再详细的讨论docker exec命令参数的具体作用,示例如下:

[root@kvm32docker2 ~]# docker exec nginx-demo ls -l /etc/nginx/total 24drwxr-xr-x 1 root root   26 Feb  9 10:16 conf.d-rw-r--r-- 1 root root 1007 Dec 28 15:28 fastcgi_params-rw-r--r-- 1 root root 5349 Dec 28 15:28 mime.typeslrwxrwxrwx 1 root root   22 Dec 28 15:40 modules -> /usr/lib/nginx/modules-rw-r--r-- 1 root root  656 Mar  9 11:07 nginx.conf-rw-r--r-- 1 root root  636 Dec 28 15:28 scgi_params-rw-r--r-- 1 root root  664 Dec 28 15:28 uwsgi_params

上例中,我们没有进入容器,而是通过docker exec命令指定在nginx-demo中运行ls -l /etc/nginx/命令,并将容器内的运行结果返回到当前的命令行中,从而查看到了容器内部/etc/nginx/目录中的信息。

你肯定已经发现了,如下两条命令的主要区别在于是否使用了-it参数

命令一:docker exec -it nginx-demo /bin/bash命令二docker exec nginx-demo ls -l /etc/nginx/

如果我们把命令一中的-it参数去掉,其实它们两个的含义是一样的,都是在指定的容器中运行对应的命令,并将返回值显示在当前的命令行中,只不过,当我们直接执行如下命令时,是没有任何返回值的

[root@kvm32docker2 ~]# docker exec nginx-demo /bin/bash[root@kvm32docker2 ~]# 

上例表示在nginx-demo容器中执行/bin/bash命令,并将返回值显示到当前命令行中,但是由于容器中的bash没有任何输出,所以给人的感觉好像没有变化一样。

那么,-it参数是什么意思呢?其实,-it是两个参数,-i-t

-i参数表示interactive,即交互模式。-t参数表示分配一个伪终端。

通常情况下,这两个参数会结合在一起使用,方便我们在终端中以交互模式运行指定的程序,所以,现在再回过头来看下面的命令,就很容易理解其意义了

docker exec -it nginx-demo bash

上述命令表示,在终端中以交互模式运行nginx-demo容器中的bash程序,最终呈现的效果就是在容器中敲命令,需要注意的是,有的镜像中没有bash程序,只有sh程序或者其他shell,所以通过上述方法进入容器内部时,需要先确定镜像中的shell是什么。

除了在容器中运行shell需要利用到-it参数,还有哪些场景会用到-it参数呢?比如,当你的容器中有vi编辑器时,可以直接使用如下命令,编辑容器中的文件。

#如下命令表示在交互终端下,执行容器中的vi命令,编辑容器内的/etc/hosts文件docker exec -it 容器名 vi /etc/hosts

刚才说过,为了尽量减少镜像的体积,镜像的制作者通常只会将必要依赖软件打包到镜像中,所以,在做实验时,没有对应工具命令的情况非常常见,没有关系,我们可以直接在镜像中安装对应的软件,方便我们进行实验,例如,当前nginx-demo容器所使用的镜像是基于debian操作系统建立的,这一点通过容器内的/etc/os-release文件内容可以确认,所以,我们可以使用apt-get的软件源安装需要的软件,就像在debian系统下安装软件一样。
此处在nginx-demo容器中安装procps包,以便可以在容器中执行ps命令,方便之后的测试。同理,也可以安装vim,方便在容器中直接编辑文件。

root@55e0b2020fc0:/# apt-get update && apt-get install procps

如果在容器中执行apt-get install命令时对应的软件下载缓慢,可以修改容器的/etc/apt/sources.list文件中的下载源,就跟平常debian系统下的操作别无二致。

安装完procps后,即可在nginx-demo容器中执行ps命令了,执行结果如下:

root@55e0b2020fc0:/# ps -efUID        PID  PPID  C STIME TTY          TIME CMDroot         1     0  0 Mar14 ?        00:00:00 nginx: master process nginx -g daemon off;nginx       26     1  0 Mar14 ?        00:00:00 nginx: worker processnginx       27     1  0 Mar14 ?        00:00:00 nginx: worker processnginx       28     1  0 Mar14 ?        00:00:00 nginx: worker processnginx       29     1  0 Mar14 ?        00:00:00 nginx: worker processroot       310     0  0 02:22 pts/1    00:00:00 bashroot       322   310  0 02:23 pts/1    00:00:00 ps -ef

可以看到当前shell的pid是310,如果此时在容器内执行exit命令,则会结束当前pid为310的进程,并退出容器,如果使用组合键Ctrl + p + q,则不会结束pid为310的bash进程,而是直接从容器中剥离出来。
如上,nginx的master进程的pid为1,其他nginx worker进程为master进程的子进程,如果此时我们在容器内部将pid为1的进程kill掉,会发生什么事情呢?我们来试试

root@55e0b2020fc0:/# kill -9 1root@55e0b2020fc0:/# kill -15 1root@55e0b2020fc0:/# [root@kvm32docker2 ~]# 

可以看到,我尝试在容器内使用kill -9强制停止pid为1的进程,但是执行命令后没有任何反应,于是,我尝试使用kill -15命令,正常终止pid为1的进程,执行命令后,命令提示符自动从容器跳转到了host主机中,那么,我们是自动退出容器了,还是容器停止了呢?我们来验证一下,在host中执行docker ps命令查看一下,docker ps命令默认只显示运行中的容器,停止的容器不会显示,如下

[root@kvm32docker2 ~]# docker psCONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES[root@kvm32docker2 ~]# 

可见,上述结果没有列出任何容器,证明nginx-demo容器是停止了。
通过上述测试,我们可以得出一个结论,当我们终止容器中pid为1的进程时,容器就会停止。

执行docker ps -a命令,可以列出所有容器,无论容器是否处于运行状态,都会列出,如下

[root@kvm32docker2 ~]#  docker ps -aCONTAINER ID   IMAGE          COMMAND                  CREATED       STATUS                      PORTS     NAMES55e0b2020fc0   nginx:latest   "/docker-entrypoint.…"   4 weeks ago   Exited (0) 31 minutes ago             nginx-demo

如上所示,nginx-demo容器目前处于Exited状态,退出码是0,证明是正常退出的。
其实,当我们通过
docker stop命令去停止容器时,也是类似的原理,docker stop命令会向容器内部的主进程(PID为1的进程)发送SIGTERM信号,主进程收到信号后,开始停止,主进程停止后,容器也会随之停止,如果超过默认时间(10秒)主进程还是没有停止,则发送SIGKILL信号强制停止。

除了docker stop命令,docker kill命令也可以用来停止容器,只不过docker kill命令默认更加激进,docker kill命令默认直接向容器主进程发送SIGKILL信号。

docker kill命令也可以根据用户的需求,向容器的主进程发送指定的信号,使用--signal参数即可,示例如下

[root@kvm32docker2 ~]# docker kill --signal=HUP nginx-demonginx-demo

上述命令表示向nginx-demo容器内的主进程(PID为1的进程)发送HUP信号,由于nginx-demo容器中的主进程就是nginx master进程,所以上例命令相当于向容器内的nginx master进程发送HUP信号。
我们知道,当执行
nginx -s reload命令时,其实就是在向nginx的master进程发送HUP信号(具体可以参考博客中的这篇文章:nginx信号),所以,上例命令相当于在nginx-demo容器中执行nginx -s reload命令,即重载配置,如果我们修改了nginx配置文件,借助上例命令,即使不进入nginx-demo容器,也可以实现重载配置的目的。

此处我重启了nginx-demo容器,方便后面的实验。

当容器启动后,除了在容器中执行ps -ef命令可以查看进程,在host主机上其实也可以看到容器中的进程,我们只需要使用docker top命令,即可在host中查看指定容器内的进程,为了对比效果,咱们先在容器中执行ps -ef命令,再在host中执行docker top命令,示例如下

#进入容器后执行ps -ef命令#注意返回结果中的PID列和CMD列[root@kvm32docker2 ~]# docker exec -it nginx-demo bashroot@55e0b2020fc0:/# root@55e0b2020fc0:/# ps -efUID        PID  PPID  C STIME TTY          TIME CMDroot         1     0  0 03:30 ?        00:00:00 nginx: master process nginx -g daemon off;nginx       24     1  0 03:30 ?        00:00:00 nginx: worker processnginx       25     1  0 03:30 ?        00:00:00 nginx: worker processnginx       26     1  0 03:30 ?        00:00:00 nginx: worker processnginx       27     1  0 03:30 ?        00:00:00 nginx: worker processroot        35     0  0 03:45 pts/0    00:00:00 bashroot        42    35  0 03:45 pts/0    00:00:00 ps -ef#我没有退出容器,在另一个命令窗口中,直接在host中执行docker top nginx-demo命令#注意命令返回结果中的PID列和CMD列[root@kvm32docker2 ~]# docker top nginx-demoUID                 PID                 PPID                C                   STIME               TTY                 TIME                CMDroot                14773               14753               0                   11:30               ?                   00:00:00            nginx: master process nginx -g daemon off;101                 14819               14773               0                   11:30               ?                   00:00:00            nginx: worker process101                 14820               14773               0                   11:30               ?                   00:00:00            nginx: worker process101                 14821               14773               0                   11:30               ?                   00:00:00            nginx: worker process101                 14822               14773               0                   11:30               ?                   00:00:00            nginx: worker processroot                15629               14753               0                   11:45               pts/0               00:00:00            bash

很明显,在host主机中通过docker top命令查询出的pid,和在容器中通过ps命令查询出的pid不同,前文说过,docker的本质是一种进程隔离技术,docker是通过Linux内核的namespace功能实现PID的隔离的,pid namespace将host上的PID映射为容器内的PID,这样可以让不同的PID namespace中存在相同的进程号,并控制进程间的关系,这样说可能不太容易理解,咱们换个方式解释一下,假设,现在有两个容器,一个nginx容器,一个redis容器,在nginx容器中,nginx的master进程的pid为1,在redis容器中,redis-server进程的pid为1,对于nginx-master进程或者redis-server进程来说,它们都以为自己是这个世界上的王者,因为它们只能看到自己的世界,而且在自己看到的世界中,自己是主进程(PID为1),其实,它们都只不过是host主机中的一个普通进程罢了,nginx容器的master进程在host主机中的PID可能是1234,redis容器中的server进程在host主机中的PID可能是5678,即使在各自的容器中,它们的PID都是1,也许这就是pid namespace进程隔离最直观的感受吧。所以,docker top命令查看的是容器进程在host中的pid,在容器内执行ps命令查看的是容器进程在容器内的pid。

从上例的执行结果中可以看出,host中的pid为14773的进程就是nginx-demo中pid为1的进程,因为从执行结果的CMD列可以对比出来,即使不通过CMD列,通过各个进程的父子关系,也可以推算出,host中的pid14773就是容器中的pid1,但是这样对比推算,总感觉很麻烦,其实,直接执行如下命令,也可以知道容器的pid1进程对应在host主机中的进程的pid

[root@kvm32docker2 ~]# docker inspect -f '{{.State.Pid}}' nginx-demo14773

如上可知,host中的pid14773就是nginx-demo容器中的pid1。

我们已经可以快速的找到容器内pid1对应的host pid了,那么容器内的其他进程与host中的pid的对应关系该怎样查看呢?具体方法我们一会儿再聊,先来说说上例中的docker inspect命令怎么使用。

docker inspect命令可以用来查看docker对象的详细信息,比如

#查看镜像的详细信息[root@kvm32docker2 ~]# docker inspect nginx:latest#查看容器的详细信息[root@kvm32docker2 ~]# docker inspect nginx-demo#结合-f选项,可以对返回的详细信息进行过滤,过滤语法使用go模板的语法#如下命令表示从nginx-demo的详细信息中过滤出容器的主机名[root@kvm32docker2 ~]#  docker inspect -f '{{.Config.Hostname}}' nginx-demo

我们已经知道,通过docker top命令可以查看到容器中正在运行的进程在host中的pid,其实在host中的/sys/fs/cgroup/memory/docker/容器ID/cgroup.procs文件中,也存放了这些pid,我们一起来看看,示例操作如下

#先通过docker ps命令查看对应容器的id,下例中的--no-trunc选项表示不简略显示返回的信息,这样可以查看到完整的容器id。[root@kvm32docker2 ~]# docker ps -a --no-truncCONTAINER ID                                                       IMAGE          COMMAND                                          CREATED       STATUS             PORTS                NAMES55e0b2020fc0df059b32dbe193651aeb48859564f93b0b3c27092667c102c8cb   nginx:latest   "/docker-entrypoint.sh nginx -g 'daemon off;'"   4 weeks ago   Up About an hour   0.0.0.0:80->80/tcp   nginx-demo#将上例命令中查看到的容器id替换到/sys/fs/cgroup/memory/docker/容器ID/cgroup.procs路径中,查看cgroup.procs文件的内容,其内容就是容器中正在运行的进程在host主机中的映射进程的pid[root@kvm32docker2 ~]# cat /sys/fs/cgroup/memory/docker/55e0b2020fc0df059b32dbe193651aeb48859564f93b0b3c27092667c102c8cb/cgroup.procs147731481914820148211482215629

通过之前介绍的方法,已经可以确定host中的pid14773就是nginx-demo容器中的pid1,如果想要确定其他进程pid的映射关系,可以通过如下两种方法查询。

方法一:
注:Linux内核版本小于4.1用此方法
进入对应的容器操作,此处进入nignx-demo容器,执行
grep threads /proc/*/sched命令
从返回信息中可以看到容器内进程号和host中进程号的对应关系,示例如下

root@55e0b2020fc0:/# grep threads /proc/*/sched/proc/1/sched:nginx (14773, #threads: 1)/proc/24/sched:nginx (14819, #threads: 1)/proc/25/sched:nginx (14820, #threads: 1)/proc/26/sched:nginx (14821, #threads: 1)/proc/27/sched:nginx (14822, #threads: 1)/proc/35/sched:bash (15629, #threads: 1)/proc/self/sched:grep (18469, #threads: 1)

方法二:
注:Linux内核版本大于4.1用此方法
不进入容器,在host中操作,首先,通过
docker top命令或者cgroup.procs文件获取到容器中的进程在host中的pid,然后查看host中的/proc/进程pid/status文件,其中NSpid的值就是host中的pid和容器中的pid的对应关系,示例如下

#如下测试在另一台升级了内核的虚拟机中进行操作#获取容器进程在host中的pid[root@cos7-1 ~]# docker top nginx-testUID                 PID                 PPID                C                   STIME               TTY                 TIME                CMDroot                2720                2701                0                   12:54               ?                   00:00:00            nginx: master process nginx -g daemon off;101                 2773                2720                0                   12:54               ?                   00:00:00            nginx: worker process101                 2774                2720                0                   12:54               ?                   00:00:00            nginx: worker process#根据host中的pid,即可获取到和容器内的pid的对应关系[root@cos7-1 ~]# grep NSpid /proc/2720/statusNSpid:  2720    1[root@cos7-1 ~]# grep NSpid /proc/2773/statusNSpid:  2773    32[root@cos7-1 ~]# grep NSpid /proc/2774/statusNSpid:  2774    33#我们可以进入容器验证一下,如下测试容器中已经安装了ps命令[root@cos7-1 ~]# docker exec -it nginx-test bashroot@c3dcef8c776b:/# root@c3dcef8c776b:/# ps -efUID        PID  PPID  C STIME TTY          TIME CMDroot         1     0  0 04:54 ?        00:00:00 nginx: master process nginx -g daemon off;nginx       32     1  0 04:54 ?        00:00:00 nginx: worker processnginx       33     1  0 04:54 ?        00:00:00 nginx: worker processroot       381     0  0 04:59 pts/0    00:00:00 bashroot       387   381  0 04:59 pts/0    00:00:00 ps -ef

经过上述一番操作,我对容器的理解似乎加深了,nginx-demo容器也完成了它的使命,我准备卸磨杀驴了,没错,我现在想要删除这个容器,使用docker rm命令即可,如下:

[root@kvm32docker2 ~]# docker rm nginx-demoError response from daemon: You cannot remove a running container 55e0b2020fc0df059b32dbe193651aeb48859564f93b0b3c27092667c102c8cb. Stop the container before attempting removal or force remove

错误提示,我们无法删除正在运行中的容器,所以需要先停止它

[root@kvm32docker2 ~]# docker stop nginx-demonginx-demo[root@kvm32docker2 ~]# docker rm nginx-demonginx-demo[root@kvm32docker2 ~]# docker ps -aCONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

删除完容器,我们可以把镜像也删了,使用docker rmi命令删除镜像,如下

#先查看一下本地的镜像,目前只有一个nginx:latest镜像[root@kvm32docker2 ~]# docker imagesREPOSITORY   TAG       IMAGE ID       CREATED        SIZEnginx        latest    605c77e624dd   2 months ago   141MB#使用docker rmi删除对应镜像,删除前需要确保没有容器基于此镜像创建,如果有容器引用了此镜像,则无法删除此镜像#删除镜像时有可能会输出很多sha256信息,这是因为镜像可能是由很多层组成的,这些sha256信息就是镜像的层的id#不用纠结这些细节,咱们后面再聊。[root@kvm32docker2 ~]# docker rmi nginx:latestUntagged: nginx:latestUntagged: nginx@sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31Deleted: sha256:605c77e624ddb75e6110f997c58876baa13f8754486b461117934b24a9dc3a85Deleted: sha256:b625d8e29573fa369e799ca7c5df8b7a902126d2b7cbeb390af59e4b9e1210c5Deleted: sha256:7850d382fb05e393e211067c5ca0aada2111fcbe550a90fed04d1c634bd31a14Deleted: sha256:02b80ac2055edd757a996c3d554e6a8906fd3521e14d1227440afd5163a5f1c4Deleted: sha256:b92aa5824592ecb46e6d169f8e694a99150ccef01a2aabea7b9c02356cdabe7cDeleted: sha256:780238f18c540007376dd5e904f583896a69fe620876cabc06977a3af4ba4fb5Deleted: sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f#再次查看镜像列表,对应的镜像已经没有了[root@kvm32docker2 ~]# docker imagesREPOSITORY   TAG       IMAGE ID   CREATED   SIZE

今天就先聊到这里吧,回见~

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言