Docker 原理與核心概念

1、Docker 簡介

Docker 是一個構建,發佈和運行應用程序的開放平臺。Docker 以容器爲資源分隔和調度的基本單位,容器封裝了整個項目運行時所須要的全部環境,經過 Docker 你能夠將應用程序與基礎架構分離,像管理應用程序同樣管理基礎架構,以便快速完成項目的部署與交付。java

Docker 使用 Go 語言進行開發,基於 Linux 內核的 cgroup,namespace,以及 AUFS 類的 Union FS 等技術,對進程進行封裝隔離,屬於操做系統層面的虛擬化技術。最初實現是基於 LXC,從 0.7 版本之後開始去除 LXC,轉而使用自行開發的 libcontainer,從 1.11 開始,則進一步演進爲使用 runC 和 containerd。linux

  • runc :是一個 Linux 命令行工具,用於根據 OCI容器運行時規範 建立和運行容器。
  • containerd :是一個守護程序,它管理容器生命週期,提供了在一個節點上執行容器和管理鏡像的最小功能集。

下圖體現了 Docker 和傳統虛擬化方式的不一樣之處:傳統虛擬機技術是虛擬出一套硬件後,在其上運行一個完整操做系統,再在該系統上運行所需應用進程;而 Docker 容器內的應用進程則是直接運行於宿主的內核,容器內沒有本身的內核,並且也沒有進行硬件虛擬,所以要比傳統虛擬機更爲輕便。git

2、Docker 架構與核心概念

Docker 使用 client-server 架構, Docker 客戶端將命令發送給 Docker 守護進程,後者負責構建,運行和分發 Docker 容器。 Docker 客戶端和守護程序使用 REST API,經過 UNIX 套接字或網絡接口進行通訊。核心概念以下:github

2.1 鏡像

Docker 鏡像(Image)是一個特殊的文件系統,包含了程序運行時候所須要的資源和環境。鏡像不包含任何動態數據,其內容在構建以後也不會被改變。spring

由於鏡像包含操做系統完整的 root 文件系統,其體積每每是龐大的,所以在 Docker 設計時,充分利用 Union FS (聯合文件系統)的技術,將其設計爲分層存儲的架構,因此一個鏡像其實是由多層文件系統聯合組成。鏡像構建時,會一層層構建,前一層是後一層的基礎;每一層構建完就不會再發生改變,後一層上的任何改變只發生在本身這一層。好比,刪除前一層文件的操做,實際不是真的刪除前一層的文件,而是僅在當前層標記爲該文件已刪除。在最終容器運行的時候,雖然不會看到這個文件,可是實際上該文件會一直跟隨鏡像。所以,在構建鏡像的時候,須要額外當心,每一層儘可能只包含該層須要添加的東西,任何額外的東西應該在該層構建結束前清理掉。docker

分層存儲的特徵使得鏡像的複用、定製變的更爲容易。甚至能夠用以前構建好的鏡像做爲基礎層,而後進一步添加新的層,以定製本身所需的內容,構建新的鏡像。shell

2.2 容器

鏡像(Image)和容器(Container)的關係,就像是面向對象程序設計中的 實例 同樣,鏡像是靜態的定義,容器是鏡像運行時的實體,容器能夠被建立、啓動、中止、刪除、暫停等。centos

容器的實質是進程,但與直接在宿主執行的進程不一樣,容器進程運行在屬於本身的、獨立的命名空間中。所以容器能夠擁有本身的 root 文件系統、本身的網絡配置、本身的進程空間,甚至本身的用戶 ID 空間。容器內的進程運行在一個隔離的環境裏,使用起來,就好像是在一個獨立於宿主的系統下操做同樣,這種特性使得容器封裝的應用比直接在宿主運行更加安全。緩存

前面講過鏡像使用的是分層存儲,容器也是如此。每個容器運行時,是以鏡像爲基礎層,在其上建立一個當前容器的存儲層,咱們能夠稱這個爲容器運行時讀寫而準備的存儲層稱爲 容器存儲層。容器存儲層的生存週期和容器同樣,容器消亡時,容器存儲層也隨之消亡。所以,任何保存於容器存儲層的信息都會隨容器刪除而丟失。安全

按照 Docker 最佳實踐的要求,容器不該該向其存儲層內寫入任何數據,容器存儲層要保持無狀態化。全部的文件寫入操做,都應該使用數據卷(Volume)、或者綁定宿主目錄,在這些位置的讀寫會跳過容器存儲層,直接對宿主(或網絡存儲)發生讀寫,其性能和穩定性更高。數據卷的生存週期獨立於容器,容器消亡,數據卷不會消亡,所以,使用數據卷後,容器刪除或者從新運行以後,數據都不會丟失。

2.3 倉庫

鏡像構建完成後,能夠很容易的在當前宿主機上運行,但若是須要在其它服務器上使用這個鏡像,就須要一個集中的存儲、分發鏡像的服務,這就是鏡像倉庫(Registry)。Docker Hub 是 Docker 官方提供的鏡像公有倉庫,提供了大量經常使用軟件的鏡像,固然出於安全和保密的須要,你也能夠構建本身的私有倉庫。

2.4 Docker daemon

Docker daemon(dockerd)負責監聽 Docker API 請求並管理 Docker 對象,如鏡像,容器,網絡和卷,守護程序彼此之間也能夠進行通信。

2.5 Docker Client

Docker 客戶端(docker)是用戶與 Docker 交互的主要方式。當你使用 docker run 等命令時,客戶端會將這些命令發送到 dockerd,dockerd 負責將其執行。一個 Docker客戶端能夠與多個 dockerd 進行通信。

3、Docker 經常使用命令

Docker 提供了大量命令用於管理鏡像、容器和服務,命令的統一使用格式爲:docker [OPTIONS] COMMAND ,其中 OPTIONS 表明可選參數。須要注意的是 Docker 命令的執行通常都須要獲取 root 權限,這是由於 Docker 的命令行工具 docker 與 docker daemon 是同一個二進制文件,docker daemon 負責接收並執行來自 docker 的命令,它的運行須要 root 權限。全部經常使用命令及其使用場景以下:

3.1 基礎命令

  • docker version:用於查看 docker 的版本信息
  • docker info:用於查看 docker 的配置信息
  • docker help:用於查看幫助信息

3.2 鏡像相關命令

1. docker search 鏡像名

從官方鏡像倉庫 Docker Hub 查找指定名稱的鏡像。經常使用參數爲 --no-trunc,表明顯示完整的鏡像信息。

2. docker images

列出全部頂層鏡像的相關信息。經常使用參數以下:

  • -a :顯示全部鏡像,包括中間隱藏的鏡像
  • -q :只顯示鏡像 ID
  • --digests :顯示摘要信息
  • --no-trunc :顯示完整鏡像信息

3. docker pull 鏡像名 [:TAG]

從官方倉庫下載鏡像,:TAG 爲鏡像版本,不加則默認下載最新版本。

4. docker rmi 鏡像名或ID [:TAG]

刪除指定版本的鏡像,不加 :TAG 則默認刪除鏡像的最新版本。若是有基於該鏡像的容器存在,則該鏡像沒法直接刪除,此時可使用參數 -f,表明強制刪除。rmi 命令支持批量刪除,多個鏡像名之間使用空格分隔。若是想要刪除全部鏡像,則可使用命令 docker rmi -f $(docker images -qa)

3.3 容器相關命令

1. docker run [OPTIONS] IMAGE [COMMAND] [ARG...]

run 是 docker 中最爲核心的一個命令,用於新建並啓動容器,其擁有衆多可用參數,可使用 docker run --help 查看全部可用參數。經常使用參數以下:

  • -i :表示使用交互模式,始終保持輸入流開放;
  • -t :表示分配一個僞終端,一般和 -i 結合使用,表示使用僞終端與容器進行交互;
  • -d :之後臺方式運行容器;
  • --name :指定容器啓動容器的名字,若是不指定,則由 docker 隨機分配;
  • -c :用於給運行在容器中的全部進程分配 CPU 的 shares 值,這是一個相對權重,實際的處理速度與宿主機的 CPU 相關;
  • -m :用於限制爲容器中全部進程分配的內存總量,以 B、K、M、G 爲單位;
  • -v :掛載數據卷 volume,能夠用多個 -v 參數同時掛載多個 volume。volume 的格式爲:[host-dir]:[container-dir]:[rw:ro][rw:ro] 用於指定數據卷的模式,rw 表明讀寫模式,ro 表明只讀模式。
  • -p :用於將容器的端口暴露給宿主機的端口,格式爲:hostPort:containerPort ,經過端口的暴露,可讓外部主機可以訪問容器內的應用。

2. docker ps [OPTIONS]

列出當前全部正在運行的容器。經常使用參數以下:

  • -a :列出全部容器,包括運行的和已經中止的全部容器
  • -n :顯示最近建立的 n 個容器
  • -q :只顯示容器編號
  • --no-trunc :不要截斷輸出信息

3. 啓動\重啓\中止\強制中止容器

與容器啓動、中止相關的命令爲:docker start|restart|stop|kill 容器名或ID ,start 命令用於啓動已有的容器,restart 用於重啓正在運行的容器,stop 用於中止正在運行的容器,kill 用於強制中止容器。

4. 進入正在運行的容器

想要進入正在運行的容器,與容器主進程交互,有如下兩種經常使用方法:

  • docker attach 容器名或ID
  • docker exec -it 容器名或ID /bin/bash

5. 退出容器

想要退出正在運行的容器,有如下兩種經常使用方法:

  • exit :退出並中止容器;
  • ctrl+P+Q :退出。

6. docker rm 容器名或ID

刪除已中止的容器,經常使用參數爲-f,表示強制刪除容器,即使容器還在運行。想要刪除全部容器,可使用 docker rm -f $(docker ps -aq) 命令。

7. 查看容器信息

可使用 docker inspect [OPTIONS] NAME|ID [NAME|ID...] 查看容器或者鏡像的詳細信息,想要查看指定的信息,可使用 -- format 參數來指定輸出的模板格式,示例以下:

docker inspect --format='{{.NetworkSettings}}'  32cb3ace3279
複製代碼

8. 查看容器運行日誌

可使用 docker logs [OPTIONS] CONTAINER 查看容器中進程的運行日誌,經常使用參數以下:

  • --details :顯示日誌詳情
  • -f :跟隨日誌輸出顯示
  • --tail :從末尾開始顯示指定行的數據
  • -t :顯示時間戳
  • --since :開始時間
  • --until : 結束時間

4、DockerFile

dockerfile 是 Docker 用來構建鏡像的文本文件,包含自定義的指令和格式,能夠經過 build 命令從 dockerfile 中構建鏡像,命令格式爲:docker build [OPTIONS] PATH | URL | -

dockerfile 描述了組裝鏡像的步驟,其中每條指令都是單獨執行的。除了 FROM 指令,其餘的每一條指令都會在上一條指令所生成鏡像的基礎上執行,執行完後會生成一個新的鏡像層,新鏡像層覆蓋在原來的鏡像之上從而造成新的鏡像。爲了提升鏡像構建的速度, Docker Daemon 會緩存構建過程當中的中間鏡像。在構建鏡像時,它會將 dockerfile 中下一條指令和基礎鏡像的全部子鏡像作比較,若是有一個子鏡像是由相同的指令生成的,則命中緩存,直接使用該鏡像,而不用再生成一個新的鏡像。經常使用指令以下:

1. FROM

FROM 指令用於指定基礎鏡像,所以全部的 dockerfile 都必須使用 FROM 指令開頭。FROM 指令能夠出現屢次,這樣會構建多個鏡像,每一個鏡像建立完成後,Docker 命令行界面會輸出該鏡像的 ID。經常使用指令格式爲:FROM <image>[:<tag>] [AS <name>]

2. MAINTAINER

MAINTAINER 指令能夠用來設置做者名稱和郵箱,目前 MAINTAINER 指令被標識爲廢棄,官方推薦使用 LABEL 代替。

3. LABEL

LABEL 指令能夠用於指定鏡像相關的元數據信息。格式爲:LABEL <key>=<value> <key>=<value> <key>=<value> ...

4. ENV

ENV 指令用於聲明環境變量,聲明好的環境變量能夠在後面的指令中引用,引用格式爲 $variable_name${variable_name} 。經常使用格式有如下兩種:

  • ENV <key> <value> :用於設置單個環境變量;
  • ENV <key>=<value> ... :用於一次設置多個環境變量。

5. EXPOSE

EXPOSE 用於指明容器對外暴露的端口號,格式爲:EXPOSE <port> [<port>/<protocol>...] ,您能夠指定端口是偵聽 TCP 仍是 UDP,若是未指定協議,則默認爲 TCP。

6. WORKDIR

WORKDIR 用於指明工做目錄,它能夠屢次使用。若是指明的是相對路徑,則它將相對於上一個WORKDIR指令的路徑。示例以下:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd # 此時pwd爲:/a/b/c
複製代碼

7. COPY

COPY 指令的經常使用格式爲:COPY <src>... <dest>,用於將指定路徑中的文件添加到新的鏡像中,拷貝的目標路徑能夠不存在,程序會自動建立。

8. ADD

ADD 指令的經常使用格式爲:COPY <src>... <dest>,做用與 COPY 指令相似,但功能更爲強大,例如 Src 支持文件的網絡地址,且若是 Src 指向的是壓縮文件,ADD 在複製完成後還會自動進行解壓。

9. RUN

RUN 指令會在前一條命令建立出的鏡像基礎上再建立一個容器,並在容器中運行命令,在命令結束後提交該容器爲新的鏡像。它支持如下兩種格式:

  • RUN <command>shell 格式)
  • RUN ["executable", "param1", "param2"] (exec 格式)

使用 shell 格式時候,命令經過 /bin/sh -c 運行,而當使用 exec 格式時,命令是直接運行的,容器不調用 shell 程序,這意味着不會發生正常的 shell 處理。例如,RUN ["echo","$HOME"] 不會對 $HOME 執行變量替換,此時正確的格式應爲:RUN ["sh","-c","echo $HOME"]。下面的 CMD 指令也存在一樣的問題。

10. CMD

  • CMD ["executable","param1","param2"] (exec 格式, 首選)
  • CMD ["param1","param2"] (做爲 ENTRYPOINT 的默認參數)
  • CMD command param1 param2 (shell 格式)

CMD 指令提供容器運行時的默認值,這些默認值能夠是一條指令,也能夠是一些參數。一個 dockerfile 中能夠有多條 CMD 指令,但只有最後一條 CMD 指令有效。CMD 指令與 RUN 指令的命令格式相同,但做用不一樣:RUN 指令是在鏡像的構建階段用於產生新的鏡像;而 CMD 指令則是在容器的啓動階段默認將 CMD 指令做爲第一條執行的命令,若是用戶在 docker run 時指定了新的命令參數,則會覆蓋 CMD 指令中的命令。

11. ENTRYPOINT

ENTRYPOINT 指令 支持如下兩種格式:

  • ENTRYPOINT ["executable", "param1", "param2"] (exec 格式,首先)
  • ENTRYPOINT command param1 param2 (shell 格式)

ENTRYPOINT 指令 和 CMD 指令相似,均可以讓容器在每次啓動時執行相同的命令。但不一樣的是 CMD 後面能夠是參數也能夠是命令,而 ENTRYPOINT 只能是命令;另外 docker run 命令提供的運行參數能夠覆蓋 CMD,但不能覆蓋 ENTRYPOINT ,這意味着 ENTRYPOINT 指令上的命令必定會被執行。以下 dockerfile 片斷:

ENTRYPOINT ["/bin/echo", "Hello"]
CMD ["world"]
複製代碼

當執行 docker run -it image 後,此時輸出爲 hello world,而當你執行 docker run -it image spring ,此時 CMD 中的參數會被覆蓋,此時輸出爲hello spring

5、案例

5.1 基於 Centos 鏡像部署 Spring Boot 項目

生產環境中的大多數項目一般都部署在 Linux 服務器上,這裏咱們從基礎的 Linux 鏡像開始,並將咱們的項目(這裏以 Spring Boot 項目爲例)一塊兒打包構建成爲一個完整的可執行的鏡像。首先須要建立Dockerfile文件,其內容以下:

# 以官方倉庫的centos鏡像爲基礎開始建立
FROM centos
# 做者信息
MAINTAINER  heibaiying@heibaiying.com
 # 把JDK安裝包拷貝到容器中並自動進行解壓
ADD jdk-8u211-linux-x64.tar.gz /usr/java/
# 拷貝項目Jar包到容器中
COPY spring-boot-base.jar  /usr/app/
# 配置Java環境變量
ENV JAVA_HOME /usr/java/jdk1.8.0_211
ENV JRE_HOME ${JAVA_HOME}/jre
ENV CLASSPATH .:${JAVA_HOME}/lib:${JRE_HOME}/lib
ENV PATH ${JAVA_HOME}/bin:$PATH
# 項目啓動命令
ENTRYPOINT ["java", "-jar", "/usr/app/spring-boot-base.jar"]
複製代碼

將 JDK 安裝包,Spring Boot 項目的 Jar 包以及 Dockerfile 文件放在同一個目錄,而後執行下面鏡像構建命令:

docker build -t spring-boot-base-java:latest .
複製代碼

鏡像構建完成後,可使用如下命令進行啓動:

docker run -it  -p 8080:8080 spring-boot-base-java
複製代碼

這裏爲了觀察到啓動效果,因此使用交互的方式啓動,實際部署時可使用-d參數來後臺啓動,輸出以下:

5.2 基於 JDK 鏡像部署 Spring Boot 項目

上面的項目咱們是基於最基礎的 Centos 鏡像開始構建,但因爲 Docker Hub 上已經提供了 JDK 的鏡像,咱們也能夠選擇從 JDK 鏡像開始構建,此時構建過程更加簡單。構建步驟和上面的徹底一致,只是 Dockerfile 的內容有所不一樣,以下:

# 因爲只須要運行環境,這裏咱們直接以官方倉庫的jre鏡像爲基礎開始建立
FROM openjdk:8u212-jre
# 做者信息
MAINTAINER  heibaiying@heibaiying.com
 # 拷貝項目Jar包到容器中
COPY spring-boot-base.jar  /usr/app/
# 項目啓動命令
ENTRYPOINT ["java", "-jar", "/usr/app/spring-boot-base.jar"]
複製代碼

參考資料

  1. Docker 官方簡介:docs.docker.com/engine/dock…
  2. Docker CLI 和 Dockerfile 官方文檔: docs.docker.com/reference/
  3. 浙江大學SEL實驗室 . Docker 容器與容器雲(第2版). 人民郵電出版社 . 2016-10
  4. Docker 從入門到實踐:yeasy.gitbooks.io/docker_prac…

更多文章,歡迎訪問 [全棧工程師手冊] ,GitHub 地址:github.com/heibaiying/…

相關文章
相關標籤/搜索