在Ubuntu上装Docker,是个典型的“看着简单、动手复杂”的操作。官方文档把流程写得清清楚楚,实际执行时却总有人在GPG密钥、软件源、用户权限、镜像拉取这些环节上卡壳。我从Ubuntu 18.04一路用到24.04,在本地虚拟机、云服务器、公司内网机器上都装过Docker,也帮朋友排查过不少安装现场。这篇文章把一套完整、可靠、出了问题知道怎么退的流程整理出来,顺便把那些文档里语焉不详的细节一次说清楚。
这里说的Docker是Docker Engine,也就是社区版引擎,不是Docker Desktop。Linux服务器上需要的是前者,Desktop版是给macOS和Windows桌面系统用的,装在Ubuntu服务器上反而多余。文章适合刚接触Ubuntu的新手,也适合装过Docker但一直被各种报错折磨的老手,照着做能少走不少弯路。
1. 安装前的准备工作
1.1 确认系统版本与架构
我习惯在任何机器上动手安装之前,先花两三分钟确认系统信息。网上很多报错案例,最后查下来都是安装前没看系统版本,导致软件源里写了一个不存在的系统代号,apt直接找不到Release文件。
cat /etc/os-release这条命令会输出系统名称、版本号和版本代号。比如Ubuntu 22.04的代号是jammy,24.04的代号是noble。稍后配置Docker软件源时需要用到这个代号,它是apt下载地址的一部分。如果代号写错,整个源就废了,安装时会提示找不到对应的Release文件。
接着查看CPU架构:
uname -m dpkg --print-architecture前者看内核架构,后者看apt包管理器识别的架构。x86_64机器上通常分别是x86_64和amd64,ARM机器上会显示aarch64和arm64。Docker官方支持amd64、arm64等主流架构,只要不是i386这类老32位架构,基本都能正常安装。继续看一下内核版本:
uname -rDocker对内核的最低要求是3.10,Ubuntu 20.04之后内核普遍在5.x以上,这步只是走个过场。如果用的是很老的Ubuntu版本,内核太旧会直接影响容器运行时的兼容性,我一般建议直接升级系统,而不是在老内核上硬装。
1.2 清理旧版本与相关残留
检查一下机器上是不是已经装过Docker相关组件:
which docker docker --version dpkg -l | grep -i docker如果之前用Ubuntu自带的软件源装过docker.io,或者手工装过旧版Docker,建议先卸载再装新版。Ubuntu软件源里的docker.io版本更新偏慢,而且默认不包含buildx、compose插件这些新特性。我见过有人先装了docker.io,过了几个月想用docker compose发现命令不存在,然后陷入“我明明装了Docker”的困惑。要清理旧版本,执行:
sudo apt remove -y docker docker-engine docker.io containerd runc这条命令只移除软件包,不会动/var/lib/docker目录里的镜像和容器数据,可以放心执行。如果机器上还运行着跟容器相关的服务,卸载前建议先sudo systemctl stop docker停掉服务,避免文件占用导致卸载不干净。接下来先更新一次apt索引并安装基础依赖:
sudo apt update sudo apt install -y curl ca-certificatesca-certificates这个包很容易被忽略,少了它,后续用curl下载Docker的GPG密钥时可能直接报证书校验失败。先装上,后面省得返工。
2. 配置Docker软件源
2.1 添加官方GPG密钥
很多人不理解为什么装个软件还要先配密钥。apt包管理器在安装软件时会校验软件源的签名,确保下载到的包确实来自官方、没有被篡改。这一步配的就是Docker官方软件源的信任凭证。
老教程里常见的做法是curl ... | sudo apt-key add -,但apt-key在Ubuntu 22.04之后已经明确废弃,全局信任所有用apt-key添加的密钥,既不安全也难管理。现在官方推荐的做法是把密钥文件下载到/etc/apt/keyrings目录,并在软件源条目里用signed-by参数显式指定,这样每个源只信任自己声明的密钥文件。
sudo apt-get update sudo apt-get install -y ca-certificates curl sudo install -m 0755 -d /etc/apt/keyrings sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc sudo chmod a+r /etc/apt/keyrings/docker.asc我来拆一下命令。install -m 0755 -d是创建目录并设置权限,0755表示属主可读写执行、其他用户可读可执行,目录创建后apt才有地方放密钥文件。curl -fsSL分别表示静默失败、静默进度、显示错误、跟随重定向,如果下载超时会直接报错而不是卡在那里装死。下载完成后必须chmod a+r,让所有用户都能读这个密钥文件,否则apt运行在非root用户上下文时可能因为读不到密钥而误报签名错误。
2.2 写入apt软件源
密钥准备妥当后,把软件源写入apt的源列表:
echo \ "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \ $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ sudo tee /etc/apt/sources.list.d/docker.list > /dev/null这条命令里有几个自动化的关键点。arch=$(dpkg --print-architecture)会自动填入当前机器的架构,省得手写amd64或arm64。signed-by指向刚才下载的密钥文件。$(. /etc/os-release && echo "$VERSION_CODENAME")会读取系统版本代号并自动拼进源地址,这样换机器时不用手动改代号。最后通过tee把内容写入/etc/apt/sources.list.d/docker.list,> /dev/null用于屏蔽输出。
写完后跑一下apt update,让软件源生效:
sudo apt-get update这一步如果没报错,说明源已经通了。如果看到Release文件不存在的报错,多半是版本代号有问题,往下看到第7章的排障部分。
2.3 软件源镜像替换建议
如果你所在的网络环境访问download.docker.com速度比较慢,curl下载密钥或者apt update可能反复超时。这种情况下,最实用的办法是使用国内云厂商提供的Docker软件源镜像。
做法很简单:把上面源地址里的https://download.docker.com整体替换成云厂商提供的镜像地址,保留后面的/linux/ubuntu路径结构不变。具体地址要去你所用云厂商的软件源文档里查,格式通常是类似https://<镜像服务域名>/docker-ce/linux/ubuntu这样的完整URL。比如在一台云服务器上装,厂商控制台的软件源页面一般会直接给出可复制的地址,照抄就行。
提示:换源后出现“证书校验失败”的报错,先确认ca-certificates已经安装,再看系统时间是否准确。证书校验依赖系统时间,机器时间偏差过大也会导致握手失败。
3. 安装Docker引擎与常用组件
3.1 用apt安装Docker
软件源配好后,安装就变得很简单:
sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin这里一次装了五个包,它们的职责不同,我列个表说明:
| 包名 | 作用 |
|---|---|
| docker-ce | Docker引擎主程序,负责容器生命周期管理 |
| docker-ce-cli | 命令行工具,执行docker命令时依赖它 |
| containerd.io | 容器运行时,真正负责创建、启动、销毁容器 |
| docker-buildx-plugin | 镜像构建插件,支持多架构构建等高级能力 |
| docker-compose-plugin | 官方Compose v2插件,多容器编排必备 |
为什么一次性装齐而不是只装docker-ce?很多教程只装主程序,后面要用docker compose时才发现没有这个命令,再回头补装,纯属浪费时间。装了compose插件后,直接用docker compose而不是老式的docker-compose,命令格式也更简洁。
安装完成后可以先看版本确认一下:
docker --version docker compose version3.2 启动服务与设置开机自启
Ubuntu上Docker安装完,服务不一定会自动处于运行状态,建议手动启动并设置开机自启:
sudo systemctl enable --now docker systemctl status dockerenable --now是enable和start的合并操作,一条命令完成“开机自启”和“立即启动”。status输出里看到Active: active (running)就说明服务正常。如果状态不是running,用这个命令看日志:
journalctl -u docker -n 50 --no-pager日志会直接说明docker守护进程为什么起不来,最常见的原因就是daemon.json的JSON格式写错——这个问题在后面的排障章节还会详细说。
3.3 验证安装:跑一个hello-world
验证Docker是否真的能用,最经典的方式是拉一个hello-world镜像并运行:
sudo docker run hello-world这条命令的执行流程是:docker先检查本地有没有hello-world镜像,没有就按配置的镜像源去拉取,拉取成功后创建一个临时容器,容器启动后会打印一段“Hello from Docker!”的提示信息。看到这段输出,说明引擎、运行时、镜像拉取链路都是通的。
如果卡在这一步报网络超时,别着急,这不会影响Docker本身的安装质量,只是说明Docker Hub拉取路径不太顺畅,下一步配好镜像源后重试即可。
4. 让镜像拉取不再超时的配置
4.1 为什么容器镜像需要镜像源
Docker Hub是Docker默认的公共镜像仓库,但它的服务器部署在海外,直接拉取时,小镜像要等很久,大镜像经常拉到一半断开。这个问题在云服务器上尤其明显,几乎每个刚装完Docker的人都会遇到。
Docker为此提供了一种叫作registry-mirrors的机制。简单理解,就是给镜像仓库配一个“本地分诊站”:拉取镜像时,Docker会优先去你指定的镜像源地址查询,如果那台服务器已经缓存了对应镜像,就直接从那里拉取,速度会有明显改观。这属于Docker官方支持的常规配置,不需要改变网络链路的任何环节,机器还是走普通的网络连接。
4.2 获取专属镜像源地址并写入daemon.json
先确认你已经有一台可以访问的镜像源服务器。如果你用的是某家云厂商的云服务器,通常可以在它的控制台里找到“容器镜像服务”相关入口,进去后一般能看到一个“镜像源”或者类似命名的页面,系统会为你生成一个专属地址,形如https://xxxx.mirror.xxx.xxx。如果只是本机实验环境,也可以直接搜索“Docker镜像源”找公开可用的地址,但优先使用你自己所属云平台提供的地址,速度和可靠性都有保障。
拿到地址后,编辑Docker守护进程的配置文件:
sudo mkdir -p /etc/docker sudo vim /etc/docker/daemon.json写入如下内容:
{ "registry-mirrors": ["https://<你的专属镜像源地址>"] }注意,json格式非常严格,最后一个键值对后面不能有多余的逗号,字符串要用英文双引号包裹。写错一个符号,docker服务都起不来。写完建议先cat /etc/docker/daemon.json看一眼再重启。
4.3 重启Docker并验证配置
修改daemon.json后需要重载配置并重启docker服务:
sudo systemctl daemon-reload sudo systemctl restart dockerdaemon-reload是让systemd重新读取服务配置,不是必须但对避免干扰有好处。重启后验证一下配置是否生效:
docker info | grep -A 2 "Registry Mirrors"输出里能看到你填写的镜像源地址就说明生效了。这时候再执行一次docker run --rm hello-world,拉取速度正常情况下要比刚才快得多。如果依旧超时,检查是不是地址写错,或者该镜像源本身服务不稳定,换一个再试。
5. 安装后的权限、日志与存储优化
5.1 免sudo执行docker命令
安装完Docker后,默认只有root用户能直接使用docker命令。普通用户执行docker ps会报“permission denied”错误。每次都加sudo太影响体验,更省心的做法是把当前用户加入docker用户组:
sudo usermod -aG docker $USER newgrp docker docker psusermod -aG把用户追加到docker组,newgrp docker是让当前终端立即刷新用户组身份,省掉一次重新登录。执行完docker ps能看到空的容器列表,说明权限已经正常。
安全提示:能操作docker的用户实际上等同于拥有root权限,因为docker socket可以直接操作宿主机文件系统。生产环境千万别随便把不信任的人加入docker组,一旦加入他就是变相root。
5.2 给日志加上限,别让磁盘悄悄爆掉
很多刚上手的人不知道,容器默认的日志驱动是json-file,会一直往宿主机写日志文件。如果容器日志输出量很大,一个月就能把磁盘填满。这几乎是Docker最常见的磁盘隐患。
我的习惯是装完Docker后立刻在daemon.json里加上日志限制:
{ "registry-mirrors": ["https://<你的专属镜像源地址>"], "log-driver": "json-file", "log-opts": { "max-size": "50m", "max-file": "3" } }max-size设置单个日志文件的最大体积,50m表示达到50MB就滚动;max-file设置最多保留的日志文件数,3个就是最多150MB。改完后重启docker:
sudo systemctl restart docker注意这个配置只对新创建的容器生效,已经存在的容器还是在用旧配置。如果机器上已经有很多容器且日志失控,可以临时清空一下:
find /var/lib/docker/containers -name "*-json.log" -exec truncate -s 0 {} \;这条命令会把所有容器日志文件清空,属于救急手段,执行前请评估是否还需要保留日志。
5.3 数据目录迁移到大数据盘
默认情况下,Docker的镜像、容器、数据卷都放在/var/lib/docker。如果系统盘分区比较小,很快会被镜像撑爆。把数据目录迁移到大容量数据盘是生产环境很常见的操作。
步骤如下:
- 先停掉docker服务:
sudo systemctl stop docker - 准备新目录,比如
sudo mkdir -p /data/docker - 把原有数据整体同步到新目录:
sudo rsync -avz /var/lib/docker/ /data/docker/ - 在daemon.json里加上data-root配置:
"data-root": "/data/docker" - 执行
sudo systemctl daemon-reload && sudo systemctl restart docker - 验证:
docker info | grep "Docker Root Dir",看到路径变成/data/docker即成功
这里我不建议直接mv加ln -s软链接,虽然能凑合用,但systemd管理下路径混乱容易出奇怪问题,直接在daemon.json里声明data-root最干净,升级也不会被覆盖。另外,不要把data-root放在NFS或者性能差的网络盘上,容器运行时I/O压力很大,网络盘会成为整个系统的瓶颈。
6. 升级、卸载与备份迁移
6.1 升级Docker组件版本
Docker版本更新很快,升级操作也不复杂:
sudo apt-get update sudo apt-get upgrade -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin升级完成后跑一下docker version,确认client和server两栏都已经是新版本。如果出现client和server版本不一致的警告,通常是因为docker服务没有重启,重启一下就好。
生产环境追求稳定,我一般建议锁版本,防止apt upgrade时不小心把Docker升了:
sudo apt-mark hold docker-ce docker-ce-cli containerd.io需要解除锁定升级时再unhold:
sudo apt-mark unhold docker-ce docker-ce-cli containerd.io6.2 彻底卸载Docker
卸载Docker也是门学问。只remove docker-ce是不彻底的,containerd还在运行,/var/lib/docker目录里的数据还占着磁盘,重新安装后新旧残留混在一起很容易出问题。彻底卸载的做法:
sudo systemctl stop docker sudo apt purge -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd sudo rm -f /etc/apt/sources.list.d/docker.list sudo rm -f /etc/apt/keyrings/docker.asc需要特别提醒:删除/var/lib/docker和/var/lib/containerd会把所有镜像、容器、数据卷一并清空。如果里面有要保留的东西,先按下一小节做好备份,别直接删。
6.3 把镜像和容器数据备份带走
如果你要换机器或者重装系统,最想带走的是自定义镜像、容器配置、持久化数据。
镜像备份用docker save。把本机所有镜像打成一个tar包:
docker save -o myimages.tar $(docker images -q)还原时执行:
docker load -i myimages.tar容器配置建议用docker compose管理的项目直接备份整个项目目录,包含docker-compose.yml和env文件,到新机器上直接docker compose up -d就能恢复整套服务。单容器场景用docker ps -a导出一份容器列表,再结合镜像备份,基本能还原。
持久化数据是最容易遗忘的部分。用bind mount或者volume挂载的数据,数据其实在宿主机上,备份时要把挂载目录一起拷走。没做挂载的容器数据都在容器可写层里,容器一删就没了,这种情况需要先用docker cp拷贝出来。
7. 实际排障经验与常见问题速查
7.1 apt源相关的报错
安装过程中如果报Release文件不存在、GPG签名错误、证书校验失败,基本都是源配置环节的问题。我把常见报错整理成一张速查表:
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
| The repository ... does not have a Release file | 系统代号写错,或该版本官方源没有对应版本 | 检查/etc/apt/sources.list.d/docker.list的内容,确认代号与cat /etc/os-release一致 |
| GPG error: ... public key not available | 密钥文件缺失或未正确导入 | 重新下载密钥到/etc/apt/keyrings/docker.asc,执行chmod a+r后apt update |
| Certificate verification failed | ca-certificates未安装,或系统时间偏差过大 | 安装ca-certificates,用date检查系统时间并校准 |
我遇到过一种很隐蔽的情况:系统从老版本升级上来后,/etc/os-release里的VERSION_CODENAME还是旧版本代号,配置源时手一抖就写错了。后来固定用自动化那段shell命令生成源,彻底避免手写代号出错。
7.2 Docker服务与权限报错
刚装完Docker后最常见的两个报错,一个是连不上守护进程,一个是权限不足。
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
| Cannot connect to the Docker daemon ... Is the docker daemon running? | docker服务没启动,或daemon.json格式错误导致启动失败 | systemctl status docker查看状态;journalctl -u docker -n 50看日志 |
| Got permission denied while trying to connect to the Docker daemon socket | 当前用户不在docker组,或组身份未刷新 | sudo usermod -aG docker $USER,然后newgrp docker或重新登录 |
第一种报错里,daemon.json的JSON解析错误是最阴的。docker服务起不来,systemctl status只提示失败,真正原因要翻journal日志才能看到,里面一般会直接指出某个字符位置的多余逗号。所以每次改daemon.json我都习惯先用python3 -m json.tool /etc/docker/daemon.json校验一下,再重启服务。
7.3 镜像拉取和磁盘问题
运行阶段的高频问题集中在镜像拉取和磁盘空间上。
| 报错信息 | 可能原因 | 解决办法 |
|---|---|---|
| Get https://registry-1.docker.io/v2/: ... timeout | 镜像源未配置,或配置地址不可用 | 检查daemon.json的registry-mirrors,重启docker后重试 |
| failed to register layer: no space left on device | 磁盘分区已满,或inode耗尽 | df -h和df -i查看磁盘与inode使用率;docker system prune -af --volumes清理 |
| Error response from daemon: pull access denied | 镜像名拼写错误,或私有镜像未登录 | 核对镜像名;私有仓库先docker login登录 |
磁盘满的情况要特别警惕:df -h看不出问题但容器仍然报no space,很可能是inode用尽。检查命令用df -i,inode耗尽通常是因为大量小文件堆积,最典型的场景就是容器日志文件过多。清理磁盘时有个实用命令组合:
docker system df docker system prune -af --volumesdocker system df先看什么占了空间,prune -af --volumes一键清理所有停止的容器、未使用的网络、悬空镜像、未被引用的数据卷。注意prune会删数据,执行前确认没有需要保留的东西。
我个人的实际体会是,Docker安装链条上真正影响体验的往往不是安装本身,而是安装前后那几个小配置。这两年我装完Docker后的第一件事永远是改daemon.json,把镜像源、日志限制、数据目录一次性写完再重启,等于把后面几个月要踩的坑提前填平。后来有几次图省事直接用默认配置跑测试机,结果日志一次吃掉40G磁盘,教训相当深刻。
最后再分享一个经验:升级Docker前一定先看一眼官方变更说明,升级后第一时间docker version确认client和server版本一致,避免出现版本不匹配的诡异问题。按照这篇流程走一遍,Docker的安装、使用、排障基本都能心里有底。祝少踩坑。