Docker初體驗1 —幾個概念的理解

本文從一種使用場景來引出docker,並討論了什麼是鏡像,容器,倉庫,以及docker的相關概念。

試想一種使用場景:

我的wordpress 博客網站現在部署在阿里雲服務器上,但是在後期的使用中我有可能有這樣一種需求,阿里雲太貴,我可能實在付不起每月月租,想把我的服務遷到其他的雲服務 上,而又想完整的將我的wordpress服務和數據從阿里雲遷出,並將其部署到另外的雲服務上。那麼我怎樣解決這個問題?

第一種方案:在新主機上部署一套環境,然後將項目目錄和mysql服務倒出,這種方法比較費力,而且由於相關依賴包比價複雜,容易出錯。

第二種方案:在主機上安裝一個KVM之類的虛擬機,可以將wordpress服務部署在 KVM的虛擬機上,當我想遷出服務時我就將虛擬機的相關文件倒出,但是由於雲服務主機的配置不是很高,才1核1G。這樣就會耗費我很多的資源,我的 wordpress明明可以跑在宿主機上,但是現在卻要跑在虛擬機裏。那麼有沒有什麼方法可以解決我的問題?

方案四:使用Docker:Build, Ship and Run Any App, Anywhere!


什麼是 Docker與Docker官方相關技術簡介(可略讀):

Docker是一套輕量級操作系統虛擬化解決方案,它由go語言編寫。它基於Linux容器技術(LXC),Namespace,Cgroup,UnionFS(聯合文件系統)等技術。

namespace(命名空間):命名空間是 Linux 內核一個強大的特性。每個容器都有自己單獨的名字空間,運行在其中的應用都像是在獨立的操作系統中運行一樣。名字空間保證了容器之間彼此互不影響。 docker實際上一個進程容器,它通過namespace實現了進程和進程所使用的資源的隔離。使不同的進程之間彼此不可見。我們可以把Docker容 器想像成進程+操作系統除內核之外的一套軟件。

cgroup(控制組)是 Linux 內核的一個特性,主要用來對共享資源進行隔離、限制、審計等。只有能控制分配到容器的資源,才能避免當多個容器同時運行時的對系統資源的競爭。控制組技術 最早是由 Google 的程序員 2006 年起提出,Linux 內核自 2.6.24 開始支持。控制組可以提供對容器的內存、CPU、磁盤 IO 等資源的限制和審計管理。

UnionFS(聯合文件系統)Union 文件系統(UnionFS)是一種分層、輕量級並且高性能的文件系統,它支持對 文件系統的修改作爲一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬文件系統下(unite several directories into a single virtual filesystem)。Union 文件系統是 Docker 鏡像的基礎。鏡像可以通過分層來進行繼承,基於基礎鏡像(沒有父鏡像),可以製作各種具體的應用鏡像。另外,不同 Docker 容器就可以共享一些基礎的文件系統層,同時再加上自己獨有的改動層,大大提高了存儲的效率。Docker 中使用的 AUFS(AnotherUnionFS)就是一種 Union FS。 AUFS 支持爲每一個成員目錄(類似 Git 的分支)設定只讀(readonly)、讀寫(readwrite)和寫出(whiteout-able)權限, 同時 AUFS 裏有一個類似分層的概念, 對只讀權限的分支可以邏輯上進行增量地修改(不影響只讀部分的)。

Docker 目前支持的 Union 文件系統種類包括 AUFS, btrfs, vfs 和 DeviceMapper。


談談我的理解

1.Docker的生命週期:

findvarlog

Docker的生命週期包含三個部分,鏡像,容器,倉庫,我們可以把鏡像,容器想像成java的類和對象,即容 器是由鏡像實例化而來的。也就是說我們想使用裝有相關軟件的鏡像,首先要把鏡像創建成容器。現在是不是對鏡像,容器,倉庫這些概念還存在一些困惑?那麼讓 我們動手體驗一下docker的神奇,然後慢慢理解這些概念


2.Docker的安裝:

對於docker的安裝,docker官網已經給出了Linux各發行版所對應的安裝方法,可以參照這裏:https://docs.docker.com/  下面我以mac爲例講一下docker的安裝:

由於docker要使用LXC,namespace,cgroup等Linux內核相關 技術,而mac是基於unix的,所以要使用boot2docker來使用docker,boot2docker實際上是一個Linux的輕量級發行版, 一共24M大小,完全運行於內存中。但有一個問題,那就是下載它需要***,so…已將其安裝文件放到了百度雲盤中,想下載的可以點擊這裏下載。另外boot2docker需要運行在virtualBox上,所以請先下載virtualBox。安裝之後直接點擊如下圖標:

屏幕快照 2014-11-22 下午10.39.45

此時會出現一個終端,並進行一些初始化操作,待其完成後,運行如下命令:

bash3.2$ boot2docker ssh

此時就可以登錄到boot2docker,boot2docker已經爲我們預安裝好docker相關環境。我們可以運行如下命令查看docker相關版本信息:

docker@boot2docker:~$ docker versionClient version: 1.3.1Client API version: 1.15Go version (client): go1.3.3Git commit (client): 4e9bbfaOS/Arch (client): linux/amd64Server version: 1.3.1Server API version: 1.15Go version (server): go1.3.3Git commit (server): 4e9bbfa

下面讓我們在boot2docker中下載第一個docker鏡像:我們下載鏡像可以從docker官方爲我們提供的dockerHub上下載,但由於dockerHub的下載速度非常慢,所以這裏推薦個不錯的國內源:docker.cn 。我們可以使用docker  pull命令來從此源下載鏡像:

docker pull docker.cn/docker/centos:centos6

3.理解Docker的生命週期中的鏡像:

在下載的過程中我們可以看到docker的鏡像好像是在一層一層的在下載,如下圖:

docker@boot2docker:~$ docker pull docker.cn/docker/centos:centos6Pulling repository docker.cn/docker/centos70441cac1ed5: Download complete 511136ea3c5a: Download complete 5b12ef8fd570: Download complete Status: Image is up to date for docker.cn/docker/centos:centos6

那麼docker的鏡像到底是什麼呢?我們來解釋一下docker的鏡像的概念。

docker 的鏡像實際上由一層一層的文件系統組成,這種層級的文件系統就是上文說到的UnionFS。在Docker鏡像的最底層是bootfs。這一層與我們典型 的Linux/Unix系統是一樣的,包含boot加載器和內核。當boot加載完成之後整個內核就都在內存中了,此時內存的使用權已由bootfs轉交 給內核,此時系統也會卸載bootfs。Docker在bootfs之上的一層是rootfs(根文件系統)。rootfs就是各種不同的操作系統發行 版,比如Ubuntu,Centos等等。

docker鏡像的層級結構圖:

0731016

那麼docker的rootfs與傳統意義的rootfs不同之處到底是什麼呢?

傳統的Linux加載bootfs時會先將rootfs設爲read-only,然後在 系統自檢之後將rootfs從read-only改爲read-write。然後我們就可以在rootfs上進行寫和讀的操作了。但docker的鏡像卻 不是這樣,他在bootfs自檢完畢之後並不會把rootfs的read-only改爲read-write。而是利用union mount(UnionFS的一種掛載機制)將一個或多個read-only的rootfs加載到之前的read-only 的rootfs層之上。並在加載了這麼多層的rootfs之後,仍然讓它看起來只像是一個文件系統,在docker的體系裏把union mount的這些read-only層的rootfs叫做docker的鏡像(image)。請注意,此時的每一層rootfs都是read-only 的,也就是說我們此時還不能對其進行操作,那麼我們怎樣對其進行讀寫操作呢?

答案是將docker鏡像進行實例化,就是上文說的從鏡像(image)變成容器 (container)的過程,當鏡像被實例化爲容器之後,系統會爲在一層或是多層的read-only的rootfs之上分配一層空的read- write的rootfs。而這個分配的動作是由docker  run命令發起的,我們可以用如下命令創建一個容器:

docker run t i docker.cn/docker/centos:centos6 /bin/bash

此時我們已經進入到容器,此處要說明一下,我們使用的docker.cn 的鏡像實際上是在dockerHub提供的鏡像之上進行了一些修改,來使我們可以更輕鬆的使用鏡像,比如docker.cn 會把/etc/skel/.b* 文件拷貝到/root 目錄之下等等,也就是說docker.cn是在空的那層rootfs(read-write)對底層的rootfs(read-only)進行了修改,那 麼問題來了,上文不是說底層的rootfs是read-only的嗎?那爲什麼docker.cn可以對它進行修改?並且還能把修改後的鏡像保存起來?這 看起來好像非常的矛盾。

其實這並不矛盾,當我們將一個鏡像實例化爲一個容器之後,docker會在read- only 的rootfs之上分配一層空的read-write的rootfs,我們對文件系統的改變實際上是在空的這層rootfs(read-write)上發 生的。打個比方,如果你想修改一個文件,系統實際上是將這個在read-only層的rootfs的文件拷貝到read-write層的rootfs之 中,然後對它進行修改,但read-only層的文件並不會被修改,依然存在於read-only層之中,只不過是在read-write層下被隱藏了。 這種模式被稱爲copy on write。這是unionFS的特性。也是docker的強大之處,爲什麼說強大呢?它允許鏡像被繼承,也就是說我們想生成一套虛擬環境不用從零開始 了,而只要在一個相對完善的基礎環境之上來創建我們的虛擬環境就可以了,比如我們想生成一個具有tomcat環境的鏡像,只要在一個裝有java環境的鏡 像之上來創建就可以了。這也是docker便捷性的體現。


4.何爲容器

上文已經解釋了什麼是docker的鏡像。那麼什麼又是容器呢?我們剛纔使用docker run命令已經創建了一個docker容器,我們先來解釋一下剛纔那條命令:

docker run t i docker.cn/docker/centos:centos6 /bin/bash

docker run:將鏡像實例化成一個容器,有點像java中的new。

docker run的兩個參數:

      -i參數: 是以交互模式啓動容器

      -t參數: 分配一個tty終端

後面還跟了一條命令 /bin/bash,指要在容器中運行的命令

我們現在在容器中運行top命令:

top  20:16:27 up 16:38,  0 users,  load average: 0.00, 0.01, 0.05Tasks:   2 total,   1 running,   1 sleeping,   0 stopped,   0 zombieCpu(s):  0.0%us,  0.0%sy,  0.0%ni,100.0%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%stMem:   2056668k total,   336912k used,  1719756k free,    46280k buffersSwap:  1438672k total,        0k used,  1438672k free,   234792k cached   PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND                                                                             1 root      20   0 14784 3056 2704 S  0.0  0.1   0:00.06 bash                                                                                15 root      20   0 14952 1944 1744 R  0.0  0.1   0:00.14 top

然後我們會看到docker容器中只運行了兩個進程:這與我們常看到的Linux系統進程貌似不一樣,它並沒有init進 程,然後我們在保持原有終端不變的情況下,再開一個終端,然後登錄到boot2docker中(使用命令:boot2docker ssh),並運行如下命令:

docker@boot2docker:~$ ps ef | grep top 2010 root top 2052 docker grep top

此時我們可能會比較奇怪,我們並沒有在boot2docker中運行top,那麼這個top進程是哪裏來的呢?

這個top進程就是我們在容器中運行的top,這時你可能已經明白了,其實docker 容器中運行的進程實際上就是宿主機上的進程。docker實際上使用了命名空間(namespace)來對進程進行隔離,使不同namespace的進程 彼此不可見,同時使用cgroup來對彼此隔離的進程的資源進行限制,docker的容器(container)其實就是一個進程的容器,而並不是一個全 虛擬化的操作系統,所以他不會有什麼init進程。docker將進程、進程所需要的操作系統、運行環境稱爲容器。所以它比傳統的基於 hypervisor的虛擬機擁有更高的效率,並使用更低的資源。它實際上是一個內核級別的虛擬化技術,容器還是在使用宿主機的內核,爲了證實上述內容, 我們可以在容器中用如下命令查看docker的內核版本:

[root@5b3a545077e0 /]# uname aLinux 5b3a545077e0 3.16.4tinycore64 #1 SMP Thu Oct 23 16:14:24 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

 你會發現docker容器使用的內核版本與宿主機相同,然後運行如下命令:

[root@5b3a545077e0 /]# cat /etc/redhatrelease CentOS release 6.6 (Final)

你會發現他的發行版是centos6.6。


5.什麼是鏡像倉庫

很簡單:它就是一個存儲和共享鏡像文件的地方。


6.附錄

附錄1.docker與傳統虛擬機的對比:
         特性                           容器               虛擬機
         啓動                           秒級               分鐘級
      硬盤使用                       一般爲MB             一般爲GB
        性能                        接近原生                 弱於
     系統支持量               單機支持上千個容器             一般幾十個
附錄2.docker使用命名空間(namespace)詳解:

名字空間

名字空間是 Linux 內核一個強大的特性。每個容器都有自己單獨的名字空間,運行在其中的應用都像是在獨立的操作系統中運行一樣。名字空間保證了容器之間彼此互不影響。

pid 名字空間

不同用戶的進程就是通過 pid 名字空間隔離開的,且不同名字空間中可以有相同 pid。所有的 LXC 進程在 Docker 中的父進程爲Docker進程,每個 LXC 進程具有不同的名字空間。同時由於允許嵌套,因此可以很方便的實現嵌套的 Docker 容器。

net 名字空間

有了 pid 名字空間, 每個名字空間中的 pid 能夠相互隔離,但是網絡端口還是共享 host 的端口。網絡隔離是通過 net 名字空間實現的, 每個 net 名字空間有獨立的 網絡設備, IP 地址, 路由表, /proc/net 目錄。這樣每個容器的網絡就能隔離開來。Docker 默認採用 veth 的方式,將容器中的虛擬網卡同 host 上的一 個Docker 網橋 docker0 連接在一起。

ipc 名字空間

容器中進程交互還是採用了 Linux 常見的進程間交互方法(interprocess communication – IPC), 包括信號量、消息隊列和共享內存等。然而同 VM 不同的是,容器的進程間交互實際上還是 host 上具有相同 pid 名字空間中的進程間交互,因此需要在 IPC 資源申請時加入名字空間信息,每個 IPC 資源有一個唯一的 32 位 id。

mnt 名字空間

類似 chroot,將一個進程放到一個特定的目錄執行。mnt 名字空間允許不同名字空間的進程看到的文件結構不同,這樣每個名字空間 中的進程所看到的文件目錄就被隔離開了。同 chroot 不同,每個名字空間中的容器在 /proc/mounts 的信息只包含所在名字空間的 mount point。

uts 名字空間

UTS(“UNIX Time-sharing System”) 名字空間允許每個容器擁有獨立的 hostname 和 domain name, 使其在網絡上可以被視作一個獨立的節點而非 主機上的一個進程。

user 名字空間

每個容器可以有不同的用戶和組 id, 也就是說可以在容器內用容器內部的用戶執行程序而非主機上的用戶。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章