十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

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

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-architectureuname -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.04Focal、22.04Jammy、24.04Noble这些长期支持版本都在官方源里有对应目录。Ubuntu各个版本的别名codename在安装源时会自动读取不需要手动填。这里必须先区分清楚两个容易被搞混的包docker.ioUbuntu官方软件源里维护的Docker好处是apt install docker.io一条命令搞定坏处是版本更新滞后上游很多。docker-ceDocker官方源里的社区版引擎跟随上游版本快速迭代装完就是当前稳定的最新版。实操里我几乎无脑推荐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 gnupgca-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 wasnt 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 ar /etc/apt/keyrings/docker.gpg这里有几个细节需要解释。install -m 0755 -d是创建/etc/apt/keyrings目录并赋予0755权限目录权限不对会导致apt update报错。chmod ar是让公钥文件对其他用户可读否则非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对应noble22.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 dockerenable设置开机自启--now表示立即启动。看到active (running)说明引擎已经跑起来了。3.4 免sudo运行和Hello World验证Linux下Docker守护进程默认以root身份运行普通用户直接敲docker命令会报权限不足。把当前用户加入docker组就能免sudo运行sudo usermod -aG docker $USER newgrp dockernewgrp 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配置镜像加速器。选择加速服务时要谨慎不要填来路不明的地址最好用自己实测可用的可靠服务。改完配置重启Dockersudo 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 versiondocker 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几个高频命令比如dcfdocker 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或者清空了iptables22端口就再也连不上了。防止这个问题有一条铁律做任何防火墙或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-net6.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:latestGitLab是一个比较吃内存的应用官方建议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、自动重启——才是决定用得顺不顺的关键。我自己每次装完都会按这套流程走一遍把绝大多数后续的坑提前堵住。如果你装完遇到奇怪的报错先别急着重装系统或者卸载重装按第五部分的思路从日志入手找根因定位到问题再动手往往比反复折腾省下大把时间。
返回列表