現在安裝 Docker 本身已經不是一件困難的事情。無論是檢視官方說明文件,還是直接讓 AI 根據當前 Linux 環境生成安裝命令,都可以很快完成部署。

真正值得理解的,是 Docker 從什麼軟體源安裝,不同發行版之間有什麼區別,以及 Docker 安裝完成以後,映像應該從哪裡獲取和分發。

Linux 上安裝 Docker 的流程其實非常統一。Ubuntu 和 Debian 使用 APT,RHEL 和 Rocky Linux 使用 DNF,但最終安裝的都是同一組 Docker Engine、CLI、containerd、Buildx 和 Compose 元件。

Linux

Debian / Ubuntu

RHEL / Rocky Linux

APT Repository

DNF Repository

Docker Packages

Docker Engine

Docker CLI

containerd

Buildx

Docker Compose

Linux

Debian / Ubuntu

RHEL / Rocky Linux

APT Repository

DNF Repository

Docker Packages

Docker Engine

Docker CLI

containerd

Buildx

Docker Compose

本文按照這個共同流程介紹 Docker 的安裝,而不是為不同發行版重複寫幾套幾乎相同的教程。

1. Docker 安裝了什麼

我們平時說“安裝 Docker”,實際上會安裝多個元件:

docker-ce
docker-ce-cli
containerd.io
docker-buildx-plugin
docker-compose-plugin

其中,docker-ce 提供 Docker Engine,也就是長期執行在後臺的 dockerd。docker-ce-cli 提供平時使用的 docker 命令,CLI 通過 Docker API 與 daemon 通訊。containerd 負責更底層的 container 生命週期管理,Buildx 提供現代 image 構建能力,而 Compose plugin 用於管理多個 container 組成的服務。

它們之間可以簡單理解為:

Docker API

Docker CLI

Docker Engine

containerd

Container

Docker API

Docker CLI

Docker Engine

containerd

Container

現在 Docker Compose 已經作為 Docker CLI plugin 安裝,因此應該使用docker compose,而不是舊教程中常見的docker-compose,也不再需要通過 pip install docker-compose 安裝 Compose。

如果機器以前安裝過系統倉庫中的 Docker、舊版本 Docker 或其他相容實現,可以先根據實際情況清理衝突軟體包。但如果機器上已經存在正在使用的 Podman workload,不應該為了安裝 Docker 而無腦刪除 Podman。

2. 設定 Docker 軟體源

不同 Linux 發行版安裝 Docker 時,最大的差異其實就是 package manager 和 repository。

Docker Repository

Debian

Ubuntu

RHEL

Rocky Linux

linux/debian

linux/ubuntu

linux/rhel

本文使用 linux/centos

APT

DNF

Docker Repository

Debian

Ubuntu

RHEL

Rocky Linux

linux/debian

linux/ubuntu

linux/rhel

本文使用 linux/centos

APT

DNF

Debian 和 Ubuntu 使用 APT。首先安裝基礎工具並建立 keyring:

sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings

可以從 /etc/os-release 獲取當前發行版:

. /etc/os-release

DISTRO="$ID"
CODENAME="${UBUNTU_CODENAME:-$VERSION_CODENAME}"

對於本文覆蓋的系統:

Ubuntu  -> DISTRO=ubuntu
Debian  -> DISTRO=debian

下載對應的 Docker GPG key:

sudo curl -fsSL \
    "https://download.docker.com/linux/${DISTRO}/gpg" \
    -o /etc/apt/keyrings/docker.asc

sudo chmod a+r /etc/apt/keyrings/docker.asc

然後建立 Docker APT source:

sudo tee /etc/apt/sources.list.d/docker.sources > /dev/null <<EOF
Types: deb
URIs: https://download.docker.com/linux/${DISTRO}
Suites: ${CODENAME}
Components: stable
Architectures: $(dpkg --print-architecture)
Signed-By: /etc/apt/keyrings/docker.asc
EOF

最後更新 package index:

sudo apt update

RHEL 和 Rocky Linux 使用 DNF。首先安裝 repository 管理工具:

sudo dnf install -y dnf-plugins-core

RHEL 可以直接使用 Docker 提供的 RHEL repository:

sudo dnf config-manager --add-repo \
    https://download.docker.com/linux/rhel/docker-ce.repo

Rocky Linux 需要特別注意。

不要根據發行版名稱自行構造:

https://download.docker.com/linux/rocky/

Docker 並沒有提供這樣的獨立 repository。

本文在 Rocky Linux 上使用 Docker 的 CentOS repository:

sudo dnf config-manager --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

Rocky Linux、RHEL 和 CentOS 都屬於 Enterprise Linux 生態,對應的軟體包通常可以看到:

.el9
.el10

例如 Rocky Linux 9 使用 EL9 軟體包,Rocky Linux 10 使用 EL10 軟體包。

設定完成以後,可以檢查 repository 是否正常:

dnf repolist | grep docker
dnf list docker-ce --showduplicates

如果以前錯誤設定過:

download.docker.com/linux/rocky/

可以先刪除對應的 /etc/yum.repos.d/docker-ce.repo,再重新設定。

3. 安裝與基礎設定

repository 設定完成以後,不同發行版之間已經幾乎沒有區別。

Debian 和 Ubuntu 使用:

sudo apt install -y \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin

RHEL 和 Rocky Linux 使用:

sudo dnf install -y \
    docker-ce \
    docker-ce-cli \
    containerd.io \
    docker-buildx-plugin \
    docker-compose-plugin

安裝完成以後啟動 Docker:

sudo systemctl enable --now docker

檢查版本:

docker --version
docker compose version

然後執行測試 container:

sudo docker run --rm hello-world

這條命令已經完成了一次最簡單的 Docker 工作流:

docker run

本地没有 Image 时 pull

Create + Start

Docker Client

Docker Engine

Docker Registry

Image

Container

docker run

本地没有 Image 时 pull

Create + Start

Docker Client

Docker Engine

Docker Registry

Image

Container

Docker daemon 預設需要 root 權限。如果希望普通使用者直接執行 docker:

sudo usermod -aG docker "$USER"

重新登入 Shell 後即可:

docker ps

需要注意,能夠訪問 Docker daemon 的使用者實際上擁有非常高的系統權限,因此不要隨意把不可信使用者加入 docker group。

Docker daemon 的主要設定檔是:

/etc/docker/daemon.json

預設資料目錄是:

/var/lib/docker

如果系統盤比較小,或者機器有單獨的資料盤,可以修改:

{
  "data-root": "/data/docker"
}

然後:

sudo systemctl restart docker

如果預設磁碟空間足夠,則沒有必要為了“最佳化”而修改 Docker 資料目錄。

4. Docker Registry 與映像分發

Docker 安裝完成以後,下一個經常遇到的問題就是:

Docker image 從哪裡來?

例如:

docker pull nginx

實際上是在從 Registry 下載 image。

一個完整的 image reference 可以寫成:

registry.example.com/project/nginx:1.0

其中包含 Registry、repository 和 tag。

Registry

Repository

registry.example.com

project/nginx

1.0

Registry

Repository

registry.example.com

project/nginx

1.0

Docker Hub 本身就是一個公共 Registry。

如果只有一臺伺服器,直接使用公共 Registry 一般已經足夠。但如果有多臺伺服器、CI/CD、內網環境或者公共 Registry 訪問不穩定,就可以自己部署 Registry。

docker push

docker pull

docker pull

docker pull

Developer / CI

Private Registry

Server A

Server B

Server C

docker push

docker pull

docker pull

docker pull

Developer / CI

Private Registry

Server A

Server B

Server C

最簡單的測試方式是:

docker run -d \
    --name registry \
    --restart always \
    -p 5000:5000 \
    registry:3

然後可以測試 push:

docker pull nginx:latest

docker tag nginx:latest \
    localhost:5000/nginx:latest

docker push \
    localhost:5000/nginx:latest

這裡的 docker tag 並不是重新複製 image,而是給現有 image 增加一個新的 reference。reference 中的 Registry hostname 決定 docker push 應該把 image 上傳到哪裡。

長期執行時,應該把 Registry 資料持久化:

services:
  registry:
    image: registry:3
    container_name: registry
    restart: unless-stopped
    ports:
      - "127.0.0.1:5000:5000"
    volumes:
      - /data/registry:/var/lib/registry

然後:

docker compose up -d

這裡故意只監聽:

127.0.0.1:5000

因為一個沒有 TLS 和認證的 Registry 不應該直接暴露到公網。

如果只是偶爾向另一臺機器傳一個 image,也沒有必要一定部署 Registry。可以直接:

docker save -o nginx.tar nginx:latest
rsync -avP nginx.tar server:/tmp/
docker load -i /tmp/nginx.tar

兩種方式的區別可以簡單理解為:

否

是

需要传输 Docker Image

是否长期、多机器使用?

docker save / load

Private Registry

否

是

需要传输 Docker Image

是否长期、多机器使用?

docker save / load

Private Registry

5. 使用 Nginx 為 Registry 提供 HTTPS

如果 Registry 需要被其他伺服器長期訪問,應該使用 HTTPS,並且設定訪問控制。

一種比較容易維護的結構是:

HTTPS

HTTP

Persistent Data

Docker Client

Nginx

registry.example.com:443

Docker Registry

127.0.0.1:5000

/data/registry

HTTPS

HTTP

Persistent Data

Docker Client

Nginx

registry.example.com:443

Docker Registry

127.0.0.1:5000

/data/registry

Registry 本身只監聽本機:

127.0.0.1:5000

域名、TLS certificate 和公網入口全部交給 Nginx。

例如:

server {
    listen 443 ssl;
    server_name registry.example.com;

    ssl_certificate \
        /etc/letsencrypt/live/registry.example.com/fullchain.pem;
    ssl_certificate_key \
        /etc/letsencrypt/live/registry.example.com/privkey.pem;

    client_max_body_size 0;

    location / {
        proxy_pass http://127.0.0.1:5000;

        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_read_timeout 900;
        proxy_request_buffering off;
    }
}

client_max_body_size 0 可以避免 Docker image layer 因普通 Web 上傳限制而失敗。

如果 Nginx 本身也執行在 Docker 中,則不能再使用:

proxy_pass http://127.0.0.1:5000;

因為 container 內部的 127.0.0.1 指向 Nginx container 自己。

這時應該讓 Nginx 和 Registry 位於同一個 Docker network,並通過 service name 訪問:

proxy_pass http://registry:5000;

結構變成:

Docker Network

HTTPS

HTTP

Docker Client

Nginx Container

Registry Container

registry:5000

/data/registry

Docker Network

HTTPS

HTTP

Docker Client

Nginx Container

Registry Container

registry:5000

/data/registry

只有 HTTPS 還不夠。如果 Registry 可以從公網訪問,還應該設定認證。

可以在 Nginx 上增加 Basic Authentication:

location / {
    auth_basic "Docker Registry";
    auth_basic_user_file /etc/nginx/registry.htpasswd;

    proxy_pass http://127.0.0.1:5000;

    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    proxy_read_timeout 900;
    proxy_request_buffering off;
}

Docker client 登入:

docker login registry.example.com

之後就可以:

docker tag nginx:latest \
    registry.example.com/nginx:latest

docker push \
    registry.example.com/nginx:latest

其他伺服器:

docker login registry.example.com

docker pull \
    registry.example.com/nginx:latest

對於公網 Registry,不建議為了方便使用 HTTP insecure-registry。正常跨機器使用應該優先設定 HTTPS。

6. 總結

Linux 上安裝 Docker,本質上並不存在幾套完全不同的流程。

Ubuntu 和 Debian 使用 APT,RHEL 和 Rocky Linux 使用 DNF,真正有差異的主要是 repository 和 package manager:

单机

离线传输

长期、多服务器

配置 Docker Repository

APT

Debian / Ubuntu

DNF

RHEL / Rocky Linux

安装 Docker Packages

启动 Docker Engine

配置用户权限

获取和分发方式?

Public Registry

docker save / load

Private Registry

单机

离线传输

长期、多服务器

配置 Docker Repository

APT

Debian / Ubuntu

DNF

RHEL / Rocky Linux

安装 Docker Packages

启动 Docker Engine

配置用户权限

获取和分发方式?

Public Registry

docker save / load

Private Registry

Debian 和 Ubuntu 可以直接使用各自對應的 Docker APT repository。RHEL 可以使用 Docker 的 RHEL repository。Rocky Linux 沒有獨立的 linux/rocky Docker repository,本文使用 CentOS Docker repository 作為相容性安裝方案。

Docker 安裝完成以後,如果只是單機執行應用,公共 Registry 通常已經足夠;如果只是偶爾離線傳輸 image,docker save/load 更簡單;如果存在 CI/CD、多伺服器部署或者長期的映像分發需求,則可以部署自己的 Registry,並通過 Nginx 提供 HTTPS 和認證。

到這裡解決的是 Docker 基礎設施層面的問題:

如何安裝 Docker,以及 Docker image 如何獲取、儲存和分發。

至於 image、container、volume、bind mount、network、埠對映和 Docker Compose 的工作方式,則更適合作為另一篇文章單獨介紹。