ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ubuntu安装Docker与Compose实战教程:配置、加速与排错

Ubuntu安装Docker与Compose实战教程:配置、加速与排错 1. 动手之前先弄明白这三件事能省掉一半的折腾在Ubuntu上装Docker搜索引擎里教程一抓一大把但照着抄完翻车的概率其实很高。我自己见过太多人把系统装到一半卡住或者装完后docker ps直接报错然后开始怀疑人生。其实大部分问题不是操作不对而是装之前少了关键的那几步。先说清楚这篇东西要解决什么在Ubuntu上把Docker引擎和Docker Compose装好、配好、跑起来第一个容器并且以后不会再被“装完不知道怎么用”困扰。不论你是刚接触Linux的小白还是已经把Ubuntu当主力机的开发者这套流程都适用。在敲任何命令之前有三件事我建议你先想清楚。第一件事确认你的Ubuntu版本。Docker对系统版本很挑剔目前官方支持的是Ubuntu的LTS版本。你可以用lsb_release -a或者cat /etc/os-release查看。绝大多数情况下Ubuntu 20.04和22.04是网上教程的主流版本24.04也早就被官方支持了。如果你还在跑16.04或者18.04听我一句劝先升级系统再装Docker别在旧版本上折腾有些依赖和内核特性跟不上装完也是带病运行。第二件事搞清楚你的机器架构。绝大多数人是x86_64也就是amd64架构。但如果你用的是树莓派或者ARM云服务器下载Docker时就要选对架构这一点在后面安装compose的时候特别容易踩坑我见过好几个人把这个忽略掉导致装完提示Exec format error。第三件事也是最重要的一件事——Docker对内核有要求。Ubuntu的内核版本不能太老否则overlay2存储驱动无法正常工作。可以用uname -r查看内核版本一般4.x以上都没问题。如果你是VMware或者VirtualBox里跑的Ubuntu请务必确认虚拟化已经开启不然Docker Desktop或者某些容器运行时会直接罢工。提示如果是在云服务器上操作记得看下发行版的镜像源用的是国内的还是官方的这个会影响后面apt源更新的速度后面我会专门讲怎么处理。这三件事确认完了再说一个几乎人人都会忽略的问题——旧版本Docker的清理。很多人机器上其实已经装过Docker要么是apt install docker.io装的老版本要么是当时测试用的残留。不清理干净直接装新版轻则命令冲突重则服务起不来。清理方式很简单sudo apt remove docker docker-engine docker.io containerd runc如果之前用的是官方源安装的docker-ce那就需要sudo apt remove docker-ce docker-ce-cli containerd.io docker-compose-plugin还有一个隐藏很深的地方——Snap版Docker。Ubuntu桌面版有时会自动通过Snap预装docker或者podman这玩意儿和apt源里的Docker有可能冲突。用snap list看一眼如果有docker相关的先sudo snap remove docker卸载掉。这些事情都处理完就可以正式开始装了。2. 为什么我不建议你用apt直接装docker.io很多教程会告诉你直接一句sudo apt install docker.io搞定。这句话在十年前是合理的现在这样做就是给自己挖坑。docker.io这个包是Ubuntu从Debian仓库里同步过来的它的版本更新永远比Docker官方源慢好几个版本。Docker是一个迭代非常快的项目新版修复的漏洞、新增的特性、优化的性能你用老版本统统享受不到。更重要的是docker.io包不会安装docker-compose-plugin也就是Compose v2插件。这就意味着你后续要用Docker Compose还得再折腾一遍。Docker官方推荐的方式是配置官方的apt源然后安装docker-ce。这里的ce是Community Edition虽然是社区版但功能已经很完善了。官方源里的包版本全、更新及时出了问题社区里也更容易找到对应方案。具体操作分四步。第一步安装依赖软件包让apt能够通过HTTPS使用新的源sudo apt update sudo apt install ca-certificates curl gnupg lsb-release这里的ca-certificates是为了让apt能验证HTTPS证书curl用来下载GPG密钥gnupg用来处理和验证密钥lsb-release会读取你系统的发行版版本信息。这四个缺一不可尤其gnupg很多教程里没提结果后续添加密钥时报错gpg: command not found。第二步添加Docker官方的GPG密钥这一步是为了让apt信任从Docker仓库下载的软件包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注意这个/etc/apt/keyrings目录新版本的apt对GPG密钥的位置要求很严格。以前老教程都是直接把密钥放到/etc/apt/trusted.gpg.d/那样在较新的系统上会触发警告。还有chmod ar这一步也别省否则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这段命令里有几个变量值得说一下。$(dpkg --print-architecture)会自动检测你的CPU架构保证下载的是对应架构的包。$(lsb_release -cs)会读取你的Ubuntu版本代号比如22.04是jammy24.04是noble。这样做的好处是当你以后升级Ubuntu大版本时这个源还能继续用不会出现源和系统版本不匹配的问题。第四步更新源并安装sudo apt update sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin看到这里你可能发现我连docker-compose-plugin也一起装了。没错Docker官方从Compose v2开始已经把Compose作为Docker的一个插件来发布了插件方式安装的Compose命令是docker compose带空格而不是老版的docker-compose带连字符。注意安装过程中如果遇到Job for docker.service failed的报错先别急着重装。八成是系统的某些内核模块没加载比如overlay。用lsmod | grep overlay检查如果没有任何输出先modprobe overlay加载一下再启动docker。装完后先别急着使用让我说一下启动和验证的正确姿势。sudo systemctl enable docker sudo systemctl start docker sudo systemctl status dockersystemctl enable是设置开机自启这一步必须做要不服务器重启后Docker不会自动起来到时候你的容器全部处于停止状态排查还麻烦。systemctl status会显示当前服务的状态只要看到active (running)就说明服务正常。验证安装是否成功一般用docker version看服务端和客户端版本docker version这个命令会输出Client和Server两段信息。有些人装完发现只有Client有内容Server那一栏报错说连不上daemon那就是docker服务没启动回到上一步重新检查服务状态。如果两个都有正常输出那恭喜Docker引擎已经装好了。补充一个国内用户很容易遇到的问题如果apt update非常慢甚至提示连不上download.docker.com那多半是网络延迟太高。解决办法是用阿里云的镜像站。阿里云提供了Docker的apt源镜像把上面的download.docker.com替换成mirrors.aliyun.com/docker-ce/linux/ubuntu即可echo deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null这里再强调一遍GPG密钥地址还是用官方源下载因为密钥只是用来做签名验证和软件源地址是否在国内无关。3. Docker Compose的安装不是你想象的那么简单装完Docker引擎之后第二个核心问题就是Docker Compose。现在网上的教程特别混乱有的教你用pip装有的教你下载个二进制文件扔到/usr/local/bin还有的让你用apt装。我第一次装的时候也踩了不少坑这里把几种方式给你理清楚。先说说为什么会有这么多安装方式。Docker Compose经历了一个分水岭式的版本变化。老版本叫Compose v1是个独立的Python程序执行命令是docker-compose。后来Docker公司用Go语言重写了一遍推出了Compose v2把它做成了Docker CLI的插件执行命令是docker compose。老版本现在基本已经停止维护了但很多教程还没更新过来。所以我的建议是直接用Compose v2也就是上面安装docker-compose-plugin的方式。这个插件跟着Docker引擎一起更新版本兼容性有保障不会出现Docker和Compose版本不匹配导致某些字段解析不了的问题。如果你之前的步骤已经装了docker-compose-plugin那就可以不用再看后面了直接在终端里敲docker compose version看到类似Docker Compose version v2.x.x的输出说明一切就绪。但有一个场景需要特别说明——离线安装。很多生产环境或者内网环境是不能访问外网的没法用apt源安装。这时候就需要下载独立的二进制文件也就是网上常见的docker-compose-linux-x86_64这种文件。具体做法是先在一台能联网的机器上从GitHub Releases页面下载对应版本的二进制文件。下载的时候注意选对平台和架构amd64的机器选docker-compose-linux-x86_64ARM64的机器选docker-compose-linux-aarch64。然后通过U盘或者内部文件系统把文件传到目标机器上。sudo mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose sudo chmod x /usr/local/bin/docker-compose docker-compose --version这种方式装出来的Compose是老版的独立命令功能是正常的对外提供的是docker-compose命令。从实用角度出发对于只需要跑通编排文件的场景它已经足够。不过这里有个坑要提醒如果你把插件版本的Compose和二进制版本的Compose同时装在了系统里你可能会遇到命令混乱的情况。docker compose和docker-compose同时存在版本还不一致排错的时候容易懵。所以建议只保留一种方式。还有一个比较远古的安装方式——pip安装。我不推荐原因很简单pip装的Compose是Python版本依赖系统里的Python环境升级一个Python库没准就把Compose搞挂了。而且官方早就停止了对pip方式的维护没必要给自己找麻烦。再补充一种情况如果你用的是Debian或者Ubuntu的较新版本系统也许能通过apt install docker-compose装到Compose v1的老版本。用apt-cache policy docker-compose看一眼版本号如果显示是v1.x那就别装了版本太老很多新的编排语法它根本不认识。那么问题来了插件版和独立二进制版到底怎么选我给你的建议是这样的如果你能正常访问外网直接用docker-compose-plugin省心省力和Docker引擎同步更新。如果你是离线环境或者你需要写自动化脚本用docker-compose命令那就下二进制文件。4. 免sudo使用Docker一次配置长期受益装完Docker之后你可能会发现一个体验特别差的问题——每次执行docker ps或者docker run都要加sudo。不加的话就会报permission denied错误。这个问题的根源是Docker守护进程默认以root身份运行它监听的socket文件/var/run/docker.sock的权限只对root用户开放。普通用户去访问这个socket权限不够自然被拒绝。解决办法也简单把当前用户加入docker组sudo usermod -aG docker $USER这条命令做的事情很简单把当前登录用户添加到docker这个用户组里。Docker安装时会自动创建这个组组成员被授予了访问docker.sock的权限。执行完这条命令后关键的一步是退出当前登录会话重新登录或者重启虚拟机/服务器。很多新手在这卡住执行完命令立刻敲docker ps发现还是报权限错误就开始怀疑人生。注意usermod只能修改用户组成员关系不会影响当前已经开启的会话权限。你必须重新登录一次让新的组权限生效。图形界面直接注销再登录SSH就断开重连或者用命令newgrp docker切换一下当前会话的组身份。重新登录后用id命令确认一下用户组信息id输出里会有一行groupsdocker看到这个就说明组权限已经生效了再敲docker ps就不会有权限报错。这里多说一句关于安全性的问题。加入docker组相当于把本机root权限交给了用户因为docker组内的用户可以任意启动容器而容器可以挂载宿主机文件系统。所以对于生产环境的服务器要谨慎添加用户进docker组别图省事把所有人加进去一旦有人恶意利用整个主机就沦陷了。如果你是在自己的开发机上用那就无所谓把日常用户加进去提升幸福感才是第一位的。另外有一种情况要提醒如果连sudo systemctl start docker都报权限错误那可能是docker服务本身没起来。先看看服务状态确认是权限问题还是服务问题别把锅全甩给用户组。5. 镜像拉不动大概率是没配置加速器Docker装好了用户组也配好了现在执行docker run hello-world测试一下。如果你在能正常访问外网的机器上这一步很顺利会看到一段说明文字告诉你Docker正常工作。但如果你发现镜像一直拉不下来pull到一半超时那恭喜你你遇到了绝大多数国内用户都会遇到的老大难问题——Docker Hub访问不稳定。Docker Hub默认的下载地址是registry-1.docker.io这个地址在部分网络环境下延迟很高甚至完全不通。解决思路不是去改网络而是给Docker配置镜像加速器让它从国内或者访问速度更快的镜像仓库拉取镜像。Docker的镜像加速配置写在/etc/docker/daemon.json里。如果你的系统里还没有这个文件直接创建即可sudo mkdir -p /etc/docker sudo vim /etc/docker/daemon.json在文件里写入{ registry-mirrors: [ https://docker.m.daocloud.io, https://dockerproxy.com, https://docker.nju.edu.cn ] }这里我列了几个公共加速地址实际使用中这些地址可能会有变动具体以当时能用的为准。配置好后重启Docker服务sudo systemctl daemon-reload sudo systemctl restart docker重启完成后用docker info验证配置是否生效看输出中的Registry Mirrors一栏如果列出了你刚才填的地址说明配置成功。配置好加速器后再执行docker run hello-world速度就会有明显提升。不过我想多说一句公共加速器本质上都是网友或者云厂商自愿提供的服务稳定性不能100%保证。如果对稳定性要求高可以考虑使用云服务商提供的容器镜像服务。阿里云、腾讯云都有免费的个人版镜像加速器在它们的控制台里能找到专属地址填到registry-mirrors里就行。还有一个小技巧如果某个镜像在你配置的加速器上始终拉不下来可以试试带上前缀直接拉取。比如原始镜像地址是nginx:latest有的加速器支持通过加速器域名/library/nginx:latest的方式显式拉取。这种方法虽然多打几个字但有时确实能绕过一些限制。补充一个和加速器完全无关但经常被混淆的坑如果你在配置daemon.json时不小心写了不合法的JSON格式比如多了一个逗号Docker服务重启时会直接报错。用sudo dockerd手动启动一下能看到详细的错误信息一般都会提示是JSON解析失败改好格式再重启就行。6. 用一个小型的Web服务把整个环境跑一遍所有基础配置都完成后可以做一次完整的上手验证。这一步不是为了验证Docker能用——那用hello-world就够了——而是要模拟一个实际场景让你看到容器从镜像拉取、启动、端口映射、访问这一整个链条是怎么流转的。我一般习惯用Nginx来做这个验证原因很简单镜像体积小、启动快、自带默认页面而且不需要额外的环境变量和配置。先跑一个最简单的Nginx容器docker run -d --name test-nginx -p 8080:80 nginx:latest这条命令里面几个参数值得拆开说。-d表示后台运行--name test-nginx给容器起个名字-p 8080:80是把宿主机的8080端口映射到容器的80端口nginx:latest是要使用的镜像和标签。提示如果你的8080端口被占用了换一个端口比如8081即可报错信息会明确告诉你是bind: address already in use。启动后用docker ps看容器状态STATUS列是Up就说明正常。然后打开浏览器访问http://你服务器的IP:8080看到Nginx的欢迎页就说明一切正常。如果想做个更贴近实际开发场景的验证可以用Docker Compose编排一个稍微复杂的服务。新建一个目录在里面放一个docker-compose.yml文件services: nginx: image: nginx:latest container_name: web-demo ports: - 8080:80 redis: image: redis:7-alpine container_name: redis-demo ports: - 6379:6379然后在同一个目录下执行docker compose up -d这条命令会根据compose文件拉起两个容器。用docker compose ps看服务状态用docker compose logs看日志用docker compose down停止并删除容器。通过这个简单的编排你能直观感受到Compose的价值所在不用写一大串docker run命令所有服务的配置都集中在一个声明式的文件里团队协作时别人拿过去一看就知道你部署了哪些服务端口是怎么暴露的依赖关系是什么样的。第一次跑docker compose up时会自动拉取镜像如果你已经配置了加速器这一步应该不会卡太久。如果遇到连接超时的报错回到上一节检查配置。注意如果提示找不到docker compose命令但docker命令能正常使用说明你装的是独立的docker-compose二进制而不是插件版。把命令换成docker-compose up -d效果一样只是写法不同。7. 几个高频问题和我的排错方法先说说我自己的经历。有次给一台新机器装Docker装的过程很顺利docker version也正常显示但docker run hello-world就是报错提示无法连接到docker daemon。我查了半天systemctl status docker显示服务没起来。手动启动时错误信息指向一个内核模块问题加载overlay模块后重启服务一切恢复正常。从那以后我养成了一个习惯新机器装完Docker第一件事是先检查内核模块而不是急着跑容器。下面这些问题是平时群里、论坛里被问得最多的我把排查思路列出来方便你对照自己的情况。问题一docker ps报错Cannot connect to the Docker daemon这个报错多半是Docker服务没启动。先执行sudo systemctl status docker看服务状态如果是inactive (dead)执行sudo systemctl start docker启动。如果提示服务不存在说明你的Docker根本没装上翻回去检查安装步骤。还有一种可能你用的是用户态的服务管理器比如在WSL环境下。WSL 2的Docker体验和普通Linux虚拟机不太一样需要在WSL发行版里也安装systemd支持否则手动启动docker的方式就变成了sudo service docker start。问题二容器启动后立刻退出docker ps看不到这是新手最容易困惑的问题。docker ps只显示运行中的容器已经退出的是看不到的。用docker ps -a显示所有容器如果状态是Exited用docker logs 容器名看日志输出基本能找到退出原因。最常见的情况是前台容器跑完就退出。比如docker run nginx如果不加-d参数会在前台运行CtrlC退出终端后容器也跟着停止。这个不算故障是Docker设计如此。问题三apt update报NO_PUBKEY错误执行sudo apt update时提示缺少公钥。这是因为GPG密钥的导入有问题。重新执行一次第二步的密钥导入命令注意确认存放路径和chmod ar有没有执行成功。问题四docker compose up提示version is obsolete这是因为你在compose文件顶部声明了version: 3.8之类的字段。Compose v2已经忽略这个字段了删掉即可不影响使用。问题五防火墙导致容器端口无法访问这个坑在云服务器上尤其常见。容器都正常启动本地curl localhost:8080也能通但外网就是访问不到。先检查云服务商的安全组策略再检查系统防火墙sudo ufw status如果开启了ufw放行对应端口sudo ufw allow 8080/tcp这个坑还涉及一个Docker和ufw联动的经典问题Docker会直接改iptables规则有时你明明在ufw里放行了端口外部还是访问不到。原因在于Docker的规则绕过了ufw的过滤链。解决方式是在/etc/docker/daemon.json里加上iptables: false但这样做也会影响Docker自身网络模式的正常工作我建议你在了解后果的前提下再选择这种处理方式。对新手来说更稳妥的做法是直接用Nginx反代或者确保安全组放行而不是和iptables硬刚。问题六磁盘空间被镜像和容器占满Docker运行久了/var/lib/docker会积累大量无用的镜像层和停止的容器。清理命令docker system prune -a这条命令会删除所有未被使用的镜像、容器、网络和构建缓存释放大量空间。第一次执行的时候会有个确认提示看清楚提示内容再回车别误删了还在用的镜像。生产环境要谨慎开发机随便用。写到这里关于Ubuntu安装Docker和Docker Compose的关键环节基本都覆盖了。从我自己的使用体验来看大多数人的问题都出现在安装前的准备和安装后的基础配置上。只要把apt源、GPG密钥、用户组、镜像加速这几件事处理好后面的使用就会很顺。每次装完新机器我建议你走一遍这套流程装引擎、配用户组、设加速器、跑一个Nginx容器、再用compose编排两个服务确认整个链路通畅后再部署正式的业务容器。最后再分享一个小经验别在同一个终端会话里反复切换sudo和免sudo的docker命令很混乱。环境装好后统一用免sudo的方式操作。如果要管理多台服务器把每台的daemon.json配置保持一致尤其是镜像加速器和日志驱动这样排错的时候不用在每台机器上重新熟悉一遍配置。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进