成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Docker 底層原理淺析

開發 開發工具
容器的實質是進程,與宿主機上的其他進程是共用一個內核,但與直接在宿主機執行的進程不同,容器進程運行在屬于自己的獨立的命名空間。命名空間隔離了進程間的資源,使得 a,b 進程可以看到 S 資源,而 c 進程看不到。

 [[350604]]

作者:vitovzhong,騰訊 TEG 應用開發工程師

容器的實質是進程,與宿主機上的其他進程是共用一個內核,但與直接在宿主機執行的進程不同,容器進程運行在屬于自己的獨立的命名空間。命名空間隔離了進程間的資源,使得 a,b 進程可以看到 S 資源,而 c 進程看不到。

1. 演進

對于統一開發、測試、生產環境的渴望,要遠遠早于 docker 的出現。我們先來了解一下在 docker 之前出現過哪些解決方案。

1.1 vagrant

Vagarant 是筆者最早接觸到的一個解決環境配置不統一的技術方案。它使用 Ruby 語言編寫,由 HashCorp 公司在 2010 年 1 月發布。Vagrant 的底層是虛擬機,最開始選用的是 virtualbox。一個個已經配置好的虛擬機被稱作 box。用戶可自由在虛擬機內部的安裝依賴庫和軟件服務,并將 box 發布。通過簡單的命令,就能夠拉取 box,將環境搭建起來。

  1. // 拉取一個ubuntu12.04的box 
  2. $ vagrant init hashicorp/precise32 
  3.  
  4. // 運行該虛擬機 
  5. $ vagrant up 
  6.  
  7. // 查看當前本地都有哪些box 
  8. $ vagrant box list 

如果需要運行多個服務,也可以通過編寫 vagrantfile,將相互依賴的服務一起運行,頗有如今 docker-compose 的味道。

  1. config.vm.define("web") do |web|web.vm.box = "apache" 
  2. end 
  3. config.vm.define("db") do |db|db.vm.box = "mysql” 
  4. end 

 

1.2 LXC (LinuX Container)

 

在 2008 年,Linux 2.6.24 將 cgroups 特性合入了主干。Linux Container 是 Canonical 公司基于 namespace 和 cgroups 等技術,瞄準容器世界而開發的一個項目,目標就是要創造出運行在 Linux 系統中,并且隔離性良好的容器環境。當然它最早也就見于 Ubuntu 操作系統上。

2013 年,在 PyCon 大會上 Docker 正式面世。當時的 Docker 是在 Ubuntu 12.04 上開發實現的,只是基于 LXC 之上的一個工具,屏蔽掉了 LXC 的使用細節(類似于 vagrant 屏蔽了底層虛擬機),讓用戶可以一句 docker run 命令行便創建出自己的容器環境。

2. 技術發展

容器技術是操作系統層面的虛擬化技術,可以概括為使用 Linux 內核的 cgroup,namespace 等技術,對進程進行的封裝隔離。早在 Docker 之前,Linux 就已經提供了今天的 Docker 所使用的那些基礎技術。Docker 一夜之間火爆全球,但技術上的積累并不是瞬間完成的。我們摘取其中幾個關鍵技術節點進行介紹。

 

2.1 Chroot

軟件主要分為系統軟件和應用軟件,而容器中運行的程序并非系統軟件。容器中的進程實質上是運行在宿主機上,與宿主機上的其他進程共用一個內核。而每個應用軟件運行都需要有必要的環境,包括一些 lib 庫依賴之類的。所以,為了避免不同應用程序的 lib 庫依賴沖突,很自然地我們會想是否可以把他們進行隔離,讓他們看到的庫是不一樣的。基于這個樸素的想法,1979 年, chroot 系統調用首次問世。來舉個例子感受一下。在 devcloud 上申請的云主機,現在我的 home 目錄下準備好了一個 alpine 系統的 rootfs,如下:

 

在該目錄下執行:

  1. chroot rootfs/ /bin/bash 

然后將/etc/os-release 打印出來,就看到是”Alpine Linux”,說明新運行的 bash 跟 devcloud 主機上的 rootfs 隔離了。

 

2.1 Namespace

簡單來說 namespace 是由 Linux 內核提供的,用于進程間資源隔離的一種技術,使得 a,b 進程可以看到 S 資源;而 c 進程看不到。它是在 2002 年 Linux 2.4.19 開始加入內核的特性,到 2013 年 Linux 3.8 中 user namespace 的引入,對于我們現在所熟知的容器所需的全部 namespace 就都實現了。

Linux 提供了多種 namespace,用于對多種不同資源進行隔離。容器的實質是進程,但與直接在宿主機執行的進程不同,容器進程運行在屬于自己的獨立的命名空間。因此容器可以擁有自己的 root 文件系統、自己的網絡配置、自己的進程空間,甚至自己的用戶 ID 空間。

還是來看一個簡單的例子,讓我們有個感性認識,namespace 到底是啥,在哪里能直觀的看到。在 devcloud 云主機上,執行:ls-l /proc/self/ns 看到的就是當前系統所支持的 namespace。

 

接著我們使用 unshare 命令,運行一個 bash,讓它不使用當前的 pid namespace:

  1. unshare --pid --fork --mount-proc bash 

然后運行: ps -a 看看當前 pid namespace 下的進程都有哪些:

 

在新起的 bash 上執行:ls -l /proc/self/ns, 發現當前 bash 的 pid namespace 與之前是不相同的。

 

既然 docker 就是基于內核的 namespace 特性來實現的,那么我們可以簡單來認證一下,執行指令:

  1. docker run –pid host --rm -it alpine sh 

運行一個簡單的 alpine 容器,讓它與主機共用同一個 pid namespace。然后在容器內部執行指令 ps -a 會發現進程數量與 devcloud 機器上的一樣;執行指令 ls -l /proc/self/ns/ 同樣會看到容器內部的 pid namespace 與 devcloud 機器上的也是一樣。

2.2 cgroups

cgroups 是 namespace 的一種,是為了實現虛擬化而采取的資源管理機制,決定哪些分配給容器的資源可被我們管理,分配容器使用資源的多少。容器內的進程是運行在一個隔離的環境里,使用起來,就好像是在一個獨立于宿主的系統下操作一樣。這種特性使得容器封裝的應用比直接在宿主運行更加安全。例如可以設定一個 memory 使用上限,一旦進程組(容器)使用的內存達到限額再申請內存,就會出發 OOM(out of memory),這樣就不會因為某個進程消耗的內存過大而影響到其他進程的運行。

還是來看個例子感受一下。在 devcloud 機器上運行一個 apline 容器,限制只能使用前 2 個 CPU 且只能使用 1.5 個核:

  1. docker run --rm -it --cpus "1.5" --cpuset-cpus 0,1 alpine 

然后再開啟一個新的終端,先看看系統上有哪些資源是我們可以控制的:

  1. cat /proc/cgroups 

 

最左邊一側就是可以設置的資源了。接著我們需要找到這些控制資源分配的信息都放在哪個目錄下:

  1. mount | grep cgroup 

 

然后我們找到剛剛運行的 alpine 鏡像的 cgroups 配置:

  1. cat /proc/`docker inspect --format='{{.State.Pid}}' $(docker ps -ql)`/cgroup 

 

這樣,把二者拼接起來,就可以看到這個容器的資源配置了。我們先來驗證 cpu 的用量是否是 1.5 個核:

  1. cat /sys/fs/cgroup/cpu,cpuacct/docker/c1f68e86241f9babb84a9556dfce84ec01e447bf1b8f918520de06656fa50ab4/cpu.cfs_period_us 

輸出 100000,可以認為是單位,然后再看配額:

  1. cat /sys/fs/cgroup/cpu,cpuacct/docker/c1f68e86241f9babb84a9556dfce84ec01e447bf1b8f918520de06656fa50ab4/cpu.cfs_quota_us 

輸出 150000,與單位相除正好是設置的 1.5 個核,接著驗證是否使用的是前兩個核心:

  1. cat /sys/fs/cgroup/cpuset/docker/c1f68e86241f9babb84a9556dfce84ec01e447bf1b8f918520de06656fa50ab4/cpuset.cpus 

輸出 0-1。

目前來看,容器的資源配置都是按照我們設定的來分配的,但實際真能在 CPU0-CPU1 上限制使用 1.5 個核嗎?我們先看一下當前 CPU 的用量:

  1. docker stats $(docker ps -ql) 

 

因為沒有在 alpine 中運行程序,所以 CPU 用量為 0,我們現在回到最開始執行 docker 指令的 alpine 終端,執行一個死循環:

  1. i=0; while true; do i=i+i; done 

再來觀察當前的 CPU 用量:

 

接近 1,但為啥不是 1.5?因為剛剛運行的死循環只能跑在一個核上,所以我們再打開一個終端,進入到 alpine 鏡像中,同樣執行死循環的指令,看到 CPU 用量穩定在了 1.5,說明資源的使用量確實是限制住了的。

 

現在我們對 docker 容器實現了進程間資源隔離的黑科技有了一定認識。如果單單就隔離性來說,vagrant 也已經做到了。那么為什么是 docker 火爆全球?是因為它允許用戶將容器環境打包成為一個鏡像進行分發,而且鏡像是分層增量構建的,這可以大大降低用戶使用的門檻。

3. 存儲

Image 是 Docker 部署的基本單位,它包含了程序文件,以及這個程序依賴的資源的環境。Docker Image 是以一個 mount 點掛載到容器內部的。容器可以近似理解為鏡像的運行時實例,默認情況下也算是在鏡像層的基礎上增加了一個可寫層。所以,一般情況下如果你在容器內做出的修改,均包含在這個可寫層中。

3.1 聯合文件系統(UFS)

Union File System 從字面意思上來理解就是“聯合文件系統”。它將多個物理位置不同的文件目錄聯合起來,掛載到某一個目錄下,形成一個抽象的文件系統。

 

如上圖,從右側以 UFS 的視角來看,lowerdir 和 upperdir 是兩個不同的目錄,UFS 將二者合并起來,得到 merged 層展示給調用方。從左側的 docker 角度來理解,lowerdir 就是鏡像,upperdir 就相當于是容器默認的可寫層。在運行的容器中修改了文件,可以使用 docker commit 指令保存成為一個新鏡像。

3.2 Docker 鏡像的存儲管理

有了 UFS 的分層概念,我們就很好理解這樣的一個簡單 Dockerfile:

  1. FROM alpine 
  2. COPY foo /foo 
  3. COPY bar /bar 

在構建時的輸出所代表的含義了。

 

 

 

但是使用 docker pull 拉取的鏡像文件,在本地機器上存儲在哪,又是如何管理的呢?還是來實際操作認證一下。在 devcloud 上確認當前 docker 所使用的存儲驅動(默認是 overlay2):

  1. docker info --format '{{.Driver}}' 

以及鏡像下載后的存儲路徑(默認存儲在/var/lib/docker):

  1. docker info --format '{{.DockerRootDir}}' 

當前我的 docker 修改了默認存儲路徑,配置到/data/docker-data,我們就以它為例進行展示。先查看一下該目錄下的結構:

  1. tree -L 1 /data/docker-data 

 

關注一下其中的 image 和 overlay2 目錄。前者就是存放鏡像信息的地方,后者則是存放具體每一分層的文件內容。我們先深入看一下 image 目錄結構:

  1. tree -L 2 /data/docker-data/image/ 

 

留心這個 imagedb 目錄,接下來以我們以最新的 alpine 鏡像為例子,看看 docker 是如何管理鏡像的。執行指令:

  1. docker pull alpine:latest 

 

緊接著查看它的鏡像 ID:docker image ls alpine:latest

 

記住這個 ID a24bb4013296,現在可以看一下 imagedb 目錄下的變化:

  1. tree -L 2 /data/docker-data/image/overlay2/imagedb/content/ | grep 
  2. a24bb4013296 

 

多了這么一個鏡像 ID 的文件,它是一個 json 格式的文件,這里包含了該鏡像的參數信息:

  1. jq . 
  2. /data/docker-data/image/overlay2/imagedb/content/sha256/a24bb4013296f61e89ba57005a7b3e52274d8edd3ae2077d04395f806b63d83e 

 

接下來我們看看將一個鏡像運行起來之后會有什么變化。運行一個 alpine 容器,讓它 sleep10 分鐘:

  1. docker run --rm -d alpine sleep 600 

然后找到它的 overlay 掛載點:

  1. docker inspect --format='{{.GraphDriver.Data}}' $(docker ps -ql) | grep MergedDir 

 

結合上一節講到的 UFS 文件系統,可以 ls 一下:

  1. ls /data/docker-data/overlay2/74e92699164736980c9e20475388568f482671625a177cb946c4b136e4d94a64/merged 

 

看到它就是合并后所呈現在 alpine 容器的文件系統。先進入到容器內:

  1. docker exec -it $(docker ps -ql) sh 

緊接著新開一個終端查看容器運行起來后跟鏡像相比,有哪些修改:

  1. docker diff $(docker ps -ql) 

 

在/root 目錄下,增加了 sh 的歷史記錄文件。然后我們在容器中手動增加一個 hello.txt 文件:

  1. echo 'Hello Docker' > hello.txt 

 

這時候來看看容器默認在鏡像之上增加的可寫層 UpperDir 目錄的變化:

  1. ls /data/docker-data/overlay2/74e92699164736980c9e20475388568f482671625a177cb946c4b136e4d94a64/diff 

 

這就認證了 overlay2 驅動是將鏡像和可寫層的內容 merged 之后,供容器作為文件系統使用。多個運行的容器共用一份基礎鏡像,而各自有獨立的可寫層,節省了存儲空間。

這個時候,我們也可以回答一下鏡像的實際內容是存儲在哪里呢:

  1. cat /data/docker-data/overlay2/74e92699164736980c9e20475388568f482671625a177cb946c4b136e4d94a64/lower 

 

查看這些分層:

  1. ls /data/docker-data/overlay2/l/ZIIZFSQUQ4CIKRNCMOXXY4VZHY/ 

 

 

就是 UFS 中低層的鏡像內容。

總結

這一次跟大家分享了 Docker 所使用的底層技術,包括 namespace,cgroups 和 overlay2 聯合文件系統,著重介紹了隔離環境是如何在宿主機上演進實現的。通過實際手動操作,對這些概念有了真實的感受。希望下一次為大家再介紹 docker 的網絡實現機制。

【本文為51CTO專欄作者“騰訊技術工程”原創稿件,轉載請聯系原作者(微信號:Tencent_TEG)】

戳這里,看該作者更多好文

責任編輯:武曉燕 來源: 51CTO專欄
相關推薦

2023-02-12 23:23:30

2024-06-13 00:54:19

2019-10-16 16:33:41

Docker架構語言

2021-12-01 06:50:50

Docker底層原理

2020-08-05 08:21:41

Webpack

2023-05-11 07:25:57

ReduxMiddleware函數

2009-07-16 10:23:30

iBATIS工作原理

2021-01-27 18:15:01

Docker底層宿主機

2023-01-04 07:54:03

HashMap底層JDK

2024-01-05 09:00:00

SpringMVC軟件

2014-11-26 10:44:33

DockerOpenStack云計算

2011-04-13 15:01:39

2019-08-05 13:20:35

Android繪制代碼

2010-09-25 14:01:11

Java跨平臺

2022-06-09 15:53:16

移動端渲染GPU

2023-10-18 10:55:55

HashMap

2022-12-19 08:00:00

SpringBootWeb開發

2021-07-05 07:51:43

JVM底層Python

2021-07-23 13:34:50

MySQL存儲InnoDB

2009-07-03 17:48:34

JSP頁面翻譯
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 99精品亚洲国产精品久久不卡 | 国产精品一二区 | 亚洲中字在线 | 久久精品一级 | 围产精品久久久久久久 | 81精品国产乱码久久久久久 | 二区在线视频 | 国产精品视频免费观看 | 色综合天天天天做夜夜夜夜做 | 国产福利91精品一区二区三区 | 日韩电影中文字幕 | 超碰人人人人 | 亚洲成人精品一区 | 国产精品久久久久婷婷二区次 | 性生生活大片免费看视频 | 91精品国产综合久久久久久丝袜 | 精品国产一区一区二区三亚瑟 | 中文字幕日韩在线 | 亚洲国产伊人 | 91免费在线播放 | 午夜视频一区二区三区 | 欧美video| 亚洲精品自在在线观看 | 日本在线观看视频 | 日韩成人国产 | 国产日韩欧美在线观看 | 精品久久国产 | 精品国产乱码久久久久久图片 | 天天草狠狠干 | 亚洲444eee在线观看 | 在线2区 | 毛片免费观看 | 成人免费视频一区二区 | 欧美 日韩 在线播放 | 久久三区 | 欧美精品一区三区 | www成人免费视频 | 成人国产精品久久 | 国产精品一区一区三区 | 日韩精品一区二区三区四区视频 | 久久久久久亚洲 |