搞Linux服务器的人十有八九都绕不开Docker。这东西把一个应用连同它的运行环境一起打包分发、部署、隔离都省事特别适合快速交付和测试环境复用。最近我又在一台Ubuntu 22.04上重新折腾了一遍Docker安装流程把踩过的坑和验证过的命令整理出来从环境检查、官方仓库安装到装完之后的权限、加速、自启配置再到常见的报错排查一条线捋清楚。这篇文章适合刚接触Docker的小白也适合需要在Ubuntu上快速装好Docker环境的运维和开发同学照着操作基本能一次跑通。1. 环境检查与安装方案选型1.1 为什么我不建议直接装 docker.io很多朋友刚上手时图省事直接执行sudo apt install docker.io一条命令就把Docker装好了。这个包的来源是Ubuntu官方仓库本质上是把Docker社区的二进制包重新打包维护确实能跑但我自己用下来发现两个问题一是版本更新明显滞后Docker官方已经更新到大版本了Ubuntu仓库里可能还停留在半年前甚至更早的版本二是docker.io在升级节奏上跟官方主线的 API 兼容性偶尔会有偏差比如新版的docker compose子命令、Buildx 插件在老仓库包里要么没有要么行为不一致排查起来很头疼。如果只是临时在本地跑一个容器实验用docker.io没问题但如果你打算长期维护、参与开源项目、做 CI/CD 流水线或者需要跟团队其他人保持环境一致我强烈建议安装 Docker 官方维护的 Docker Engine也就是大家常说的 Docker CE。它的更新路径清晰插件机制完整遇到问题还能直接查官方文档社区方案也都是基于这套环境来写的。1.2 确认系统版本与前置条件安装前我会先确认三样东西系统版本、系统架构、内核版本。# 查看 Ubuntu 版本 lsb_release -a # 查看系统架构 dpkg --print-architecture # 查看内核版本 uname -rDocker Engine 对 Ubuntu 的官方支持范围是 LTS 版本比如 20.04、22.04、24.04非 LTS 版本能用但出了问题官方不会优先处理我自己也只在 LTS 上跑。架构方面x86_64和arm64是主流如果你用的是树莓派这类 ARM 设备把架构列出来很关键后面换源的时候会用到。内核版本方面官方要求 3.10 以上实际现在 Ubuntu 20.04 以后的内核基本都是 5.x完全满足需求。我习惯顺手看一眼/proc/version确认当前内核确实正常加载这一步虽然大多数时候没问题但能帮你排除掉那些“换了内核没重启”的隐藏问题。1.3 三条安装路线的取舍Ubuntu 上装 Docker 大致有三条路我分别试过使用场景不太一样安装方式操作难度适用场景缺点官方 apt 仓库安装中等生产环境、长期维护需要手动配置 GPG 密钥和源官方便捷脚本最简单临时测试、快速验证脚本升级频率高不易审计离线 deb 包安装较复杂内网隔离环境需要手动处理依赖关系官方便捷脚本就是执行curl -fsSL https://get.docker.com | sh一条命令搞定但我个人不太推荐在生产环境里直接这么干。管道执行远程脚本属于“看完再执行”的典型反面教材脚本每次更新你都不知道它额外改了哪些系统配置出了问题很难定位。所以我下面的完整流程会以“官方 apt 仓库安装”为主线把每一步都拆开讲清楚这样出了问题你知道该查哪里。2. 使用官方仓库完整安装 Docker Engine2.1 清理历史残留如果你之前用docker.io或脚本装过 Docker建议先清理干净避免后续产生冲突。# 卸载可能存在的旧版本 sudo apt-get remove docker docker-engine docker.io containerd runc这一步需要注意卸载包不会自动删除/var/lib/docker目录里面存着你的镜像、容器、卷。如果你是有意重新安装想要保留这些数据卸载时不要手动删这个目录如果是彻底重来等卸载完成后可以再执行sudo rm -rf /var/lib/docker把它清掉。我遇到过不止一次旧版 docker 卸载不干净导致新版安装后启动时报iptables failed所以这个清理动作别省。2.2 配置 GPG 密钥与 apt 源先用旧版方式的人容易在这里踩坑。以前官方文档让你用apt-key add导入密钥新版本已经明确不推荐了改用把公钥文件放到/etc/apt/keyrings目录下然后通过signed-by参数指定更安全也更好维护。# 安装必要依赖 sudo apt-get update sudo apt-get install -y ca-certificates curl # 创建密钥目录 sudo install -m 0755 -d /etc/apt/keyrings # 下载 Docker 官方 GPG 密钥 sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod ar /etc/apt/keyrings/docker.asc这里有一点我特别想强调密钥文件的路径和名称影响着后续能否正常使用。22.04 和 24.04 较新版本的官方文档用的是/etc/apt/keyrings/docker.asc而更早的文档里写的是docker.gpg两者都能用但你的sources.list配置必须跟文件名对应上不然apt update会报公钥错误。接下来把 Docker 仓库写进 apt 源列表echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release echo $VERSION_CODENAME) stable | \ sudo tee /etc/apt/sources.list.d/docker.list /dev/null这条命令里的$(. /etc/os-release echo $VERSION_CODENAME)会自动读取当前系统的代号比如jammy22.04或noble24.04这样你就不用手动去改版本号了换机器部署的时候特别方便。添加完源之后跑一遍更新sudo apt-get update正常的话你会在输出里看到Get:... https://download.docker.com/linux/ubuntu jammy stable这样的字样说明源已经生效。2.3 执行安装并验证源弄好之后就是安装本体一条命令装齐运行时和常用插件sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这四个包的分工我说一下docker-ceDocker 守护进程和服务管理文件核心中的核心。docker-ce-clidocker命令行工具你敲的每个docker命令都靠它。containerd.io容器运行时负责真正地把容器跑起来Docker 通过它来管理容器生命周期。早期版本里 Docker 直接管运行时后来拆出去独立成 containerd系统里如果没有它会直接启动失败。docker-buildx-plugin和docker-compose-plugin镜像构建插件和 Compose 插件新版 Docker 已经把这两块做成插件体系了一次性装齐能省很多后面的事。安装完成先启动服务再跑官方测试镜像sudo systemctl start docker sudo systemctl enable docker sudo docker run hello-worldhello-world是个极小的测试镜像如果看到Hello from Docker!那段输出就说明 Docker 已经能正常拉取镜像并把容器跑起来了整个安装链路验证通过。2.4 新手必看验证失败怎么判断原因hello-world跑不起来时不要慌先分方向排查。最常见的三类情况是第一种输出里提示Unable to find image hello-world:latest locally然后卡住不动或显示网络错误。这通常是拉取镜像的网络链路有问题原因可能是服务器到镜像仓库的连接不稳定。这种时候先重试两次问题还在的话就在/etc/docker/daemon.json里配置一个可用的镜像加速源具体配置我在后面第 3 章专门讲。这里注意hello-world虽然很小但也要走一次镜像拉取流程网络问题会直接暴露出来。第二种提示权限不足Got permission denied while trying to connect to the Docker daemon socket。这是因为你当前用户不在docker用户组里需要用sudo执行或者按照下一章的配置把当前用户加进组里。第三种提示Cannot connect to the Docker daemon。这个通常是服务本身没起来先systemctl status docker看守护进程状态再用journalctl -u docker看最近的日志。大多数情况是配置文件写坏了或者端口被占用。3. 装完必做的四件小事3.1 配置镜像加速很多人装完 Docker 兴冲冲去拉镜像结果发现速度很慢甚至直接拉不动。这跟网络链路有关解决方案不是去改什么终端代理而是给 Docker 配置一个镜像加速器让它通过更近的节点拉取公共镜像。操作很简单先创建或修改 Docker 的配置文件sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [https://你的加速器地址] } EOF加速器地址怎么找阿里云和腾讯云都提供容器镜像服务登录控制台就能看到专属的加速器地址。拿到地址后替换到上面的配置里然后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker重新跑一次docker run hello-world你会发现镜像拉取速度快了很多。需要提醒的是加速器只影响 Docker Hub 上的公共镜像如果你用的是私有的镜像仓库配置是另外一套别混淆。3.2 让当前用户免 sudo 使用 docker装完 Docker 后每次执行docker命令都要带sudo用久了真的很烦。解决方法是把当前用户加入docker用户组sudo usermod -aG docker $USER newgrp dockernewgrp docker是让当前终端立即生效不用注销重登。如果你在 SSH 会话里操作执行完这行命令后后续命令就不需要sudo了。这里我必须负责任地说清楚加入docker用户组等价于获得 root 权限。因为 Docker 守护进程是 root 权限运行的能操作 Docker 基本就能以 root 身份挂载宿主机目录、执行容器命令。所以这个操作只推荐在单用户开发机上做。如果你是多人共用服务器或者环境里有特权账户管理要求让每个人自己加组前要想清楚风险。安全意识强的话也可以继续用sudo docker无非多敲几个字母。3.3 开启开机自启与常用命令速查咱们第 2 章里已经执行过sudo systemctl enable docker这时开机自启其实是已经打开的。那为什么还要单独提因为我发现很多人装完 Docker 直接忽略了这步结果服务器重启后 Docker 服务没起来业务容器全部挂掉排查了半天才发现是服务没启动。确认一下自启状态systemctl is-enabled docker输出enabled就说明没问题。再看一眼服务运行状态systemctl status docker看到active (running)就放心。最后我把平时用得最多的命令整理成一张速查表刚入门的朋友直接照着抄就行操作命令查看容器列表docker ps -a启动容器docker start 容器名或ID停止容器docker stop 容器名或ID进入容器内部docker exec -it 容器名 /bin/bash查看日志docker logs -f 容器名拉取镜像docker pull 镜像名:标签查看所有本地镜像docker images删除无用镜像docker image prune刚开始不需要死记硬背把ps、logs、exec这三个用熟日常就够用了。3.4 顺手装好 Docker Compose 插件上一章的安装命令里已经带了docker-compose-plugin所以你的环境里应该已经有了docker compose命令。验证一下docker compose version在比较老的环境里你可能会遇到只有docker-compose带横杠的情况那是独立的 Python 版 Compose已经停止维护了。新版统一用docker compose注意这是两个不同工具别混用。为什么要强调 Compose因为单容器场景docker run就够了但一旦涉及多个服务之间的配合比如 Web 应用加 MySQL 加 Redis用 Compose 写一个 YAML 文件就能把整个环境定义清楚一条docker compose up -d全部拉起比手动执行一堆docker run靠谱得多也方便版本管理。这里给个最简单的示例你在项目目录下创建compose.yamlservices: nginx: image: nginx:stable-alpine ports: - 8080:80然后执行docker compose up -d浏览器访问http://服务器IP:8080就能看到 Nginx 欢迎页。这就是 Compose 的基本玩法写多服务时再加几个services下的段落就行。4. 安装踩坑实录与排查速查4.1 添加源后 update 报错或拉取 GPG 密钥超时我自己的踩坑记录里apt-get update阶段出问题最让人恼火因为这时你还没机会装 Docker容易怀疑是不是源地址输错了。最常见的是添加了 Docker 官方源之后apt update卡在Get:... download.docker.com这一步然后超时。除了网络链路本身的原因外还有可能是系统里残留了旧的 Docker 源配置。比如某些教程让你把源写进/etc/apt/sources.list而不是单独放在/etc/apt/sources.list.d/docker.list两者共存时 apt 会去读取两次内容冲突就会出现奇怪问题。排查思路是这样先grep -r docker /etc/apt/sources.list /etc/apt/sources.list.d/看有没有重复配置有的话删掉多余的然后确认 GPG 密钥文件路径和signed-by参数一致最后再看daemon.json里有没有不小心配置了跟仓库源相关的错误参数。把这三处捋顺绝大多数 update 问题都能解决。另外还有一个高频坑是密钥过期或下载不完整。解决方式很简单重新把 GPG 密钥下载一遍覆盖到原路径sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod ar /etc/apt/keyrings/docker.asc sudo apt-get update4.2 “Package docker-ce has no installation candidate”这个报错我见过不少次字面意思是“找不到 docker-ce 这个包”但实际原因通常不是包不存在而是源没有真正生效或者源地址跟系统版本不匹配。排查步骤我建议按顺序来# 1. 确认 docker.list 文件存在且内容正确 cat /etc/apt/sources.list.d/docker.list # 2. 确认 apt 能读到该源 sudo apt-get update # 3. 搜索 docker-ce 包 apt-cache policy docker-ce如果apt-cache policy docker-ce输出里没有候选版本说明 apt 根本没把这个源里符合条件的包暴露出来。这时重点检查deb [arch... signed-by...]这一段arch是否跟你dpkg --print-architecture输出的架构一致signed-by是否指向真实存在的密钥文件。还有一种情况是系统版本不匹配比如你在 Ubuntu 的某个非 LTS 测试版上装VERSION_CODENAME取到的代号在 Docker 官方源里没有对应的稳定仓库自然找不到包。这种情况下要么换回 LTS 版本要么手动把代号改成相近的 LTS 代号但后者可能引入依赖兼容问题我不太推荐。4.3 “Cannot connect to the Docker daemon”这个报错的完整提示一般是Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?一句“is the docker daemon running”其实已经指出了方向。先检查服务状态sudo systemctl status docker如果服务没起来看日志精确定位sudo journalctl -u docker --no-pager -n 50根据我的经验服务起不来最常见的原因是/etc/docker/daemon.json配置写坏了。比如registry-mirrors地址格式不对、JSON 语法少了个逗号Docker 守护进程一启动读取配置就崩。这种时候先把配置文件备份然后临时移走再重启sudo mv /etc/docker/daemon.json /etc/docker/daemon.json.bak sudo systemctl restart docker服务能起来说明问题就出在这个文件上再小心翼翼地改配置。改动配置的原则是“一次只改一处改完就重启验证”不要一次性堆很多不确定的选项上去否则报错了都不知道是哪行的问题。4.4 权限不足“Got permission denied”这个报错我前面提过典型提示是Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock原因就是当前用户不在docker组里Docker 守护进程通过 Unix socket 跟客户端通信而这个 socket 默认只允许root和docker组的用户访问。最简单的解决方式我已经在上文写过了执行sudo usermod -aG docker $USER然后重新登录。但要注意某些发行版对 docker 组名的处理不太一样如果usermod报错提示组不存在可以手动创建sudo groupadd docker sudo usermod -aG docker $USER另外如果你在脚本或 CI 里遇到这个报错不要让脚本去自动加组直接在命令前加sudo或者提前把执行用户配置好避免权限扩散。4.5 WSL2 环境下的特殊注意事项现在很多开发者在 Windows 上用 WSL2 跑 Ubuntu 做开发装 Docker 的方式跟纯 Linux 服务器基本一致但有几点差异需要注意。首先安装前确认 WSL2 已经启用。在 PowerShell 里执行wsl --status如果输出显示默认版本是 2说明 WSL2 正常。如果还在用 WSL1Docker 是跑不起来的因为 WSL1 的架构跟 Docker 需要的 Linux 内核特性不匹配会直接报发行版错误。其次在 WSL2 里我给的建议是直接安装 Docker Engine不要装 Docker Desktop 的 WSL 后端。因为 WSL2 本身已经是一个完整的 Linux 虚拟机直接在里面装 Docker Engine配合 VS Code 的 Dev Containers 扩展开发体验已经很舒服没必要再叠一层 Docker Desktop白白多占内存。第三点WSL2 的内存分配默认是宿主机的一半如果跑多个容器导致内存不够可以在%UserProfile%/.wslconfig文件里限制[wsl2] memory6GB改完执行wsl --shutdown再重启 WSL 让配置生效。这个文件是 Windows 侧的全局配置对里面所有 WSL 发行版生效注意别一下给太多了要给宿主机留足空间。4.6 常见报错速查表把上面这些坑汇总成一张表方便大家直接对照排查报错信息常见原因解决方案apt-get update卡在 docker 官方源网络链路问题或重复源配置检查 sources.list 文件重试或换网络Package docker-ce has no installation candidate仓库源未生效、架构或版本代号不对检查 docker.list 和 GPG 密钥路径Cannot connect to the Docker daemon服务未启动或 daemon.json 错误journalctl -u docker查看日志并修正配置Got permission denied当前用户不在 docker 组sudo usermod -aG docker $USER后重新登录iptables failed旧版残留或网络配置冲突清理旧包重启 docker 服务镜像拉取超时网络链路到 Docker Hub 不稳定配置镜像加速器5. 卸载与升级时容易忽略的坑5.1 彻底卸载干净卸载 Docker 不是直接apt remove就完事了尤其当你后面还想重装时残留的配置和数据会让你一头雾水。完整的清理流程是# 卸载 Docker 相关软件包 sudo apt-get 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/containerdpurge会连配置文件一起删掉如果只想保留数据只留软件用apt-get remove就行但一般卸载都是为了重来或者彻底清掉用purge更干净。另外/etc/docker/daemon.json、/etc/apt/keyrings/docker.asc、/etc/apt/sources.list.d/docker.list这几个位置经常被忽略重装后残留的旧配置和旧密钥会干扰新环境建议卸载时一起手动清理掉。需要注意的是rm -rf /var/lib/docker会把所有本地镜像、容器、网络配置和数据卷一并清除执行前务必确认你的数据已经有别的备份路径。我见过有人卸载时顺手把别人数据库容器的数据卷也删了那真是哭都来不及。5.2 升级与版本锁定日常升级 Docker 非常简单sudo apt-get update sudo apt-get install --only-upgrade docker-ce docker-ce-cli containerd.io但“简单”不代表“安全”。Docker 升级时守护进程会重启这意味着跑在上面的所有容器都会中断。如果你的服务是restart: always策略升级后容器会自动拉起来但短暂停机难以避免。生产环境升级前我建议先把容器数据卷备份一份或者在业务低峰期操作。还有一类场景是需要锁定版本比如线上环境为了稳定性不想让 Docker 跟着全量升级跑。这时用apt-mark hold把不需要升级的包锁住sudo apt-mark hold docker-ce docker-ce-cli containerd.io之后apt upgrade就不会动这几个包。后面确定要升了再取消锁定sudo apt-mark unhold docker-ce docker-ce-cli containerd.io版本锁定有个副作用Docker 官方源里的包是滚动更新的你长期不升级可能会错过安全补丁。所以锁版本这个操作适合短期紧耦合的联调窗口不适合长期生产环境生产环境还是要建立定期升级和回滚预案。再提一个容易被忽略的细节升级完 Docker 后老版本创建的容器网络和卷通常能兼容但如果你用到了一些较新的特性比如特定版本的 Buildx 插件跨大版本升级后可能行为不一致。升级完我会习惯性跑一遍docker run --rm hello-world和docker build --help确认运行时和构建链路都没问题再继续干活。最后分享点个人的实际体会。Ubuntu 上装 Docker 这件事本身不难跟着官方文档一步步来基本不会错真正容易出问题的永远在“你以为装完就没事了”的那段权限没配好、加速没设置、服务没自启、升级没规划。我后来每装一台新机器都会把第 3 章那四件事当成固定流程走一遍宁可多花五分钟也别等到半夜服务挂了再排查。如果你也想给常用镜像扩展点玩法装好 Docker 后可以试试用 Compose 一键部署全家桶环境比如把 Nginx、MySQL、Redis 写在同一个 compose 文件里一条命令建立起本地开发环境这个体验用过就回不去了。
