ARTICLE DETAIL

资讯详情

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

Linux服务器Docker安装避坑指南:从环境检查到容器验证

Linux服务器Docker安装避坑指南:从环境检查到容器验证 刚装完 Docker 就要重启生产服务器结果发现 service docker start 直接超时最后定位到是内核模块加载异常。折腾了几台 CentOS 7.9 和 Ubuntu 22.04 之后我决定把 Docker 的完整安装流程按发行版分开梳理一遍把手头踩过的坑和验证方法都写出来。这篇文章不只告诉你“敲哪几条命令”更会解释每一步为什么要这么做、遇到报错时从哪个方向查。无论你是第一次在 Linux 上装 Docker还是装过但没搞懂组件之间关系相信都能从里面找到有用的内容。1. 安装前的四件套检查内核、架构、磁盘与网络很多教程上来就让你执行curl ... | sh但我要先说一句安装前花 5 分钟检查环境比装完排错 2 小时划算得多。尤其是服务器上已经跑了数据库或业务进程的情况装 Docker 动了 iptables、网桥、cgroup 配置随时可能影响其他服务。下面这几项是我固定的检查清单换新机器时照做一遍就能避开大多数基础问题。1.1 内核版本低于 3.10 就别折腾了Docker 容器不是虚拟机它靠宿主机内核来隔离进程。Linux namespace 负责隔离 pid、网络、挂载点这些视图cgroup 负责限制 CPU 内存资源OverlayFS 提供镜像层级能力。内核版本太老这些机制要么缺失、要么有 bug所以 Docker 官方要求内核至少 3.10。检查方式uname -rUbuntu 22.04 默认内核一般是 5.15CentOS 7.9 默认是 3.10.0满足条件。如果你看到2.6.32-xxx多半是 CentOS 6 或更老的系统直接升级操作系统再谈 Docker不要浪费时间强行安装。顺手看看系统版本cat /etc/os-release注意发行版的代号和版本后面配置软件源时要用到。1.2 系统架构x86 和 ARM 的仓库路径不一样Docker 的安装包和镜像都区分架构最常见的两种是 amd64x86_64和 arm64aarch64。树莓派、飞腾、鲲鹏这类 ARM 服务器上装 Docker很多人直接复制网上 x86 教程结果要么源地址 404要么装出来的包架构不匹配启动时一堆诡异报错。检查架构uname -m拿到结果后在配置软件源、下载离线包、拉镜像时注意选择对应架构。像 Ubuntu 源里的[archamd64]就是给 x86 机器用的ARM 机器要写成[archarm64]。1.3 磁盘布局给 /var/lib/docker 留足空间Docker 默认把所有镜像层、容器读写层、卷文件都存在/var/lib/docker目录下。一个 MySQL 镜像几百 MB再跑几个数据库容器、堆几个日志文件系统盘很容易被写满。更麻烦的是很多云服务器根分区只有 40GBdocker 的 overlay2 空间不足时容器不会立刻崩但写入会出现no space left on device排查起来非常费劲。先看当前分区的使用情况df -h如果根分区空间紧张建议买数据盘或者划独立分区把 Docker 数据目录指向空间更大的路径。这个操作我会在后面的配置章节详细讲安装前你只需要记住数据目录所在分区至少要留 50GB 以上的余量数据库类的容器应用建议更大。1.4 网络连通性与 DNSDNS 错了才叫真折磨安装 Docker 时需要访问download.docker.com拉取安装包验证镜像时要访问 Docker Hub 拉镜像。如果服务器在内网或者有特殊网络策略先确认到底能不能连通这些地址curl -I https://download.docker.com/linux/ubuntu curl -I https://registry-1.docker.io/v2/如果 curl 一直卡住或者 TLS 握手失败立刻用nslookup或dig查一下 DNS 是否正常nslookup download.docker.com不要一发现问题就冲过去改/etc/resolv.conf先确认是不是防火墙、安全组、路由策略导致的问题。DNS 配置错了后果是所有域名解析都异常不只是 Docker 用不了。网络这块如果要走离线环境那就要提前拿到所有 rpm/deb 包和 docker 镜像 tar 文件放到内网机器上手动导入。具体流程后面在“离线环境”相关内容里再展开。2. Ubuntu 下安装 Docker CE仓库源配置的细节与坑Ubuntu 是 Docker 支持得最好的发行版之一官方文档也以 Ubuntu 作为第一参考平台。网上教程五花八门有的让你直接apt install docker.io有的让你跑官方脚本我建议你走这一条最稳的路配置 Docker 官方 apt 源手动安装 docker-ce。原因很简单docker.io是 Ubuntu 自带的旧版本打包更新节奏慢而且和 Docker Engine 的 containerd 版本绑定关系不清晰。而官方源能装到最新的稳定版后续升级也只用apt upgrade就能统一管理。2.1 先卸载可能存在的旧包如果你的系统之前通过其他方式装过 Docker先清理干净避免配置文件冲突。sudo apt remove docker docker-engine docker.io containerd runc -y这一步只删软件包不会删/var/lib/docker里的数据。如果你确认旧数据不要了也可以单独删目录sudo rm -rf /var/lib/docker不过刚接触 Docker 时建议不要删先备份再动数据目录。2.2 安装依赖工具并添加 GPG 密钥Docker 官方源是 HTTPS 地址apt 需要相关工具才能正常解析和验证sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release然后创建密钥目录并拉取 Docker 的 GPG 公钥用gpg --dearmor转成 apt 能识别的二进制格式sudo install -m 0755 -d /etc/apt/keyrings sudo 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为什么需要这一层apt 在安装软件包时会验证包签名如果你添加的源没有对应的公钥apt update会直接报NO_PUBKEY错误而且拒绝安装任何来自该源的软件。所以这个步骤不是形式主义少了它后面装不上。2.3 写入软件源并安装 docker-ce写入源文件时关键是架构和系统代号不要写错。系统代号可以用lsb_release -cs获取例如 Ubuntu 22.04 返回的是jammyUbuntu 20.04 返回的是focal。填错代号会得到 404。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更新源sudo apt update如果一切正常就能看到 Docker 官方源的索引。接着安装全套组件sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin影响首次安装的组件。docker-ce是守护进程主体containerd.io是容器运行时docker-buildx-plugin负责新式镜像构建docker-compose-plugin用来跑 compose 编排。建议一次全装免得后面折腾 buildx 和 compose 时报错。2.4 启动、自启与用户组包安装完不会自动启动部分新版系统会自动启动建议手动确认sudo systemctl enable --now docker systemctl status docker如果看到active (running)就说明守护进程已经正常。接下来的操作很多人都忽略把当前用户加入 docker 组这样执行 docker 命令就不用每次加 sudo。sudo usermod -aG docker $USER改完组后需要重新登录或者执行newgrp docker让组权限立即在当前终端生效。但这里必须泼盆冷水docker 组权限等价于 root 权限因为组内有权限的用户可以通过挂载宿主机目录、特权容器等方式完全拿到宿主机控制权。如果你的机器是多用户共用的别贪图方便把所有人都加进 docker 组否则等于变相发 root 通行证。3. CentOS 上安装 Dockeryum 仓库、SELinux 与版本选择CentOS 7 和 CentOS 8/Stream 的安装逻辑略有区别但核心路径一致配置 docker-ce 的 yum 源安装完整组件处理 SELinux 和防火墙。CentOS 7 是老牌稳定套餐很多线上业务服务器还在用CentOS 8 Stream 滚动更新模式更激进。下面按 CentOS 7.9 为主要对象来写CentOS Stream 的差异单独说明。3.1 先解决 yum 依赖工具Docker 官方提供了一个 yum 插件yum-utils里面包含yum-config-manager命令方便往系统里添加软件源。sudo yum install -y yum-utils然后用它添加 docker-ce 的官方仓库sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo如果这条命令因为网络原因失败也可以手动创建/etc/yum.repos.d/docker-ce.repo内容写[docker-ce-stable] nameDocker CE Stable - $basearch baseurlhttps://download.docker.com/linux/centos/$releasever/$basearch/stable enabled1 gpgcheck1 gpgkeyhttps://download.docker.com/linux/centos/gpg这里$releasever会自动替换成 7 或 8对应实际的系统版本。3.2 安装 docker-ce 并锁定版本CentOS 下安装的包名和 Ubuntu 基本一致sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin如果你有生产环境在跑 Kubernetes 或者对版本有严格管控先列出可用版本再挑一个安装yum list docker-ce --showduplicates | sort -r安装指定版本时完整包名要带 epoch 前缀格式类似sudo yum install -y docker-ce-3:24.0.7-1.el7 docker-ce-cli-1:24.0.7-1.el7 containerd.io为什么要锁版本Docker 跨大版本升级偶尔不兼容 containerd 或 runc生产环境里贸然yum update把 docker 一起升上去是有点冒险的。锁版本可以在后续维护中保证行为可预期等确认新版本没问题后再手动升级。安装完成后启动并设置自启sudo systemctl enable --now docker sudo systemctl status docker3.3 CentOS 8 / Stream 要注意的特殊情况CentOS 8 于 2024 年 5 月正式停止维护很多 yum 源已经迁移或者删除安装软件时容易遇到源 404。CentOS Stream 是滚动发行版它的软件包基础比传统 CentOS 新但也意味着系统更新频繁Docker 安装后要留意内核和容器组件之间的兼容性。在 CentOS 8 Stream 上添加源时部分老教程还要求启用powertools或extras仓库但 Docker 官方源本身不依赖这些。如果安装后启动失败优先检查journalctl -u docker --no-pager -n 50还有一个 CentOS 8 特有的坑系统自带的container-tools模块和 Docker 组件可能冲突。如果你之前装过 Podman、Buildah 这类工具先确认是否与 docker-ce 冲突必要时移除冲突包再重试。3.4 SELinux 和 firewalld劝你一句别为了装 Docker 直接关掉CentOS 默认开启 SELinuxDocker 官方支持在 SELinux enforcing 模式下运行但需要额外注意挂载卷的上下文问题。比如你跑到容器里挂载宿主机目录可能遇到Permission denied这时需要给目录打上正确的 SELinux 标签sudo chcon -Rt svirt_sandbox_file_t /data/docker-volumes或者更简单一点在docker run时加--security-opt labeldisable但只在测试环境建议这么干。安装完成后如果想对外提供服务防火墙别直接systemctl stop firewalld而是逐个放行端口sudo firewall-cmd --permanent --add-port80/tcp sudo firewall-cmd --permanent --add-port3306/tcp sudo firewall-cmd --reloadDocker 默认会往 iptables 里写入大量规则firewalld 如果和 Docker 网络同时管理 NAT很容易出现“容器之间通了但外部访问不了”的诡异问题。后面我会单独讲网络排查。4. 安装完成后立刻改的四项配置镜像加速、数据目录、日志与自启Docker 装好后先别急着跑业务容器有几个基础配置建议立刻做掉不然用段时间再改会更麻烦。这四项配置都写在/etc/docker/daemon.json里你需要先确认文件不存在或为空然后写入 JSON 内容。4.1 配置镜像加速器拉镜像慢的通用解法Docker Hub 在全球有多个节点但不同网络环境访问速度差别很大。如果你发现拉镜像速度惨不忍睹或者直接超时最简单可行的方式是在 daemon.json 里配置 mirror。{ registry-mirrors: [ https://docker.m.daocloud.io, https://mirror.baidubce.com ] }国内可用的公共镜像源地址变化较快建议先测试连通性再写入不通的源反而会拖慢拉取流程。配置完后重启 Dockersudo systemctl daemon-reload sudo systemctl restart docker验证是否生效docker info看Registry Mirrors字段是否列出了刚才填入的地址。要说明的是镜像加速器只对 Docker Hub 官方镜像生效如果你在拉第三方仓库的镜像加速器完全不起作用那是另一个话题。4.2 数据目录迁移把根分区空间留出来前面说过 Docker 默认把数据放在/var/lib/docker。如果根分区不大强烈建议把数据目录迁移到独立数据盘。假设数据盘挂载在/data要迁移到/data/docker分四步走。第一步停止 Dockersudo systemctl stop docker第二步同步数据内容到新目录注意保留权限和属性sudo rsync -avzP /var/lib/docker/ /data/docker/第三步修改 daemon.json{ data-root: /data/docker }第四步重启并确认sudo systemctl daemon-reload sudo systemctl start docker docker info | grep Docker Root Dir看到输出/data/docker就说明迁移成功。同步完成后先别急着删除/var/lib/docker验证几天没问题再清理给自己留条退路。4.3 日志容量限制不然磁盘分分钟被撑爆容器日志默认全量输出到 json-file如果程序一直打日志/var/lib/docker/containers/容器ID/*-json.log会无限增长。我见过一台测试机跑了个 debug 模式的服务两天写了 80GB 日志直接把磁盘塞满。在 daemon.json 里加上{ log-driver: json-file, log-opts: { max-size: 50m, max-file: 6 } }这个配置对之后新建的容器生效。已经跑起来的容器可以通过修改容器配置或重建容器来应用。规则很简单单个日志文件最大 50MB保留 6 个滚动文件超过就轮转。生产环境想长期集中管理日志再接入 Loki、ELK 这类系统但本地先限制住保证磁盘安全是第一步。4.4 容器自启动策略别把所有容器都设成 alwayssystemctl enable docker只保证了 docker 守护进程开机自启但容器本身是否跟着起来取决于创建容器时的--restart参数。always意味着进程退出后自动重启包括手动 stop 也会被强制拉起这有时候会踩坑。比较推荐的策略是docker run -d --restart unless-stopped --name myapp myimage:v1unless-stopped的含义是只要容器不是被人主动 stop 的守护进程启动时都会拉起它。这样既保证了断电、重启后服务自动恢复又能让你在维护时手动停掉容器而不会被拉起。已经创建的容器可以用docker update --restart unless-stopped 容器名修改策略不需要重建。5. 从 hello-world 到 MySQL 8.0、Redis 主从验证安装是否真的可靠配置改完后下一步是跑真实业务容器来验证。这里我不只跑一个 hello-world因为 hello-world 只能证明“镜像能拉下来、容器能启动”对网络、数据卷、端口映射都没有深入验证。我通常按三层递进测试基础镜像、数据库容器、主从集群。5.1 首跑 hello-world 和 docker infodocker run hello-world正常会输出一段英文说明表明容器从镜像启动并成功执行。如果这一步失败先别怀疑镜像有问题多半是网络层没通。赶紧回到网络检查的思路拉取镜像的路径包括 DNS、镜像加速器、防火墙这几个环节。同时再看一遍关键参数docker info | grep -E Server Version|Storage Driver|Cgroup Driver|Registry Mirrors确认存储驱动是 overlay2cgroup 驱动是 systemd。如果你看到 cgroup 驱动是 cgroupfs而宿主机用的是 systemd 管理服务建议在 daemon.json 里加上{ exec-opts: [native.cgroupdriversystemd] }然后重启 Docker不然后续在 systemd 环境下管理容器资源可能会有冲突。5.2 跑一个 MySQL 8.0 容器顺便实践数据卷验证数据库类容器是最实在的测试涉及端口映射、环境变量、数据持久化三层能力。以 MySQL 8.0 为例docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDYourStrongPass123 \ -e MYSQL_DATABASEtestdb \ -v /data/mysql8:/var/lib/mysql \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci解释一下参数-p 3306:3306把宿主机 3306 映射到容器 3306外部通过宿主机 IP 就能连数据库-e传入初始化环境变量首次启动时 MySQL 会按这些变量初始化 root 密码和数据目录-v把宿主机/data/mysql8挂载到容器内的数据目录容器删掉后数据还在宿主机上。启动后查看初始化过程docker logs -f mysql8看到ready for connections就说明数据库启动成功。进入容器验证docker exec -it mysql8 mysql -uroot -p输入密码后执行SHOW DATABASES;能看到testdb就说明初始化正常。这里有个细节宿主机的 MySQL 端口如果被占用了-p 3306:3306会启动失败换成-p 3307:3306即可外部就通过 3307 访问。5.3 用自定义网络搭 Redis 主从很多人在同一台机器上跑多个容器直接默认用 bridge 网络互相访问靠 IP 地址。但容器重建后 IP 会变配置里写死 IP 就会出问题。正确做法是自己创建 Docker 网络让容器之间用服务名互访。创建网络docker network create redis-cluster-net启动主节点docker run -d \ --name redis-master \ --network redis-cluster-net \ -p 6379:6379 \ redis:7 \ redis-server --appendonly yes启动两个从节点docker run -d \ --name redis-slave1 \ --network redis-cluster-net \ redis:7 \ redis-server --replicaof redis-master 6379 docker run -d \ --name redis-slave2 \ --network redis-cluster-net \ redis:7 \ redis-server --replicaof redis-master 6379注意从节点不需要映射端口到宿主机因为它们只在 Docker 内部互相通信。验证主从状态docker exec redis-master redis-cli info replication输出里connected_slaves:2就说明两个从节点已经连上主节点。这一步同时验证了自定义网络的 DNS 解析能力也能看出 Docker 网络隔离是否正常。5.4 docker 网络不通的快速排查链路装完 Docker 拉取镜像都没问题但容器之间或容器访问外网失败这类问题我排查的固定顺序是这样的。先看容器网络状态docker network ls docker inspect 容器名 | grep -A 20 Networks确认容器在哪个网络里IP 是什么。如果两个容器在不同自定义网络里互相 ping 不通是正常的网络隔离就是设计成这样的把容器加到同一个网络即可docker network connect redis-cluster-net redis-slave1再看宿主机路由和 iptablesiptables -L -n | grep DOCKER如果自定义网络之间的规则不对或者 firewalld 和 Docker 的链冲突最简单的解决办法是重启 Docker 让规则重新生成sudo systemctl restart docker但要注意这一步会打断所有运行中的容器生产环境慎用。还有一个常见原因是内核参数net.ipv4.ip_forward没有开启Docker 安装时一般会自动设置但被其他工具覆盖后就会出现外网不通的问题sysctl net.ipv4.ip_forward输出如果是 0执行echo net.ipv4.ip_forward 1 /etc/sysctl.conf sysctl -p如果是在虚拟机里装 Docker想要玩 Docker Desktop 那个图形界面反而会遇到virtualization support not detected的提示因为 Docker Desktop 的虚拟机需要 CPU 虚拟化扩展。Linux 服务器上直接用 Docker Engine 没有这个限制不需要硬件虚拟化支持这也是大多数生产环境选择原生安装的原因之一。6. 卸载与重装的干净姿势Docker 装好一段时间后因为升级失败、配置污染、或系统环境变化你可能需要卸载重来。很多人直接rm -rf /var/lib/docker然后重装结果旧配置残留导致新环境各种异常。干净卸载的流程其实不复杂分开两个发行版说。6.1 Ubuntu 上的卸载流程移除软件包sudo apt remove --purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y--purge会把软件包相关的系统配置文件也删掉避免残留 systemd unit 文件和配置文件干扰重装。手动删除残留目录sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd sudo rm -rf /etc/apt/sources.list.d/docker.list sudo rm -rf /etc/apt/keyrings/docker.gpg最后更新一下 apt 索引sudo apt update6.2 CentOS 上的卸载流程同样先移除软件包sudo yum remove docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y删除数据目录和源文件sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd sudo rm -rf /etc/yum.repos.d/docker-ce.repo用yum list installed | grep docker再检查一遍确保没有残留其他 docker 相关包比如docker-scan-plugin这类旧名称组件。全部清理完再走前面的安装流程就是一套全新环境了。6.3 重装后还能找回旧容器数据吗如果你只是想操作失误后恢复卸载前一定把数据目录拷贝出去不管在哪个发行版都是同一个逻辑。比如sudo cp -a /var/lib/docker /var/lib/docker.bak重装完成后把备份目录里的对应文件放回/var/lib/docker再启动 Docker。docker ps -a应该能看到旧的容器列表。不过这种“复活”方式有兼容性风险跨大版本升级时不建议这样做镜像层数据和新引擎的存储驱动如果对不上反而会启动失败。更稳妥的恢复方式是从干净的镜像重新启动容器再把刚才备份的数据卷挂载回去。还有一个关于重装的建议如果卸载前装过镜像加速器、改过 cgroup driver、设置过数据目录迁移重装后一定要检查 daemon.json 是否还在。/etc/docker/daemon.json这个文件不是我上一条 purge 命令能删掉的它需要单独处理。保留旧配置有时候是好事但如果配置内容过期重装后启动就可能直接 crash。我的习惯是重装前把它备份到一个带日期后缀的文件里重装时根据新版本环境重新评估而不是盲目恢复。整套流程走下来从环境检查到安装配置再到容器验证和卸载重装实际上覆盖了我在 Linux 服务器上部署 Docker 的大部分日常操作。平时遇到问题最多的不是装不装得上而是网络配置和数据目录管理。希望这篇文章能帮你少走点弯路。
返回列表