天天看點

Docker 核心概念:鏡像、容器、倉庫,架構核心設計理念

Docker 核心概念:鏡像、容器、倉庫,架構核心設計理念

Docker 的操作圍繞鏡像、容器、倉庫三大核心概念。在學架構設計之前,我們需要先了解 Docker 的三個核心概念。 

Docker 核心概念

鏡像

鏡像是什麼呢?通俗地講,它是一個隻讀的檔案和檔案夾組合。它包含了容器運作時所需要的所有基礎檔案和配置資訊,是容器啟動的基礎。是以你想啟動一個容器,那首先必須要有一個鏡像。鏡像是 Docker 容器啟動的先決條件。

如果你想要使用一個鏡像,你可以用這兩種方式:

自己建立鏡像。通常情況下,一個鏡像是基于一個基礎鏡像建構的,你可以在基礎鏡像上添加一些使用者自定義的内容。例如你可以基于centos鏡像制作你自己的業務鏡像,首先安裝nginx服務,然後部署你的應用程式,最後做一些自定義配置,這樣一個業務鏡像就做好了。

從功能鏡像倉庫拉取别人制作好的鏡像。一些常用的軟體或者系統都會有官方已經制作好的鏡像,例如nginx、ubuntu、centos、mysql等,你可以到 Docker Hub 搜尋并下載下傳它們。

容器

 容器是什麼呢?容器是 Docker 的另一個核心概念。通俗地講,容器是鏡像的運作實體。鏡像是靜态的隻讀檔案,而容器帶有運作時需要的可寫檔案層,并且容器中的程序屬于運作狀态。即容器運作着真正的應用程序。容器有初建、運作、停止、暫停和删除五種狀态。

雖然容器的本質是主機上運作的一個程序,但是容器有自己獨立的命名空間隔離和資源限制。也就是說,在容器内部,無法看到主機上的程序、環境變量、網絡等資訊,這是容器與直接運作在主機上程序的本質差別。

倉庫

Docker 的鏡像倉庫類似于代碼倉庫,用來存儲和分發 Docker 鏡像。鏡像倉庫分為公共鏡像倉庫和私有鏡像倉庫。

目前,Docker Hub 是 Docker 官方的公開鏡像倉庫,它不僅有很多應用或者作業系統的官方鏡像,還有很多組織或者個人開發的鏡像供我們免費存放、下載下傳、研究和使用。除了公開鏡像倉庫,你也可以建構自己的私有鏡像倉庫

 鏡像、容器、倉庫,三者之間的聯系

Docker 核心概念:鏡像、容器、倉庫,架構核心設計理念

可以看到,鏡像是容器的基石,容器是由鏡像建立的。一個鏡像可以建立多個容器,容器是鏡像運作的實體。倉庫就非常好了解了,就是用來存放和分發鏡像的。

了解了 Docker 的三大核心概念,接下來認識下 Docker 的核心架構和一些重要的元件。

Docker 架構

在了解 Docker 架構前,我先說下相關的背景知識——容器的發展史。

容器技術随着 Docker 的出現變得炙手可熱,所有公司都在積極擁抱容器技術。此時市場上除了有 Docker 容器,還有很多其他的容器技術,比如 CoreOS 的 rkt、lxc 等。容器技術百花齊放是好事,但也出現了很多問題。比如容器技術的标準到底是什麼?容器标準應該由誰來制定?

也許你可能會說, Docker 已經成為了事實标準,把 Docker 作為容器技術的标準不就好了?事實并沒有想象的那麼簡單。因為那時候不僅有容器标準之争,編排技術之争也十分激烈。當時的編排技術有三大主力,分别是 Docker Swarm、Kubernetes 和 Mesos 。Swarm 毋庸置疑,肯定願意把 Docker 作為唯一的容器運作時,但是 Kubernetes 和 Mesos 就不同意了,因為它們不希望排程的形式過度單一。

在這樣的背景下,最終爆發了容器大戰,OCI也正是在這樣的背景下應運而生。

OCI全稱為開放容器标準(Open Container Initiative),它是一個輕量級、開放的治理結構。OCI組織在 Linux 基金會的大力支援下,于 2015 年 6 月份正式注冊成立。基金會旨在為使用者圍繞工業化容器的格式和鏡像運作時,制定一個開放的容器标準。目前主要有兩個标準文檔:容器運作時标準 (runtime spec)和容器鏡像标準(image spec)。

正是由于容器的戰争,才導緻 Docker 不得不在戰争中改變一些技術架構。最終形成了下圖所示的技術架構。

Docker 核心概念:鏡像、容器、倉庫,架構核心設計理念

                                                                                Docker 架構圖

我們可以看到,Docker 整體架構采用 C/S(用戶端 / 伺服器)模式,主要由用戶端和服務端兩大部分組成。用戶端負責發送操作指令,服務端負責接收和處理指令。用戶端和服務端通信有多種方式,既可以在同一台機器上通過UNIX套接字通信,也可以通過網絡連接配接遠端通信。

下面逐一介紹用戶端和服務端。

Docker 用戶端

Docker 用戶端其實是一種泛稱。其中 docker 指令是 Docker 使用者與 Docker 服務端互動的主要方式。除了使用 docker 指令的方式,還可以使用直接請求 REST API 的方式與 Docker 服務端互動,甚至還可以使用各種語言的 SDK 與 Docker 服務端互動。目前社群維護着 Go、Java、Python、PHP 等數十種語言的 SDK,足以滿足你的日常需求。

Docker 服務端

Docker 服務端是 Docker 所有背景服務的統稱。其中 dockerd 是一個非常重要的背景管理程序,它負責響應和處理來自 Docker 用戶端的請求,然後将用戶端的請求轉化為 Docker 的具體操作。例如鏡像、容器、網絡和挂載卷等具體對象的操作和管理。

Docker 從誕生到現在,服務端經曆了多次架構重構。起初,服務端的元件是全部內建在 docker 二進制裡。但是從 1.11 版本開始, dockerd 已經成了獨立的二進制,此時的容器也不是直接由 dockerd 來啟動了,而是內建了 containerd、runC 等多個元件。

雖然 Docker 的架構在不停重構,但是各個子產品的基本功能和定位并沒有變化。它和一般的 C/S 架構系統一樣,Docker 服務端子產品負責和 Docker 用戶端互動,并管理 Docker 的容器、鏡像、網絡等資源。

Docker 重要元件

下面,我以 Docker 的 18.09.2 版本為例,看下 Docker 都有哪些工具群組件。在 Docker 安裝路徑下執行 ls 指令可以看到以下與 docker 有關的二進制檔案。

-rwxr-xr-x 1 root root 27941976 Dec 12  2019 containerd
-rwxr-xr-x 1 root root  4964704 Dec 12  2019 containerd-shim
-rwxr-xr-x 1 root root 15678392 Dec 12  2019 ctr
-rwxr-xr-x 1 root root 50683148 Dec 12  2019 docker
-rwxr-xr-x 1 root root   764144 Dec 12  2019 docker-init
-rwxr-xr-x 1 root root  2837280 Dec 12  2019 docker-proxy
-rwxr-xr-x 1 root root 54320560 Dec 12  2019 dockerd
-rwxr-xr-x 1 root root  7522464 Dec 12  2019 runc      

可以看到,Docker 目前已經有了非常多的元件和工具。這裡我不對它們逐一介紹,這裡我先介紹一下 Docker 的兩個至關重要的元件:​

​runC​

​​和​

​containerd​

​。

  • ​runC​

    ​是 Docker 官方按照 OCI 容器運作時标準的一個實作。通俗地講,runC 是一個用來運作容器的輕量級工具,是真正用來運作容器的。
  • ​containerd​

    ​​是 Docker 服務端的一個核心元件,它是從​

    ​dockerd​

    ​中剝離出來的 ,它的誕生完全遵循 OCI 标準,是容器标準化後的産物。​

    ​containerd​

    ​​通過 containerd-shim 啟動并管理 runC,可以說​

    ​containerd​

    ​真正管理了容器的生命周期。
Docker 核心概念:鏡像、容器、倉庫,架構核心設計理念

 通過上圖,可以看到,​

​dockerd​

​​通過 gRPC 與​

​containerd​

​​通信,由于​

​dockerd​

​​與真正的容器運作時,​

​runC​

​​中間有了​

​containerd​

​​這一 OCI 标準層,使得​

​dockerd​

​可以確定接口向下相容。

gRPC​ 是一種遠端服務調用。想了解更多資訊可以參考https://grpc.iocontainerd-shim 的意思是墊片,類似于擰螺絲時夾在螺絲和螺母之間的墊片。containerd-shim 的主要作用是将 containerd 和真正的容器程序解耦,使用 containerd-shim 作為容器程序的父程序,進而實作重新開機 dockerd 不影響已經啟動的容器程序。

了解了 dockerd、containerd 和 runC 之間的關系,下面可以通過啟動一個 Docker 容器,來驗證它們程序之間的關系。

Docker 各元件之間的關系

首先通過以下指令來啟動一個 busybox 容器:

$ docker run -d busybox sleep 3600      

容器啟動後,通過以下指令檢視一下 dockerd 的 PID:

$ sudo ps aux |grep dockerd
root      4147  0.3  0.2 1447892 83236 ?       Ssl  Jul09 245:59 /usr/bin/dockerd      
$ sudo pstree -l -a -A 4147
dockerd

  |-containerd --config /var/run/docker/containerd/containerd.toml --log-level info

  |   |-containerd-shim -namespace moby -workdir /var/lib/docker/containerd/daemon/io.containerd.runtime.v1.linux/moby/d14d20507073e5743e607efd616571c834f1a914f903db6279b8de4b5ba3a45a -address /var/run/docker/containerd/containerd.sock -containerd-binary /usr/bin/containerd -runtime-root /var/run/docker/runtime-runc

  |   |   |-sleep 3600