在計算機技術日新月異的今天, Docker在國內發展的如火如荼,特別是在一線互聯網公司,Docker的使用是十分普遍的,在理解docker之前,我們先熟悉兩個概念,容器和虛擬機。
一、容器與虛擬機
傳統的虛擬機有VMware, VirtualBox ,它們需要模擬整臺機器包括硬件,每臺虛擬機都需要有自己的操作系統,虛擬機一旦開啟,預分配給它們的資源將全部被占用。每一臺虛擬機包括應用,必要的二進制和庫,以及一個完整的用戶操作系統。
而容器技術是和我們的宿主機共享硬件資源及操作系統,可以實現資源的動態分配。容器包含應用和其所有的依賴包,但是與其他容器共享內核。容器在宿主機操作系統中,在用戶控件以分離的進程運行。
容器技術是實現操作系統虛擬化的一種途徑,可以讓您在資源受到隔離的進程中運行應用程序及其依賴關系。通過使用容器,我們可以輕松打包應用程序的代碼、配置和依賴關系,將其編程容易使用的構建塊,從而實現環境一致性、運營效率、開發人員生產力和版本控制等諸多目標。容器幫助保證應用程序快速、可靠、一致性部署,其間不受部署環境的影響。容器還賦予我們對資源更多的精細化控制能力,讓我們的基礎設施效率更高、通過下面這張圖我們可以很直觀的反映出二者的區別。
Docker屬于Linux容器的一種封裝,提供簡單易用的容器使用接口。它是目前最流行的Linux容器解決方案。
Linux容器不是模擬一個完整的操作系統,而是對進程進行隔離,相當于在正常進程的外面套了一個保護層。對于容器里面的進程來說,它接觸到的各種資源都是虛擬的,從而實現與底層系統的隔離。
Docker將應用程序與該程序的依賴,打包在一個文件里。運行這個文件,就會生成一個虛擬容器,程序在這個虛擬容器里運行,就好像在真實的物理機上運行一樣,有了Docker,就不用擔心環境問題。
總體來說,Docker的接口相當簡單,用戶可以方便的創建和使用容器,把自己的應用放入容器,容器還可以進行版本管理,復制,分享,修改,就像管理代碼一樣。
二、Docker的優勢
1、Docker啟動快速,屬于秒級別。虛擬機通常需要幾分鐘去啟動。
2、Docker需要的資源更少,Docker在操作系統級別進行虛擬化,Docker容器和內核交互,幾乎沒有性能損耗,性能優于通過 Hypervisor
層與內核層的虛擬化
3、Docker更輕量,Docker的架構可以共用一個內核與共享應用程序庫,所占內存極小。同樣的硬件環境,Docker運行的鏡像數遠低于虛擬機數量,對系統的利用率非常高
4、安全性,
Docker的安全性也更弱。Docker的租戶root和宿主機root等同,一旦容器內的用戶從普通用戶權限提升為root權限,它就直接具備了宿主機的root權限,進而可進行無限制的操作。虛擬機租戶root權限和宿主機的root虛擬機權限是分離的,并且虛擬機利用如intel的vt-d和vt-x的ring-1硬件隔離技術,這種隔離技術可以防止虛擬機突破和彼此交互,而容器至今還沒有任何形式的硬件隔離,這使得容器容易受到攻擊
5、可管理性:Docker的集中化管理工具還不算成熟。各種虛擬化技術都有成熟的管理工具,例如VMware Vcentor提供完備的虛擬機管理能力
6、高可用和可恢復性: Docker對業務的高可用支持是通過快速重新部署實現的。虛擬化具備負載均衡,高可用,容錯,遷移和數據保護等經過生產實踐檢驗的成熟保障機制,VMware可承諾虛擬機99.9999%高可用,保證業務連續性
7、 交付、部署:虛擬機可以通過鏡像實現環境交付的一致性,但鏡像分發無法體系化。 Docker在Dockerfile中記錄了容器構建過程,可在集群中實現快速分發和快速部署
三、Docker的三個基本概念
從上圖我們可以看到,Docker中包括三個基本的概念:
- Image(鏡像)
- Container(容器)
- Repository(倉庫)
1、鏡像是Docker運行容器的前提,倉庫是存放鏡像的場所,可見鏡像是Docker的核心。
那么鏡像到底是什么呢?
Docker鏡像可以看作是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時準備的一些配置參數。鏡像不包含任何動態數據,其內容在構建之后也不會被改變。
2、Container (容器)
容器的定義和鏡像幾乎一模一樣,也是一堆層的統一視角,唯一區別在于容器的最上面那一層是可讀可寫的。
由于容器的定義并沒有提及是否要運行容器,所以實際上,容器 = 鏡像 + 讀寫層。
3、Repository(倉庫)
Docker倉庫是集中存放鏡像文件的場所,鏡像構建完成后,可以很容易在當前宿主上運行,但是,如果需要在其他服務器上使用這個鏡像,我們需要一個集中的存儲、分發鏡像的服務,Docker
Registry(倉庫注冊服務器)就是這樣的服務。 有時候會把倉庫(Repository)和倉庫注冊服務器(Registry)混為一談,并不嚴格區分。 實際上,一個Docker Registry中可以包含多個倉庫(Repository),每個倉庫可以包含多個標簽
,每個標簽對應著一個鏡像。所以說,鏡像倉庫是Docker用來集中存放鏡像文件的地方類似于我們之前常用的代碼倉庫。
倉庫又可以分為兩種形式:
(1)public(共有倉庫)
Docker Registry公有倉庫是開放給用戶使用、允許用戶管理鏡像的 Registry
服務。一般這類公開服務允許用戶免費上傳、下載公開的鏡像,并可能提供收費服務供用戶管理私有鏡像。
(2)private(私有倉庫)
Docker官方提供了Docker Registry鏡像,可以直接使用做為私有Registry服務。當用戶創建了自己的鏡像之后就可以使用push命令將它上傳到公有或者私有倉庫,這樣下次在另外一臺機器上使用這個鏡像時候,只需要從倉庫上 pull下來就可以了。
四、Docker的架構
Docker使用C/S架構,即客戶端/服務器體系結構。Docker客戶端與Docker服務器進行交互,Docker服務端負責構建、運行和分發Docker鏡像。Docker客戶端與服務端可以運行在一臺機器上,也可以通過RESTful、stock或網絡接口與遠程Docker服務端進行通信。
這張圖展示了Docker客戶端、服務端和Docker倉庫(即Docker
Hub和Docker Cloud ),默認情況下Docker會在Docker中央倉庫尋找鏡像文件,這種利用倉庫管理鏡像的設計理念類似于Git,當然這個倉庫是可以通過修改配置來指定的,甚至我們可以創建我們自己的私有倉庫。
Docker采用的是C/S架構,客戶端向服務器發送請求,服務器負責構建、運行和分發容器。客戶端和服務器可以運行在同一個Host上,客戶端也可以通過socket或REST API與遠程的服務器通信。
1、Docker Client
Docker客戶端其實就是Docker提供命令行界面工具,是許多Docker 用戶與Docker
進行交互的主要方式。客戶端可以構建、運行和停止應用程序,還可以遠程與Docker_HOST進行交互。 最常用的Docker客戶端就是Docker命令,我們可以通過Docker命令很方便地在host上構建和運行Docker容器。
2、Docker Daemon
Docker Daemon是服務器組件,以Linux后臺服務的方式運行,是Docker最核心的后臺進程,我們也把它稱為守護進程。它負責相應來自Docker
Client的請求,然后將這些請求翻譯成系統調用完成容器管理操作。該進程會在后臺啟動一個API Server,負責接收由Docker
Client發送的請求,接收到的請求將通過Docker Daemon內部的一個路由分發調用,由具體的函數來執行請求。
Docker Daemon的架構如下所示:
Docker Daemon可以認為是通過Docker Server模塊接受Docker
Client的請求,并在Engine中處理請求,然后根據請求類型,創建出指定的Job并運行。Docker Daemon運行在Docker
Host上,負責創建、運行、監控容器,構建、存儲鏡像。
運行過程的作用有以下幾種可能:
- 向Docker Registry獲取鏡像
- 通過grapthdriver執行容器鏡像的本地化操作
- 通過networkdriver執行容器網絡環境的配置
- 通過execdriver執行容器內部運行的執行工作
由于Docker Daemon和Docker Client的啟動都是通過可執行文件Docker來完成的,因此兩者的啟動流程非常相似。Docker可執行文件運行時,運行代碼通過不同的命令行flag參數,區分兩者,并最終運行兩者各自相應的部分。
啟動Docker Daemon時,一般可以使用一下命令來完成
再由docker的main函數來解析以上命令的相應的flag參數,并最終完成Docker Daemon的啟動。
Docker Daemon的啟動流程:
默認配置下, Docker Daemon只能相應來自本地host的客戶端請求。如果要允許遠程客戶端請求,需要在配置文件中打開TCP監聽。我們可以照著如下步驟進行配置:
(1)編輯配置文件 /etc/systemd/system/multi-user.target.wants/docker.service
,在環境變量 ExecStart
后面添加 -H tcp://0.0.0.0
,允許來自任意 IP 的客戶端連接。
(2)重啟 Docker Daemon
systemctl restart docker.service
(3)我們通過以下命令即可實現與遠程服務器通信
docker -H 服務器IP地址 info
-H 是用來指定服務器主機,info子命令用于查看docker服務器的信息
3、Docker Image
Docker鏡像可以看作是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些為運行時準備的一些配置參數(如匿名卷、環境變量、用戶等)。鏡像不包含任何動態數據,其內容在構建之后也不會被改變。我們可將Docker鏡像看成只讀模板,通過它可以創建Docker容器。
鏡像有多種生成方法:
- 從無到有開始創建鏡像
- 下載并使用別人創建好的現成的鏡像
- 在現有鏡像上創建新的鏡像
我們可以將鏡像的內容和創建步驟描述在一個文本文件中,這個文本文件稱作Dockerfile,通過執行docker build<docker-file>命令可以構建出docker鏡像。
4、Docker Registry
Docker Registry是存儲Docker Image的倉庫,它在Docker生態環境中的位置如下圖所示:
運行Docker Push、Docker pull、Docker search時,實際上是通過 Docker Daemon與 Docker registry通信。
5、Docker Container
Docker容器就是Docker鏡像的運行實例,是真正運行項目程序、消耗系統資源、提供服務的地方。Docker Container提供了系統硬件環境,我們可以使用Docker
Image這些制作好的系統盤,再加上我們編寫好的項目代碼,run一下就可以提供服務了。
五、Docker組件如何協作運行容器
容器啟動過程如下:
- Docker客戶端執行docker run命令
- Docker Daemon發現本地沒有hello-world鏡像
- Daemon從Docker Hub下載鏡像
- 下載完成,鏡像hello-world被保存到本地
- Docker Daemon啟動容器
具體過程可以看如下這幅演示圖:
我們可以通過Docker Image可以查看到hello-world已經下載到本地
六、Docker常用命令
我們可以通過docker -h去查看命令的詳細的幫助文檔。在這里我只會講一些平常日常比賽或者生活中我們可能會用的比較多的一些命令。
例如,我們需要拉取一個docker鏡像,我們可以用如下命令:
docker pull image_name
image_name為鏡像的名稱,而如果我們想從Docker Hub上去下載某個鏡像,我們可以使用以下命令:
docker pull centos:latest
centos:lastest是鏡像的名稱,Docker daemon發現本地沒有我們需要的鏡像,會自動去Docker Hub上去下載鏡像,下載完成后,該鏡像被默認保存到/var/lib/docker目錄下。
接著我們如果想查看下主機下存在多少鏡像,我們可以用如下命令:
docker images
我們要想知道當前有哪些容器在運行,我們可以用如下命令:
docker ps -a
-a是查看當前所有的容器,包括未運行的
我們該如何去對一個容器進行啟動,重啟和停止呢?我們可以用如下命令:
docker start container_name/container_id
docker restart container_name/container_id
docker stop container_name/container_id
這個時候我們如果想進入到這個容器中,我們可以使用attach命令:
docker attach container_name/container_id
那如果我們想運行這個容器中的鏡像的話,并且調用鏡像里面的bash,我們可以使用如下命令:
docker run -t -i container_name/container_id /bin/bash
那如果這個時候,我們想刪除指定鏡像的話,由于image被某個container引用(拿來運行),如果不將這個引用的container銷毀(刪除),那image肯定是不能被刪除。我們首先得先去停止這個容器:
然后我們用如下命令去刪除這個容器:
docker stop container_name/container_id
然后這個時候我們再去刪除這個鏡像:
docker rmi image_name
此時,常用的Docker相關的命令就講到這里為止了,我們在后續的文章中還會反復地提到這些命令。
七、Dockerfile
Dockerfile是自動構建docker鏡像的配置文件,用戶可以使用Dockerfile快速創建自定義的鏡像,Dockerfile中的命令非常類似于Linux下的shell命令。
我們可以通過下面這幅圖來直觀地感受下 Docker 鏡像、容器和 Dockerfile 三者之間的關系。
我們從上圖中可以看到,Dockerfile可以自定義鏡像,通過Docker命令去運行鏡像,從而達到啟動容器的目的。
Dockerfile 是由一行行命令語句組成,并且支持已#開頭的注釋行。
一般來說,我們可以將Dockerfile分為四個部分:
- 基礎鏡像(父鏡像)信息指令FROM
- 維護者信息指令MAINTAINER
- 鏡像操作指令RUN、EVN、ADD和WORKER等
- 容器啟動指令CMD、ENTRYPOINT和USER 等
下面是一段簡單的Dockerfile的例子:
1、從Docker Hub上pull下python 2.7的基礎鏡像
2、顯示維護者的信息
3、copy 當前目錄到容器中的/app目錄下 復制本地主機的<src>(Dockerfile所在目錄的相對路徑)到容器里<dest>
4、指定工作路徑為/app
5、安裝依賴包
6、暴露5000端口
7、啟動app
這個例子是啟動一個Python flask app的Dockerfile(flask是Python的一個輕量級的web框架),相信大家從這個例子中能夠稍微理解了Dockerfile的組成以及指令的編寫過程。
八、構建Dockerfile代碼實例
MAINTAINER Angel_Kitty <angelkitty6698@gmail.com>
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
我們在Dockerfile文件所在目錄執行:
docker build -t angelkitty/nginx_web:v1 .
我們解釋一下,-t是為新鏡像設置倉庫和名稱,其中angelkitty為倉庫名,nginx_web為鏡像名,:v1為標簽(不添加為默認latest)
我們構建完成之后,使用docker images命令查看所有鏡像,如果存在REPOSTORY為nginx和TAG是v1的信息,就表示構建成功。
接下來使用docker run命令來啟動容器
docker run --name nginx_web -d -p 8080:80 angelkitty/nginx_web:v1
這條命令會用nginx鏡像啟動一個容器,命名為nginx_web,并且映射了 8080 端口,這樣我們可以用瀏覽器去訪問這個nginx服務器:http:localhost:8080或者 http://本機的IP地址:8080/,頁面返回信息:
這樣一個簡單使用Dockerfile構建鏡像,運行容器的示例就完成了!