先说实话:在 Ubuntu 上装 Docker,很多人觉得难,其实难的不是敲那几条命令,而是搞不清每一步在干嘛。网上教程一大堆,但要么版本太老,要么直接跳步骤,小白照着敲完一头雾水,报错也不知道去哪查。这篇文章我会把从零到跑起来整个流程拆开讲清楚,包括怎么确认系统环境、为什么用官方仓库而不是apt install docker.io、装完要做什么配置、第一个容器怎么跑,以及我这些年踩过的坑和排查思路。适合刚接触 Linux 和 Docker 的初学者,也适合想把手里 Ubuntu 虚拟机或服务器认真搭成开发环境的小伙伴。文章尽量说人话,每条命令都会解释为什么这么做,这样下次遇到问题你能自己判断。
1. 安装前的准备工作与环境确认
1.1 为什么要先确认系统版本和架构
Docker 官方对 Ubuntu 版本是有适配范围的,支持的是 Ubuntu 的 LTS 长期支持版本,包括 Ubuntu 24.04、22.04、20.04,再早一些的 18.04 也曾经支持过。使用 LTS 版本的好处是 Docker 官方仓库会持续提供安全更新和适配包,非 LTS 版本不是不能用,但仓库里的二进制包不一定能对上号,有时候 apt 会报依赖不满足,折腾起来很费时间。
先看一下自己的系统版本:
lsb_release -a或者:
cat /etc/os-release重点看Description和Codename两项,Codename 很关键,后面添加 Docker 仓库的时候会自动用到。比如 Ubuntu 22.04 的 Codename 是 jammy,24.04 是 noble,如果仓库地址里的 Codename 和系统对不上,apt update 就会报错。
还要确认系统架构:
dpkg --print-architecture绝大多数 PC 和服务器输出的是amd64,ARM 开发板或云上的 ARM 实例输出的是arm64,树莓派一般是armhf。这个信息决定了 Docker 仓库源要选哪个架构目录,也决定了你拉取的镜像是否能在本机运行。
1.2 清理旧版本 Docker
如果这台机器之前装过 Docker,或者系统里预装了docker.io、docker-engine这类包,建议先卸载干净。这里有个容易混淆的点:docker.io是 Ubuntu 软件源里自带的 Docker 老版本,和 Docker 官方源里的docker-ce不是同一个包,两者混着装很容易冲突。
清理命令:
sudo apt-get remove docker docker-engine docker.io containerd runc这个命令只删除软件包本身,不会动/var/lib/docker目录,也就是说你之前拉取的镜像、创建的容器、数据卷都还在。如果确认这台机器不需要保留旧数据,可以顺手清掉:
sudo rm -rf /var/lib/docker我个人建议新机器别着急删,先确认真的不需要旧数据再动手。毕竟镜像和容器数据有时候很重要,删了就真没了。
1.3 更新软件包索引并安装基础依赖
在添加 Docker 仓库之前,先让系统的软件包索引保持最新:
sudo apt update然后安装几个基础工具:
sudo apt install -y apt-transport-https ca-certificates curl gnupg lsb-release这几个包各有用途:apt-transport-https让 apt 支持通过 HTTPS 协议访问软件源;ca-certificates提供 CA 证书,用来验证仓库地址的合法性;curl用来下载 GPG 密钥文件;gnupg用来导入和管理密钥;lsb-release用来获取 Linux 发行版的具体版本信息。缺了其中任何一个,后面添加 Docker 官方源的时候都可能出问题。
2. 安装 Docker 的三种主流方式(含推荐方案)
2.1 方式一:官方 apt 仓库安装(推荐)
我最推荐的方式,也是 Docker 官方文档一直在用的方式,就是把 Docker 官方的 apt 仓库加到系统里,然后通过正常的 apt 流程安装。这样做的好处有三个:可以用sudo apt upgrade统一升级 Docker,不需要手动管理二进制文件;依赖关系由 apt 自动处理;安装、卸载、回滚都比较规范,适合长期使用。
第一步是添加 Docker 官方的 GPG 密钥:
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 a+r /etc/apt/keyrings/docker.gpg简单解释一下这几条命令在干嘛。install -m 0755 -d是创建目录同时把权限设置成 755,专门用来放密钥文件。curl下载的 GPG 密钥文件是 ASCII 格式,apt 不认这种格式,所以要用gpg --dearmor把它转换成二进制格式再保存。chmod a+r是让所有用户都能读取这个密钥,否则 apt 在更新索引时会因为没权限读密钥而报错。
第二步是添加 Docker apt 仓库:
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这条命令看起来复杂,其实核心就是把一行仓库地址写进/etc/apt/sources.list.d/docker.list文件。关键点是arch=$(dpkg --print-architecture)会自动代入系统架构,$(lsb_release -cs)会自动代入系统 Codename,比如 Ubuntu 22.04 会是 jammy。这样手动写错架构或版本代号的概率就大大降低了。
第三步是更新索引并安装:
sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin注意这里的包名,现在 Docker 官方把组件拆得很细:docker-ce是 Docker 服务端,docker-ce-cli是命令行工具,containerd.io是底层的容器运行时,docker-buildx-plugin是高级构建插件,docker-compose-plugin是编排工具。建议一次性都装上,以后用得到。
2.2 方式二:便捷脚本安装
如果你想快速搭一个实验环境,不想一步步敲上面的命令,Docker 官方提供了一个自动化脚本:
curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh这个脚本会把官方仓库配置、密钥导入、软件安装全部自动化完成,还会帮你设置好 systemd 服务并开启开机自启。优点是省事,特别适合临时测试或学习环境。
但我不建议生产环境用这个方式,原因是脚本永远是安装当前最新版本,你无法指定版本号,万一新版本有兼容性问题,回滚会比较麻烦。另外脚本内部做的事情太多,出了问题不好定位,你不知道它到底配置了哪些文件。
一个小习惯:任何从网上下载下来要执行的脚本,我都建议先看一遍内容再执行。上面第一步只下载不执行,所以你可以先less get-docker.sh大概扫一眼,确认没有异常再运行。
2.3 方式三:直接下载 deb 包离线安装
有些内网服务器无法访问外部软件源,或者你对版本有严格要求,这种情况可以手动下载 deb 包安装。Docker 官方仓库的 deb 包在https://download.docker.com/linux/ubuntu/dists/下面,先找到对应的 Codename 目录,然后进pool/stable/选择架构目录,把需要的包下载到本地。
手动下载安装命令:
sudo dpkg -i ./docker-ce_*.deb sudo dpkg -i ./docker-ce-cli_*.deb sudo dpkg -i ./containerd.io_*.deb如果装的过程中提示依赖缺失,跑一句:
sudo apt-get install -fapt 会自动补装缺失的依赖。离线安装最烦的就是依赖链,所以有条件还是优先用仓库方式。
2.4 三种安装方式如何选
| 安装方式 | 优点 | 缺点 | 适合场景 |
|---|---|---|---|
| 官方 apt 仓库 | 升级方便、依赖自动处理 | 步骤较多 | 绝大多数个人和服务器 |
| 便捷脚本 | 一键完成、速度快 | 无法指定版本、过程不透明 | 学习测试、临时环境 |
| 离线 deb 包 | 可指定版本、无需公网访问 | 依赖需要手动处理 | 内网环境、版本强约束生产环境 |
我自己的习惯是:笔记本和虚拟机用官方 apt 仓库,图省事图稳定;公司内网服务器用离线 deb 包,因为安全策略不允许随便连外网;临时开一台测试机,那就直接跑脚本,快是真的快。
3. 安装后的必要配置与验证
3.1 启动 Docker 服务并设置开机自启
安装完成不等于 Docker 正在运行,需要手动启动服务并设置为开机自启:
sudo systemctl enable docker sudo systemctl start docker sudo systemctl status dockerenable是把 Docker 服务加入开机启动项,这样重启机器后 Docker 会自动起来。start是立即启动服务。status是查看当前运行状态,看到active (running)才是正常的。
如果status显示失败,别慌,先看日志:
journalctl -u docker.service -n 50-n 50表示只看最后 50 行日志。大多数情况下,启动失败是因为配置文件写错了,尤其是/etc/docker/daemon.json,格式稍有不对整个服务就起不来。
3.2 验证 Docker 是否安装成功
先看版本号:
docker --version能输出类似Docker version 27.3.1的内容,说明命令行工具已经正常工作。
再验证整个链路是否通畅:
sudo docker run hello-world这条命令会先从 Docker Hub 拉取一个极小的hello-world镜像,然后在容器里执行。看到类似Hello from Docker!的输出,说明 Docker 客户端、服务端、镜像拉取、容器创建整条链路都通了。
3.3 免 sudo 运行 Docker 的用户组配置
默认情况下,执行docker命令需要 sudo,否则会报这样的错:
docker: permission denied while trying to connect to the Docker daemon socket原因是 Docker 的守护进程套接字/var/run/docker.sock属主是root:docker,当前用户不在 docker 组里。解决办法是把自己加进 docker 用户组:
sudo usermod -aG docker $USER newgrp dockerusermod -aG是把用户追加到 docker 组,newgrp docker是让当前终端立即生效,不用重新登录。之后直接docker ps就能用了。
这里必须提醒一句:加入 docker 组的用户实际上拥有 root 级别的权限,因为 Docker 可以挂载宿主机目录、修改网络配置,本质上等同于 root。所以生产服务器上不要把不信任的账号加进 docker 组。
3.4 配置镜像加速器
国内网络环境下,直接从 Docker Hub 拉取镜像经常很慢,甚至超时。常规解决办法是配置镜像加速器,也就是用云厂商提供的 registry mirror。
先创建或编辑/etc/docker/daemon.json:
{ "registry-mirrors": ["https://your-mirror-service.example.com"] }注意这是个 JSON 文件,格式必须严格正确,多一个逗号都会导致 Docker 服务启动失败。写完后重载配置并重启:
sudo systemctl daemon-reload sudo systemctl restart docker重启后用docker info查看Registry Mirrors那一栏,确认配置生效。加速服务各个地区效果不一样,最好实测一下拉取速度再确定用哪个。另外要说明一点,镜像加速只是针对 Docker Hub 官方镜像的常规访问加速,不同服务商的稳定性和速度差异很大,建议选择口碑好、有备案的云服务厂商提供的加速地址。
4. 第一个容器:从入门到跑起来
4.1 hello-world 验证
hello-world是 Docker 官方提供的最小测试镜像,它只做一件事:容器启动后输出一段说明文字然后退出。虽然简单,但它验证了整个链路:
- 客户端能连接到 Docker 守护进程
- 守护进程能从远程仓库拉取镜像
- 容器镜像能正常解压和启动
如果这条命令报错,大概率是网络原因拉不到镜像,回到 3.4 配置加速器后再试。
4.2 运行一个 Nginx 容器并理解端口映射
hello-world跑完就退出了,看不出容器的实际使用价值,所以我们来跑一个真正提供服务的容器。Nginx 是首选,镜像小、依赖少、效果直观:
docker run -d --name my-nginx -p 8080:80 nginx参数逐个解释:-d表示后台运行;--name my-nginx给容器起个名字,方便后续管理;-p 8080:80是端口映射,把宿主机的 8080 端口映射到容器内的 80 端口;nginx是镜像名。
运行完以后,浏览器访问http://localhost:8080,如果看到 Nginx 的欢迎页,说明这个容器已经在正常工作了。
端口映射是 Docker 最核心也最容易搞混的概念。容器里的 Nginx 默认监听 80 端口,但这个 80 端口是容器内部的网络命名空间,和宿主机是隔离的。没有-p参数,宿主机根本访问不到容器内的服务。-p 8080:80就是把容器内的 80 翻译成宿主机的 8080,外面的人访问宿主机的 8080,数据就被转发到容器的 80。
4.3 常用容器操作命令速记
跑起来只是第一步,日常运维还需要掌握下面这些命令的场景:
docker ps # 查看运行中的容器 docker ps -a # 查看所有容器,包括已停止的 docker logs -f my-nginx # 实时查看容器日志 docker exec -it my-nginx bash # 进入容器终端 docker stop my-nginx # 停止容器 docker start my-nginx # 启动已停止的容器 docker rm my-nginx # 删除容器,需先停止 docker images # 查看本地镜像列表 docker rmi nginx # 删除本地镜像 docker system df # 查看 Docker 磁盘占用ps查看容器状态,logs排查应用问题,exec进容器内看进程和文件。这几个命令频率最高,先记住它们,后面再慢慢拓展。
5. 常见问题与排查技巧实录
5.1 安装时出现 Unable to locate package docker-ce
这是新手最常见的报错,原因是 apt 源里根本没有docker-ce这个包。可能的情况有两种:
第一种是你没执行sudo apt update,新加的 Docker 仓库还没被索引。解决办法是先更新索引再安装。
第二种是你之前添加仓库那一步出了问题,比如 Codename 不匹配。检查一下仓库文件内容:
cat /etc/apt/sources.list.d/docker.list看看里面的$(lsb_release -cs)最终被替换成了什么。如果系统不是 LTS 版本,或者/etc/os-release里的 Codename 和 Docker 官方仓库目录对不上,就会出现这个错误。
还有一种情况是源里只有stable但系统架构不匹配,比如在 ARM 机器上用了amd64的仓库。检查一下dpkg --print-architecture的输出,确认架构和仓库地址对应。
5.2 启动 Docker 报错 Cannot connect to the Docker daemon
这个报错说明命令行工具连不上 Docker 守护进程。先区分两种情况:守护进程没运行,或者守护进程运行了但套接字访问不了。
先用systemctl status docker看服务状态。如果服务根本没起来,看日志:
journalctl -u docker.service -n 50日志里最常见的启动失败原因是/etc/docker/daemon.json格式不对。我曾经见过有人把 registry-mirrors 后面的逗号写错,导致整个 JSON 解析失败,Docker 服务直接起不来。可以用docker daemon --validate验证配置文件,或者用python3 -m json.tool /etc/docker/daemon.json检查 JSON 格式。
如果服务正常但命令还是连不上,检查环境变量:
echo $DOCKER_HOST有些教程会让你设置DOCKER_HOST,但如果你用的是本地安装的 Docker,这个变量不应该被设置,设置了反而会让客户端去找一个不存在的远程地址。
5.3 运行 docker 命令出现 permission denied
这个问题的原因很清楚:当前用户不在 docker 组。解决方式前面已经讲过了,sudo usermod -aG docker $USER然后重新登录。
需要注意两个细节。第一,newgrp docker只对当前终端有效,新开的终端自然生效,但如果你的 SSH 会话是旧的,可能需要断开重连。第二,不要因为嫌麻烦就一直用sudo docker,这样所有 Docker 相关文件都会以 root 身份创建,后续清理起来权限纠缠会很痛苦。
5.4 拉取镜像慢或超时
镜像拉取慢主要有两个原因:网络链路问题和 DNS 解析问题。首选方案是配置镜像加速器,这是目前最直接有效的办法。配置方法在 3.4。
如果已经配了加速还是慢,试试修改 DNS:
sudo vim /etc/resolv.conf把 nameserver 换成公共 DNS,比如223.5.5.5。改完记得保存,然后重新拉一次镜像。
另外一个容易被忽略的点是,有些网络环境下 IPv6 会导致 Docker 连接超时。可以试试强制走 IPv4,在/etc/docker/daemon.json里加:
{ "ipv6": false }然后重启 Docker。这个方法不是万能的,但确实在一些双栈网络下有效。
5.5 磁盘空间不足与数据目录迁移
Docker 默认把所有数据放在/var/lib/docker,包括镜像、容器、数据卷。如果根分区不够大,跑一段时间就会发现df -h里/已经 100% 了。
先看磁盘占用情况:
docker system df这个命令会列出镜像、容器、数据卷分别占了多少空间。如果只是悬空资源多,执行:
docker system prune -a可以清理掉所有未被容器使用的镜像和缓存。
如果清理完还是不够,就得考虑把整个 Docker 数据目录迁移到更大的分区:修改/etc/docker/daemon.json:
{ "data-root": "/data/docker" }重启 Docker,确认服务正常。注意迁移前要停掉所有容器和 Docker 服务,然后把原目录完整拷贝过去,避免丢数据:
sudo systemctl stop docker sudo rsync -av /var/lib/docker/ /data/docker/ sudo systemctl start docker5.6 排查技巧速查表
| 故障现象 | 排查命令 | 常见解决办法 |
|---|---|---|
| docker 命令提示无权限 | id查看所属组 | usermod -aG docker $USER |
| 服务启动失败 | journalctl -u docker.service | 检查 daemon.json 格式 |
| 客户端连不上服务端 | systemctl status docker | 确认服务运行、检查 DOCKER_HOST |
| 拉取镜像超时 | docker info查看 mirror | 配置镜像加速器、改 DNS |
| 容器端口不通 | docker ps查看映射 | 检查-p参数、确认容器内服务监听 |
| 根分区被占满 | docker system df | prune -a清理、迁移>
|