1. 项目概述:为什么Docker是Linux环境下的“标准答案”?

如果你在Linux服务器上折腾过应用部署,大概率经历过“环境依赖地狱”——明明在测试机上跑得好好的服务,换台机器就各种报错,不是缺这个库就是那个版本不对。我最早做运维的时候,最头疼的就是给几十台服务器统一部署一套Java环境,光是处理不同发行版的包管理差异就够喝一壶了。直到Docker出现,它把应用和它所有的依赖、配置、环境打包成一个标准化的“集装箱”,彻底解决了“在我机器上能跑”这个世纪难题。

今天要聊的,就是在Linux环境下从零开始安装部署Docker的完整过程。这不仅仅是敲几条命令那么简单,我会带你理解每一步背后的逻辑,比如为什么官方推荐用仓库安装而不是直接下载deb/rpm包?不同Linux发行版的核心配置差异在哪里?以及那些只有踩过坑才知道的“隐藏”优化项。无论你是刚接触Linux的新手,还是需要为团队搭建标准化开发环境的老鸟,这篇超详细的指南都能让你少走弯路,一次搞定。

2. 核心需求与方案选型:为什么不用二进制包?

在动手之前,我们先明确目标:我们需要的是一个 稳定、易于维护、且能跟随官方及时更新 的Docker环境。Linux上安装软件通常有几种方式:直接用发行版的包管理器(如 apt yum )、下载编译好的二进制文件、或者从源码编译。对于Docker,官方强烈推荐使用其维护的软件仓库进行安装,原因有三点。

2.1 官方仓库 vs 系统仓库:更新的及时性与稳定性

大多数Linux发行版(如Ubuntu、CentOS)自己的软件仓库里也有Docker包,通常叫 docker.io docker-engine 。但这里有个关键问题: 系统仓库的版本更新严重滞后 。比如Ubuntu 22.04 LTS自带的 docker.io 版本可能是一年多前的旧版,你会错过大量重要的安全更新、性能优化和新功能。而Docker官方仓库( download.docker.com )会保持与上游最新稳定版的同步。

另一个核心区别是 包名和组件拆分 。系统仓库的 docker.io 是一个“元包”,它可能包含了较旧的打包逻辑。而官方仓库提供的是 docker-ce (社区版),其组件如 docker-ce-cli containerd.io 等都是独立、清晰分层的,这更符合现代容器生态的架构,也便于分步升级和故障排查。

2.2 二进制安装与源码编译:为什么不推荐?

直接从Docker官网下载静态二进制包,解压就能用,听起来很酷。但这方式 缺少了与系统服务管理器的集成 (如systemd)。你需要手动配置服务文件、环境变量、用户组,后续升级也需要手动替换二进制文件,维护成本很高,容易出错。

至于源码编译,那更是适用于极少数需要深度定制或研究Docker本身的情景。对于99.9%的应用和部署场景,这完全是“杀鸡用牛刀”,耗时耗力且没有必要。

注意 :生产环境部署,务必使用官方仓库安装。这确保了软件来源可信、更新路径明确、并且能无缝接入系统的安全更新机制(如 unattended-upgrades )。这是保障服务长期稳定运行的基础。

2.3 版本选择:CE、EE与Edge

Docker分为社区版(CE)和企业版(EE)。对于绝大多数个人开发者、测试和生产环境, Docker CE完全足够 。它包含了核心的容器引擎、CLI、API等所有必需功能。Docker EE主要提供额外的企业级支持、安全扫描、镜像管理等高级功能,通常与付费订阅绑定。

此外,官方仓库还提供两个发布通道: stable (稳定版)和 edge (边缘版)。 生产环境必须且只能选择 stable 通道 edge 版每月更新,包含实验性功能,可能不稳定,仅适用于尝鲜和测试。

所以,我们的选型结论非常明确: 通过配置Docker官方提供的 stable 通道仓库,使用系统包管理器安装 docker-ce 及相关组件 。这是兼顾了稳定性、可维护性和安全性的最佳实践。

3. 系统准备与依赖清理:打好地基

安装前的准备工作至关重要,这能避免很多因环境不纯净导致的诡异问题。主要分为三步:卸载旧版本、安装基础依赖、配置仓库。

3.1 彻底卸载任何旧版或冲突的Docker

如果你的系统之前装过Docker(无论什么版本),或者有发行版自带的 docker.io ,第一步就是彻底清理。不同发行版的卸载命令不同,但思路一致:用包管理器移除相关包,并手动删除残留的数据和配置。

对于基于Debian/Ubuntu的系统:

sudo apt-get remove docker docker-engine docker.io containerd runc

对于基于RHEL/CentOS/Fedora的系统:

sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

执行移除后,Docker的数据(镜像、容器、卷、网络配置)默认会保留在 /var/lib/docker/ 目录下。如果你确定不需要这些数据,或者这是全新安装,可以手动删除这个目录以释放空间并获得一个绝对干净的环境:

sudo rm -rf /var/lib/docker
sudo rm -rf /var/lib/containerd

警告 rm -rf /var/lib/docker 不可逆 操作,会删除所有本地镜像、容器和数据卷。请务必在确认无需备份后执行。

3.2 安装必要的工具和依赖包

为了让 apt yum 能够通过HTTPS协议访问远程仓库,我们需要先安装一些工具包。

Ubuntu/Debian系统需要:

sudo apt-get update # 首先更新本地软件包索引
sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
  • ca-certificates :让系统信任CA证书,用于HTTPS连接。
  • curl :命令行下载工具,用于获取密钥和仓库配置。
  • gnupg :用于管理GPG密钥,验证软件包签名。
  • lsb-release :一个工具,可以方便地获取发行版信息(如 lsb_release -cs 输出系统代号),在配置仓库地址时会用到。

CentOS/RHEL/Fedora系统需要:

sudo yum install -y yum-utils
  • yum-utils :提供了 yum-config-manager 等工具,它能帮我们轻松地添加和管理yum仓库。

3.3 添加Docker官方GPG密钥与软件仓库

这是保证安装包来源可信且完整的关键步骤。GPG密钥用于验证从仓库下载的软件包签名,确保它们没有被篡改。

对于Ubuntu/Debian:

  1. 创建密钥环目录并添加Docker官方GPG密钥:

    sudo mkdir -p /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    
    • curl -fsSL -f 静默失败, -s 静默模式, -S 显示错误, -L 跟随重定向。组合使用是下载密钥的可靠写法。
    • gpg --dearmor :将下载的ASCII格式密钥转换为二进制格式,供 apt 使用。
    • 输出到 /etc/apt/keyrings/docker.gpg 是当前推荐的位置。
  2. 添加Docker CE稳定版仓库。这里需要一个 关键技巧 :使用 $(lsb_release -cs) 自动获取系统代号(如 jammy ),但某些衍生版(如Linux Mint)可能需要手动指定其对应的Ubuntu基础版代号。

    echo \
      "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
      $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
    
    • [arch=$(dpkg --print-architecture) :自动适配系统架构(amd64, arm64等)。
    • signed-by= :明确指定用于验证的GPG密钥路径。
    • tee 命令将echo的内容同时写入文件并显示在屏幕, > /dev/null 抑制屏幕输出。

对于RHEL/CentOS/Fedora:

  1. 添加yum仓库。同样, $releasever $basearch 是yum的变量,会自动替换为系统版本和基础架构。
    sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
    这条命令会在 /etc/yum.repos.d/ 目录下生成一个 docker-ce.repo 文件,里面已经配置好了稳定版仓库的地址。

4. 安装与验证:核心步骤实操

仓库配置好后,安装过程就变得非常标准化。

4.1 执行安装命令

更新包索引(或缓存)并安装Docker引擎及相关组件。

Ubuntu/Debian:

sudo apt-get update # 这步必须!让apt识别新加的docker仓库
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin
  • docker-ce : Docker社区版引擎。
  • docker-ce-cli : Docker命令行工具。
  • containerd.io : 行业标准的容器运行时,Docker引擎依赖于它。
  • docker-compose-plugin : 这是新的 docker compose (V2版本),作为Docker CLI的一个插件安装,比独立的Python版 docker-compose 更优。

RHEL/CentOS/Fedora:

sudo yum makecache fast # 更新yum元数据缓存
sudo yum install docker-ce docker-ce-cli containerd.io docker-compose-plugin

安装过程中,包管理器会解析依赖并提示需要安装的软件包列表,确认即可。

4.2 启动Docker服务并设置开机自启

安装完成后,Docker服务( docker.service )和containerd服务( containerd.service )并不会自动启动,需要手动开启并设为开机启动。

sudo systemctl start docker        # 立即启动Docker服务
sudo systemctl enable docker       # 设置开机自动启动
sudo systemctl enable containerd   # 同样建议设置containerd开机启动

使用 systemctl status docker 可以查看服务运行状态,确认状态为 active (running)

4.3 至关重要的非root用户配置

默认情况下,Docker守护进程以root用户运行,而Docker CLI通过Unix socket( /var/run/docker.sock )与守护进程通信。该socket文件属于root用户和 docker 用户组。这意味着,要直接使用 docker 命令,你必须要么是root,要么属于 docker 用户组。

将当前用户加入 docker 组是标准做法:

sudo usermod -aG docker $USER
  • -aG -a 表示追加(append), -G 指定附加组。这样不会覆盖用户原有的其他组。
  • $USER :环境变量,代表当前登录用户名。

这里有一个巨大的“坑” :执行此命令后, 需要完全退出当前终端会话并重新登录 ,或者新开一个终端,组权限变更才会生效。很多新手在这一步卡住,发现还是报“权限拒绝”,原因就是没有重新登录。

4.4 运行验证与信息查看

权限生效后,运行经典的“Hello World”容器来验证安装是否成功:

docker run hello-world

这个命令会做几件事:1) 从Docker Hub拉取 hello-world 镜像(如果本地没有);2) 基于该镜像创建并启动一个容器;3) 容器执行一段程序,输出欢迎信息后退出。如果看到“Hello from Docker!”等字样,说明安装、服务、网络都正常。

你还可以查看详细的版本和配置信息:

docker version      # 显示客户端和服务器(引擎)的版本信息
docker info         # 显示更详细的系统级信息,如容器/镜像数量、存储驱动、运行时等

5. 存储驱动与镜像加速:关键配置优化

默认安装的Docker可以工作,但针对生产环境或国内网络环境,有几项配置能极大提升体验和性能。

5.1 理解并选择合适的存储驱动

存储驱动决定了Docker如何在宿主机上存储和管理镜像与容器的文件系统层。不同Linux内核版本和文件系统有对应的推荐驱动。

  • overlay2 现代Linux内核(4.x以上)的默认且首选驱动 。它性能好,稳定性高,几乎适用于所有主流发行版(如Ubuntu 16.04+, CentOS 7.4+,内核需>=3.10.0-693)。如果你的系统支持,无需任何改动,Docker默认就会使用它。
  • devicemapper :在老版本CentOS/RHEL(内核较旧)上可能被默认使用,但需要配置独立的存储池( direct-lvm ),配置复杂且性能一般, 不推荐 ,应尽量升级内核改用 overlay2
  • aufs :Ubuntu早期版本的默认驱动,现在已被 overlay2 取代。

检查当前存储驱动:

docker info | grep "Storage Driver"

如果显示 overlay2 ,那是最好的。如果需要更改驱动(比如从 devicemapper 切换到 overlay2 ),需要编辑Docker守护进程的配置文件 /etc/docker/daemon.json (如果不存在则创建):

{
  "storage-driver": "overlay2"
}

然后重启Docker服务: sudo systemctl restart docker 注意 :切换存储驱动会 导致所有现有本地镜像和容器不可访问 ,务必在全新环境或已备份的情况下操作。

5.2 配置国内镜像加速器

从Docker Hub拉取镜像,在国内速度可能很慢甚至超时。配置一个国内的镜像加速器是必备操作。常见的加速器有阿里云、腾讯云、网易云等,你需要注册相应平台获取专属加速地址。

以阿里云为例(容器镜像服务控制台可获取你的专属地址):

  1. 编辑或创建 /etc/docker/daemon.json
  2. 加入 registry-mirrors 配置项。 重要:如果文件已存在其他配置(如上面的存储驱动),请将新内容合并到同一个JSON对象中,用逗号分隔。
    {
      "registry-mirrors": ["https://your_id.mirror.aliyuncs.com"],
      "storage-driver": "overlay2"
    }
    
  3. 重新加载配置并重启Docker:
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    
  4. 验证加速器是否生效:
    docker info
    
    在输出中查找 Registry Mirrors ,应该能看到你配置的地址。

5.3 调整默认数据目录与Cgroup驱动

默认情况下,Docker的所有数据(镜像、容器等)存放在 /var/lib/docker 。如果系统根分区空间紧张,可以将其迁移到大容量分区。这需要在安装后首次启动Docker前,通过 daemon.json data-root 参数指定,或修改服务启动文件,操作较为复杂,涉及数据迁移,初期不建议新手改动。

另一个高级配置是Cgroup驱动。Linux系统通过Cgroup(控制组)来限制和隔离进程资源。Docker需要与系统的初始化系统(如systemd)使用的Cgroup驱动保持一致。 对于使用systemd且版本较新的发行版,Docker默认会使用 systemd 作为Cgroup驱动,这通常是最佳选择 ,可以通过 docker info | grep Cgroup 确认。除非有特殊需求,否则保持默认即可。

6. 核心组件解析与日常使用入门

安装配置好后,我们来理解一下Docker的核心组件和几个最常用的命令,这是你日后熟练使用的基础。

6.1 Docker架构核心三组件

  1. Docker Daemon(守护进程) :常驻后台的 dockerd 进程,负责管理Docker对象(镜像、容器、网络、卷),并响应Docker CLI的请求。
  2. Docker Client(客户端) :我们使用的 docker 命令就是客户端。它通过REST API与守护进程通信。
  3. Docker Registry(镜像仓库) :存储Docker镜像的地方。最著名的是公共仓库Docker Hub,你也可以搭建私有仓库(如Harbor)。

当我们运行 docker run hello-world 时,流程是:Client -> Daemon -> 检查本地是否有 hello-world 镜像 -> 若无,从Docker Hub拉取 -> Daemon创建容器并运行。

6.2 镜像(Image)基础操作

镜像是只读的模板,包含了运行应用所需的代码、库、环境变量和配置。

  • 拉取镜像 docker pull nginx:latest (拉取最新的Nginx镜像)。
  • 列出本地镜像 docker images docker image ls
  • 删除镜像 docker rmi <image_id> 。如果镜像有容器依赖,需要先删除容器。
  • 导出/导入镜像
    • 导出为文件: docker save -o nginx.tar nginx:latest
    • 从文件导入: docker load -i nginx.tar
    • 这常用于离线环境迁移。

6.3 容器(Container)生命周期管理

容器是镜像的运行实例。

  • 运行容器
    docker run -d --name my_nginx -p 8080:80 nginx:latest
    
    • -d :后台运行。
    • --name :给容器起个名字,否则Docker会分配一个随机名。
    • -p 8080:80 :端口映射,将宿主机的8080端口映射到容器的80端口。
  • 查看容器
    • docker ps :查看正在运行的容器。
    • docker ps -a :查看所有容器(包括已停止的)。
  • 进入运行中的容器 docker exec -it my_nginx /bin/bash -it 是交互式终端,这让你像SSH一样进入容器内部操作。
  • 停止/启动/重启容器
    docker stop my_nginx
    docker start my_nginx
    docker restart my_nginx
    
  • 删除容器 docker rm my_nginx 。删除前需先停止容器,或使用 -f 强制删除运行中的容器(不推荐)。
  • 查看容器日志 docker logs my_nginx ,加 -f 可以实时跟踪日志输出。

7. 常见问题与故障排查实录

即使按照步骤操作,你也可能会遇到一些问题。这里记录了几个最常见的问题和排查思路。

7.1 权限问题: Got permission denied while trying to connect to the Docker daemon socket

这是最常见的问题,原因就是当前用户不在 docker 组里。

  • 解决方案 :执行 sudo usermod -aG docker $USER ,然后 务必注销当前用户并重新登录 ,或者打开一个新的终端窗口。单纯退出终端标签页可能不够,需要完全结束登录会话。
  • 验证 :运行 groups 命令,查看输出中是否包含 docker 组。
  • 临时方案 :在所有 docker 命令前加 sudo ,但这很麻烦且不安全(让普通用户拥有root权限执行docker命令)。

7.2 镜像拉取失败或超时: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection

这几乎都是网络问题,特别是国内访问Docker Hub不稳定。

  • 解决方案 :务必配置国内镜像加速器,具体步骤见第5.2节。配置后记得重启Docker服务。
  • 手动指定镜像源 :拉取时可以使用完整仓库地址,例如从阿里云镜像站拉取: docker pull registry.cn-hangzhou.aliyuncs.com/library/nginx:latest

7.3 端口冲突: Error starting userland proxy: listen tcp4 0.0.0.0:8080: bind: address already in use

这意味着你宿主机上的8080端口已经被其他程序(可能是另一个容器,也可能是系统服务)占用了。

  • 排查 :在宿主机上运行 sudo netstat -tlnp | grep :8080 查看哪个进程占用了8080端口。
  • 解决
    1. 停止占用端口的进程。
    2. 或者,修改 docker run 命令的端口映射,例如改为 -p 8081:80 ,用宿主机的另一个空闲端口。

7.4 服务启动失败: Job for docker.service failed because the control process exited with error code.

Docker服务启动失败,通常是因为 daemon.json 配置文件有语法错误,或者与现有配置冲突。

  • 排查 :使用 sudo systemctl status docker.service sudo journalctl -xe -u docker 查看详细的错误日志。错误信息通常会明确指出问题所在,例如“invalid character”、“unknown configuration option”。
  • 解决 :仔细检查 /etc/docker/daemon.json 文件的JSON格式(可以使用在线JSON校验工具)。确保没有多余的逗号,括号匹配。如果刚修改过此文件,可以尝试先将其移走或重命名,然后重启Docker看默认配置能否启动,以此定位问题。

7.5 磁盘空间不足: No space left on device

Docker频繁拉取镜像、运行容器会产生很多数据层和日志,可能很快占满 /var/lib/docker 所在分区。

  • 查看磁盘使用 docker system df ,这个命令能清晰显示镜像、容器、本地卷和构建缓存占用的空间。
  • 清理无用数据
    • docker image prune :删除所有未被容器引用的悬空镜像( <none> 标签的镜像)。
    • docker system prune -a 危险命令,谨慎使用 。它会删除所有停止的容器、所有未被任何容器使用的网络、所有悬空镜像和构建缓存。加 -a 还会删除所有未被容器引用的镜像(不仅仅是悬空镜像)。执行前务必确认。
  • 根本解决 :规划大容量分区,并修改Docker的 data-root 到新分区。

8. 进阶配置与生产环境考量

当你熟悉基础操作后,为了更安全、更高效地使用Docker,还需要了解一些进阶配置。

8.1 日志驱动与日志管理

默认情况下,容器的标准输出(STDOUT/STDERR)会被Docker捕获,并通过 json-file 驱动存储为JSON文件在宿主机上。长时间运行的容器会产生巨大的日志文件。

  • 查看日志位置 :容器日志默认在 /var/lib/docker/containers/<容器ID>/<容器ID>-json.log
  • 配置日志轮转 :在 /etc/docker/daemon.json 中配置,限制日志文件的大小和数量,防止磁盘被撑爆。
    {
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "10m",
        "max-file": "3"
      }
    }
    
    这表示每个容器日志文件最大10MB,最多保留3个文件(如 xxx-json.log , xxx-json.log.1 , xxx-json.log.2 ),旧的会被自动删除。
  • 其他日志驱动 :对于集中式日志管理,可以配置 syslog journald fluentd 等驱动,将日志直接发送到日志服务器。

8.2 容器资源限制

默认容器可以使用宿主机的所有CPU和内存资源,这可能导致某个容器耗尽资源影响其他容器或宿主机本身。

  • 内存限制 docker run -m 512m --memory-swap=1g nginx -m 限制物理内存为512MB, --memory-swap 限制总内存(物理+交换分区)为1GB。设置 --memory-swap 等于 -m 时,表示禁用交换分区。
  • CPU限制 docker run --cpus=1.5 nginx 。限制容器最多使用1.5个CPU核心的计算能力。更精细的控制可以用 --cpuset-cpus 指定容器可以运行在哪些CPU核心上。
  • 生产建议 :为所有生产容器设置合理的资源限制,这是实现稳定性和公平性的基础。可以通过 docker stats 命令实时查看容器的资源使用情况。

8.3 用户命名空间隔离(User Namespace Remapping)

这是一个重要的安全特性。默认情况下,容器内的root用户映射到宿主机的root用户。如果容器被攻破,攻击者就获得了宿主机的root权限。启用用户命名空间重映射,可以让容器内的root用户映射到宿主机上一个无特权的普通用户,提升安全性。 配置相对复杂,需要在 daemon.json 中指定 userns-remap ,并提前创建好对应的子UID/GID映射文件。由于启用后会影响现有容器的文件权限, 建议仅在规划新环境时初期启用

8.4 使用Docker Compose编排多容器应用

当你的应用需要多个容器协同工作时(比如一个Web应用需要Web服务器、数据库、缓存),手动管理每个容器非常繁琐。Docker Compose(现在作为插件 docker compose )通过一个YAML文件( docker-compose.yml )来定义和运行多容器应用。 一个简单的例子:

version: '3.8'
services:
  web:
    image: nginx:latest
    ports:
      - "80:80"
    depends_on:
      - db
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: example_password
    volumes:
      - db_data:/var/lib/mysql
volumes:
  db_data:

在包含此文件的目录下,只需运行 docker compose up -d ,所有服务就会按依赖关系自动启动。 docker compose down 则会停止并移除所有相关容器、网络。这是管理开发、测试环境乃至简单生产部署的利器。

从最开始的系统准备、仓库配置,到安装验证、核心优化,再到问题排查和进阶安全配置,这套流程覆盖了在Linux上部署Docker的完整生命周期。我个人的体会是,越是生产环境,越要重视前期规划和配置的规范性。比如镜像加速和日志轮转这种配置,一开始就做好,能避免后续很多突发问题。另外,把常用的运维命令(如清理镜像、查看资源)写成脚本或做成alias,能极大提升效率。最后,Docker的世界很大,今天讲的是基石,在这之上,还有容器网络、存储卷、安全扫描、集群编排(如Kubernetes)等更深的领域值得探索。先把单机环境玩熟玩稳,再去挑战更复杂的场景,路会走得扎实很多。

更多推荐