
在 Ubuntu 22.04 jammy 上做 Docker 安装说难不难但每次都能踩出几个新坑。官方文档写得顺滑可真到了生产机器或在新装的云主机上实操GPG 密钥、apt 源签名、docker.sock 权限、镜像拉不动……每一个问题都能卡住一下午。这篇文章是我在多台 Ubuntu 22.04 / jammy 服务器上完成 Docker 安装、配置和排错后整理的完整步骤与问题处理清单适合刚入门 Docker 的同学照着做也适合运维朋友在批量部署时拿来当参考。Docker 在 Ubuntu 上的安装方式有好几条路一开始选错后面全是隐患。下面我先讲清楚为什么我坚持用官方 apt 仓库再给出完整流程然后重点展开常见错误的排查思路。读完这篇文章你应该能自己装出一套干净、稳定、可维护的 Docker 环境。1. 安装前的环境检查与方案选型1.1 为什么我推荐用官方 apt 仓库安装Docker 在 Ubuntu 上其实有好几条安装路线官方 apt 仓库、get.docker.com 的一键脚本、snap 商店的 docker 包还有下载二进制包手动布。我在 Ubuntu 22.04 jammy 上装机首选一定是官方 apt 仓库原因很朴素跟系统包管理器走同一条路装、卸、升级都能用 apt 管理版本对得上依赖关系也清楚。snap 版的 Docker 我不是没用过但路径和权限的问题让人头疼。容器目录、socket 位置都跟标准布局不一致哪怕是简单的 docker compose 项目有时候都会因为 snap 严格的权限隔离机制出现怪毛病。一键脚本虽然快但它往系统里写入了哪些文件、改了哪些 systemd 配置完全不透明出问题时排查起来两眼一抹黑。二进制包手动布则更适合高度定制或完全离线的场景一般用户没必要这么折腾。用 apt 仓库安装就像在应用商店装软件系统知道这个东西装在哪、怎么升级、怎么彻底删除一键脚本更像是朋友给你拷了个绿色安装包双击能用但它在注册表里留了什么东西你可能永远不知道。所以我的建议很直接除非你有强约束否则就跟着下面的官方 apt 仓库流程走。1.2 环境检查与旧版本清理装之前先确认系统确实是 Ubuntu 22.04 LTS jammy不要看着界面像就跳过我见过不少表面写着 22.04、实际内核和 apt 源停留在老版本的模板镜像。cat /etc/os-release lsb_release -aOS 版本确认之后再确认内核版本uname -rDocker 要求 64 位架构、内核 3.10 以上。Ubuntu 22.04 默认内核一般是 5.15 系列只要不是特别老的定制内核都没有问题。如果用 Server 版的精简镜像可能需要自己补装一些基础工具不过 ca-certificates、curl、gnupg 这几个我们稍后会装问题不大。接下来是很多人容易忽略的一步旧版本清理。如果系统里之前装过 docker.io、docker-engine、docker-ce、containerd 或 runc 的任何版本先用 apt 卸载sudo apt remove docker docker-engine docker.io containerd runc这一步的核心是防止新旧两个运行时打架。我遇到过一次很典型的案例某台机器之前初始化过 Kubernetes 集群系统里已经装了 containerd直接跑 Docker 安装命令结果 apt 因为 containerd.io 和已有 containerd 冲突直接报错装不上。所以先清理旧包、再看看有没有其他容器运行时占着位置是非常必要的预检动作。需要特别注意的是apt remove 并不会删除 /var/lib/docker 里的镜像、容器和数据卷。如果确认旧数据都不要了可以手动删掉这个目录和 /var/lib/containerd给新 Docker 一个干净的数据盘。在没确认数据是否需要备份之前千万别手滑。注意/var/lib/docker 和 /var/lib/containerd 是容器数据所在目录删除前务必确认不需要任何旧镜像、容器或卷数据。否则新环境刚起好就发现磁盘被旧数据占了一半又要停下来折腾。2. 官方 apt 仓库安装 Docker 全流程2.1 添加 GPG 密钥与软件源先把依赖工具装上sudo apt update sudo apt install ca-certificates curl gnupg lsb-release这三个依赖各司其职ca-certificates 提供 HTTPS 所需的 CA 证书链curl 用来下载 Docker 的 GPG 密钥gnupg 负责处理和校验密钥。lsb-release 不是装依赖时严格必要的但后面如果要写脚本自动识别发行版代号它会很有用。添加 Docker 官方 GPG 密钥时我建议把密钥文件放到 /etc/apt/keyrings/ 目录并且用 signed-by 参数显式指定sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg这里有两个细节值得解释。第一个为什么不用老的 apt-key 命令因为 apt-key 已经是废弃状态使用它会把密钥写进 /etc/apt/trusted.gpg 这个全局信任数据库里apt update 时会出现 “Key is stored in legacy trusted.gpg keyring” 的警告。把密钥放到独立目录并配合 signed-by 使用只有 docker.list 这个源才会信任这个密钥更安全也更好管理。第二个chmod ar 是为了让所有用户都能读取密钥文件否则 apt 在校验源时会因为没有读权限报错。接着添加软件源。Ubuntu 22.04 的代号是 jammy所以仓库地址里的发行版代号写 jammyecho deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu jammy stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null这里没有硬编码 archamd64而是用 $(dpkg --print-architecture) 动态获取这样在 ARM64 的云服务器或者树莓派上执行也不会出错。写完源之后再跑一次 apt update 让系统索引到 Docker 的软件包sudo apt update走到这里如果顺利docker-ce 等包已经出现在 apt 的可安装列表里。可以用命令验证软件源是否生效apt-cache policy docker-ce如果之前忘了把旧钥匙残留清掉或者命令里的 signed-by 路径写错这一步就会暴露出来最常见的报错放在后面的错误处理章节详细说明。2.2 安装 Docker 引擎、containerd 与 Compose 插件软件源就绪后直接安装sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin安装的是完整一套docker-ce 是 Docker 引擎本体docker-ce-cli 是 docker 命令行工具containerd.io 是底层的容器运行时docker-buildx-plugin 提供 docker buildx 扩展构建能力docker-compose-plugin 提供 docker compose 子命令也就是 Compose V2。有些精简党觉得 compose 和 buildx 用不上可以不装但我建议一次装齐。日常部署中 docker compose 的使用频率非常高一个编排文件把服务、网络、卷全定义好比一条条 docker run 方便太多。buildx 则是在做多架构镜像或需要 BuildKit 缓存加速时必备。这俩都是插件形式不占多少空间装了以后不用再回头补。装完后 Docker 服务默认应该是启动的但很多镜像和脚本化部署场景里服务状态不一定正确所以最好手动确认并设置开机自启见下一小节。2.3 启动 Docker 服务并验证安装启动并设置开机自启sudo systemctl start docker sudo systemctl enable dockerenable 这一步很重要如果不做服务器重启后 Docker daemon 不会自动起来到时候所有容器服务一起失联排查半天才发现是没开机自启这种尴尬我经历过不止一次。启动后看一眼服务状态systemctl status docker状态是 active (running) 基本就说明 daemon 进程没问题。接着跑官方验证用的 hello-world 镜像sudo docker run hello-world这个命令会先从镜像仓库拉取一个不到几 KB 的测试镜像然后创建并运行一个容器容器打印一段 “Hello from Docker!” 消息后退出。整个过程能同时验证 daemon 通信、containerd 运行时、网络拉取和镜像运行能力链路完整。再确认一下 compose 和 buildx 插件是否可用docker compose version docker buildx version看到版本号输出说明配套插件都正常挂载。到这里一套最基础的 Docker 环境就算装好了。3. 安装后的三件实事权限、加速、日志3.1 免 sudo 运行 Docker 的用户组配置装好之后你会发现每次敲 docker 命令都要带 sudo不仅麻烦还会带来一个隐藏问题sudo 提升权限后docker 操作产生的文件在宿主目录里是 root 归属后续管理起来容易混乱。解决方法是把当前用户加入 docker 组sudo usermod -aG docker $USER newgrp dockernewgrp 是让当前终端会话立即切换到新组不用重新登录。如果换一个终端验证还是报 permission denied多半是因为 SSH 会话没有刷新组信息退出重新登录即可。验证命令docker ps能正常显示表格就说明免 sudo 生效。这里必须讲清楚一个安全权衡加入 docker 组的普通用户事实上拥有了接近 root 的权限。因为 Docker 可以挂载宿主机目录、可以以特权模式运行容器所以在这个组里的用户几乎能操作宿主机上的一切文件。生产服务器上给谁加 docker 组要在心里过一遍这个账。提示docker 组等同于高权限通道生产环境要严格控制可加入该组的账号不要把所有开发账号无差别加进去。如果只是临时用一下 Docker又不想开放长期权限可以考虑只在会话里用 sudo或者用专门的部署账号。3.2 daemon.json 配置镜像加速与日志上限Docker daemon 的配置集中在 /etc/docker/daemon.json。这个文件默认不存在需要自己创建。我的基础建议是至少配置三块内容镜像加速、日志上限、可能需要的数据目录路径。{ registry-mirrors: [https://你的加速地址], log-driver: json-file, log-opts: { max-size: 10m, max-file: 3 }, data-root: /var/lib/docker }逐项说下为什么。registry-mirrors 用来给 docker pull 提供镜像仓库的加速访问。如果你所在网络访问 Docker Hub 速度不理想或者公司内网有自建的镜像仓库可以把地址填进去。注意这里只能填 registry 镜像加速服务的地址不是普通 HTTPS 网址填错会导致拉取行为异常。日志上限是针对容器标准输出日志的json-file 驱动下如果不设 max-size一个疯狂打日志的容器可以把宿主机的 /var/lib/docker 所在磁盘写到满。max-size 10m 表示单份日志最大 10 MBmax-file 3 表示最多保留 3 份也就是单容器日志总量最多约 30 MB。data-root 默认就是 /var/lib/docker但如果系统盘小、数据盘挂载在其他路径可以在这里改比如 /data/docker。改完配置必须重载并重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker这里有个非常容易踩的坑daemon.json 只要写错一个标点、多一个逗号Docker 就起不来而且不会给太明确的报错。改之前先备份改完用 python3 检查 JSON 格式sudo cp /etc/docker/daemon.json /etc/docker/daemon.json.bak python3 -m json.tool /etc/docker/daemon.json输出正常后再重启服务。3.3 开机自启与版本管理前面已经提过 systemctl enable docker这里再补充一个验证命令systemctl is-enabled docker输出 enabled 就说明 OK。如果之前关闭过重新 enable 即可。版本管理方面apt 有两个很实用的命令。查看可安装版本apt-cache policy docker-ce如果哪天升级后发现问题想回滚可以指定某个版本号重新安装sudo apt install docker-ce5:26.0.1-1~ubuntu.22.04~jammy注意这种带具体版本号的安装方式只适用于 apt 仍从官方仓库索引到这个旧版本的场景。时间久了官方仓库会清理掉太旧的版本所以生产环境的回滚策略建议配合内部仓库的快照机制一起考虑而不是长期依赖官方源里的历史包。4. 常见错误排查与解决实录4.1 daemon 连接失败类问题这类错误的标志性输出是Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?很多人第一反应是重装 Docker其实大部分时候根本不用。我的排查顺序是固定的看服务状态systemctl status docker看服务日志journalctl -u docker.service --since today检查 socket 是否真实存在ls -l /var/run/docker.sock如果改了 daemon.json先校验 JSON 格式确认是不是刚加入 docker 组没重新登录导致的权限问题实际遇到的情况里最常见的是服务本身没启动直接 systemctl start docker 就恢复。其次是 daemon.json 写错这种情况 journalctl 日志里会带 JSON 解析错误相关信息恢复办法是先用备份恢复再修正文件。第三种比较隐蔽某些云厂商镜像或虚拟化模板里 iptables 规则被自定义过Docker daemon 启动时想注册网络规则但权限不足日志里会看到 iptables 相关错误。这种场景要结合具体环境处理必要时优先调整网络策略而不是急着动 Docker。4.2 GPG 密钥与 apt 源签名问题症状一W: GPG error: https://download.docker.com/linux/ubuntu jammy InRelease: The following signatures couldnt be verified because the public key is not available: NO_PUBKEY ...原因基本是密钥文件缺失或路径不对。排查时先确认密钥文件存在ls -l /etc/apt/keyrings/docker.gpg不存在就重新执行添加密钥的步骤。存在但 apt 仍报 NO_PUBKEY检查 docker.list 里的 signed-by 路径是否写对。我还碰到过目录权限不对导致 apt 无法读取密钥文件的情况chmod ar 就能解决。症状二W: http://download.docker.com/.../Release.gpg: Key is stored in legacy trusted.gpg keyring这是用旧方式 apt-key add 添加密钥后的典型警告。虽然不影响安装但每次 apt update 都会刷一遍很烦人而且从安全角度讲这种全局信任模型不够干净。清理方法把密钥从 /etc/apt/trusted.gpg 或 /etc/apt/trusted.gpg.d/ 移走然后用 signed-by 方式重新添加。4.3 依赖冲突与卸载残留安装 docker-ce 时比较常见的一类错误The following packages have unmet dependencies: containerd.io : Depends: ...或者 dpkg 配置阶段崩溃dpkg: error processing package containerd.io (--configure)优先检查是否已装有其他 containerd。典型场景是 Kubernetes 相关组件或 docker.io 老包残留。处理方式sudo apt remove containerd runc docker.io sudo apt --fix-broken install sudo dpkg --configure -a sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这几条命令的意图是先清掉可能导致冲突的旧包修复 apt/dpkg 的损坏状态再重新安装。如果机器上还要跑 Kubernetes那 containerd 的卸载要特别谨慎最好先确认 kubelet 的容器运行时是否依赖它。两个容器运行时并存的情况虽然技术上可以处理但没人愿意在生产环境给自己找这种麻烦。4.4 镜像拉取超时与网络问题docker pull 卡住、超时或者报Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled这类问题可以按三个方向排查网络链路、DNS 解析、代理配置。首先确认机器能不能正常访问 Docker Hub 的域名如果网络访问不顺畅优先考虑配置镜像加速器或者改用内网仓库。其次检查 DNScat /etc/resolv.conf 看看解析服务器是否正常必要时换一组可靠 DNS 再试。第三如果公司环境有标准 HTTP(S) 代理需要给 Docker daemon 配置用 systemd 的 drop-in 配置方式sudo mkdir -p /etc/systemd/system/docker.service.d sudo tee /etc/systemd/system/docker.service.d/http-proxy.conf /dev/null EOF [Service] EnvironmentHTTP_PROXYhttp://proxy.example.com:3128 EnvironmentHTTPS_PROXYhttp://proxy.example.com:3128 EnvironmentNO_PROXYlocalhost,127.0.0.1 EOF sudo systemctl daemon-reload sudo systemctl restart docker这里用 drop-in 配置而不是直接改 docker.service 原始文件后续升级不会冲突。改完后 docker info 里 Proxy 相关字段可以看到生效情况。另外注意 NO_PROXY 一定要带上 localhost 和本机回环地址否则连本地通信都要走代理会出现各种诡异问题。4.5 内核模块与存储驱动问题某些精简系统或特殊虚拟化环境下Docker 启动时报Error starting daemon: error initializing graphdriver: operation not permitted或者failed to load module: modprobe: failed to load module overlay这通常指向存储驱动问题。Docker 默认在 Ubuntu 上使用 overlay2它依赖内核的 overlay 模块。排查时确认模块状态lsmod | grep overlay sudo modprobe overlay如果 modprobe 失败可能是内核版本或者内核模块缺失需要走系统内核模块的安装流程而不是在 Docker 层面绕。文件系统类型也要注意btrfs 上跑 overlay2 有历史坑xfs 的 overlay 支持也需要内核开启相关特性生产环境优先把数据盘格式化成 ext4 或 xfs 并挂载好可以少很多麻烦。还有一种报错docker: Error response from daemon: cgroups: cannot find cgroup mount destination这个和容器运行时的 cgroup 配置有关。Ubuntu 22.04 默认用 systemd cgroup driver配合 containerd 一般没有这个问题。如果是从别的发行版或老版本升级过来的环境看到这个错误可以优先检查 systemd 和相关挂载点是否正常必要时在 daemon.json 里显式指定 cgroupdriver 为 systemd{ exec-opts: [native.cgroupdriversystemd] }4.6 常见错误速查表症状最可能原因快速处理方法Cannot connect ... Is the docker daemon running?docker 服务未启动systemctl start dockerpermission denied while trying to connect ... docker.sock用户不在 docker 组或会话未刷新usermod -aG docker $USER 后重新登录NO_PUBKEY ...GPG 密钥缺失、路径错误或文件不可读重新添加密钥chmod arKey is stored in legacy trusted.gpg keyring以前用过 apt-key 添加改用 signed-by 方式dial tcp: lookup ... no such hostDNS 配置异常检查 /etc/resolv.confnet/http: request canceled ... timeout镜像仓库访问不通配置镜像加速或代理containerd.io unmet dependencies已有其他 containerd 版本清理旧 containerd 再装overlay/overlay2 operation not permitted内核 overlay 模块或文件系统不支持modprobe overlay换 ext4/xfsdocker: error during connect: Post ... EOFdaemon 崩溃或 socket 异常重启 docker看 journalctldpkg was interrupted ... configuration上次安装未完成dpkg --configure -a这张表是给紧急情况下快速对照用的。真正处理问题时还是建议先看一遍 journalctl -u docker.service 和 docker infodocker info 的输出里有很多 daemon 层面的状态信息能帮你少走弯路。5. 卸载与升级给 Docker 留条退路5.1 彻底卸载 Docker 的完整清单如果因为迁移、系统重建或其他原因需要彻底卸载 Docker完整命令如下sudo apt purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd sudo rm -f /etc/apt/sources.list.d/docker.list sudo rm -f /etc/apt/keyrings/docker.gpgpurge 会连带把配置清掉但不会删除 /var/lib/docker 里的镜像、容器数据这是很多文档没说清楚的地方。所以要手动把数据目录、软件源文件、密钥文件一并清理干净。最后还有一步把用户从 docker 组移除或者清理残留的 docker 用户组sudo groupdel docker卸载完成后建议重启一次机器确认没有残留进程再重新评估是否要用其他运行时。特别是容器网络相关的虚拟网桥和 iptables 规则有时候不会随卸载自动消失重启后系统网络状态会干净很多。5.2 版本升级与回滚注意事项日常升级跑sudo apt update sudo apt install --only-upgrade docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin升级最大的风险点不是包本身而是已有容器和卷的状态。升级前建议先把重要容器 stop 一下确认数据卷挂载正常再执行升级。升级后不要只看 docker version 的数字一定要跑一个实例验证容器网络和数据卷确实没问题。回滚的思路在 3.3 小节已经写过用 apt-cache policy docker-ce 找出可用的旧版本号然后以指定版本方式重装。有一个容易被忽略的细节docker-ce、docker-ce-cli、containerd.io 三个包的版本不一定要严格对齐但 compose 和 buildx 插件最好和主版本保持同步否则偶尔会出现 CLI 与 daemon 版本兼容性警告。就好比手机 App 和服务器端的版本号不完全一致也能跑但长期不匹配迟早出问题。最后分享一些个人体会装了这么多台机器我给自己的实操规则是安装流程尽量固定成一套脚本或配置管理代码不要在每台新机器上重新手工敲一遍命令。密钥、源、daemon.json 都抽成模板用配置管理工具统一推送这样再碰到同版本 Ubuntu 的批量部署基本就是跑一遍的活儿。踩了几次坑之后我还养成了一个习惯任何一次修改 /etc/docker/daemon.json 前先备份、先校验 JSON 格式再重启服务。这个文件出错导致 Docker 起不来的案例我见过不下十来次而且越是在离线环境或者现场客户那边恢复起来越狼狈。最后再分享一个小技巧Docker 装好后的健康检查不要只跑 hello-world。我一般会同时验证 docker compose version、docker buildx version、以及拉取一个常用基础镜像。如果这三项都正常说明引擎、插件、网络三个关键链路都是通的这台机器才算真正可以交付使用。希望这篇 Ubuntu 22.04 jammy 的 Docker 安装与排错笔记能帮你少踩几个我刚接触 Docker 时踩过的坑。