Docker

**Docker** 是一個開放原始碼的開放平臺軟體，用於開發應用、交付（shipping）應用和執行應用。Docker 允許使用者將基礎設施（Infrastructure）中的應用單獨分割出來，形成更小的顆粒（容器），從而提高交付軟體的速度。

**Docker 容器**與虛擬機器類似，但二者在原理上不同。容器是將作業系統層虛擬化，虛擬機器則是虛擬化硬體，因此容器更具有可攜式性、更能高效地利用伺服器。 容器更多的用於表示軟體的一個標準化單元。由於容器的標準化，因此它可以無視基礎設施（Infrastructure）的差異，部署到任何一個地方。另外，Docker 也為容器提供更強的業界的隔離相容。

**Docker** 利用 Linux 核心中的資源分離機制，例如 cgroups，以及 Linux 核心命名空間（namespaces），來建立獨立的容器（containers）。這可以在單一 Linux 實體下運作，避免啟動一個虛擬機器造成的額外負擔。Linux 核心對命名空間的支援完全隔離了工作環境中應用程式的視野，包括行程樹、網路、使用者 ID 與掛載檔案系統，而核心的 cgroup 提供資源隔離，包括 CPU、記憶體、block I/O 與網路。從 0.9 版本起，Dockers 在使用抽象虛擬是經由 libvirt 的 LXC 與 systemd - nspawn 提供介面的基礎上，開始包括 libcontainer 函式庫做為以自己的方式開始直接使用由 Linux 核心提供的虛擬化的設施。

## 基礎架構

專業名詞 Docker 有兩個意思：

* 代指整個 Docker 專案。
* 代指 Docker 引擎。

### Docker 引擎

**Docker 引擎**(Docker Engine) 是一個伺服器端 - 客戶端結構的應用，主要有這些部分：Docker 守護行程、Docker Engine API（頁面存檔備份，存於網際網路檔案館）、Docker 客戶端。

* **Docker 守護行程**(Docker daemons)，也叫 `dockerd` ，是一個持久化的行程，使用者管理容器。守護行程會監聽 Docker Engine API（頁面存檔備份，存於網際網路檔案館） 的請求。
* **Docker Engine API**（頁面存檔備份，存於網際網路檔案館）是用於與 Docker 守護行程互動用的的 API。它是一個 RESTful API，因此它不僅可以被 Docker 客戶端呼叫，也可以被 `wget` 和 `curl` 等命令呼叫。
* **Docker 客戶端**，也叫 `docker`，是大部分使用者與 Docker 互動的主要方式。使用者通過客戶端將命令傳送給守護行程。命令會遵循 Docker Engine API （頁面存檔備份，存於網際網路檔案館） （頁面存檔備份，存於網際網路檔案館）（頁面存檔備份，存於網際網路檔案館）

### Docker 註冊中心

Docker 註冊中心 (Docker registry) 是用於儲存 Docker 的鏡像。Docker Hub 是一個公共的註冊中心，任何人都可以使用，預設組態下，Docker 將會在這裡尋找鏡像。

另外，使用者可以自行構建私有註冊中心。Docker Datacenter (DDC) 的使用者，可以直接使用 Docker Trusted Registry (DTR)。

### 物件

Docker 的物件是指 Images、Containers、Networks、Volumes、Plugins 等等。

* **容器**（Containers）是鏡像的可執行的實例。容器可通過 API 或 CLI（命令列）進行操控。
* **鏡像**（Images）是一個唯讀模板，用於指示建立容器。 鏡像分層 (layers) 構建的，而定義這些層次的檔案叫 **Dockerfile**。
* **服務**（Services）允許使用者跨越不同的 Docker 守護行程（Docker daemons）的情況下增加容器，並將這些容器分為管理者（managers）和工作者（workers），讓他們為 swarm 共同工作。

## 擴充架構

### Docker Compose

Compose 可譯為組合物。Compose 是用於定義和執行 多個容器 Docker 應用程式 的工具。通過 Compose，你可以使用 YAML 檔案來組態應用程式需要的所有服務，然後通過使用一個命令，就可以建立並啟動所有服務。Compose 對應的命令為 `docker-compose`。

### Swarm Mode

當說到 Docker Swarm 時，一般是指單獨專案 Docker Swarm。而在 Docker 1.12 時，將 swarm mode 整合到 Docker 引擎中，可用 Docker 引擎 API 和 CLI 命令直接使用。官方推薦使用者使用整合的 swarm mode 。

Swarm Mode 內建 kv 儲存功能，提供了眾多的新特性，比如：具有容錯能力的去中心化設計、內建服務發現、負載均衡、路由網格、動態伸縮、滾動更新、安全傳輸等。使得 Docker 原生的 `Swarm` 叢集具備與 Mesos、Kubernetes 競爭的實力。

cluster (中文：叢集)，Docker 將叢集定義為：一群共同作業並提供高可用性的機器 。swarm (中文：群)，是指一個叢集的 Docker 引擎以 swarm mode 形式執行。swarm mode 是指 Docker 引擎內嵌的叢集管理和編排功能。當你初始化了一個 swarm (cluster) 或者將節點加入一個 swarm 時，其 Docker 引擎就會以 swarm mode 的形式執行。

#### 原理

swarm 中的 Docker 機器中分為 managers（管理者） 和 workers（員工），管理者用於處理叢集的關係和委派，員工則用於執行 swarm 服務。 當你建立 swarm 服務時，你可以為其增加各種額外的狀態（如：數量、網路、埠、儲存資源等等）。Docker 會去維持使用者想要的狀態。如：一個工作節點如果掛了，那麼 Docker 會去把這個節點的任務給另外一個節點。此處的任務（task）是指：被 swarm 管理者管理的一個執行中的容器。

swarm 服務比單獨容器好在，修改 swarm 服務的組態之後不用重新啟動。同時，Docker 以 swarm mode 形式執行時，也可以選擇直接啟動單獨的容器。另外，swarm mode 下，你也可以通過 `docker stack deploy` 使用 Compose file 部署應用棧。 swarm 服務分為兩種，一種是 replicated services ，可以指定節點任務的總數量；global services，則是每個節點都會執行一個指定任務。 swarm 管理員使用 ingress 負載均衡使服務可被外部接觸。 swarm 管理員會自動地給服務分配 PublishedPort（或者手動組態）。外部組件，如雲負載均衡器能通過叢集中任何節點上的 PublishedPort 去接入服務（不管該服務是否啟動）。另外 swarm mode 有內部 DNS 組件，它會為每個服務分配一個 DNS 條目。swarm 管理員使用 internal load balancing 去分發請求時，就是依靠這個 DNS 組件。

swarm mode 的功能是由 swarmkit (一個獨立專案) 提供的，它實現了 Docker 的編排層。swarm 可以直接被 Docker 使用。

## 檔案格式

Docker 有兩種檔案格式，Dockerfile 和 Compose file。Dockerfile 定義了單個容器的內容和啟動時候的行為。Compose file 定義了一個多容器應用。

### Dockerfile

Docker 可以依照 Dockerfile 的內容，自動化地構建鏡像。 Dockerfile 是包含著使用者想要如何構建鏡像的所有命令的文字。

關鍵詞：

* `RUN`。`RUN` 會在當前鏡像的頂層上添加新的一層 (layer)，並在該層上執行命令，執行結果將會被提交。提交後的結果將會用於 `Dockerfile` 的下一步。
* `ENTRYPOINT`，入口點。`ENTRYPOINT` 允許你組態容器，使之成為可執行程式。即，`ENTRYPOINT` 允許你為容器增加一個入口點。`ENTRYPOINT` 和 `CMD` 類似，均在容器啟動時執行，但是 `ENTRYPOINT` 為了提供穩定且不可被覆蓋的操作。通過在命令列中指定 `--entrypoint 命令` 的方式，可在執行時將 Dockerfile 檔案中的 `ENTRYPOINT` 覆蓋。
* `CMD`，是 command 的縮寫。`CMD` 用於為已建立的鏡像提供預設的操作，當不想要用預設操作時候，可用 `docker run IMAGE[:TAG|@DIGEST] [COMMAND]` 進行替換 。但當 Dockerfile 擁有入口點時，`CMD` 用於賦予入口點參數。

### Compose 檔案

Compose 檔案 是一個 YAML 檔案，定義了服務（service）、網路、卷（volume）。

* **服務（service）** 定義 各容器的組態，定義內容將以命令列參數的方式 傳給 `docker run` 命令。
* **網路（network）** ，類似地，將定義內容傳給 `docker network create` 命令 。
* **卷（volume）** ，類似地，將定義內容傳給 `docker volume create` 命令。

`docker run` 命令中有一些選項，和 Dockerfile 檔案中的指令效果一樣（如：`CMD`, `EXPOSE`, `VOLUME`, `ENV`），如果 Dockerfile 檔案中使用這些指令，那麼這些指令就會被視為預設參數，所以開發者無需特意在 Compose 檔案中再指定一次。

Compose 檔案 可使用 Shell 變數（Variable），如：

Compose 檔案 可通過自身的 `ARGS` 變數，將參數傳給 Dockerfile 的 `ARGS` 指令。

## 網路

參考文件：Docker 文件 - 網路概要（頁面存檔備份，存於網際網路檔案館）

### bridge

在 Docker 裡，網橋網路 使用的是 軟體形式的網橋。使用相同的網橋的容器連接進入該網路，而非該網路的容器刷故無法接入。Docker 網橋驅動會自動地在 Docker 主機上安裝規則，這些規則讓不同橋接網路之間不能直接通信。 橋接經常用於：在單獨容器上執行應用時，可通過 網橋 進行通信。 網橋網路 適用於容器執行在相同地 Docker 守護行程的主機上。不同 Docker 守護行程主機上的容器，它們之間的通信需要依靠作業系統層次的路由，或者你應該使用 overlay 網路 進行代替。

`bridge` 是網橋驅動，是 Docker 預設的網路驅動（介面名為 `docker0`），當你不為容器指定一個網路時候，Docker 將會使用該驅動。 可通過 `daemon.json` 檔案修改相關組態。

自訂網橋可通過 `brctl` 命令進行組態。

### host

主機模式

`host` 用於單獨容器，該網路下容器只能和 Docker 主機進行直接連接。`host` 只適用於 Docker 17.06 或以上版本的 swarm 服務。

host 網路和 VirtualBox 的 僅主機網路（Host-only Networking） 類似。

### overlay

`overlay` （中文：覆蓋網路）網路驅動將會建立分散式網路，該網路可以覆蓋若干個 Docker 守護行程主機。該網路是基於 主機特定網路（host-specific networks），允許 swarm 服務 和 容器 進行安全通信（當加密功能開啟時）。在該網路下，Docker 能夠清晰地掌握 封包的路由 以及 傳送接收容器。

`overlay` 有兩種網路類型網路：

* `ingress` 網路，可掌控 swarm 服務 的網路流量 。該網路是 `overlay` 的預設網路。
* `docker_gwbridge` 網路是 網橋網路。該網路會將 單獨的 Docker 守護行程 連接至 swarm 里的另外一個守護行程。

在 `overlay` 網路下，單獨的容器 和 swarm 服務 的行為和組態概念 是不一樣的。

該策略不需要 容器們 具有作業系統級別的路由，因為 Docker 負責路由。

### macvlan

`Macvlan` 網路允許你賦予容器 MAC 位址 ，在該網路里，容器會被認為是物理裝置。Docker 守護行程會路由

### none

該策略下，容器不使用任何網路。`none` 常常用於連接自訂網路驅動的情況下。

### 其他

其他第三方外掛程式。

## 資料管理

Docker 預設下，所有檔案將會儲存在容器里的可寫的容器層（container layer）。

* 資料與容器為一體。隨著容器消失，資料將消失；難以與其他程式（容器）共享。
* 由於容器的寫入層是與宿主機器緊緊耦合。所以你難以行動資料到其他機器。
* 容器的寫入層的是通過 儲存驅動（頁面存檔備份，存於網際網路檔案館）（storage driver） 管理檔案系統。儲存驅動（頁面存檔備份，存於網際網路檔案館） 會使用 Linux 核心的 鏈合檔案系統（union filesystem）進行掛載。相比起直接操作於宿主機器檔案系統的 資料卷，這額外的抽象層將會降低效能。

容器有兩種永久化儲存方式：**卷（volumes）** 和 **繫結掛載（bind mounts）** 。另外，Linux 使用者還可使用 tmpfs 進行掛載；Window 使用者還可以使用 **命名管道（named pipe）** 。在容器中，不管是哪種永久化儲存，表現形式都是一樣的。

### 卷

卷（volumes）是宿主機器的檔案系統的一部分，由 Docker 進行管理（ 在 Linux，儲存於 `/var/lib/docker/volumes/`）。非 Docker 程式不應該去修改這些檔案。Docker 推薦使用 卷 進行持久化資料。 卷 可支援 卷驅動（volume drivers），該驅動允許使用者將資料儲存到 遠端主機 或 雲服務商（cloud provider）或 其他。

沒有名字的卷叫匿名卷（anonymous volume），有名字的卷叫命名卷（named volume）。匿名卷沒有明確的名字，當被初始化時，會被賦予一個隨機名字。

### 繫結掛載

繫結掛載（bind mounts）通過將宿主機器的路徑掛載到容器里的這種方式，從而資料持續化，因此繫結掛載可將資料儲存在宿主機器的檔案系統的任何地方。非 Docker 程式可修改這些檔案。 繫結掛載是 Docker 早期就存在的，相比起卷，繫結掛載十分簡單明了。 在開發 Docker 應用時，應使用命名卷（named volume）代替繫結掛載，因為使用者不能對繫結掛載進行 Docker CLI 命令操作。

繫結掛載常用於：

* 同步設定檔，如： 將 宿主主機的 DNS 設定檔（`/etc/resolv.conf`）同步至容器中
* 在開發程式時，將 原始碼 或 Artifact 同步至容器中。 這種用法與 Vagrant 類似。

### tmpfs

`tmpfs` 掛載（tmpfs mounts），僅僅儲存於記憶體中，並不操作 宿主機器的檔案系統（不持久化於磁碟）。它可用於儲存一些 非持久化狀態、敏感資料。 舉例，swarm 服務 通過 `tmpfs` 將 secrets（頁面存檔備份，存於網際網路檔案館）（密碼、金鑰、憑證等）儲存到 swarm 服務。 

### 命名管道

命名管道（named pipes），通過 `npipe` 掛載的形式，使 Docker 主機 和 容器 之間能互相通訊。常見用例是在容器內執行第三方工具，並使用命名管道連接到 Docker Engine API。

### 覆蓋問題

當掛載 空的卷 至一個目錄中，目錄中的內容會被複製於卷中（不會覆蓋）。如果掛載 非空的卷 或 繫結掛載 至一個目錄中，那麼該目錄的內容將會被隱藏（obscured ），當解除安裝後內容將會恢復顯示。

## 紀錄檔

在 UNIX 和類 Unix 系統中，常見的 I/O 流（英語：I/O streams） 分為三種：`STDIN`（輸入 ）、 `STDOUT`（正常輸出）、`STDERR`（錯誤輸出）。

預設組態下，Docker 的紀錄檔（如：`docker logs`、`docker service log`）所記載的是命令列的輸出結果（`STDOUT` 和 `STDERR`）。而 `STDOUT` 和 `STDERR` 對應的檔案路徑分別是 `/dev/stderr` 和 `/dev/stdout`。 另外，也可以在宿主主機上檢視容器的紀錄檔，使用以下命令可以檢視到容器的紀錄檔位置。

## Kubernetes in docker

kind（全稱：Kubernetes IN Docker）是部署本地 Kubernetes 叢集的工具，而叢集的節點是由 Docker 生成的。

## 操作細節

在安裝完 kind 之後，通過 `kind create cluster` 命令生成叢集。 生成 Kubernetes 叢集後，可以通過 Docker 命令進行檢視節點概覽：

## 歷史

Docker 命令在過去的發展中誕生了 40 種以上的命令，過多的命令導致混亂以及難以使用 tab 自動補充，因此在 docker 1.13 版本（2017.1.19）中，命令列被重構，根據邏輯物件進行劃分。官方建議使用者使用新的語法進行操作。

## 已過時

### Docker Swarm

參考 Swarm Mode 章節。

### Docker Machine

Docker Machine 是一個工具，它允許你在虛擬宿主機上安裝 Docker 引擎，並使用 docker-machine 命令管理這些宿主機。你可以使用 Machine 在你原生的 Mac 或 Windows box、公司網路、資料中心、或像 AWS 或 Digital Ocean 這樣的雲提供商上建立 Docker 宿主機。

Docker Machine 最後更新時間是在 0.16.0 (2018-11-08) 版本。官方建議在 1.12 以及之後版本使用 Docker Desktop for Mac 和 Docker Desktop for Windows 進行代替。

### Docker Toolbox

Docker Toolbox 是用於幫 Windows 系統和 Mac 系統 安裝 Docker 環境。新版本建議使用 Docker Desktop for Mac 和 Docker Desktop for Windows 進行安裝。最後更新版本是 19.03.1 (2019-08-01)。

## 安全性爭議

2021 年，安全公司 Palo Alto Networks 研究人員 Aviv Sasson，在 Docker Hub 上發現的惡意容器映像檔，分別來自 10 個不同帳號，總下載次數超過 2000 萬次，其中內含的挖礦軟體。

## 參考文獻

## 外部連結

* 官方網站
* Source code repository（頁面存檔備份，存於網際網路檔案館）
* Multi-tenancy using Docker（頁面存檔備份，存於網際網路檔案館）
* Docker 101 Tutorial（頁面存檔備份，存於網際網路檔案館）
* libcontainer git repo（頁面存檔備份，存於網際網路檔案館）
* 如何在 Linux 下安裝 Docker（頁面存檔備份，存於網際網路檔案館）
* Windows 容器（頁面存檔備份，存於網際網路檔案館） - 在 Windows 10 部署 Docker

## 參見

* 作業系統層虛擬化
* cgroups
* LXC
* Dotcloud