ARTICLE DETAIL

资讯详情

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

Linux解压安装Docker与Compose:离线部署与版本控制实战

Linux解压安装Docker与Compose:离线部署与版本控制实战 聊个技术选型的题。很多人在 Linux 上装 Docker第一反应就是跑官方一键脚本或者apt install docker.io、yum install docker-ce。但真正到了生产环境尤其是离线内网、等保要求严的机房、或者需要精确控制 Docker 引擎版本的场景这些方式往往会卡壳。我个人的选择是解压安装下载官方静态二进制包手动解压、放路径、注册 systemd 服务Docker Compose 也一样处理。这套流程在 Linux 全系发行版通用不依赖包管理器也不会被系统仓库版本牵着鼻子走。这篇文章就完整走一遍解压安装 Docker 与 Docker Compose 的实操流程适合三类人网络环境不自由打算在内网离线部署的人想精确控制 Docker 和 Compose 版本不想被系统包管理干扰的人还有刚入门但不想无脑复制粘贴脚本想搞清楚每个文件装到哪、服务怎么拉起来的人。读完之后你不仅能自己装上还会知道为什么这样装、装坏了怎么排查。1. 为什么选择解压安装跳过包管理器直接吃二进制1.1 一键脚本与系统仓库的隐藏成本curl -fsSL https://get.docker.com | sh确实方便但这个脚本会帮你改系统源、装一堆依赖、设置开机启动整个过程对系统的侵入性很强。万一脚本执行到一半网络断了或者源里缺了某个依赖你得到的是一个半吊子环境想清理干净还得自己手动查改了哪些文件。更麻烦的是脚本安装的版本是它决定的不是你的。同一套 Docker 引擎在 Ubuntu 20.04 和 CentOS 7 上装出来的版本可能有差异后续编排文件和镜像兼容性就会出问题。系统仓库里的docker.io或docker包也好不到哪去。Debian 和 Ubuntu 官方源里的 Docker 版本经常落后好几个大版本CentOS 源里的老版本更不用提甚至会出现 containerd 和 runc 版本不匹配的坑。对于只是本地跑着玩的人来说无所谓但如果要部署微服务、用 Docker Compose 编排 Nacos、MySQL 这类有版本要求的中间件Docker 引擎版本太老会导致镜像内部依赖异常问题非常隐蔽。解压安装就不存在这些麻烦。官方静态二进制包把运行时依赖都打在包里拷到目标机器上就能解压运行不碰系统包管理器的源和依赖关系。版本由你自己决定想用 24.0.7 就用 24.0.7想升级到 27.x 就重新解压一份替换旧文件整个过程可控、可回滚、可复制。1.2 解压安装解决的典型场景我在实际项目中用解压安装主要解决三类问题。第一类是离线内网部署。运维环境经常是物理隔离的不能访问外网包管理器源也往往是空的。靠yum或apt装 Docker 基本无解即便配置了内网源也可能版本不全。解压安装只需要把 Docker 的 tgz 文件和 Compose 二进制通过移动介质拷进去配合内网镜像仓库整套容器环境不需要外网就能跑起来。第二类是版本一致性控制。我给多个机房的服务器部署同一个 Docker 版本如果每台机器都用在线脚本安装可能因为网络延迟和源状态不同最后装出不同的版本。解压安装则可以把二进制包固化成固定版本局域网内分发每台机器解压后的文件内容一模一样。第三类是特殊系统环境的适配。有些国产 Linux 系统、轻量路由系统或者自研操作系统既没有 systemd也没有可用的包管理器仓库或者官方脚本不支持该系统。解压出来的二进制包放在任意目录下执行不需要编译不依赖发行版特定的包管理机制兼容性好得多。1.3 环境准备动手前检查这几项开始之前先花两分钟确认基础环境避免装到一半才发现平台不对。先看 CPU 架构。几乎所有安装步骤里下载文件的路径都带架构标识x86 服务器选 x86_64ARM 服务器选 aarch64查看方式很简单uname -m然后在 /tmp 或者 /opt 下准备一个工作目录预留 1GB 以上磁盘空间用于解压缓存Docker 二进制本身只有几十到上百 MB不要紧但运行时的数据目录还是要预留充足。再确认 Linux 系统有tar和wget或者curl绝大多数发行版默认都有。如果连这些都没有那就用apt install tar wget或yum install tar wget临时补一下后续步骤就顺畅了。最后如果你的服务器上有旧版本的 Docker保险起见先停掉旧的再继续。注意卸载旧版本时/var/lib/docker目录里的数据卷和镜像会保留如果不需要旧数据可以一起清掉有业务数据就千万别乱删。2. Docker 引擎二进制包下载与解压部署2.1 获取官方静态二进制包Docker 官方把所有平台的静态二进制包放在 download.docker.com 的 static 目录下稳定版的路径格式一般是这样的https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz把路径里的x86_64换成aarch64就是 ARM 版本。版本号可以按需替换我建议取一个常用的稳定大版本比如 24.0.7 或者 27.x不要一上来就追最新版因为最新版对内核、iptables 和 cgroup 版本都有要求老系统可能带不动。下载的时候顺带把校验文件也下下来避免传输过程文件损坏cd /tmp wget https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz wget https://download.docker.com/linux/static/stable/x86_64/docker-27.3.1.tgz.sha256 sha256sum -c docker-27.3.1.tgz.sha256sha256sum -c输出OK就说明文件完整可以继续。这一步在离线环境尤其重要移动存储介质拷贝过程中偶尔会出现文件损坏校验不过的话解压出来出现各种诡异问题排查时完全摸不着头脑。2.2 解压文件并安装到系统路径下载好的是压缩包解压后是一个 docker 目录tar xzvf docker-27.3.1.tgz解压出来的目录里包含docker、dockerd、containerd、containerd-shim-runc-v2、ctr、runc、docker-init、docker-proxy这些文件。注意这里面的可执行文件是分散的不像某些软件包那样有一个统一安装脚本。我们需要把这些二进制复制到/usr/local/bin下因为这个目录默认在 PATH 环境变量里而且比/usr/bin优先级更高不会和系统自带的命令冲突。sudo cp docker/* /usr/local/bin/ sudo chmod x /usr/local/bin/docker /usr/local/bin/dockerd做完之后可以快速验证一下docker version这个命令此时会输出 Client 版本信息但 Server 版本会显示ERROR: Cannot connect to the Docker daemon这是正常的因为我们还没启动服务。这个输出已经能证明二进制文件可以正常运行了。有一些老手不喜欢把文件直接复制进系统目录而是解压到一个固定目录如/opt/docker/再通过软链接接入 PATHsudo mkdir -p /opt/docker sudo tar -xzf docker-27.3.1.tgz -C /opt/docker --strip-components1 sudo ln -s /opt/docker/docker /usr/local/bin/docker sudo ln -s /opt/docker/dockerd /usr/local/bin/dockerd这种方式的好处是升级和卸载更清晰把/opt/docker目录删掉就能清理干净软链接集中管理不会散落到系统目录里。缺点是其他二进制比如containerd、runc也要同步做软链接否则后续调用容易有问题。2.3 校验二进制依赖完整静态二进制包虽然不依赖系统安装包但依赖一些系统级的共享库。用ldd检查一下 docker 和 dockerd 的依赖是否能找到ldd /usr/local/bin/dockerd正常输出里会有libc.so.6、libpthread、libsystemd之类的库如果出现not found说明系统缺少基础运行库。一般的云服务器或物理机系统不会缺但如果你在精简到极致的容器场景或嵌入式系统上做解压安装就需要注意这个问题。另外如果你的系统用 SELinux 或者 AppArmor可能会对二进制文件的执行有一些限制。临时关闭 SELinux 测试setenforce 0 docker version如果能正常输出 Client 信息说明是 SELinux 策略拦截后续再针对策略做调整而不是安装没成功。3. 创建 systemd 服务让 Docker 引擎跑起来3.1 编写 docker.service 与 docker.socket二进制放好了但 Docker 引擎需要以后台守护进程方式运行并随开机自启所以我们必须给它注册 systemd 服务。解压包里并不会自带服务文件网上常见做法是直接下载在线安装脚本生成的 service但我们手动创建完全不难。先创建 docker 用户组方便后续非 root 用户使用 docker 命令sudo groupadd docker接着创建 socket 单元文件/etc/systemd/system/docker.socket。很多教程会忽略这一步直接写 service但实际上 Docker 的默认启动方式是先建 socket 再监听如果你只写 service启动时也可能正常工作但架构上不完整。[Unit] DescriptionDocker Socket for the API PartOfdocker.service [Socket] ListenStream/var/run/docker.sock SocketMode0660 SocketUserroot SocketGroupdocker [Install] WantedBysockets.target然后创建主服务文件/etc/systemd/system/docker.service[Unit] DescriptionDocker Application Container Engine Documentationhttps://docs.docker.com Afternetwork-online.target firewalld.service containerd.service Wantsnetwork-online.target Requiresdocker.socket [Service] Typenotify ExecStart/usr/local/bin/dockerd -H fd:// --containerd/run/containerd/containerd.sock ExecReload/bin/kill -s HUP $MAINPID TimeoutSec0 RestartSec2 Restartalways LimitNOFILEinfinity LimitNPROCinfinity LimitCOREinfinity TasksMaxinfinity Delegateyes KillModeprocess [Install] WantedBymulti-user.target几个关键配置值得解释一下。Typenotify表示 dockerd 启动完成后会通过通知机制告知 systemd而不是简单地按进程存活判断。ExecStart里有-H fd://意思是让 dockerd 通过 socket 激活方式接收来自 systemd 的文件描述符而不是直接自己监听/var/run/docker.sock。Delegateyes允许 Docker 管理自己的 cgroup 系统这是容器资源隔离的基础别随手删掉。Restartalways表示如果守护进程崩溃systemd 自动拉起生产环境强烈建议保留。还有个容易忽略的点containerd.sock的路径。新版 Docker 依赖 containerd这个 socket 文件由解压包里的 containerd 创建。后面启动如果报错找不到 containerd.sock多半是 containerd 没起来或者路径不对。3.2 启动 Docker 并做最小化验证单元文件写好后先重新加载 systemd 配置再设置开机自启并启动sudo systemctl daemon-reload sudo systemctl enable --now docker sudo systemctl status dockerstatus输出出现active (running)之后先别急着拉镜像先验证引擎能否响应 APIdocker info这条命令输出里能看到 Kernel Version、Operating System、Docker Root Dir、Server Version 这些关键信息。如果报Cannot connect to the Docker daemon大概率是服务和 socket 没有同时启动可以强制重启sudo systemctl stop docker sudo systemctl start docker.socket sudo systemctl start docker能跑通docker info就说明引擎已经正常工作了此时拉一个 hello-world 做进一步验证。如果网络受限拉不动镜像直接用docker info作为验收依据也是够的引擎是否可用并不依赖外部镜像。3.3 调整 daemon.json目录、日志与网络刚装好的 Docker 默认数据根目录是/var/lib/docker日志驱动是 json-fileiptables 默认打开。这些都可以在/etc/docker/daemon.json里覆盖。我一般新建一个基础配置{ data-root: /data/docker, log-driver: json-file, log-opts: { max-size: 50m, max-file: 5 }, storage-driver: overlay2, registry-mirrors: [ https://加速服务地址 ] }>sudo systemctl daemon-reload sudo systemctl restart docker docker info | grep Docker Root Dir注意修改>https://github.com/docker/compose/releases/download/v2.24.7/docker-compose-linux-x86_64如果系统是 ARM 架构把文件名末尾换成aarch64wget https://github.com/docker/compose/releases/download/v2.24.7/docker-compose-linux-aarch64 -O /tmp/docker-composeGitHub 在大陆环境可能下载慢这是比较正常的情况。没有外网条件的离线服务器可以让能联网的机器下载后再拷贝过去。文件不大一般不到一百 MB。下载完成后记得加执行权限sudo chmod x /tmp/docker-compose在拷贝到最终目录前可以先执行一次验证文件是否完整/tmp/docker-compose version能输出Docker Compose version v2.24.7就说明文件没损坏。4.2 插件模式与传统 docker-compose 模式安装 Compose 有两条路径插件模式即把它作为 Docker CLI 的子命令docker compose传统模式即把它作为独立命令docker-compose。现在官方推荐插件模式新版 docker compose 的环境信息、docker compose语法和 docker 命令集成得更紧密。插件模式的文件必须放在 Docker CLI 插件目录。用户级目录是~/.docker/cli-plugins/系统级目录是/usr/local/lib/docker/cli-plugins/。为了让所有用户都能用放在系统目录sudo mkdir -p /usr/local/lib/docker/cli-plugins sudo mv /tmp/docker-compose /usr/local/lib/docker/cli-plugins/docker-compose sudo chmod x /usr/local/lib/docker/cli-plugins/docker-compose放好后验证docker compose version输出Docker Compose version v2.24.7就成功了。传统模式则简单很多直接把二进制放到/usr/local/bin/docker-composesudo mv /tmp/docker-compose /usr/local/bin/docker-compose sudo chmod x /usr/local/bin/docker-compose docker-compose version两种方式可以同时存在并不冲突。我建议以插件模式为主如果你还有老项目里的 shell 脚本调用的是docker-compose -f ... up -d那就在保留插件模式的同时再加一个传统模式的软链接两者兼容老脚本和新命令最稳妥。4.3 解决 docker compose 找不到命令的问题经常有人在群里问明明安装了 compose运行docker compose version却提示docker: unknown command: compose。这个错误的根源只有一个Docker CLI 在指定插件目录里找不到名为docker-compose的可执行文件。排查思路很简单。第一步确认文件确实在插件目录里ls -l /usr/local/lib/docker/cli-plugins/docker-compose第二步确认文件有可执行权限没有 x 权限 Docker 不会加载它chmod x /usr/local/lib/docker/cli-plugins/docker-compose第三步确认这个可执行文件是正常的、没有损坏。有时候从 Windows 拷贝过来的文件可能是带特殊属性的文本文件不是 Linux 可执行格式直接file /usr/local/lib/docker/cli-plugins/docker-compose可以看文件类型。第四步检查 PATH 环境变量是否包含了/usr/local/lib/docker/cli-plugins。Docker 默认会扫描这个目录但如果你手动改过环境也可能漏掉。如果还是不行就退一步用传统模式把二进制放到/usr/local/bin/docker-compose至少能保证老项目的docker-compose up不受影响。插件模式的问题后续再逐个环节排查。5. 离线环境与常见问题排查实录5.1 离线内网环境下怎么把整套环境带进去解压安装最大的价值就是离线部署。顺一遍完整流程在有网机器上下载 docker tgz 和 compose 二进制连同要用到的镜像 tar 文件一起打包docker save nginx:alpine mysql:8.0 -o images.tar然后把这些文件拷贝到离线服务器。Docker 引擎本身不需要额外依赖包按之前的流程解压安装就行镜像则用 load 导入sudo tar -xzf docker-27.3.1.tgz sudo cp docker/* /usr/local/bin/ sudo groupadd docker # 创建 systemd 文件并启动 sudo systemctl enable --now docker docker load -i images.tar docker images如果你的团队有内网镜像仓库那更简单把daemon.json里的registry-mirrors换成内网仓库地址或者直接docker pull 内网仓库地址/nginx:alpine镜像拉取速度非常快。离线安装时最需要关心的是内核版本和依赖库。如果你的服务器内核太老overlay2存储驱动起不来可以把daemon.json改成storage-driver: vfs先应急等换成新内核后再调回 overlay2。这点在二手设备、老旧国产系统上经常遇到。5.2 常见错误速查表实际操作中我踩过的坑整理成表按出现频率从高到低排列错误现象根本原因解决办法docker: unknown command: composeCompose 插件未安装或不在插件目录检查cli-plugins目录和可执行权限或改用docker-compose独立命令Cannot connect to the Docker daemon at unix:///var/run/docker.sockdockerd未启动或socket文件未生成systemctl status docker看日志手动启动containerd和dockerGot permission denied while trying to connect to the Docker daemon非root用户不在docker组sudo usermod -aG docker 用户名重新登录iptables: No chain/target/match by that nameDocker创建iptables链失败或内核模块未加载重启系统或加载br_netfilter模块必要时daemon.json设iptables: falseError starting daemon: error initializing graphdriver: operation not supported内核不支持overlay2改存储驱动为vfs或升级内核listen tcp :53: bind: address already in use端口被占用停止占用进程或调整Docker代理监听端口docker: Error response from daemon: pull access denied镜像不存在或没有权限检查镜像名拼写、是否登录私有仓库、是否配置registry-mirrorsFailed to connect to containerd: dial unix /run/containerd/containerd.sockcontainerd未启动手动启动containerd或者在daemon.json里指定containerd路径有些问题看起来像是配置问题其实根子在系统内核版本。比如 iptables 规则异常多半是因为内核没有加载br_netfilter模块先执行modprobe br_netfilter然后写入/etc/modules-load.d/br_netfilter.conf保证开机自动加载。这个坑在 CentOS 7 的 3.10 内核上特别常见不做这一步容器网络经常不通。5.3 升级、卸载与回滚的思路解压安装的另一个好处是升级和回滚就像换文件一样简单。升级 Docker 引擎停服、替换二进制、重启服务三步走sudo systemctl stop docker sudo tar -xzf docker-27.3.2.tgz sudo cp docker/* /usr/local/bin/ sudo systemctl start docker docker version这套流程不会动/var/lib/docker下的数据镜像和容器卷都还在。但升级前最好还是备份一下重要目录sudo cp -a /var/lib/docker /var/lib/docker.bak.$(date %Y%m%d)回滚也同样是替换二进制把旧版本的文件重新放回去重启服务即可。注意如果升级后新版本自动迁移了数据目录的元数据结构比如从 overlay2 的旧格式迁移到新格式这时候用旧二进制回滚可能会识别不了新数据所以升级前备份数据比备份二进制更重要。卸载则是另一回事。如果确定哪天不用 Docker 了先把服务停掉再删除 systemd 单元文件和二进制目录sudo systemctl stop docker docker.socket sudo systemctl disable docker docker.socket sudo rm -f /usr/local/bin/docker /usr/local/bin/dockerd /usr/local/bin/containerd* /usr/local/bin/runc /usr/local/bin/docker-proxy /usr/local/bin/docker-init sudo rm -rf /etc/systemd/system/docker.service /etc/systemd/system/docker.socket /etc/docker sudo systemctl daemon-reload但是/var/lib/docker里如果有数据库容器的数据卷删除前务重复确认建议先整体转移到备份目录避免误删。我用这套方式维护好几台离线服务器最大的感触就是一切都在明面上哪个文件在哪个位置、版本是多少、怎么换心里清清楚楚不像在线脚本装完还得猜它改了什么。最后再分享一个小技巧整套安装过程整理成一个 shell 脚本把下载、解压、写 systemd 文件、配置 daemon.json 都写成可重复执行的形式以后每来一台新机器拿着脚本跑一遍就结束了。这样才叫真正的通用安装。
返回列表