和我一起學docker(二)docker的進程隔離

和我一起學docker(二)docker的進程隔離

docker


作者: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之一,實現的是資源的隔離

和我一起學docker(二)docker的進程隔離

那麼,容器技術既然是實現進程級別的隔離,就先談談PID NAMESPACE吧。

1、docker的進程模型

當我們在centos7的主機上啟動了docker之後,查詢進程,可以看到

<code>[root@k8s-master ~]# ps -ef | grep docker 
root       1022      1  0 8月19 ?       00:00:33 /usr/bin/dockerd
root       1198   1022  0 8月19 ?       00:00:03 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
root       1924   1198  0 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-runc
root      13766   1198  0 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-runc
root      14388   1198  0 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-runc
root      14444   1198  0 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-runc
root      14515   1198  0 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-runc
root      14622   1198  0 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-runc
root      14686   1198  0 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-runc
root      14882   1198  0 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-runc
root      14911   1198  0 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-runc
root      15106   1198  0 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-runc
root      15287   1198  0 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-runc
root      35432  35286  0 00:32 pts/0    00:00:00 grep --color=auto docker/<code>

可以看到,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

<code>FROM redis:v1
CMD redis-server/<code>

dockerfile_exec

<code>FROM redis:v1
CMD ["redis-server"]/<code>

然後基於以上內容構建鏡像

<code>[root@harbor dockerfile]# docker build -t redis:shell -f dockerfile_shell .
Sending build context to Docker daemon  401.9MB
Step 1/2 : FROM redis:v1
 ---> 6f28a7e0584f
Step 2/2 : CMD redis-server
 ---> Running in 25ab7fc5450f
Removing intermediate container 25ab7fc5450f
 ---> 75fc5d056605
Successfully built 75fc5d056605
Successfully tagged redis:shell
[root@harbor dockerfile]# docker build -t redis:exec -f dockerfile_exec .
Sending build context to Docker daemon  401.9MB
Step 1/2 : FROM redis:v1
 ---> 6f28a7e0584f
Step 2/2 : CMD ["redis-server"]
 ---> Running in ad8a324c4b5e
Removing intermediate container ad8a324c4b5e
 ---> 528277f613e8
Successfully built 528277f613e8
Successfully tagged redis:exec/<code>

接下來分別運行兩個鏡像,我們觀察一下進程

<code>[root@harbor dockerfile]# docker ps --no-trunc
CONTAINER ID                                                       IMAGE               COMMAND                     CREATED             STATUS              PORTS               NAMES
9f1d971b25f26084450f8c8a74c818372e11f98337d5c52d54b162ff5c0fe4b7   redis:exec          "redis-server"              18 seconds ago      Up 17 seconds                           adoring_dhawan
b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a   redis:shell         "/bin/sh -c redis-server"   21 seconds ago      Up 20 seconds                           objective_mcnulty/<code>

我們可以看到 shell模式下,COMMAND為"/bin/sh -c redis-server",在容器內又fork出了/usr/bin/tail進程,而exec模式下為 "redis-server"。

此時,我們在宿主機層面關注一下進程,在宿主機上執行一下ps 命令

<code>[root@harbor ~]# ps -ef | grep docker 
root       1524      1  0 17:09 ?        00:00:41 /usr/bin/dockerd
root       1815   1524  0 17:09 ?        00:00:26 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
root       4211   1815  0 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-runc
root       4277   1815  0 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-runc
root       5824   5784  0 20:00 pts/0    00:00:00 grep --color=auto docker
[root@harbor ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
9f1d971b25f2        redis:exec          "redis-server"           2 hours ago         Up 2 hours                              adoring_dhawan
b29e9823bd53        redis:shell         "/bin/sh -c redis-se…"   2 hours ago         Up 2 hours                              objective_mcnulty
[root@harbor ~]# ps -ef | grep redis 
root       4229   4211  0 17:40 pts/0    00:00:08 redis-server *:6379
root       4293   4277  0 17:40 pts/0    00:00:08 redis-server *:6379
root       5832   5784  0 20:00 pts/0    00:00:00 grep --color=auto redis/<code>

我們可以清晰地看到容器9f1d971b25f2在宿主機上的進程為4277,容器b29e9823bd53的進程為4211是,而這也是其中redis的父進程。

但是當我們進入到兩個容器內,執行ps -ef 命令時,可以看到

<code>[root@9f1d971b25f2 /]# ps -ef    
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 09:40 pts/0    00:00:00 redis-server *:6379
root          8      0  0 09:42 pts/1    00:00:00 bash
root         21      8  0 09:42 pts/1    00:00:00 ps -ef
[root@9f1d971b25f2 /]# exit
exit
[root@harbor dockerfile]# docker exec -it b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a bash 
[root@b29e9823bd53 /]# ps -ef 
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 09:40 pts/0    00:00:00 redis-server *:6379
root          8      0  0 09:42 pts/1    00:00:00 bash
root         21      8  0 09:42 pts/1    00:00:00 ps -ef/<code>

可以很直觀的看到,在每個容器內都是獨立計算PID的,這就是docker容器所實現的進程級別的隔離。

2、PID NameSpace

那麼docker是怎麼實現的進程級別的隔離呢?這個是依託於linux內核提供的PID NameSpaced。我們在日常應用中可以發現,當linux在啟動一個進程時,會為進程分配一個唯一的進程號。

當創建一個新的PID NameSpace後,在這個PID NameSpace中進程從1開始計算,當然,這個不是真正的PID=1,下面可以用命令更直觀的看一下

<code># 在宿主機
[root@harbor ~]# ps -ef | grep docker 
root       1524      1  0 17:09 ?        00:00:41 /usr/bin/dockerd
root       1815   1524  0 17:09 ?        00:00:26 containerd --config /var/run/docker/containerd/containerd.toml --log-level info
root       4211   1815  0 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-runc
root       4277   1815  0 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-runc
root       5824   5784  0 20:00 pts/0    00:00:00 grep --color=auto docker
[root@harbor ~]# docker ps 
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
9f1d971b25f2        redis:exec          "redis-server"           2 hours ago         Up 2 hours                              adoring_dhawan
b29e9823bd53        redis:shell         "/bin/sh -c redis-se…"   2 hours ago         Up 2 hours                              objective_mcnulty
[root@harbor ~]# ps -ef | grep redis 
root       4229   4211  0 17:40 pts/0    00:00:08 redis-server *:6379
root       4293   4277  0 17:40 pts/0    00:00:08 redis-server *:6379
root       5832   5784  0 20:00 pts/0    00:00:00 grep --color=auto redis

# 在容器內
[root@harbor dockerfile]# docker exec -it b29e9823bd53be5c569f49393f15aa44ad3f1b1ff4fe7ba03dadd94aae5d272a bash 
[root@b29e9823bd53 /]# ps -ef 
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 09:40 pts/0    00:00:00 redis-server *:6379
root          8      0  0 09:42 pts/1    00:00:00 bash
root         21      8  0 09:42 pts/1    00:00:00 ps -ef/<code>

進程模擬圖如下,可以看出來,在不同的級別的namespace中,分配的PID是不同的,而這也正是docker實現進程級別隔離的基石。

和我一起學docker(二)docker的進程隔離

進程模擬圖


分享到:


相關文章: