和我一起学docker(二)docker的进程隔离
https://p3-sign.toutiaoimg.com/dfic-imagehandler/65288676-1b98-4416-8491-eb23f11cf94a~tplv-tt-large.image?x-expires=1974087260&x-signature=X0tWW7JkQQsMixw6MidRy%2F6i14c%3Ddocker
作者:DevOps旭
来自:DevOps探路者
一、容器是什么
1、传统虚拟化和容器
在传统VM时代,我们要解决的核心问题是资源调配。在这一阶段,通过HyperV层抽象底层设施资源,提供互相隔离的机制,实现了统一管理、统一配置、计算资源的可运维性和资源利用率,让一台物理机能够同时运行多台虚拟主机,实现了资源利用率的有效提升。而到了容器时代,可以直接使用宿主机操作系统调度硬件资源,使得资源利用率远超传统VM,同时秒级的创建速度,使得核心问题转变成了应用开发、测试和部署。而docker则是容器时代用户最多的容器引擎之一。
2、初始docker
docker是由GO语言开发的,基于Linux内核的CGroup和Namespace以及UnionFS技术,实现的对进程进行封装隔离的虚拟化技术。docker通过虚拟化、共享内核,可以把应用需要的运行环境,缓存环境,数据库环境等进行封装,以最简化的方式运行应用,以追求最佳的性能。但是由于共享内核,所以在安全性和隔离性上远低于虚拟机。
那么,docker在解决应用开发测试和部署方面的优势有哪些呢?
首先、简化了配置。可以无缝迁移到任何环境中运行,实现了应用运行环境和底层支持环境的解耦。
第二、开发环境生产化,可以使代码从开发者机器上无缝发布到测试和生产环境,极大程度上避免了因为环境不一致导致的各种问题出现,这也是云原生实现的基础。
第三、作为云计算的多租户容器,可以更容易得为每一个租户创建,运行实例。
第四、快速部署,快速的启停容器,实现了秒级的系统启动,为服务实现灰度发布,滚动发布奠定了基础。
第五、进程级别的隔离,大规模微服务集群的最优选择。
第六,降低了运维成本。
二、深入理解docker的进程隔离
使用容器的同学都知道,容器的基石为namespace和cgroup,那么namespace和cgroup是什么呢?又怎么帮助容器技术实现隔离的呢?那么我们需要先认识一下namespace和cgroup,严格地讲,cgroup也是namespace之一,实现的是资源的隔离
https://p3-sign.toutiaoimg.com/pgc-image/2f0db9c234ad42e498a72a45c2e56d46~tplv-tt-large.image?x-expires=1974087260&x-signature=%2FXFwGm1Eln1f9gS7JyCfZQHR%2Fg8%3D
那么,容器技术既然是实现进程级别的隔离,就先谈谈PID NAMESPACE吧。
1、docker的进程模型
当我们在centos7的主机上启动了docker之后,查询进程,可以看到
# ps -ef | grep docker root 1022 10 8月19 ? 00:00:33 /usr/bin/dockerdroot 1198 10220 8月19 ? 00:00:03 containerd --config /var/run/docker/containerd/containerd.toml --log-level inforoot 1924 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/ca3a70c6cab9fd9b25d5f608460b5463a2b83369bc4e1efef2ee1556ed88da15 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 13766 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/661aa0b4186b09bbe1ad986c56515bbe1d5bdd54579b9511d4967f398968166f -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14388 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/1a52099eac9fdaa12a13943d9ca779d4ea1801e9b3566b422c9bae717bd8f42a -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14444 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/94350e389f3d6968a43cfe44b86a92d240f1b55345dd12fe5105f04148f7270c -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14515 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/e5d09f22e24935bbff8e0b21a2755ef2fecaadd50c25675a42e55331d0632053 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14622 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/c1ee8c758c8f05671871a198062facdaa33f90683dce561f0c9c0eb9cff050e2 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14686 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/8c6ad25b86a8b9a6462adf863a96fcb1d141efbb5c1440c5089d88fe32410b69 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14882 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/70567de818e599482930c663e7a8546a2eee5d4426c6b35a788463da6c2fa7dc -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 14911 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/6afb44ca8c2c26997de0b4b484bb65b4095396bc961e3a80e2cb0565bd75f04d -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 15106 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/001a68c9f11b5fd91dcd1cbe7c1949b5517be4f69cdfa56e1e1ab7e497610410 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 15287 11980 8月19 ? 00:00:00 containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/c9cc60f7bf49b3aa7a6b82a4f20ccff0352558bdcdf52c0aad1d5806d28f2838 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 35432352860 00:32 pts/0 00:00:00 grep --color=auto docker可以看到,docker启动的第一个进程为/usr/bin/dockerd,这个是整个docker服务的入口。而dockerd的子进程docker-containerd,则是docker服务端的核心进程,负责与docker客户端进行通信交互,fork出docker容器进程。
那么每个容器内可以启动几个进程呢?每个容器内的进程号是不是隔离的?容器内的应用又是怎么启动的呢?容器内是否会有僵尸进程呢?针对这些问题,我想先谈一下容器内启动进程的两种方式:shell和exec。
在shell模式下,1号进程是以/bin/sh -c "command parameter1 parameter2 ..."的方式启动的。而exec模式下,则是 command parameter1 parameter2 ...的形式启动1号进程。下面我将构建两个镜像,以便更直观的看到这一现象。
dockerfile_shell
FROM redis:v1CMD redis-serverdockerfile_exec
FROM redis:v1CMD ["redis-server"]然后基于以上内容构建镜像
# docker build -t redis:shell -f dockerfile_shell .Sending build context to Docker daemon401.9MBStep 1/2 : FROM redis:v1 ---> 6f28a7e0584fStep 2/2 : CMD redis-server ---> Running in 25ab7fc5450fRemoving intermediate container 25ab7fc5450f ---> 75fc5d056605Successfully built 75fc5d056605Successfully tagged redis:shell# docker build -t redis:exec -f dockerfile_exec .Sending build context to Docker daemon401.9MBStep 1/2 : FROM redis:v1 ---> 6f28a7e0584fStep 2/2 : CMD ["redis-server"] ---> Running in ad8a324c4b5eRemoving intermediate container ad8a324c4b5e ---> 528277f613e8Successfully built 528277f613e8Successfully tagged redis:exec接下来分别运行两个镜像,我们观察一下进程
# docker ps --no-truncCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9f1d971b25f26084450f8c8a74c818372e11f98337d5c52d54b162ff5c0fe4b7 redis:exec "redis-server" 18 seconds ago Up 17 seconds adoring_dhawanb29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a redis:shell "/bin/sh -c redis-server" 21 seconds ago Up 20 seconds objective_mcnulty我们可以看到 shell模式下,COMMAND为"/bin/sh -c redis-server",在容器内又fork出了/usr/bin/tail进程,而exec模式下为 "redis-server"。
此时,我们在宿主机层面关注一下进程,在宿主机上执行一下ps 命令
# ps -ef | grep docker root 1524 10 17:09 ? 00:00:41 /usr/bin/dockerdroot 1815 15240 17:09 ? 00:00:26 containerd --config /var/run/docker/containerd/containerd.toml --log-level inforoot 4211 18150 17:40 ? 00:00:00 containerd-shim -namespace moby -workdir /mnt/docker-lib/containerd/daemon/io.containerd.runtime.v1.linux/moby/b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 4277 18150 17:40 ? 00:00:00 containerd-shim -namespace moby -workdir /mnt/docker-lib/containerd/daemon/io.containerd.runtime.v1.linux/moby/9f1d971b25f26084450f8c8a74c818372e11f98337d5c52d54b162ff5c0fe4b7 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 5824 57840 20:00 pts/0 00:00:00 grep --color=auto docker# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9f1d971b25f2 redis:exec "redis-server" 2 hours ago Up 2 hours adoring_dhawanb29e9823bd53 redis:shell "/bin/sh -c redis-se…" 2 hours ago Up 2 hours objective_mcnulty# ps -ef | grep redis root 4229 42110 17:40 pts/0 00:00:08 redis-server *:6379root 4293 42770 17:40 pts/0 00:00:08 redis-server *:6379root 5832 57840 20:00 pts/0 00:00:00 grep --color=auto redis我们可以清晰地看到容器9f1d971b25f2在宿主机上的进程为4277,容器b29e9823bd53的进程为4211是,而这也是其中redis的父进程。
但是当我们进入到两个容器内,执行ps -ef 命令时,可以看到
# ps -ef UID PID PPIDC STIME TTY TIME CMDroot 1 00 09:40 pts/0 00:00:00 redis-server *:6379root 8 00 09:42 pts/1 00:00:00 bashroot 21 80 09:42 pts/1 00:00:00 ps -ef# exitexit# docker exec -it b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a bash # ps -ef UID PID PPIDC STIME TTY TIME CMDroot 1 00 09:40 pts/0 00:00:00 redis-server *:6379root 8 00 09:42 pts/1 00:00:00 bashroot 21 80 09:42 pts/1 00:00:00 ps -ef可以很直观的看到,在每个容器内都是独立计算PID的,这就是docker容器所实现的进程级别的隔离。
2、PID NameSpace
那么docker是怎么实现的进程级别的隔离呢?这个是依托于linux内核提供的PID NameSpaced。我们在日常应用中可以发现,当linux在启动一个进程时,会为进程分配一个唯一的进程号。
当创建一个新的PID NameSpace后,在这个PID NameSpace中进程从1开始计算,当然,这个不是真正的PID=1,下面可以用命令更直观的看一下
# 在宿主机# ps -ef | grep docker root 1524 10 17:09 ? 00:00:41 /usr/bin/dockerdroot 1815 15240 17:09 ? 00:00:26 containerd --config /var/run/docker/containerd/containerd.toml --log-level inforoot 4211 18150 17:40 ? 00:00:00 containerd-shim -namespace moby -workdir /mnt/docker-lib/containerd/daemon/io.containerd.runtime.v1.linux/moby/b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 4277 18150 17:40 ? 00:00:00 containerd-shim -namespace moby -workdir /mnt/docker-lib/containerd/daemon/io.containerd.runtime.v1.linux/moby/9f1d971b25f26084450f8c8a74c818372e11f98337d5c52d54b162ff5c0fe4b7 -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runcroot 5824 57840 20:00 pts/0 00:00:00 grep --color=auto docker# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES9f1d971b25f2 redis:exec "redis-server" 2 hours ago Up 2 hours adoring_dhawanb29e9823bd53 redis:shell "/bin/sh -c redis-se…" 2 hours ago Up 2 hours objective_mcnulty# ps -ef | grep redis root 4229 42110 17:40 pts/0 00:00:08 redis-server *:6379root 4293 42770 17:40 pts/0 00:00:08 redis-server *:6379root 5832 57840 20:00 pts/0 00:00:00 grep --color=auto redis# 在容器内# docker exec -it b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a bash # ps -ef UID PID PPIDC STIME TTY TIME CMDroot 1 00 09:40 pts/0 00:00:00 redis-server *:6379root 8 00 09:42 pts/1 00:00:00 bashroot 21 80 09:42 pts/1 00:00:00 ps -ef进程模拟图如下,可以看出来,在不同的级别的namespace中,分配的PID是不同的,而这也正是docker实现进程级别隔离的基石。
https://p3-sign.toutiaoimg.com/pgc-image/4dbc356989194c6abeb192c7be54c424~tplv-tt-large.image?x-expires=1974087260&x-signature=hoLhEEvkJEvEZ9F1o2qZKbVcRCE%3D
进程模拟图 转发了 转发了 转发了 转发了 转发了 转发了 转发了 转发了 转发了
页:
[1]
2