淺嘗docker

作者:拔劍少年

簡書地址:http://m.itdecent.cn/u/dad4d9675892
博客地址:https://it18monkey.github.io
轉(zhuǎn)載請注明出處

什么是docker

? 我們先來看下官方給出的說法

Package Software into Standardized Units for Development, Shipment and Deployment

? docker是一個工具,一個用于軟件打包的工具。那這個打包工具有什么特殊的呢,他能將運(yùn)行一個應(yīng)用所需要的所有東西,包括程序代碼、系統(tǒng)工具、系統(tǒng)庫和系統(tǒng)設(shè)置都打成一個包。這個包被稱之為容器。

我個人更傾向于將他描述為一個輕量級的虛擬機(jī),因?yàn)樗吞摂M機(jī)有著很多相似之處。

docker與虛擬機(jī)的區(qū)別

虛擬機(jī)結(jié)構(gòu)示意
Docker結(jié)構(gòu)示意

上面兩幅圖展示了用虛擬機(jī)和docker運(yùn)行程序的大體結(jié)構(gòu)。讓我們從下往上來對比。

先看虛擬機(jī)這邊:

最底層是某種類型的基礎(chǔ)設(shè)施,可能是一臺個人電腦,或者是服務(wù)器。往上,是一個名為hypervisor的虛擬機(jī)管理程序,在這之上運(yùn)行客戶操作系統(tǒng),也就是虛擬系統(tǒng),每個虛擬系統(tǒng)都是相互隔離的。然后,所有的應(yīng)用程序都運(yùn)行在虛擬系統(tǒng)上。這里提下,虛擬機(jī)也是可以運(yùn)行在操作系統(tǒng)上的。它分為兩種,一種直接運(yùn)行在系統(tǒng)硬件上,創(chuàng)建硬件全仿真實(shí)例,被稱為裸機(jī)型。一種運(yùn)行在傳統(tǒng)操作系統(tǒng)上,同樣創(chuàng)建的是硬件全仿真實(shí)例,被稱為“托管(宿主)”型。

再看docker這邊:

最底層和虛擬機(jī)一樣,是某種類型的基礎(chǔ)設(shè)施,可能是一臺個人電腦,或者是服務(wù)器。往上是一個宿主操作系統(tǒng),可能是windows或者是linux。也就是說docker不能直接運(yùn)行在系統(tǒng)硬件上。目前來說支持linux和windows。在這個宿主操作系統(tǒng)上,我們安裝一個docker,而我們的應(yīng)用程序全部都運(yùn)行在docker上,由docker來統(tǒng)一管理。每個應(yīng)用程序都是相互隔離的。

docker和虛擬機(jī)最大的區(qū)別就是他們的隔離級別不同。虛擬機(jī)是硬件級別的隔離,而docker只到進(jìn)程級別。這一點(diǎn)也比較影響我們選擇使用docker或是虛擬機(jī)。當(dāng)然他們也不是互斥的,完全可以根據(jù)實(shí)際需要相互結(jié)合。

為什么要用docker

作為一種新興的虛擬化方式,Docker 跟傳統(tǒng)的虛擬化方式相比具有眾多的優(yōu)勢。

  • 更高效的利用系統(tǒng)資源
  • 更快速的啟動時間
  • 一致的運(yùn)行環(huán)境
  • 持續(xù)交付和部署
  • 更輕松的遷移
  • 更輕松的維護(hù)和擴(kuò)展

docker基本概念

  1. 鏡像

我們都知道,操作系統(tǒng)分為內(nèi)核和用戶空間。對于 Linux 而言,內(nèi)核啟動后,會掛載 root 文件系統(tǒng)為其提供用戶空間支持。而 Docker 鏡像(Image),就相當(dāng)于是一個 root 文件系統(tǒng)。比如官方鏡像 ubuntu:18.04 就包含了完整的一套 Ubuntu 18.04 最小系統(tǒng)的 root 文件系統(tǒng)。

Docker 鏡像是一個特殊的文件系統(tǒng),除了提供容器運(yùn)行時所需的程序、庫、資源、配置等文件外,還包含了一些為運(yùn)行時準(zhǔn)備的一些配置參數(shù)(如匿名卷、環(huán)境變量、用戶等)。鏡像不包含任何動態(tài)數(shù)據(jù),其內(nèi)容在構(gòu)建之后也不會被改變。

  1. 容器

鏡像( Image )和容器( Container )的關(guān)系,就像是面向?qū)ο蟪绦蛟O(shè)計中的類 和 實(shí)例 一樣,鏡像是靜態(tài)的定義,容器是鏡像運(yùn)行時的實(shí)體。容器可以被創(chuàng)建、啟動、停止、刪除、暫停等。

  1. 倉庫

鏡像構(gòu)建完成后,可以很容易的在當(dāng)前宿主機(jī)上運(yùn)行,但是,如果需要在其它服務(wù)器上使用這個鏡像,我們就需要一個集中的存儲、分發(fā)鏡像的服務(wù),DockerRegistry 就是這樣的服務(wù)。一個 Docker Registry 中可以包含多個倉庫( Repository );每個倉庫可以包含多個標(biāo)簽( Tag );每個標(biāo)簽對應(yīng)一個鏡像。

通常,一個倉庫會包含同一個軟件不同版本的鏡像,而標(biāo)簽就常用于對應(yīng)該軟件的各個版本。我們可以通過 <倉庫名>:<標(biāo)簽> 的格式來指定具體是這個軟件哪個版本的鏡像。如果不給出標(biāo)簽,將以 latest 作為默認(rèn)標(biāo)簽。

Docker Registry 分為公開和私有兩種,用戶可以根據(jù)自己的需要搭配使用。

Docker常用命令

鏡像倉庫操作

  • docker login/logout

    登陸/登出到一個Docker鏡像倉庫,如果未指定鏡像倉庫地址,默認(rèn)為官方倉庫 Docker Hub。

    格式:docker login [選項(xiàng)][SERVER] ,docker logout [選項(xiàng)][SERVER]。

  • docker pull

    從Docker Registry獲取鏡像。格式:docker pull [選項(xiàng)][Docker Registry 地址[:端口號]/]倉庫名[:標(biāo)簽]。

  • docker push

    將本地的鏡像上傳到鏡像倉庫,要先登陸到鏡像倉庫。格式:docker push [選項(xiàng)] 倉庫名[:標(biāo)簽]。

  • docker search

    從Docker Hub查找鏡像。格式docker search [選項(xiàng)] 關(guān)鍵字。

鏡像操作

  • docker images

    列出本地鏡像。格式:docker images [選項(xiàng)][倉庫名[:標(biāo)簽]]。

  • docker run

    創(chuàng)建一個新的容器并運(yùn)行一個命令。格式:docker run [選項(xiàng)] 鏡像名[命令][參數(shù)]。

  • docker rmi

    刪除本地一個或多少鏡像。格式:docker rmi [選項(xiàng)] 鏡像名[鏡像名...]。

  • docker build

    使用 Dockerfile 創(chuàng)建鏡像。格式:docker build [選項(xiàng)] 路徑 | URL | -。

  • docker history
    查看指定鏡像的創(chuàng)建歷史。格式:docker history [選項(xiàng)] 鏡像名。

容器操作

  • docker ps
    列出容器。格式:docker ps [選項(xiàng)]。

  • docker inspect

    獲取容器/鏡像的元數(shù)據(jù)。docker inspect [選項(xiàng)] 名稱|ID。

  • docker start/stop/restart
    docker start :啟動一個或多個已經(jīng)被停止的容器。格式:docker start [選項(xiàng)] 容器名[ [容器名...]]。
    docker stop :停止一個運(yùn)行中的容器。docker stop [選項(xiàng)] 容器名[ [容器名...]]
    docker restart :重啟容器。docker restart [選項(xiàng)] 容器名[ [容器名...]]

  • docker kill
    殺掉一個運(yùn)行中的容器。docker kill [選項(xiàng)] 容器名 [容器名...]。

  • docker rm
    刪除一個或多個容器。docker rm [選項(xiàng)] 容器名[容器名...]

技術(shù)要點(diǎn)

  1. UnionFS與鏡像分層存儲

    所謂UnionFS就是把不同物理位置的目錄合并mount到同一個目錄中。UnionFS的一個最主要的應(yīng)用是,把一張CD/DVD和一個硬盤目錄給聯(lián)合mount在一起,然后,你就可以對這個只讀的CD/DVD上的文件進(jìn)行修改(當(dāng)然,修改的文件存于硬盤上的目錄里)。

    UnionFS主要有以下幾種實(shí)現(xiàn):aufs,overlayfs。下圖展示了overlayfs的基本結(jié)構(gòu)

docker-overlay.jpg

其中l(wèi)ower dirA / lower dirB目錄和upper dir目錄為來自底層文件系統(tǒng)的不同目錄,用戶可以自行指定,內(nèi)部包含了用戶想要合并的文件和目錄,merge dir目錄為掛載點(diǎn)。當(dāng)文件系統(tǒng)掛載后,在merge目錄下將會同時看到來自各lower和upper目錄下的內(nèi)容,并且用戶也無法(無需)感知這些文件分別哪些來自lower dir,哪些來自upper dir,用戶看見的只是一個普通的文件系統(tǒng)根目錄而已(lower dir可以有多個也可以只有一個)。

雖然overlayfs將不同的各層目錄進(jìn)行合并,但是upper dir和各lower dir這幾個不同的目錄并不完全等價,存在層次關(guān)系。首先當(dāng)upper dir和lower dir兩個目錄存在同名文件時,lower dir的文件將會被隱藏,用戶只能看見來自upper dir的文件,然后各個lower dir也存在相同的層次關(guān)系,較上層屏蔽叫下層的同名文件。除此之外,如果存在同名的目錄,那就繼續(xù)合并(lower dir和upper dir合并到掛載點(diǎn)目錄其實(shí)就是合并一個典型的例子)。

各層目錄中的upper dir是可讀寫的目錄,當(dāng)用戶通過merge dir向時其中一個來自upper dir的文件寫入數(shù)據(jù)時,那數(shù)據(jù)將直接寫入upper dir下原來的文件中,刪除文件也是同理;而各lower dir則是只讀的,在overlayfs掛載后無論如何操作merge目錄中對應(yīng)來自lower dir的文件或目錄,lower dir中的內(nèi)容均不會發(fā)生任何的改變。當(dāng)用戶想要往來自lower層的文件添加或修改內(nèi)容時,overlayfs首先會的拷貝一份lower dir中的文件副本到upper dir中,后續(xù)的寫入和修改操作將會在upper dir下的copy-up的副本文件中進(jìn)行,lower dir原文件被隱藏。

以上就是overlayfs最基本的特性,簡單的總結(jié)為以下3點(diǎn):

(1)上下層同名目錄合并;

(2)上下層同名文件覆蓋;

(3)lower dir文件寫時拷貝。

這三點(diǎn)對用戶都是無感知的。

  1. Linux Namespace

    為了提供更加精細(xì)的資源分配管理機(jī)制,Linux給出了namespace解決方法。Docker利用這一技術(shù)實(shí)現(xiàn)了容器運(yùn)行環(huán)境的隔離。

    linux內(nèi)核實(shí)現(xiàn)了六種namespace。列表如下:

    namespace 引入的相關(guān)內(nèi)核版本 被隔離的全局系統(tǒng)資源 在容器語境下的隔離效果
    Mount namespaces Linux 2.4.19 文件系統(tǒng)掛接點(diǎn) 每個容器能看到不同的文件系統(tǒng)層次結(jié)構(gòu)
    UTS namespaces Linux 2.6.19 主機(jī)名和域名 每個容器可以有自己的 主機(jī)名和 域名
    IPC namespaces Linux 2.6.19 特定的進(jìn)程間通信資源,包括System V IPC和 POSIX message queues 每個容器有其自己的 System V IPC 和 POSIX 消息隊(duì)列文件系統(tǒng),因此,只有在同一個 IPC namespace 的進(jìn)程之間才能互相通信
    PID namespaces Linux 2.6.24 進(jìn)程 ID 數(shù)字空間 每個 PID namespace 中的進(jìn)程可以有其獨(dú)立的 PID; 每個容器可以有其 PID 為 1 的root 進(jìn)程;也使得容器可以在不同的宿主機(jī)之間遷移,因?yàn)?namespace 中的進(jìn)程 ID 和 宿主機(jī)無關(guān)了。這也使得容器中的每個進(jìn)程有兩個PID:容器中的 PID 和宿主機(jī)上的 PID。
    Network namespaces 始于Linux 2.6.24 完成于 Linux 2.6.29 網(wǎng)絡(luò)相關(guān)的系統(tǒng)資源 每個容器用有其獨(dú)立的網(wǎng)絡(luò)設(shè)備,IP 地址,IP 路由表,/proc/net 目錄,端口號等等。這也使得一個 宿主機(jī) 上多個容器內(nèi)的同一個應(yīng)用都綁定到各自容器的 80 端口上。
    User namespaces 始于 Linux 2.6.23 完成于 Linux 3.8) 用戶和組 ID 空間 在 user namespace 中的進(jìn)程的用戶和組 ID 可以和在宿主機(jī)上不同; 每個 container 可以有不同的 user 和 group id;一個宿主機(jī)上的非特權(quán)用戶可以成為 user namespace 中的特權(quán)用戶;

    Linux namespace 的概念說簡單也簡單說復(fù)雜也復(fù)雜。簡單來說,我們只要知道,處于某個 namespace
    中的進(jìn)程,能看到獨(dú)立的它自己的隔離的某些特定系統(tǒng)資源;復(fù)雜來說,可以去看看 Linux 內(nèi)核中實(shí)現(xiàn) namespace 的原理。

  2. Linux CGroups

    Docker 容器使用 linux namespace 來隔離其運(yùn)行環(huán)境,使得容器中的進(jìn)程看起來就像愛一個獨(dú)立環(huán)境中運(yùn)行一樣。但是,光有運(yùn)行環(huán)境隔離還不夠,因?yàn)檫@些進(jìn)程還是可以不受限制地使用系統(tǒng)資源,比如網(wǎng)絡(luò)、磁盤、CPU以及內(nèi)存等。因此,為了讓容器中的進(jìn)程更加可控,Docker 使用 Linux cgroups 來限制容器中的進(jìn)程允許使用的系統(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)
    

參考資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容