Docker

Docker是一個開放原始碼的容器構建、分發與運行的工具鏈與平台，用於開發、交付和運行應用。Docker允許用戶將基礎設施（Infrastructure）中的應用單獨分割出來，形成更小的顆粒（容器），從而提高交付軟體的速度。

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

Docker 利用Linux核心中的資源分離機制，例如cgroups，以及Linux核心Linux命名空間（namespaces），來建立獨立的容器（containers）。這可以在單一Linux實體下運作，避免啟動一個虛擬機器造成的額外負擔。Linux核心對命名空間的支援完全隔離了工作環境中應用程式的視野，包括進程樹、網路、使用者ID與掛載檔案系統，而核心的cgroup提供資源隔離，包括CPU、記憶體、block I/O與網路。從 0.9 版本開始，Docker 逐步減少對 LXC 的依賴，轉而引入 libcontainer 程序庫，直接調用 Linux 核心提供的命名空間（namespaces）與控制組（cgroups）等機制來創建和管理容器，從而擺脫對傳統容器管理工具的依賴，並實現對容器運行時的完全自主控制。

## 基礎架構

專業名詞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）。

2023年5月，在中國大陸地區被防火長城阻斷屏蔽，同年9月恢復訪問，此後多次斷續屏蔽。

### 對象

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`。

### Docker Desktop

Docker Desktop 是適用於Windows、Linux、MacOS作業系統的一鍵式安裝程序，提供了圖形使用者界面，便於管理Docker的容器、鏡像以及應用等對象。Docker Desktop包含了：Docker Engine、CLI、構建以及擴展等組件。

### Swarm Mode

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

Swarm Mode 內置 kv 存儲功能，提供了眾多的新特性，比如：具有容錯能力的去中心化設計、內置服務發現、負載均衡、路由網格、動態伸縮、滾動更新、安全傳輸等。Swarm 在設計上曾試圖與 Kubernetes 競爭，但目前已逐漸邊緣化，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 是包含著使用者想要如何構建鏡像的所有命令的文本。

```dockerfile
FROM ubuntu:24.04
COPY . /app
RUN make /app
CMD python /app/app.py
```

關鍵詞：

- `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），如：

```yaml
db:
  image: "postgres:${POSTGRES_VERSION}"
```

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

## 網路

參考文檔： Docker文檔-網路概要

### bridge

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

`bridge` 是網橋驅動，是Docker默認的網路驅動（接口名為 `docker0`），當你不為容器指定一個網路時候，Docker將會使用該驅動。可通過 `[https://docs.docker.com/config/daemon/#configure-the-docker-daemon daemon.json]` 文件修改相關配置。

自定義網橋可通過 `brctl` 命令進行配置。

### host

主機模式

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

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

### overlay

`overlay` （中文：覆蓋網絡）網路驅動將會創建分布式網路，該網路可以覆蓋若干個 Docker守護進程主機。該網路是基於 主機特定網路（host-specific networks），允許 swarm服務 和 容器 進行安全通信（當加密功能開啟時）。在該網路下，Docker 通過 VXLAN 等機制實現跨主機容器網路通信

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

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

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

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

### macvlan

`Macvlan`網路配置提供了一種機制，允許單獨的容器具有獨立的MAC地址，使得這些容器在網路上表現得如同物理設備。

### none

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

### 其他

截止2023年5月18日，Docker官方倉庫域名 https://hub.docker.com/ 在中國大陸被屏蔽，方式為DNS污染。隨後短暫解封，2024年6月6日再次被封。此後多次斷續屏蔽。

## 數據管理

Docker默認下，所有文件將會存儲在容器里的可寫的容器層（container layer）。

- 數據與容器為一體。隨著容器消失，數據將消失；難以與其他程序（容器）共享。可以採用掛載文件的方式解決。
- 由於容器的寫入層是與宿主機器緊緊耦合。所以你難以移動數據到其他機器。
- 容器的寫入層的是通過 存儲驅動（storage driver）管理文件系統。存儲驅動 會使用Linux內核的 鏈合文件系統（union filesystem）進行掛載。相比起直接操作於宿主機器文件系統的 數據卷，這額外的抽象層將會降低性能。

容器有兩種永久化存儲方式：卷（volumes）和 綁定掛載（bind mounts）。另外，Linux使用者還可使用 tmpfs 進行掛載；Windows 使用者還可以使用 命名管道（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`。另外，也可以在宿主主機上查看容器的日誌，使用以下命令可以查看到容器的日誌位置。

```console
$ docker inspect --format='{{.LogPath}}' $INSTANCE_ID
```

## Kubernetes in docker

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

## 操作細節

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

```console
$ docker container ls

CONTAINER ID        IMAGE                  COMMAND                  CREATED             STATUS              PORTS                       NAMES

99c96c1f21ab        kindest/node:v1.17.0   "/usr/local/bin/entr…"   4 minutes ago       Up 4 minutes        127.0.0.1:32769->6443/tcp   kind-control-plane
```

## 歷史

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萬次。

## 外部連結

- https://www.docker.com
- Source code repository
- Multi-tenancy using Docker
- Docker 101 Tutorial
- libcontainer git repo
- 如何在Linux下安裝Docker
- Windows 容器 - 在 Windows 10 部署 Docker

## 基本資料

- 作者：Solomon Hykes
- 開發者：Docker, Inc.
- 發行日期：2013年3月13日
- 程式語言：Go
- 作業系統：Linux、Windows、macOS
- 平台：x86-64、ARM、s390x、ppc64le
- 類型：作業系統層虛擬化
- 許可協議：可執行檔：免費增值軟體即服務、原始碼：Apache許可證 2.0