现在安装 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 的工作方式,则更适合作为另一篇文章单独介绍。