news 2026/9/18 3:20:21

Ubuntu安装Docker避坑指南:从Engine到Desktop全面详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Ubuntu安装Docker避坑指南:从Engine到Desktop全面详解

前阵子在几个技术群里连续看到同一种提问:Ubuntu下装Docker,明明每一步都照着教程敲了,docker --version也输出了版本号,但一执行docker run就报permission denied,或者干脆告诉你docker command not found。仔细一问,才发现大部分人根本不清楚自己装的是Docker Engine还是Docker Desktop,也不知道apt源里的docker.io和官方源里的docker-ce其实是两个东西。

这种状况我太有体会了。Docker在Ubuntu上的安装路径有好几条,每条路径适合的场景、后续维护方式完全不一样。这篇文章我把自己这几年在Ubuntu(从20.04到24.04都折腾过)上安装Docker的经验完整捋一遍,覆盖安装前的准备、两种安装方式的取舍、官方源完整安装流程、装完必做的配置、高频问题排查,以及装好之后用几个典型应用验证环境。适合两类人:刚接触Linux、想把Docker跑起来的新手;装过但总出各种怪问题、想搞明白底层原理的老手。

1. 动手之前先弄清三件事:架构、版本、权限

1.1 用两条命令确认系统架构

很多人上来就复制粘贴安装命令,第一步就错在没确认架构。Ubuntu可以跑在x86_64、ARM64(树莓派、苹果M系列机器、不少云服务器都是),还有龙芯这类的国产平台,而Docker的安装包和镜像是严格区分架构的。在ARM机器上强行装amd64的包,能装上但不一定能跑起来,最典型的就是容器启动后立刻退出,日志里一行exec format error。

确认架构只需要两条命令:

uname -m dpkg --print-architecture

uname -m输出x86_64,说明是Intel/AMD的64位架构,对应Docker源里的amd64包;输出aarch64,对应arm64;龙芯平台一般输出loongarch64。dpkg --print-architecture的作用是确认当前系统的apt能接受哪些架构的软件包,它和uname -m正常情况下是对应的。偶尔会碰到不一致的情况,比如手动换过内核或者开启了多架构支持,这时候要以dpkg的输出为准,因为它直接决定了apt源URL里的arch字段。

这一步千万别跳过。我见过有人把arm64的安装命令硬搬到x86机器上,报错刷了一屏也不知道问题在哪,其实就是最基础的架构匹配没做。

1.2 Ubuntu版本与Docker版本的兼容对照

Docker官方APT源对Ubuntu的版本支持很宽泛。20.04(Focal)、22.04(Jammy)、24.04(Noble)这些长期支持版本都在官方源里有对应目录。Ubuntu各个版本的别名(codename)在安装源时会自动读取,不需要手动填。

这里必须先区分清楚两个容易被搞混的包:

  • docker.io:Ubuntu官方软件源里维护的Docker,好处是apt install docker.io一条命令搞定,坏处是版本更新滞后上游很多。
  • docker-ce:Docker官方源里的社区版引擎,跟随上游版本快速迭代,装完就是当前稳定的最新版。

实操里我几乎无脑推荐docker-ce。原因不是docker.io有多差,而是用docker-ce等于把Docker的升级节奏掌握在自己手里,遇到需要特定新特性的场景不会卡壳。docker.io的问题在于版本老,而且和后来官方主推的Docker Compose插件配合时经常有兼容性问题,因为它默认不包含几个关键的插件包。

查版本可以通过apt-cache policy命令,比如:

apt-cache policy docker-ce docker.io

如果输出里只有docker.io而没有docker-ce,说明还没添加Docker官方源,这是正常现象,后面第3章会讲到。

1.3 内存、磁盘和依赖的底限要求

Docker引擎本身不重,真正吃资源的是你跑起来的容器。Ubuntu桌面版建议至少4GB内存、20GB空闲磁盘;纯服务器版本只跑几个轻量容器的话,2GB内存也够用,但你要在同一个机器上跑MySQL加GitLab,那4GB以下就不要想了。

官方安装方式还依赖几个基础包,用来下载、校验GPG密钥和解析HTTPS源:

sudo apt update sudo apt install -y ca-certificates curl gnupg

ca-certificates负责HTTPS证书校验,curl用来拉取密钥文件,gnupg用来做GPG密钥的导入和解析。缺任何一个,后续添加源的步骤都会失败。有些精简系统连apt-add-repository都没有,建议顺手把software-properties-common也装上,后面加源会方便很多。

2. Docker Engine和Docker Desktop,选哪个

2.1 两者的本质区别

Docker Engine是一个后台守护进程加一套命令行工具,跑在Linux上,完全没有图形界面。它直接使用宿主机的Linux内核能力来管理容器,性能开销极小。

Docker Desktop虽然名字里带Docker,但在Ubuntu上的实现方式完全不一样——它内置了一个轻量级虚拟机,通过VM来运行Docker引擎。因为在macOS或Windows上,Docker无法直接调用宿主机的Linux内核,所以Desktop选择用VM兜底。到了Ubuntu上,Desktop也沿用了这套方案,好处是图形界面直观、文件挂载和资源配置都有面板可以调,坏处是多了一层虚拟化,内存占用明显增加,而且依赖宿主机开启了虚拟化支持。

2.2 我给不同场景的取舍建议

云服务器、CI节点、树莓派这类无头环境,直接装Engine,不用考虑Desktop。个人Ubuntu桌面机,我反而更建议先装Engine,配合VS Code的Dev Containers插件和Docker官方插件,日常使用体验已经很接近Desktop,还没有那层VM的额外开销。

Desktop更适合这种情况:你习惯用图形界面操作,需要在一台机器上管理多个Docker上下文,或者开发环境需要频繁切换不同版本的Docker引擎。它是把学习和操作成本换成了可视化和便捷性,代价是性能。

有一点必须警告:Desktop和Engine不能同时运行。它们抢同一个/var/run/docker.sock,谁先启动谁占用。见过有人装了Desktop又按教程装Engine,结果docker info的输出一会儿是Desktop的VM环境,一会儿是本地Engine,极其混乱。要切换必须先彻底停掉另一个。

2.3 虚拟机里装Ubuntu再装Docker的特殊情况

热词里有一批搜索"VMware虚拟机安装Ubuntu"的人,所以这个场景值得单独说。如果你在VMware里装好了Ubuntu,下一步要装Docker,优先装Engine。纯Engine直接调用内核能力,不要求嵌套虚拟化,VMware默认配置就能跑。

但你要是非要在虚拟机里装Docker Desktop,大概率会碰到那种"virtualisation support wasn't detected"的报错。这不是Docker的问题,是Desktop的VM需要嵌套虚拟化而VMware默认没开启。解决办法是关掉虚拟机,在VMware的虚拟机设置里找到处理器配置,勾选"虚拟化Intel VT-x/EPT或AMD-V/RVI",重启虚拟机。VirtualBox里对应的选项叫"启用嵌套VT-x/AMD-V"。

如果你一开始就打算在虚拟机里用Docker,老老实实走Engine路线,能省掉一整套嵌套虚拟化的麻烦。

3. 手把手装Docker Engine:官方源方式

3.1 先清理历史残留

如果这台机器之前用apt装过docker.io,或者用过某个第三方脚本装过Docker,先卸载干净再装官方版。不清理的话可能出现两个Docker进程抢同一个socket,或者systemd服务名冲突导致服务反复重启。

sudo apt remove docker docker-engine docker.io containerd runc

注意,这条命令不会删除镜像、容器和已创建的卷,它们默认放在/var/lib/docker目录下。如果你确定这些数据都不要了,手动清一下这个目录可以释放磁盘。不确定就留着,毕竟删了找不回来。

3.2 添加Docker官方GPG密钥和APT源

这是整个安装过程出错率最高的环节,也是很多人卡住的地方。GPG密钥的作用是让apt在下载软件包时校验签名,防止源被污染或软件包被篡改。

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是创建/etc/apt/keyrings目录并赋予0755权限,目录权限不对会导致apt update报错。chmod a+r是让公钥文件对其他用户可读,否则非root用户执行apt update会有权限警告,虽然不一定致命,但很烦。

然后写入APT源:

echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] 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")会自动读取系统版本代号,比如24.04对应noble,22.04对应jammy。如果这个变量读出来是空的,多半是系统文件异常,手动把版本代号写死也行。

3.3 apt update加install完成安装

源添加完之后必须更新索引,让apt认识和docker-ce相关的包:

sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

有人只装了docker-ce,漏掉containerd.io,后面会发现容器根本起不来。原因在于containerd是Docker真正用来管理容器生命周期的基础组件,Docker的守护进程只是它的客户端。docker-buildx-plugin是新一代镜像构建工具,docker-compose-plugin是官方推荐的Compose插件,都建议一步到位装上。

安装完先验证服务状态:

sudo systemctl enable --now docker sudo systemctl status docker

enable设置开机自启,--now表示立即启动。看到active (running)说明引擎已经跑起来了。

3.4 免sudo运行和Hello World验证

Linux下Docker守护进程默认以root身份运行,普通用户直接敲docker命令会报权限不足。把当前用户加入docker组就能免sudo运行:

sudo usermod -aG docker $USER newgrp docker

newgrp docker让当前终端立即生效,不用注销重登。这里必须提醒一句:docker组内的用户相当于拥有宿主机root权限,因为docker socket可以挂载宿主机任意目录到容器、以任意权限执行命令。所以只给可信用户加组,别图方便给一堆人加。

验证安装是否打通,跑一下官方测试镜像:

docker run hello-world

这条命令会自动从Docker Hub拉取一个极小的测试镜像并执行,看到"Hello from Docker!"就说明从守护进程到镜像仓库的整条链路都是通的。

4. 装完别急着用:这几项配置能帮你少踩很多坑

4.1 daemon.json:镜像加速加日志限制

Docker守护进程的配置集中在/etc/docker/daemon.json,文件不存在就自己建一个。我装完Docker的第一件就是写日志大小上限,否则一个服务端容器跑几周,日志文件能轻松占掉几个GB磁盘,到时候排查故障还要先清理日志,浪费时间不说,弄不好直接拖垮磁盘。

{ "registry-mirrors": ["https://你的加速地址"], "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } }

registry-mirrors配置镜像加速器。选择加速服务时要谨慎,不要填来路不明的地址,最好用自己实测可用的可靠服务。改完配置重启Docker:

sudo systemctl daemon-reload sudo systemctl restart docker

特别注意,daemon.json必须是合法的JSON格式。多一个逗号、少一个引号,直接导致Docker服务启动失败。写完后可以先验证一下格式:

sudo dockerd --validate

这个命令可以提前发现JSON语法错误,避免重启后才发现问题。

4.2 Docker Compose插件

现在官方推荐的是docker compose插件(注意是docker compose,中间有空格),不是老牌的docker-compose(带横杠)。前面安装步骤里已经装了docker-compose-plugin,验证一下:

docker compose version

docker compose是Go语言写的插件,集成在Docker CLI里,不需要额外装在PATH里;docker-compose是Python生态的老工具,需要独立安装。两者命令格式基本一样,但新环境直接使用插件版本,后续升级维护都更省心。

4.3 常用命令速查

使用场景命令
查看运行中的容器docker ps
查看所有容器docker ps -a
进入容器交互终端docker exec -it 容器名 bash
查看容器实时日志docker logs -f 容器名
停止/启动容器docker stop/start 容器名
构建镜像docker build -t 名称:标签 .
拉取镜像docker pull 镜像名
查看镜像列表docker images
查看磁盘占用docker system df

这几个命令覆盖了日常运维和开发的绝大部分需求。我自己的习惯是alias几个高频命令,比如dcf='docker compose',省去重复敲长命令的时间。

4.4 进程守护与自动重启

跑长期服务的容器一定记得加--restart unless-stopped,这样宿主机重启后容器会自动拉起,除非你手动停止过。举个Nginx的例子:

docker run -d --name nginx --restart unless-stopped -p 80:80 nginx:alpine

对于长期服务容器,这个配置我觉得是必须的。不然宿主机一次意外重启,所有容器全部处于停止状态,排查起来还以为是Docker坏了,实际上只是没设置自动重启策略。

5. 安装与使用中的高频坑:从启动失败到容器起不来

5.1 docker服务启动失败的排查链路

装完Docker执行sudo systemctl start docker卡住,或者status显示failed,第一步永远是看日志而不是重装:

sudo journalctl -u docker --no-pager | tail -50

我遇到过的几类高频原因:

  • iptables相关报错:宿主机启用了较严格的防火墙,和Docker默认的NAT规则冲突。日志里会出现iptables failed的提示。先执行iptables -L看看链的情况,再做针对性放行。
  • 存储驱动问题:overlay2不可用时Docker会自动回退到vfs,性能会差很多。日志里会有storage-driver的警告。
  • daemon.json写坏:JSON语法错误会让守护进程拒绝启动,日志会明确指向配置文件。改回合法格式就行。

排查的原则是从日志找根因,定位到具体哪一个环节出问题再动手,盲目重启或重装只会让问题更隐蔽。

5.2 虚拟化支持未开启的报错

在VMware虚拟机里跑Docker Desktop,启动时大概率遇到"虚拟化支持未检测到"之类的问题。这其实是Desktop的虚拟机需要嵌套虚拟化。前面2.3节已经说了设置位置,这里再补充一点排查思路:先确认宿主机的BIOS/固件里CPU虚拟化是否开启(Intel VT-x或AMD-V),再去虚拟机设置里勾选,两层都满足才行。

纯Engine用户遇到这个问题的情况少得多,因为Engine不依赖嵌套虚拟化,它直接把容器跑在宿主机内核对容器共享的cgroup和namespace上。

5.3 SSH连不上和Docker安装的潜在关联

这个坑很有意思,搜索热词里"ubuntu ssh无法连接"和"Docker安装"经常出现在同一个人身上。Docker安装过程本身完全不影响SSH,但安装后很多教程会让你调试防火墙、iptables规则,这时候手一抖把默认策略设成DROP,或者清空了iptables,22端口就再也连不上了。

防止这个问题有一条铁律:

做任何防火墙或iptables改动前,先开一个新的SSH会话测试现有的连接能保持,再动手修改。万一改了立刻失联,还能用保留的会话回滚。

如果不幸已经连不上,只能通过控制台(云厂商的VNC或VMware的本地控制台)登录,恢复到之前的防火墙规则。

5.4 PATH环境变量被覆盖的恢复方法

热词里"ubuntu环境变量配置错误"经常和Docker一起出现,典型操作是想把Docker的路径加到PATH里,却写成了:

export PATH=/usr/local/bin

少了$PATH后缀,等于把系统原有的PATH全部覆盖。结果ls、grep、sudo全部失效,终端基本瘫痪。这时候不要慌,用绝对路径调用系统命令来恢复:

/bin/echo $PATH /usr/bin/export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

然后把.bashrc或.profile里那行错误的export删除或改正。这件事给我的教训很深刻:改环境变量之前先把原始值复制出来保存,养成这个习惯能省掉后面的所有麻烦。

5.5 架构不匹配导致的exec format error

在所有Docker报错里,exec format error属于最让人摸不着头脑的一类。容器能跑起来,但一执行命令就报这个错误,或者容器秒退。根源几乎都是镜像架构和宿主机架构不一致——比如在ARM机器上拉了amd64镜像,或者在龙芯这类非主流架构上默认拉到了x86镜像。

用docker image inspect查看镜像的实际架构,再和uname -m对照。多架构镜像通常会自动选择匹配平台,但老镜像或某些特定包会拉错,这时可以显式指定平台:

docker run --platform linux/amd64 ...

如果你的场景是龙芯这样的小众架构,需要额外确认软件源里的docker-ce是否匹配本机架构,以及业务镜像是否提供了对应的版本。这类问题不是装得不仔细,而是整个容器生态对特定架构的支持还在完善,遇到了能想到这一层,排查方向就不会错。

6. 装好之后能干什么:用三个典型场景验证安装没问题

6.1 MySQL 8.0单机与主从

装好Docker后我习惯用数据库容器验证持久化卷和端口映射是否正常工作:

docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=你的密码 \ -v mysql-data:/var/lib/mysql \ --restart unless-stopped \ mysql:8.0

-v mysql-data:/var/lib/mysql创建了一个命名卷,容器删除后数据仍然保留。验证持久化是否生效最简单的办法:docker rm -f mysql8,再用同样的命令重建,数据还在,说明卷挂载没问题。

MySQL主从在Docker里的思路是启动两个MySQL容器,主库开启二进制日志并配置server-id,从库指定主库地址执行CHANGE REPLICATION SOURCE TO。两个容器之间要能互访,最简单的方式是同一个自定义网络:

docker network create mysql-net

6.2 GitLab私有代码仓库

小团队做代码托管,Docker跑GitLab非常省事,一条Compose或一条docker run就能搭起来:

export GITLAB_HOME=/srv/gitlab docker run -d \ --name gitlab \ --hostname gitlab.example.com \ -p 443:443 -p 80:80 -p 22:22 \ --restart always \ -v $GITLAB_HOME/config:/etc/gitlab \ -v $GITLAB_HOME/logs:/var/log/gitlab \ -v $GITLAB_HOME/data:/var/opt/gitlab \ gitlab/gitlab-ce:latest

GitLab是一个比较吃内存的应用,官方建议4GB起步。2GB的服务器跑起来会非常吃力,swap报警基本是常态。第一次启动初始化大概要三到五分钟,期间容器状态是healthy之前不要反复重启,等它自己完成。访问页面出现root初始密码时,其实安装就已经得到了验证。

6.3 定时任务管理工具:青龙面板

青龙面板是一个带Web界面的定时任务管理和依赖管理工具,很多人用它统一管理服务器上的定时脚本,以及容器内Node.js和Python的运行依赖。

docker run -d \ --name qinglong \ -p 5700:5700 \ -v qinglong-data:/ql/data \ --restart unless-stopped \ whyour/qinglong:latest

启动后浏览器访问http://服务器IP:5700,按向导初始化即可。面板里的依赖管理功能可以给容器内的脚本运行环境安装常用库,本质是调用容器内的包管理器来实现,界面化操作省去了每次docker exec进去手动安装的流程。对于通过这个案例,你还能顺带理解一个通用原理:Docker容器里的环境和宿主机是隔离的,容器内缺什么依赖,要在容器内安装,不能指望宿主机上的包对容器产生作用。

6.4 数据持久化与容器生命周期管理

这些应用验证下来,核心其实都在数据持久化。Docker容器本身是无状态的,删除重建就是全新环境,所以有状态服务必须使用卷或目录挂载。我的管理习惯有三条:

  • 给每个长期服务取明确的名字,docker ps一眼能认出来是干什么的。
  • 所有需要保留的数据都挂载到命名卷或宿主机固定目录,绝不让状态留在容器可写层里。
  • 删除容器用docker rm -f之前,先确认数据目录还在,养成这个习惯就不会出现"删容器把数据库一起删了"的悲剧。

做到这三点,Docker就可以像管理普通系统服务一样稳定使用,这也是我敢把MySQL、GitLab长期跑在容器里的底气。

最后说一点个人体会。装Docker这一步本身确实不难,熟练的几分钟就能弄完,但装完之后的配置细节——日志上限、镜像加速、免sudo、自动重启——才是决定用得顺不顺的关键。我自己每次装完都会按这套流程走一遍,把绝大多数后续的坑提前堵住。如果你装完遇到奇怪的报错,先别急着重装系统或者卸载重装,按第五部分的思路从日志入手找根因,定位到问题再动手,往往比反复折腾省下大把时间。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 3:19:41

代理模型双路线:工程仿真响应面与AI助手本地云端路由

1. 先把“代理模型”这词拆开,它其实是两条完全不同的技术路线“代理模型”这四个字,在技术圈里被用得相当混乱。我在不同场合听到它,指的东西能差出十万八千里:做结构优化的工程师说的是surrogate model,做 AI 应用的…

作者头像 李华
网站建设 2026/9/18 3:16:23

切 Claude 聊天与 Cowork,TaoToken Key 差异在哪

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 3:12:22

Agent-Reach:AI Agent 工具调用受控触达与可观测实践

1. Agent-Reach 是什么:把"会聊天"变成"能动手"的那一层Agent-Reach 这个项目名我第一次看到的时候,第一反应不是"又一个 Agent 框架",而是想起了去年让我们半夜爬起来的那次线上事故。模型的回答漂亮得挑不出…

作者头像 李华