从第一次在 Ubuntu 上装 Docker 到现在,我前前后后已经折腾过几十次了。这篇文章就是想把这条路上的坑一个个填平,让刚接触 Docker 的小白少走弯路,照着做就能把环境搭起来。教程会从系统准备一直讲到镜像加速、权限配置、常用命令,最后还附上我踩过的那些错误和排查方法,尽量做到每一步都有依据、每一处都讲清楚为什么。
我自己平时的主力开发环境就是 Ubuntu,不管是物理机还是虚拟机都试过,所以下面的步骤在 Ubuntu 20.04、22.04、24.04 上基本都能跑通,部分老版本系统只要内核满足条件也能用。如果你只是想快速体验 Docker,用虚拟机装一个 Ubuntu 也完全没问题,镜像源和网络这块我会专门说明怎么配置,避免下载慢到让人怀疑人生。
1. 安装前的准备工作与思路拆解
1.1 为什么选择 Ubuntu 作为 Docker 的学习环境
Docker 本身是基于 Linux 容器技术实现的,虽然 macOS 和 Windows 也能通过虚拟机套一层来运行,但真正原生的体验还得看 Linux。Ubuntu 是绝大多数教程、生产服务器和云镜像默认采用的发行版,用 Ubuntu 学 Docker,最大的好处就是遇到问题时你能搜到的资料最多、踩过坑的人最多、解决方案也最全。
还有一个现实原因是,Docker 官方对 Ubuntu 的支持非常积极,软件源里直接提供 docker-ce(社区版)的包,安装路径成熟稳定。相比之下,如果一开始就用 CentOS 或者 Debian 的旧版本,虽然也能装,但会遇到额外的不确定性,新手容易把这些系统问题误当成 Docker 问题,排查起来会非常折磨人。
1.2 安装前的系统检查和架构确认
开始之前,建议先确认两件事:系统版本和 CPU 架构。不同架构对应的 Docker 安装包不一样,镜像源的配置方式也有差异。
打开终端,执行下面的命令:
bash复制cat /etc/os-release
uname -m
第一行会显示系统版本信息,比如 Ubuntu 22.04.3 LTS,确认自己是 64 位系统即可,Docker 官方支持的 Ubuntu 版本中,20.04 和 22.04 是当前最常见也最稳定的选择。uname -m 输出的是 CPU 架构,常见三种:
| 输出结果 | 架构说明 | 安装包选择 |
|---|---|---|
| x86_64 | Intel/AMD 64位 | 标准版 docker-ce |
| aarch64 | ARM 64位(树莓派等) | arm64 版 docker-ce |
| armv7l | 32位 ARM | 旧版支持有限,不建议折腾 |
如果你是在 VMware 或 VirtualBox 虚拟机里装的 Ubuntu,大概率是 x86_64,直接按本文后面的步骤走就行。我见过不少人在 ARM 开发板上装 Docker,其实步骤差不多,但镜像源要针对 arm64 做调整,后面我会提到。
1.3 要不要用 Docker Desktop
很多新手看到热词里有 Docker Desktop,会纠结到底装哪个。这里我要直说:在 Ubuntu 上学习 Docker,不推荐用 Docker Desktop。Docker Desktop 在 Linux 上本质上是一个带图形界面的管理工具,它额外引入了一层虚拟机,占用资源更高,而且很多生产环境根本不会用它。命令行版本的 Docker Engine 才是真正的主角,学会它之后,到哪台服务器上你都能无缝衔接。
当然,如果你只是想在 Windows 或 macOS 上体验 Docker,Docker Desktop 是无可争议的首选,那是另一套东西。本文的定位是 Ubuntu 原生环境,所以后面的内容全部围绕 docker-ce 和 docker compose 插件展开。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种主流安装方式详解与选型
2.1 方式一:使用 Docker 官方 apt 源安装(推荐)
这是我最推荐的安装方式,因为它装的是最新稳定版,而且后续升级方便。整个流程分四步。
第一步,更新软件包索引并安装依赖工具:
bash复制sudo apt update
sudo apt install -y ca-certificates curl gnupg lsb-release
这些包是用来做 HTTPS 传输和密钥管理的。lsb-release 能自动识别当前系统版本,后面写 apt 源的时候会用上。
第二步,添加 Docker 官方的 GPG 密钥。官方源要求先导入密钥,否则 apt 会拒绝从该源安装软件,这是安全机制:
bash复制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
这里--dearmor的作用是把 GPG 密钥从 ASCII 格式转换成二进制格式,因为 Ubuntu 22.04 之后的 apt 默认要求密钥文件放在指定目录并设置好权限。我一开始没加chmod a+r,导致后续 apt update 时报了权限错误,整整折腾了半小时。
第三步,添加 Docker 仓库信息到 apt 源列表:
bash复制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
这条命令的核心在于$(dpkg --print-architecture)和$(lsb_release -cs),前者自动识别架构,后者自动识别系统代号(比如 jammy、noble)。如果你看到有人手动把 jammy 写死,在 24.04 上执行就会出错。
第四步,安装 Docker 及相关组件:
bash复制sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
这里重点说一下,docker-compose-plugin是现在官方主推的 Compose V2 插件,装完直接能使用docker compose命令,不用再单独装 Python 版的 docker-compose。docker-buildx-plugin 则是用于多架构镜像构建的插件,进去装一下不亏,后面打包镜像时随时能用。
安装完成后,查看版本:
bash复制sudo docker --version
sudo docker compose version
能看到版本号就说明核心装好了。
2.2 方式二:使用官方安装脚本(快速应急)
如果你不想手动添加源,Docker 官方提供了一个自动化脚本,一条命令就能完成安装:
bash复制curl -fsSL https://get.docker.com | bash
这条命令会检测系统、添加源、安装依赖,一步到位。我做测试环境时经常用这个方法,省时省力。但它也有缺点:没有手动流程可定制,不方便在企业内网离线环境使用。而且直接把远程脚本通过管道交给 root 执行,真要认真审计的话心里会有点没底,在生产服务器上我一般不用这种方式。
如果网络环境不方便访问 get.docker.com,也可以把脚本下载到本地,先看一遍内容再执行,至少做到心里有数。
2.3 方式三:使用离线 deb 包安装(内网环境)
有些服务器是内网环境,无法访问外网,这时候只能离线安装。方法是找一台能上网且系统版本一致的机器,从 Docker 官方源或镜像站下载 deb 包,拷到目标机器上手动安装。
bash复制# 在有网的机器上下载
sudo apt download docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo apt download docker-buildx-plugin
然后把下载的 .deb 文件拷贝到目标机器,在同一目录下执行:
bash复制sudo dpkg -i *.deb
这种方式我用的场景较少,但真的遇到离线环境时,这就是救命方案。唯一要提醒的是,dpkg 安装容易遇到依赖缺失,如果报依赖错误,需要额外下载对应的依赖包,所以能联网的时候尽量用 apt 安装。
3. 镜像加速配置与 Docker 环境优化
3.1 为什么镜像下载这么慢
装好 Docker 之后,很多小白第一次拉镜像就被打回原形:下载速度只有几十 KB/s,甚至直接超时。这是因为 Docker 默认从 Docker Hub 拉取镜像,而 Docker Hub 的服务器在海外,从国内直连的链路质量很不稳定。
应对方案就是配置国内镜像加速器,把默认的 Docker Hub 访问请求转发到速度更快的镜像仓库。很多云厂商都提供了免费的镜像加速服务,我自己实测下来,各家速度在不同地区差异很大,建议多试几个。
3.2 Docker 镜像加速配置步骤
Docker Engine 支持通过 /etc/docker/daemon.json 文件来配置镜像源。如果这个文件不存在,手动创建即可:
bash复制sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://dockerproxy.com",
"https://docker.nju.edu.cn"
]
}
EOF
需要注意,不同时期可用的镜像加速地址变化很快,有些源可能某一天就失效了。配置之后,重启 Docker 让它生效:
bash复制sudo systemctl daemon-reload
sudo systemctl restart docker
检查是否启用了加速器:
bash复制sudo docker info | grep -A 2 "Registry Mirrors"
能看到列表里有刚才填的地址,说明加速配置已经生效。之后拉取镜像的速度通常会有明显提升,尤其是 MySQL、Redis 这些热门镜像,往往能跑满带宽。
3.3 Ubuntu 系统软件源也值得一并优化
很多人只给 Docker 配了镜像源,却忘了 Ubuntu 本身的 apt 源也可能很慢。如果你安装基础包时就卡得要死,建议先把系统源切换到国内镜像,再做 Docker 安装。
最简单的做法是修改 /etc/apt/sources.list,把默认的 archive.ubuntu.com 替换成国内镜像地址,比如清华源、阿里源或者中科大源。注意 24.04 版本开始,Ubuntu 开始使用 ubuntu.sources 文件(/etc/apt/sources.list.d/ubuntu.sources),修改前先确认你的系统版本格式。
我通常不建议直接用 sed 批量替换,因为新旧版本源格式差异较大,容易改错。稳妥做法是先备份原文件,然后去对应镜像站查看官方给出的源模板,直接粘贴覆盖,这样最省心。
4. 权限配置、自启动与第一个容器
4.1 免 sudo 使用 Docker 的正确姿势
Docker 安装后,每次执行 docker 命令都需要加 sudo,因为 docker 守护进程默认以 root 权限运行,普通用户想要连接它的 Unix Socket,必须拥有相应权限。对小白来说,频繁输入 sudo 既麻烦又容易出错,我们可以把当前用户加入 docker 用户组:
bash复制sudo groupadd docker
sudo usermod -aG docker $USER
newgrp docker
newgrp docker 是为了让当前终端会话立即生效,不用重新登录。如果设置了这一步还是提示权限不足,要么是这个终端里没有执行 newgrp,要么是系统缓存了旧组信息,注销重新登录一次基本都能解决。
这里要专门提醒一句:加入 docker 用户组等于让该用户获得了和 root 几乎相同的容器管理权限,因为容器可以通过挂载宿主机目录来读写宿主机文件。不要随便在生产服务器上给非信任用户加入 docker 组,这是安全红线。
4.2 设置 Docker 开机自启动
Ubuntu 使用 systemd 管理服务,把 Docker 设置成开机自启很简单:
bash复制sudo systemctl enable docker
sudo systemctl start docker
sudo systemctl status docker
enable 是把服务加入开机启动项,start 是立即启动,status 是查看当前状态。如果看到 active (running) 绿色字样,并且没有报错,说明 Docker 服务一切正常。我见过很多人只安装不开启启动,结果服务器重启后 Docker 不见了,还以为是系统坏了,其实只是服务没起来。
4.3 跑通第一个容器:hello-world
环境准备完毕,可以跑一个小小的测试镜像来验证整个链路:
bash复制docker run hello-world
这个命令会自动从镜像仓库拉取一个体积极小的 hello-world 镜像并运行。如果看到输出中有 “Hello from Docker!” 这样的提示,说明你本地的 Docker 引擎、网络、权限都已经正常工作了。
我第一次跑通这个命令的时候,虽然只输出了一段话,但整个环境是否可用的谜底全部揭晓了。这也是为什么我一直建议小白装完 Docker 先跑 hello-world 再往下走,不是因为它有多酷,而是它能把安装问题最快速地暴露出来,避免后面因为环境问题怀疑自己的容器配置写错了。
4.4 顺手上手:用 Docker 运行 MySQL 8.0
很多热词都指向 docker 安装 MySQL,这里我顺便演示一个最常用的场景。体验过 hello-world 之后,你大概也想看看 Docker 实战起来是什么样子。跑一个 MySQL 8.0 容器只需要一条命令:
bash复制docker run -d \
--name mysql8 \
-p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=123456 \
-v mysql_data:/var/lib/mysql \
mysql:8.0
简单拆解一下:-d 表示后台运行,--name 是容器名字,-p 3306:3306 把宿主机的 3306 端口映射到容器的 3306 端口,-e 传入环境变量来设置 root 密码,-v 创建了一个名为 mysql_data 的卷来持久化数据库文件。
这里用到了 Docker 的一个核心思想:数据卷。如果不做映射,容器一删数据全没了,这会让很多人崩溃。习惯性地给数据库这类有状态服务挂载数据卷,是使用 Docker 的基本素养。
5. Docker Compose 的引入与多容器管理
5.1 为什么需要 Docker Compose
单个容器用 docker run 还算方便,但真实项目里通常需要多个服务配合,比如 Web 应用要连数据库,Redis 做缓存,Nginx 做反向代理。如果每个都写一长串 docker run 命令,维护起来非常痛苦。
Docker Compose 的价值就是用一份 YAML 文件把多个容器的配置统一管理起来,一条 docker compose up -d 就能启动整个服务栈。我刚才介绍安装方式时专门让你装了 docker-compose-plugin,现在就能用上了。这套工具是现代 Docker 工作流的标配,学会它是早晚的事。
5.2 一个简单的 docker-compose.yml 示例
这里以最常见的 MySQL + Redis 组合为例,创建一个项目目录:
bash复制mkdir ~/docker-demo && cd ~/docker-demo
然后在目录里创建 docker-compose.yml:
yaml复制version: "3.8"
services:
mysql:
image: mysql:8.0
container_name: demo-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: 123456
ports:
- "3306:3306"
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:7
container_name: demo-redis
restart: always
ports:
- "6379:6379"
volumes:
- redis_data:/data
volumes:
mysql_data:
redis_data:
使用 Compose 启动:
bash复制docker compose up -d
查看运行状态:
bash复制docker compose ps
如果要停止并移除所有容器:
bash复制docker compose down
和直接跑 docker run 相比,Compose 的配置都在文件里,可以进 Git 版本管理,换台机器直接拷贝过去安装就能复现,这就是可复现性带来的生产力提升。
5.3 理解 Compose 里的网络模式
Compose 会自动创建一个默认的 bridge 网络,同一个 Compose 文件里的服务之间可以通过服务名互相访问。比如在 demo-mysql 容器里,其他服务只需要用 hostname mysql 就能连上数据库,不需要关心容器的 IP 地址。这个设计极大简化了服务间的通信配置,我在本地做项目联调时,特别喜欢这个特性。
不过要注意,当多个 Compose 项目同时运行时,它们的网络是相互隔离的,跨项目访问需要手动加入同一个外部网络。这个知识点用到时再查文档也不迟,但心里要有这么个概念,避免以后遇到连不上的问题摸不着头脑。
6. 常用 Docker 命令速查与新手避坑指南
6.1 日常高频命令整理
Docker 的命令非常多,但真正每天用到的其实就十几个。我整理了一份速查表,建议你保存一份,刚开始不熟悉的时候随时翻:
| 场景 | 命令 |
|---|---|
| 查看本地镜像 | docker images |
| 拉取镜像 | docker pull 镜像名:标签 |
| 启动容器 | docker run -d --name 自定义名 镜像名 |
| 查看运行中的容器 | docker ps |
| 查看所有容器(含停止) | docker ps -a |
| 进入容器终端 | docker exec -it 容器名 bash |
| 查看容器日志 | docker logs 容器名 |
| 停止容器 | docker stop 容器名 |
| 删除容器 | docker rm 容器名 |
| 删除镜像 | docker rmi 镜像名 |
| 删除所有无用数据 | docker system prune -a |
docker system prune -a 是个高危命令,它会删除所有未被容器使用的镜像和构建缓存,释放硬盘空间的效果很显著,但用之前一定要确认没有你想要保留的镜像。
6.2 查看日志的冷门技巧
新手遇到容器启动失败,第一反应是去看日志。但有时候 docker logs 输出的内容并不完整,尤其是应用在启动早期就崩溃的场景,日志可能根本没有刷到标准输出。这时候可以多看几眼 docker inspect 里的 State 信息:
bash复制docker inspect 容器名 | grep -A 10 "State"
这段输出能告诉你容器的退出码、重启次数、最后一次运行的错误信息。退出码 137 通常表示被系统 OOM 杀掉,退出码 2 可能是启动命令写错了,这些信息比日志有时候更能快速定位问题。
另外,如果日志非常多,可以搭配 --tail 参数只看最后几行,比如:
bash复制docker logs --tail 100 -f 容器名
加上 -f 可以持续跟踪日志输出,这在调试代码的时候非常实用。
6.3 容器时间与日志时区问题
容器默认的时区是 UTC,和北京时间相差 8 小时。你会发现容器内的日志时间总是比宿主机早 8 个小时,这对排查问题是很致命的。
最简单的解决办法是在启动容器时注入时区配置:
bash复制docker run -d \
--name myapp \
-e TZ=Asia/Shanghai \
myapp:latest
对于已有的容器,可以进入容器内部查看当前时区:
bash复制docker exec -it 容器名 date -R
如果发现时区确实不对,通常是容器内没有安装 tzdata 包,或者系统时间没同步。虽然有些镜像支持动态设置时区,但最稳的方案还是在基础镜像构建时就固定好时区,一劳永逸。
7. 常见问题排查实录与避坑经验
7.1 网络层错误:拉取镜像一直超时
这是咨询量最大的一个问题。症状通常是执行 docker pull 时等待很久,然后报错 net/http: TLS handshake timeout 或者 EOF。
排查思路按顺序来:
- 检查系统网络本身是否正常,先执行
ping -c 4 baidu.com,如果 ping 不通,是系统网络问题。 - 检查 DNS 配置,修改
/etc/resolv.conf,换成223.5.5.5或114.114.114.114这类公共 DNS,重启 Docker 后再试。 - 检查是否已配置镜像加速器,如果没配或配置的地址失效,参照第 3 节重新配置。
我遇到过一种特殊场景,虚拟机里配置了代理网络,导致 apt 和 docker 的流量走了不同的代理策略,docker 拉取时一直超时。后来直接把代理环境变量清掉才解决。所以如果你的环境配过代理,优先检查这里。
7.2 权限问题:Got permission denied while trying to connect
这个错误几乎可以断定是没有加入 docker 用户组,或者加入后没有重新加载用户组信息。按我前面第 4.1 节的操作执行即可。
还有一种情况是在脚本里执行 docker 命令,即使当前用户已经在 docker 组里,但脚本是通过 cron 或其他服务执行,没有继承用户组权限。这时要么在脚本里加 sudo -E,要么在 crontab 前列出 docker 用户组来规避。
7.3 root 文件系统打满:磁盘空间不足
容器运行时间长了,镜像越来越大,日志文件也会持续增长。如果没有定期清理,/var/lib/docker 可能把系统盘塞满。Ubuntu 系统里查看磁盘空间的命令是:
bash复制df -h
如果发现使用率达到 90% 以上,先看下大文件主要在哪:
bash复制sudo du -sh /var/lib/docker/*
常见清理办法是执行 docker system prune 清掉无用容器和悬空镜像,再用 journalctl --vacuum-size=200M 把系统日志限制住。日志轮转问题最好在启动容器时就加上 --log-opt max-size=10m --log-opt max-file=3,从源头控制单个容器日志大小。
7.4 内核兼容性问题:Ubuntu 旧版本上的奇怪报错
Docker 对 Linux 内核有最低版本要求,一般要求内核版本在 3.10 以上。如果你的 Ubuntu 版本太老,安装时可能会报 Unsupported kernel 或者服务启动失败。
排查命令:
bash复制uname -r
内核在 4.15 以上基本都能跑,如果过旧,建议直接升级系统。这个问题主要出现在老设备上,大部分人用 20.04 及以上版本都不会遇到。
7.5 Docker 服务起不来的排查路径
如果安装完成后,systemctl status docker 显示启动失败,常见的诱因包括:
- 配置文件
/etc/docker/daemon.json写成了非法 JSON 格式,Docker 守护进程读不了配置直接退出。 - containerd 和 dockerd 的服务端口被占用。
- 系统资源不足,cgroup 配置异常。
排查时可以先用:
bash复制sudo dockerd --debug
用调试模式在前台启动 Docker 守护进程,看到具体报错信息再对症下药。这个方法非常有效,几乎能查到 90% 的启动问题。我之前配置 daemon.json 时不小心加了个注释,结果 JSON 解析失败,前台启动时立刻报错了。
7.6 虚拟机环境中的额外注意点
如果你是 VMwaWare 或 VirtualBox 里跑的 Ubuntu,容器内部网络默认是通过 NAT 方式访问外网的,一般不用额外设置就能联网。但如果你在容器里启动了一个 Web 服务,需要从宿主机访问,请确保端口映射正确,且防火墙没有拦截相关端口。
Ubuntu 自带的 ufw 防火墙如果开启了,记得放行 Docker 映射的端口。这个坑我踩过很多次,容器明明在跑,但宿主机浏览器就是访问不了,十有八九是防火墙拦住了。
8. 写在最后:我的安装经验总结
装 Docker 这几次下来的最大感受是,大部分问题都出在环境细节上,比如系统版本、架构、网络、源配置、权限。很多教程默认大家都懂这些前置知识,但小白恰恰缺少这些背景,所以总是照着教程一步步执行却反复报错。
我个人在实际操作中最常用的组合是:Ubuntu 22.04 系统 + 官方 apt 源安装 docker-ce + 国内镜像加速器 + 加入 docker 组。这套组合在物理机和虚拟机上都验证得很稳,学习成本低,也完全够日常使用。等理解了 Docker 的基本模型之后,再去研究 Dockerfile、数据卷、网络模式、Compose 编排,就会顺畅很多。
最后再分享一个小技巧:安装完成后,建议立刻确认当前用户、Docker 服务、镜像源这三件事都在预期状态,再跑 hello-world 做验证。只要这三步没问题,后面遇到的容器技术问题就不是环境问题了,而是真正的用法问题,搜索和讨论起来都会更有条理。
