1. 程式人生 > >虛擬化與Docker容器

虛擬化與Docker容器

1、虛擬化 虛擬化技術即是對資源的抽象。
如果把x86平臺的cpu,記憶體和外設作為資源,那對應的虛擬化技術就是平臺虛擬化,在同一個x86平臺上面,可以虛擬多個x86平臺,每個平臺可以執行自己獨立完整的作業系統。
如果把作業系統以及提供的系統呼叫作為資源,那虛擬化就表現為作業系統虛擬化,例如Linux容器虛擬化技術就是在同一個Linux作業系統之上虛擬多個同樣的作業系統,每個應用程式都認為自己執行在一個獨立的os上,例如LXL,Docker。作業系統虛擬化允許作業系統核心擁有彼此隔離和分割的多使用者空間例項instance ,這些例項也稱之為容器,基於Linux核心中的namespace 、cgroup、LXC 、Docker。

2、Docker Docker 是一個開源的應用容器引擎,基於Go語言並遵從Apache2.0協議開源。可以讓開發者打包他們的應用以及依賴包到一個輕量級、可移植的容器中,然後釋出到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何介面(類似 iPhone 的 app),更重要的是容器效能開銷極低。 Docker 提供了一個可以執行你的應用程式的封套,或者說容器。Docker 是一個開源的應用容器引擎,讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後釋出到任何流行的Linux機器上,也可以實現虛擬化。容器是完全使用沙箱
機制,相互之間不會有任何介面。
Docker擴充套件了Linux 容器(Linux Containers),通過一個高層次的API為程序單獨提供了一個輕量級的虛擬環境Docker 利用了 LXC, cgroups 和 Linux 自己的核心。和傳統的虛擬機器不同的是,一個 Docker 容器並不包含一個單獨的作業系統,而是基於已有的基礎設施中作業系統提供的功能來執行的。 Docker類似虛擬機器的概念,但是與虛擬化技術的不同點在於下面幾點: 1)、虛擬化技術依賴物理CPU和記憶體,是硬體級別的;而docker構建在作業系統上,利用作業系統的containerization技術,所以docker甚至可以在虛擬機器上執行。 2)、虛擬化系統一般都是指作業系統映象,比較複雜,稱為“系統”;而docker開源而且輕量,稱為“容器”,單個容器適合部署少量應用,比如部署一個redis、一個memcached。 3)、傳統的虛擬化技術在構建系統的時候較為複雜,需要大量的人力;而docker可以通過Dockfile來構建整個容器,重啟和構建速度很快。更重要的是Dockfile可以手動編寫,這樣應用程式開發人員可以通過釋出Dockfile來指導系統環境和依賴,這樣對於持續交付有利。 4)、Dockerfile可以基於已經構建好的容器映象,建立新容器。Dockerfile可以通過社群分享和下載,有利於該技術的推廣。 Docker會像一個可移植的容器引擎那樣工作它把應用程式及所有程式的依賴環境打包到一個虛擬容器中,這個虛擬容器可以執行在任何一種 Linux 伺服器上。這大大地提高了程式執行的靈活性和可移植性,無論需不需要許可、是在公共雲還是私密雲、是不是裸機環境等等。

同時,Docker也是一個雲端計算平臺,它利用Linux的LXC、AUFU、Go語言、cgroup實現了資源的獨立,可以很輕鬆的實現檔案、資源、網路等隔離,其最終的目標是實現類似PaaS平臺的應用隔離。 Docker 採用了 C/S架構,包括客戶端和服務端。 Docker daemon 作為服務端接受來自客戶的請求,並處理這些請求(建立、執行、分發容器)。客戶端和服務端既可以執行在一個機器上,也可通過socket或者RESTful API來進行通訊。

我們知道,在作業系統中,包括核心、檔案系統、網路、PID、UID、IPC、記憶體、硬碟、CPU 等等,所有的資源都是應用程序直接共享的。 要想實現虛擬化,除了要實現對記憶體、CPU、網路IO、硬碟IO、儲存空間等的限制外,還要實現檔案系統、網路、PID、UID、IPC等等的相互隔離。 前者相對容易實現一些,後者則需要宿主機系統的深入支援。
隨著 Linux 系統對於名稱空間功能的完善實現,程式設計師已經可以實現上面的所有需求,讓某些程序在彼此隔離的名稱空間中執行。大家雖然都共用一個核心和某些執行時環境(例如一些系統命令和系統庫),但是彼此卻看不到,都以為系統中只有自己的存在。這種機制就是容器(Container),利用名稱空間來做許可權的隔離控制,利用 cgroups 來做資源分配。
名稱空間是 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, 也就是說可以在容器內用容器內部的使用者執行程式而非主機上的使用者。

而控制組(cgroups)是 Linux 核心的一個特性,主要用來對共享資源進行隔離、限制、審計等。只有能控制分配到容器的資源,才能避免當多個容器同時執行時的對系統資源的競爭。控制組技術最早是由 Google 的程式設計師 2006 年起提出,Linux 核心自 2.6.24 開始支援,可以提供對容器的記憶體、CPU、磁碟 IO 等資源的限制和審計管理。

這樣的產品設計架構,體現了Docker 的優點:

1)、簡化程式:

Docker 讓開發者可以打包他們的應用以及依賴包到一個可移植的容器中,然後釋出到任何流行的 Linux 機器上,便可以實現虛擬化。Docker改變了虛擬化的方式,使開發者可以直接將自己的成果放入Docker中進行管理。方便快捷已經是 Docker的最大優勢,過去需要用數天乃至數週的    任務,在Docker容器的處理下,只需要數秒就能完成。

2)、避免選擇恐懼症:

如果你有選擇恐懼症,還是資深患者。Docker 幫你    打包你的糾結!比如 Docker 映象;Docker 映象中包含了執行環境和配置,所以 Docker 可以簡化部署多種應用例項工作。比如 Web 應用、後臺應用、資料庫應用、大資料應用比如 Hadoop 叢集、訊息佇列等等都可以打包成一個映象部署。

3)、節省開支:

一方面,雲端計算時代到來,使開發者不必為了追求效果而配置高額的硬體,Docker 改變了高效能必然高價格的思維定勢。Docker 與雲的結合,讓雲空間得到更充分的利用。不僅解決了硬體管理的問題,也改變了虛擬化的方式。

3、Docker的生命週期

Docker 包括三個基本概念:映象(Image)、容器(Container)、倉庫(Repository)。

這三部分組成了Docker的整個生命週期,如下圖所示,容器是由映象例項化而來的,這和我們學習的面向物件的概念十分相似,我們可以把映象想象成類,把容器想象成類經過例項化後的物件,這樣就非常好理解映象和容器的關係了。

Docker 使用客戶端-伺服器 (C/S) 架構模式,使用遠端API來管理和建立Docker容器。

Docker 容器通過 Docker 映象來建立。

容器與映象的關係類似於面向物件程式設計中的物件與類。



Docker的映象概念類似於虛擬機器裡的映象,是一個只讀的模板,一個獨立的檔案系統,包括執行容器所需的資料,可以用來建立新的容器。例如:一個映象可以包含一個完整的 ubuntu 作業系統環境,裡面僅安裝了Mysql或使用者需要的其它應用程式。

Docker的映象實際上由一層一層的檔案系統組成,這種層級的檔案系統被稱為UnionFS。映象可以基於Dockerfile構建,Dockerfile是一個描述檔案,裡面包含若干條命令,每條命令都會對基礎檔案系統建立新的層次結構。Docker 提供了一個很簡單的機制來建立映象或者更新現有的映象,使用者甚至可以直接從其他人那裡下載一個已經做好的映象來直接使用。注:映象是隻讀的,可以理解為靜態檔案。

Docker容器:Docker 利用容器來執行應用。Docker容器是由Docker映象建立的執行例項。Docker容器類似虛擬機器,可以支援的操作包括啟動,停止,刪除等。每個容器間是相互隔離的,容器中會執行特定的應用,包含特定應用的程式碼及所需的依賴檔案。可以把容器看做是一個簡易版的 Linux 環境(包括root使用者許可權、程序空間、使用者空間和網路空間等)和執行在其中的應用程式。注:相對於映象來說容器是動態的,容器在啟動的時候建立一層可寫層作為最上層。

Docker倉庫:如果你使用過git和github就很容易理解Docker的倉庫概念。Docker 倉庫的概念跟Git 類似,註冊伺服器可以理解為 GitHub 這樣的託管服務。Docker 倉庫是用來包含映象的位置,Docker提供一個註冊伺服器(Register)來儲存多個倉庫,每個倉庫又可以包含多個具備不同tag的映象。Docker執行中使用的預設倉庫是 Docker Hub 公共倉庫。

倉庫支援的操作類似git,當用戶建立了自己的映象之後就可以使用 push 命令將它上傳到公有或者私有倉庫,這樣下次在另外一臺機器上使用這個映象時候,只需要從倉庫上 pull 下來就可以了。.

4、Docker簡單命令

基本語法:Docker 命令有兩大類,客戶端命令和服務端命令。前者是主要的操作介面,後者用來啟動 Docker daemon。

客戶端命令:基本命令格式為 docker [OPTIONS] COMMAND [arg...];

服務端命令:基本命令格式為 docker daemon [OPTIONS]。

可以通過 man docker 或 docker help 來檢視這些命令。

1)、安裝docker:

[root@NameNode ~]#  yum -y install docker-io

2)、檢視docker版本和資訊:

[root@NameNode ~]# docker version
Client version: 1.7.1
Client API version: 1.19
Go version (client): go1.4.2
Git commit (client): 786b29d/1.7.1
OS/Arch (client): linux/amd64
Server version: 1.7.1
Server API version: 1.19
Go version (server): go1.4.2
Git commit (server): 786b29d/1.7.1
OS/Arch (server): linux/amd64
[root@NameNode~]# docker info
Containers: 0
Images: 0
Storage Driver: devicemapper
Pool Name: docker-252:1-1687722-pool
Pool Blocksize: 65.54 kB
Backing Filesystem: extfs
Data file: /dev/loop0
Metadata file: /dev/loop1
Data Space Used: 305.7 MB
Data Space Total: 107.4 GB
Data Space Available: 38.33 GB
Metadata Space Used: 729.1 kB
Metadata Space Total: 2.147 GB
Metadata Space Available: 2.147 GB
Udev Sync Supported: true
Deferred Removal Enabled: false
Data loop file: /var/lib/docker/devicemapper/devicemapper/data
Metadata loop file: /var/lib/docker/devicemapper/devicemapper/metadata
Library Version: 1.02.117-RHEL6 (2016-08-15)
Execution Driver: native-0.2
Logging Driver: json-file
Kernel Version: 2.6.32-642.15.1.el6.x86_64
Operating System: <unknown>
CPUs: 1
Total Memory: 996.1 MiB
Name: wenzhiyi
ID: KAJD:B23V:MBIX:43LO:GENE:EIXT:UI3V:A2W5:E2UI:OAYB:VFEJ:FQLR
3)、Hub倉庫查詢和下載自己想要的映象: [root@NameNode~]# docker  search tensorflow [root@NameNode~]# docker  pull   tensorflow/tensorflow
4)、啟動一個容器nginx: [root@NameNode ~]# docker run -it -v /root/tensorflow:/root/tensorflow tensorflow/tensorflow bash
-t:在新容器內指定一個偽終端或終端。
-i:允許你對容器內的標準輸入 (STDIN) 進行互動。
這樣在容器啟動後,容器內會自動建立/root.tensorflow的目錄。通過這種方式,我們可以明確一點,即-v引數中,冒號前面的目錄是宿主機目錄,冒號後面的目錄是容器內目錄。
在使用docker容器時,有時候裡邊沒有安裝vim,敲vim命令時提示說:vim: command not found,這個時候就需要安裝vim,可是當你敲apt-get install vim命令時,提示: 
Reading package lists... Done 
Building dependency tree       
Reading state information... Done 
E: Unable to locate package vim 
這時候需要敲:apt-get update,這個命令的作用是:同步 /etc/apt/sources.list 和 /etc/apt/sources.list.d 中列出的源的索引,這樣才能獲取到最新的軟體包。 
等更新完畢以後再敲命令:apt-get install vim命令即可。 
5)、儲存容器修改: 儲存對容器的修改,當你對某一個容器做了修改之後(通過在容器中執行某一個命令),可以把對容器的修改儲存下來,這樣下次可以從儲存後的最新狀態執行該容器。docker中儲存狀態的過程稱之為committing,它儲存的新舊狀態之間的區別,從而產生一個新的版本。 執行docker commit,可以檢視該命令的引數列表。你需要指定要提交儲存容器的ID,通過docker ps -l 命令獲得,無需拷貝完整的id,通常來講最開始的三至四個字母即可區分。(譯者按:非常類似git裡面的版本號) [root@NameNode ~]# docker commit 20d6bf258be3  tensorflow/tensorflow
b289f2a691c384e2d6d815ea20ea03ebcfb5010ad5d95f439548f21c2af38bb5
然後,docker images檢視有沒有儲存成功
[root@NameNode ~]# docker images
REPOSITORY              TAG                IMAGE ID            CREATED            VIRTUAL SIZE
tensorflow/tensorflow  latest              b289f2a691c3        2 minutes ago      1.32 GB
hello-world            latest              690d80202531        6 weeks ago        1.848 kB
檢視容器的詳細資訊: [root@TensorFlow ~]#  docker inspect tensorflow/tensorflow
[
{
    "Id": "b289f2a691c384e2d6d815ea20ea03ebcfb5010ad5d95f439548f21c2af38bb5",
    "Parent": "dc688d70399a43a05103e144f1b3056530b5cc044b48499e7503e8b100fd26ac",
    "Comment": "",
    "Created": "2018-01-05T01:05:05.951493913Z",
    "Container": "20d6bf258be303755f692be72289e8b959f789cf979f8a4d33d40f5f40ca1e6c",
    "ContainerConfig": {
        "Hostname": "20d6bf258be3",
        "Domainname": "",
        "User": "",
        "AttachStdin": true,
        "AttachStdout": true,
        "AttachStderr": true,
        "PortSpecs": null,
        "ExposedPorts": {
            "6006/tcp": {},
            "8888/tcp": {}
        },
        "Tty": true,
        "OpenStdin": true,
        "StdinOnce": true,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd": [
            "bash"
        ],
        "Image": "tensorflow/tensorflow",
        "Volumes": null,
        "VolumeDriver": "",
        "WorkingDir": "/notebooks",
        "Entrypoint": null,
        "NetworkDisabled": false,
        "MacAddress": "",
        "OnBuild": null,
        "Labels": {}
    },
    "DockerVersion": "1.7.1",
    "Author": "",
    "Config": {
        "Hostname": "",
        "Domainname": "",
        "User": "",
        "AttachStdin": false,
        "AttachStdout": false,
        "AttachStderr": false,
        "PortSpecs": null,
        "ExposedPorts": {
            "6006/tcp": {},
            "8888/tcp": {}
        },
        "Tty": false,
        "OpenStdin": false,
        "StdinOnce": false,
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
        ],
        "Cmd": [
            "bash"
        ],
        "Image": "",
        "Volumes": null,
        "VolumeDriver": "",
        "WorkingDir": "/notebooks",
        "Entrypoint": null,
        "NetworkDisabled": false,
        "MacAddress": "",
        "OnBuild": null,
        "Labels": {}
    },
    "Architecture": "amd64",
    "Os": "linux",
    "Size": 73972013,
    "VirtualSize": 1319690723
}
]