作者:拔劍少年
簡書地址:http://www.itdecent.cn/u/dad4d9675892
博客地址:https://it18monkey.github.io
轉(zhuǎn)載請注明出處
什么是docker
? 我們先來看下官方給出的說法
Package Software into Standardized Units for Development, Shipment and Deployment
? docker是一個工具,一個用于軟件打包的工具。那這個打包工具有什么特殊的呢,他能將運行一個應用所需要的所有東西,包括程序代碼、系統(tǒng)工具、系統(tǒng)庫和系統(tǒng)設(shè)置都打成一個包。這個包被稱之為容器。
我個人更傾向于將他描述為一個輕量級的虛擬機,因為它和虛擬機有著很多相似之處。
docker與虛擬機的區(qū)別


上面兩幅圖展示了用虛擬機和docker運行程序的大體結(jié)構(gòu)。讓我們從下往上來對比。
先看虛擬機這邊:
最底層是某種類型的基礎(chǔ)設(shè)施,可能是一臺個人電腦,或者是服務(wù)器。往上,是一個名為hypervisor的虛擬機管理程序,在這之上運行客戶操作系統(tǒng),也就是虛擬系統(tǒng),每個虛擬系統(tǒng)都是相互隔離的。然后,所有的應用程序都運行在虛擬系統(tǒng)上。這里提下,虛擬機也是可以運行在操作系統(tǒng)上的。它分為兩種,一種直接運行在系統(tǒng)硬件上,創(chuàng)建硬件全仿真實例,被稱為裸機型。一種運行在傳統(tǒng)操作系統(tǒng)上,同樣創(chuàng)建的是硬件全仿真實例,被稱為“托管(宿主)”型。
再看docker這邊:
最底層和虛擬機一樣,是某種類型的基礎(chǔ)設(shè)施,可能是一臺個人電腦,或者是服務(wù)器。往上是一個宿主操作系統(tǒng),可能是windows或者是linux。也就是說docker不能直接運行在系統(tǒng)硬件上。目前來說支持linux和windows。在這個宿主操作系統(tǒng)上,我們安裝一個docker,而我們的應用程序全部都運行在docker上,由docker來統(tǒng)一管理。每個應用程序都是相互隔離的。
docker和虛擬機最大的區(qū)別就是他們的隔離級別不同。虛擬機是硬件級別的隔離,而docker只到進程級別。這一點也比較影響我們選擇使用docker或是虛擬機。當然他們也不是互斥的,完全可以根據(jù)實際需要相互結(jié)合。
為什么要用docker
作為一種新興的虛擬化方式,Docker 跟傳統(tǒng)的虛擬化方式相比具有眾多的優(yōu)勢。
- 更高效的利用系統(tǒng)資源
- 更快速的啟動時間
- 一致的運行環(huán)境
- 持續(xù)交付和部署
- 更輕松的遷移
- 更輕松的維護和擴展
docker基本概念
- 鏡像
我們都知道,操作系統(tǒng)分為內(nèi)核和用戶空間。對于 Linux 而言,內(nèi)核啟動后,會掛載 root 文件系統(tǒng)為其提供用戶空間支持。而 Docker 鏡像(Image),就相當于是一個 root 文件系統(tǒng)。比如官方鏡像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系統(tǒng)的 root 文件系統(tǒng)。
Docker 鏡像是一個特殊的文件系統(tǒng),除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時準備的一些配置參數(shù)(如匿名卷、環(huán)境變量、用戶等)。鏡像不包含任何動態(tài)數(shù)據(jù),其內(nèi)容在構(gòu)建之后也不會被改變。
- 容器
鏡像( Image )和容器( Container )的關(guān)系,就像是面向?qū)ο蟪绦蛟O(shè)計中的類 和 實例 一樣,鏡像是靜態(tài)的定義,容器是鏡像運行時的實體。容器可以被創(chuàng)建、啟動、停止、刪除、暫停等。
- 倉庫
鏡像構(gòu)建完成后,可以很容易的在當前宿主機上運行,但是,如果需要在其它服務(wù)器上使用這個鏡像,我們就需要一個集中的存儲、分發(fā)鏡像的服務(wù),DockerRegistry 就是這樣的服務(wù)。一個 Docker Registry 中可以包含多個倉庫( Repository );每個倉庫可以包含多個標簽( Tag );每個標簽對應一個鏡像。
通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標簽就常用于對應該軟件的各個版本。我們可以通過 <倉庫名>:<標簽> 的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標簽,將以 latest 作為默認標簽。
Docker Registry 分為公開和私有兩種,用戶可以根據(jù)自己的需要搭配使用。
Docker常用命令
鏡像倉庫操作
-
docker login/logout
登陸/登出到一個Docker鏡像倉庫,如果未指定鏡像倉庫地址,默認為官方倉庫 Docker Hub。
格式:
docker login [選項][SERVER] ,docker logout [選項][SERVER]。 -
docker pull
從Docker Registry獲取鏡像。格式:
docker pull [選項][Docker Registry 地址[:端口號]/]倉庫名[:標簽]。 -
docker push
將本地的鏡像上傳到鏡像倉庫,要先登陸到鏡像倉庫。格式:
docker push [選項] 倉庫名[:標簽]。 -
docker search
從Docker Hub查找鏡像。格式
docker search [選項] 關(guān)鍵字。
鏡像操作
-
docker images
列出本地鏡像。格式:
docker images [選項][倉庫名[:標簽]]。 -
docker run
創(chuàng)建一個新的容器并運行一個命令。格式:
docker run [選項] 鏡像名[命令][參數(shù)]。 -
docker rmi
刪除本地一個或多少鏡像。格式:
docker rmi [選項] 鏡像名[鏡像名...]。 -
docker build
使用 Dockerfile 創(chuàng)建鏡像。格式:
docker build [選項] 路徑 | URL | -。 docker history
查看指定鏡像的創(chuàng)建歷史。格式:docker history [選項] 鏡像名。
容器操作
docker ps
列出容器。格式:docker ps [選項]。-
docker inspect
獲取容器/鏡像的元數(shù)據(jù)。
docker inspect [選項] 名稱|ID。 docker start/stop/restart
docker start :啟動一個或多個已經(jīng)被停止的容器。格式:docker start [選項] 容器名[ [容器名...]]。
docker stop :停止一個運行中的容器。docker stop [選項] 容器名[ [容器名...]]。
docker restart :重啟容器。docker restart [選項] 容器名[ [容器名...]]。docker kill
殺掉一個運行中的容器。docker kill [選項] 容器名 [容器名...]。docker rm
刪除一個或多個容器。docker rm [選項] 容器名[容器名...]
技術(shù)要點
-
UnionFS與鏡像分層存儲
所謂UnionFS就是把不同物理位置的目錄合并mount到同一個目錄中。UnionFS的一個最主要的應用是,把一張CD/DVD和一個硬盤目錄給聯(lián)合mount在一起,然后,你就可以對這個只讀的CD/DVD上的文件進行修改(當然,修改的文件存于硬盤上的目錄里)。
UnionFS主要有以下幾種實現(xiàn):aufs,overlayfs。下圖展示了overlayfs的基本結(jié)構(gòu)

其中l(wèi)ower dirA / lower dirB目錄和upper dir目錄為來自底層文件系統(tǒng)的不同目錄,用戶可以自行指定,內(nèi)部包含了用戶想要合并的文件和目錄,merge dir目錄為掛載點。當文件系統(tǒng)掛載后,在merge目錄下將會同時看到來自各lower和upper目錄下的內(nèi)容,并且用戶也無法(無需)感知這些文件分別哪些來自lower dir,哪些來自upper dir,用戶看見的只是一個普通的文件系統(tǒng)根目錄而已(lower dir可以有多個也可以只有一個)。
雖然overlayfs將不同的各層目錄進行合并,但是upper dir和各lower dir這幾個不同的目錄并不完全等價,存在層次關(guān)系。首先當upper dir和lower dir兩個目錄存在同名文件時,lower dir的文件將會被隱藏,用戶只能看見來自upper dir的文件,然后各個lower dir也存在相同的層次關(guān)系,較上層屏蔽叫下層的同名文件。除此之外,如果存在同名的目錄,那就繼續(xù)合并(lower dir和upper dir合并到掛載點目錄其實就是合并一個典型的例子)。
各層目錄中的upper dir是可讀寫的目錄,當用戶通過merge dir向時其中一個來自upper dir的文件寫入數(shù)據(jù)時,那數(shù)據(jù)將直接寫入upper dir下原來的文件中,刪除文件也是同理;而各lower dir則是只讀的,在overlayfs掛載后無論如何操作merge目錄中對應來自lower dir的文件或目錄,lower dir中的內(nèi)容均不會發(fā)生任何的改變。當用戶想要往來自lower層的文件添加或修改內(nèi)容時,overlayfs首先會的拷貝一份lower dir中的文件副本到upper dir中,后續(xù)的寫入和修改操作將會在upper dir下的copy-up的副本文件中進行,lower dir原文件被隱藏。
以上就是overlayfs最基本的特性,簡單的總結(jié)為以下3點:
(1)上下層同名目錄合并;
(2)上下層同名文件覆蓋;
(3)lower dir文件寫時拷貝。
這三點對用戶都是無感知的。
-
Linux Namespace
為了提供更加精細的資源分配管理機制,Linux給出了namespace解決方法。Docker利用這一技術(shù)實現(xiàn)了容器運行環(huán)境的隔離。
linux內(nèi)核實現(xiàn)了六種namespace。列表如下:
namespace 引入的相關(guān)內(nèi)核版本 被隔離的全局系統(tǒng)資源 在容器語境下的隔離效果 Mount namespaces Linux 2.4.19 文件系統(tǒng)掛接點 每個容器能看到不同的文件系統(tǒng)層次結(jié)構(gòu) UTS namespaces Linux 2.6.19 主機名和域名 每個容器可以有自己的 主機名和 域名 IPC namespaces Linux 2.6.19 特定的進程間通信資源,包括System V IPC和 POSIX message queues 每個容器有其自己的 System V IPC 和 POSIX 消息隊列文件系統(tǒng),因此,只有在同一個 IPC namespace 的進程之間才能互相通信 PID namespaces Linux 2.6.24 進程 ID 數(shù)字空間 每個 PID namespace 中的進程可以有其獨立的 PID; 每個容器可以有其 PID 為 1 的root 進程;也使得容器可以在不同的宿主機之間遷移,因為 namespace 中的進程 ID 和 宿主機無關(guān)了。這也使得容器中的每個進程有兩個PID:容器中的 PID 和宿主機上的 PID。 Network namespaces 始于Linux 2.6.24 完成于 Linux 2.6.29 網(wǎng)絡(luò)相關(guān)的系統(tǒng)資源 每個容器用有其獨立的網(wǎng)絡(luò)設(shè)備,IP 地址,IP 路由表,/proc/net 目錄,端口號等等。這也使得一個 宿主機 上多個容器內(nèi)的同一個應用都綁定到各自容器的 80 端口上。 User namespaces 始于 Linux 2.6.23 完成于 Linux 3.8) 用戶和組 ID 空間 在 user namespace 中的進程的用戶和組 ID 可以和在宿主機上不同; 每個 container 可以有不同的 user 和 group id;一個宿主機上的非特權(quán)用戶可以成為 user namespace 中的特權(quán)用戶; Linux namespace 的概念說簡單也簡單說復雜也復雜。簡單來說,我們只要知道,處于某個 namespace
中的進程,能看到獨立的它自己的隔離的某些特定系統(tǒng)資源;復雜來說,可以去看看 Linux 內(nèi)核中實現(xiàn) namespace 的原理。 -
Linux CGroups
Docker 容器使用 linux namespace 來隔離其運行環(huán)境,使得容器中的進程看起來就像愛一個獨立環(huán)境中運行一樣。但是,光有運行環(huán)境隔離還不夠,因為這些進程還是可以不受限制地使用系統(tǒng)資源,比如網(wǎng)絡(luò)、磁盤、CPU以及內(nèi)存等。因此,為了讓容器中的進程更加可控,Docker 使用 Linux cgroups 來限制容器中的進程允許使用的系統(tǒng)資源。
目前 docker 已經(jīng)幾乎支持了所有的 cgroups 資源,可以限制容器對包括 network,device,cpu 和memory 在內(nèi)的資源的使用
我們可以通過給docker run 命令傳參來控制分配容器的資源。Docker run 命令中 cgroups 相關(guān)命令如下:
block IO: --blkio-weight value Block IO (relative weight), between 10 and 1000 --blkio-weight-device value Block IO weight (relative device weight) (default []) --cgroup-parent string Optional parent cgroup for the container CPU: --cpu-percent int CPU percent (Windows only) --cpu-period int Limit CPU CFS (Completely Fair Scheduler)period --cpu-quota int Limit CPU CFS (Completely Fair Scheduler) quota -c, --cpu-shares int CPU shares (relative weight) --cpuset-cpus string CPUs in which to allow execution (0-3, 0,1) --cpuset-mems string MEMs in which to allow execution (0-3, 0,1) Device: --device Add a host device to the container --device-cgroup-rule Add a rule to the cgroup allowed devices list --device-read-bps Limit read rate (bytes per second) from a device --device-read-iops Limit read rate (IO per second) from a device --device-write-bps Limit write rate (bytes per second) to a device --device-write-iops Limit write rate (IO per second) to a device Memory: --kernel-memory string Kernel memory limit --memory , -m Memory limit --memory-reservation Memory soft limit --memory-swap Swap limit equal to memory plus swap: ‘-1’ to enable unlimited swap --memory-swappiness Tune container memory swappiness (0 to 100)