接触Linux这些年,帮人排查Docker安装问题的次数已经数不清了。每次重装完Ubuntu,我都会把"装Docker"列在必做清单前几位。为什么?因为这件事看着简单,翻车点却特别多:教程版本参差不齐,有人装的是Docker Engine,有人以为Docker Desktop就是Docker,还有人装完发现拉不了镜像,又或者docker命令一直提示Permission denied。这篇文章不玩虚的,直接把"Ubuntu上安装Docker"这件事的完整流程、选型逻辑、踩坑记录和后续用法一次讲透。
适合刚接触Ubuntu、在虚拟机或服务器上折腾环境、以及被各种教程相互矛盾搞蒙圈的同学。如果你已经装好了Docker,也可以直接跳到自己关心的章节看排查和进阶内容。
1. 安装前先分清:Docker Engine与Docker Desktop不是一回事
很多新手一上来就懵,因为网上教程一会儿说"Docker Desktop安装教程",一会儿说"Ubuntu安装Docker"。这其实是两个不同的东西,搞混了后面全是坑。
1.1 先搞清楚你要装的是哪个
Docker Desktop是带图形界面的完整开发环境,在Windows和macOS上用得最多,后来也出了Linux版。它把Docker Engine、CLI、Kubernetes、文件共享等功能打包在一起,用傻瓜化的界面管理容器。
但你在Ubuntu服务器上做部署、跑微服务,绝大多数场景用的是Docker Engine,也就是纯命令行的那一套。它的组成其实不复杂:Docker Engine(守护进程)、Docker CLI(客户端命令)、containerd(容器运行时)、docker-compose-plugin(编排插件)。把这几个装好,日常开发部署基本全覆盖了。
我个人的建议是:如果是在服务器或云主机上,装Docker Engine足够了,不需要Docker Desktop。Docker Desktop更适合有图形界面的开发机,而且它对虚拟化支持有额外要求,纯命令行环境反而更省心。
1.2 检查系统版本和CPU架构
动手安装之前,先花一分钟看看自己是什么系统环境。Docker官方要求64位系统,内核版本不能太低。命令如下:
bash复制cat /etc/os-release
lsb_release -a
uname -a
接着看CPU架构,这个直接影响后面添加apt源时用的架构标识,以及镜像拉取时的版本选择。x86_64对应amd64,ARM64对应arm64。树莓派、飞腾、鲲鹏这类ARM设备上装,下载地址和后续操作会有差异。
bash复制uname -m
dpkg --print-architecture
Ubuntu 22.04 LTS是我目前用得最多、也最稳定的版本,24.04 LTS也进入维护期了。如果你还在用20.04之前的版本,建议先升级系统再装Docker,不然容易遇到依赖冲突和一些底层内核特性缺失的问题。
1.3 清掉历史残留版本
如果你以前用旧教程装过docker.io、docker-engine这类包,或者手动搞过乱七八糟的安装,先卸干净再装新版,避免系统里出现两套冲突的配置。
bash复制sudo apt-get remove docker docker-engine docker.io containerd runc
注意,这个操作不会删除镜像、容器和数据卷,它们默认存放在/var/lib/docker目录。如果你确定里面的数据都不需要了,想彻底做一次全新安装,可以手动删掉这个目录:
bash复制sudo rm -rf /var/lib/docker
这一步很关键。我遇到过不止一次,用户装新版Docker后守护进程死活起不来,最后发现是旧版残留的配置文件或服务单元在捣乱。与其装完再排查,不如装之前就清理干净。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 三种主流安装方式,怎么选、怎么装
Ubuntu上装Docker,网上能搜到三种主流方法:apt仓库安装、官方一键脚本、二进制包安装。它们各有各的应用场景,我一个个说清楚。
2.1 apt官方仓库安装(服务器首选)
这是官方文档推荐的方式,也是在生产环境里最可控的一种。优点是后续升级方便,直接用apt upgrade就能跟着官方仓库走。
先安装必要依赖:
bash复制sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
然后添加Docker官方的GPG密钥,用来验证软件包签名:
bash复制sudo mkdir -p /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
接下来把Docker官方仓库写进apt源列表。这里有个细节需要注意:仓库地址里的系统代号必须跟当前系统匹配,比如22.04是jammy,24.04是noble,别复制错。下面命令里的$(lsb_release -cs)会自动读取当前系统的代号:
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
最后更新源并安装:
bash复制sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
这里面的组件各司其职:docker-ce是社区版引擎,docker-ce-cli是命令行工具,containerd.io负责管理容器生命周期,docker-buildx-plugin支持多架构镜像构建,docker-compose-plugin则提供容器编排能力。我建议五个都装上,后面用Compose编排服务时会省很多事。
2.2 官方一键脚本(适合快速体验)
Docker官方还提供了一个自动化安装脚本:
bash复制curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh
脚本会自动识别发行版和CPU架构,帮你装好所有组件并配置systemd服务。优点是真的省事,适合本地虚拟机或临时测试环境。
但我不建议在一台要长期维护的服务器上用这种方式。因为脚本默认安装的是当时的最新稳定版,你无法精确控制版本,后续想切换版本时反而麻烦。而且脚本执行过程中一旦网络不稳定,错误提示也不够友好,不方便排查。相比之下,apt方式更透明,每一步干了什么你心里都有数。
2.3 二进制包安装(离线环境专用)
内网环境或完全无法访问Docker仓库的机器,可以用离线安装。去Docker官方release页面下载对应架构的.deb包,下载文件名通常包含系统版本和架构信息。然后用dpkg按依赖顺序安装:
bash复制sudo dpkg -i containerd.io_*.deb
sudo dpkg -i docker-ce_*.deb
sudo dpkg -i docker-ce-cli_*.deb
sudo dpkg -i docker-buildx-plugin_*.deb
sudo dpkg -i docker-compose-plugin_*.deb
依赖顺序有讲究,得先装底层运行时containerd,再装引擎和CLI。如果报依赖缺失,就用sudo apt-get -f install补一下。这种方式虽然繁琐,但适合只允许内网访问的机器,我碰到过几次政企服务器的环境,基本都是靠这个方案搞定的。
2.4 三种方式怎么选
| 安装方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| apt官方仓库 | 可控性强、升级方便、易排查 | 需要能访问官方源 | 服务器、生产环境、长期维护 |
| 官方一键脚本 | 极简、快 | 版本不可控、网络要求高 | 临时测试、新机器快速体验 |
| 二进制包 | 完全离线可用 | 依赖顺序容易出错、升级麻烦 | 内网、离线、特殊合规环境 |
3. 装完别急着跑:权限、自启、镜像源一个都不能少
到了这一步,Docker已经装好了,但如果你直接开跑,很快会碰到第二个沮丧的点:命令跑不通。所以装完后的三件套,顺序不能乱。
3.1 把当前用户加入docker组
默认情况下,执行docker命令需要root权限或sudo。每次都要敲sudo说实话挺烦的,Docker官方提供了一个docker用户组,把当前用户加进去,重新登录后就能免sudo执行docker命令。
bash复制sudo usermod -aG docker $USER
newgrp docker
newgrp docker只是让当前终端临时切换到docker组,验证一下:
bash复制docker ps
如果你看到Got permission denied while trying to connect to the Docker daemon socket,说明当前终端还没生效。这时候别怀疑命令打错了,重新登录一次就好了。SSH连接就断开重连,桌面环境就注销再登录。这个坑我见得太多了,明明加了组,就是不重登,然后各种报错。
3.2 配置开机自启并验证
安装完成后,docker服务一般会自动注册到systemd,但还是要确认一下开机自启:
bash复制sudo systemctl enable docker
sudo systemctl start docker
然后检查服务状态和Docker信息:
bash复制sudo systemctl status docker --no-pager
docker info
docker info输出里可以关注两个关键信息:Server Version和Storage Driver,还有Docker Root Dir(默认是/var/lib/docker)。如果服务启动失败,先看journalctl -u docker.service --no-pager的日志,排查方向比瞎猜靠谱得多。
3.3 配置镜像源,别让拉取超时卡死你
装好Docker后第一件事通常是拉镜像。如果你发现docker pull执行后长时间卡住或者直接超时,大概率是网络访问Docker Hub不顺畅。解决办法是配置公共镜像源。
编辑或新建/etc/docker/daemon.json:
json复制{
"registry-mirrors": [
"https://docker.m.daocloud.io",
"https://docker.nju.edu.cn"
]
}
注意,这些公共镜像源地址可能会随时间和网络环境变化,配置前建议先确认一下当前可用的地址。改完重启Docker:
bash复制sudo systemctl daemon-reload
sudo systemctl restart docker
这里有个非常容易踩的坑:daemon.json必须是合法JSON,不能有注释,不能有多余逗号。改坏了Docker守护进程可能直接起不来。改完可以用docker info检查Registry Mirrors是否生效。
3.4 存储驱动与内核模块检查
Ubuntu默认使用overlay2存储驱动,需要内核支持overlay文件系统。绝大多数现代内核都自带,但有些精简系统或云镜像可能没加载。如果启动容器时报failed to mount overlay,先检查模块:
bash复制lsmod | grep overlay
没输出就手动加载:
bash复制sudo modprobe overlay
想永久生效,在/etc/modules-load.d/modules.conf里加一行overlay。这个操作虽然不常遇到,但遇到了就是启动不了,网上查可能要折腾半天。
4. 从hello-world到日常管理:验证安装与高频命令盘点
环境配好后,别急着部署一堆东西,先按顺序跑通几个最简单的容器,把Docker的基本操作练熟。
4.1 跑通第一个容器
bash复制docker run hello-world
这个命令会先从本地找镜像,找不到就去配置的镜像源拉取,然后运行一个打印欢迎信息的容器并退出。看到"Hello from Docker!"说明整套环境是通的。
再跑一个真实服务验证端口映射和容器网络:
bash复制docker run -d --name nginx-test -p 8080:80 nginx
浏览器访问http://服务器IP:8080,能看到nginx欢迎页说明一切正常。这里有个新手极其容易踩的坑:页面打不开,先别怀疑Docker装错了,先看docker ps里容器状态是不是Up,再检查云服务器安全组或防火墙有没有放行8080端口。我帮人排查过太多次这种"假故障"了。
4.2 日常管理命令速查
下面这张表是我平时使用频率最高的命令,先记住这些就足够应付日常操作了。
| 操作 | 命令 |
|---|---|
| 查看运行中容器 | docker ps |
| 查看所有容器 | docker ps -a |
| 查看本地镜像 | docker images |
| 拉取镜像 | docker pull nginx:latest |
| 运行容器 | docker run -d -p 80:80 --name web nginx |
| 停止/启动/重启容器 | docker stop web / docker start web / docker restart web |
| 删除容器 | docker rm -f web |
| 删除镜像 | docker rmi nginx |
| 查看容器日志 | docker logs -f web |
| 进入容器内部 | docker exec -it web bash |
| 查看资源占用 | docker stats |
| 清理悬空资源 | docker system prune |
| 查看空间占用 | docker system df |
docker system prune这个命令要慎用,它会删除所有停止的容器、未被使用的网络、悬空镜像和构建缓存。在测试环境用起来很爽,生产环境建议先看清楚再执行。
4.3 三个高频操作
进容器排障是最常见的操作。docker exec -it 容器名 bash可以打开容器内的交互式shell。但注意,有些精简镜像(比如alpine)没有bash只有sh,那就改用docker exec -it 容器名 sh。
容器和宿主机之间拷贝文件:
bash复制docker cp ./local.txt 容器名:/tmp/
docker cp 容器名:/etc/nginx/nginx.conf ./
查看日志:docker logs -f 容器名会持续跟随输出,适合排查启动失败或者看实时业务日志。容器启动失败时,先跑docker logs 容器名看最后几行,比瞎猜原因有效得多。
5. 排查实录:虚拟化检测失败、权限拒绝与镜像拉取超时
这里集中写几个我实际遇到过的报错,每个都附上完整的排查思路。这些报错在搜索热词里出现频率极高,说明大家都卡在类似问题上。
5.1 Docker Desktop虚拟化支持检测失败
现象是启动Docker Desktop时报错:Virtualization support not detected。这个提示常见于Windows或macOS下的Docker Desktop,但Linux下如果使用Docker Desktop,同样要求底层虚拟化被识别。
先看CPU是否开启了虚拟化扩展:
bash复制egrep -c '(vmx|svm)' /proc/cpuinfo
输出为0说明CPU虚拟化没有在BIOS中开启,或者你的CPU根本不支持。这时需要进BIOS开启VT-x或AMD-V。如果你用的是虚拟机里的Ubuntu,那还要在宿主机虚拟机软件里开启"嵌套虚拟化"功能,否则虚拟机内部无法再次使用虚拟化特性。
这里要多说一句:如果你只是用命令行的Docker Engine,其实不依赖CPU虚拟化扩展。这也是为什么很多人用Docker Engine没问题,一装Docker Desktop就报虚拟化错误。判断好自己到底需要哪个,能省掉很多麻烦。
5.2 权限拒绝:Got permission denied
报错全文通常是:
code复制Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock
原因几乎只有两个:用户不在docker组,或者加入了docker组但没重新登录。先确认:
bash复制groups $USER
如果输出里没有docker,重新执行sudo usermod -aG docker $USER,然后重启终端或重新登录。如果确认自己在docker组里还报权限错,多半是登录会话没有刷新组信息,直接newgrp docker切一次就能临时解决。
5.3 镜像拉取超时或卡住
现象是docker pull nginx执行后一直卡在waiting状态。排查链路是这样的:
先确认网络能通到镜像仓库:
bash复制curl -I https://registry-1.docker.io
如果curl输出正常,问题多半出在Docker配置的镜像源上;如果curl也卡住,那就是Ubuntu系统层面的网络或DNS问题。试着将DNS改成公网DNS,再重新curl。确认网络没问题后,检查Docker当前生效的镜像源:
bash复制docker info | grep -A 10 "Registry Mirrors"
如果发现配置的镜像源没生效,重新检查daemon.json的格式,然后重启Docker。如果镜像源失效了,就去换一个当前可用的公共镜像源地址。这个排查顺序能解决90%以上的拉取超时问题。
5.4 /var/lib/docker空间不足
容器、镜像、数据卷默认都存放在/var/lib/docker下。系统盘小的机器跑一段时间,很容易出现磁盘占用100%导致容器无法启动的情况。
解决办法是把Docker数据目录迁移到大分区。先停服务,再同步数据:
bash复制sudo systemctl stop docker
sudo rsync -avzP /var/lib/docker/ /data/docker/
然后修改daemon.json,加上:
json复制{
"data-root": "/data/docker"
}
重启Docker:
bash复制sudo systemctl daemon-reload
sudo systemctl start docker
注意rsync命令里源目录末尾的斜杠,写错会导致目录层级多套一层。迁移完成后确认容器都能正常启动,再把旧的/var/lib/docker清理掉。这个操作我做过很多次,每次都能让一台"卡死"的机器立刻恢复活力。
6. 实战:用Compose一键部署MySQL 8.0和Redis,装完Docker之后接着干什么
装好Docker只是开始,真正的价值在于用它快速拉起各种服务。这一章直接演示最常见的场景:用Docker Compose部署MySQL 8.0和Redis。
6.1 为什么用Compose
当容器数量多起来之后,一条条docker run命令是管不过来的。Compose用一份YAML文件描述一组服务,一条docker compose up -d全部搞定,停止时docker compose down一起清理。这才是Docker在日常开发部署中的正确打开方式。
6.2 部署MySQL 8.0并验证
在工作目录新建docker-compose.yml:
yaml复制services:
mysql:
image: mysql:8.0
container_name: my-mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: ChangeMe123
ports:
- "3306:3306"
volumes:
- ./mysql-data:/var/lib/mysql
command:
- --character-set-server=utf8mb4
- --collation-server=utf8mb4_unicode_ci
启动:
bash复制docker compose up -d
docker ps
有几个细节值得注意。一是MYSQL_ROOT_PASSWORD必须设置,否则MySQL容器起不来,这是初始化流程的硬性要求;二是数据目录挂载到宿主机./mysql-data,容器删了数据还在,这是容器化部署的基本素养;三是端口映射"3306:3306",如果宿主机3306已被占用,改成"33061:3306"。
验证连接:
bash复制docker exec -it my-mysql mysql -uroot -p
宿主机有MySQL客户端的话,也可以用mysql -h127.0.0.1 -P3306 -uroot -p连接。这里有一个坑:MySQL 8.0默认认证插件是caching_sha2_password,老版本客户端可能连不上,需要创建用户时指定mysql_native_password插件,或者升级客户端驱动。
6.3 在同一份Compose文件里加Redis
在docker-compose.yml里继续加一段:
yaml复制 redis:
image: redis:7
container_name: my-redis
restart: always
ports:
- "6379:6379"
command: redis-server --appendonly yes
volumes:
- ./redis-data:/data
--appendonly yes开启AOF持久化,数据写入./redis-data。如果你的Redis需要外部访问,可以在command里加--requirepass yourpassword设置密码,或者用配置文件挂载的方式管理。
启动:
bash复制docker compose up -d
docker exec -it my-redis redis-cli -a yourpassword ping
看到PONG说明Redis正常。我现在部署很多轻量级中间件都是这么干的,省心省力,迁移机器时把目录和compose文件一拷就完事。
6.4 安全提醒:别把端口裸暴露到公网
数据库、Redis这类服务不要直接映射到0.0.0.0。默认的-p 3306:3306会监听所有网卡,如果你的服务器有公网IP,等于把数据库暴露到了公网上,这是非常危险的行为。建议只监听本机或内网:
yaml复制ports:
- "127.0.0.1:3306:3306"
这样只有本机能访问,配合防火墙或安全组规则,把风险降到最低。另外容器中用root账号跑数据库也会带来额外的安全风险,生产环境建议通过环境变量指定普通用户运行,或者使用官方提供的加固镜像。
我个人的实际体会是,Docker装好只是第一步,真正决定后面顺不顺的是三个习惯:加组后记得重新登录、安装后就配好镜像源、时刻注意数据卷和端口映射。这三个小事我在无数台机器上踩过,写出来就是希望你能少走点弯路。刚装完第一台机器的话,别急着把环境搞复杂,先跑一个nginx,再跑一个MySQL,把docker ps、docker logs、docker exec这几个基本操作练熟,后面不管部署什么项目都更有底气。
