1. 内网离线部署的真实痛点与整体思路
1.1 为什么内网环境装 Docker 这么折腾
但凡在金融、制造、医疗这类对网络管控严格的行业待过,你一定遇到过这种场景:客户现场给了一台 CentOS 7 的物理机,网络完全隔离,连外网 DNS 都不通,但项目要求必须用 Docker 跑微服务。这时候你习惯性地敲下yum install -y docker-ce,等来的只有一行Could not resolve host: mirrors.aliyun.com,然后就是漫长的沉默。
内网离线安装 Docker 的核心矛盾在于:Docker 本身不是一个孤零零的二进制文件,它依赖一整套运行时组件——containerd、runc、docker-ce-cli、docker-ce-rootless-extras,还有一堆系统级的依赖包如libcgroup、container-selinux、fuse-overlayfs等。在线安装时 yum 会自动帮你解决这些依赖关系,离线环境下这些全得你自己搞定。更麻烦的是docker-compose,它从 v2 开始变成了一个独立的 Go 二进制文件,虽然安装简单了,但版本匹配和路径配置又成了新的坑点。
我见过太多人在内网部署时踩坑:有人只拷了docker-ce的 rpm 包,结果装到一半报依赖缺失;有人把 compose 装到了/usr/local/bin但 systemd 服务找不到;还有人忽略了 SELinux 和防火墙的配置,容器起来了但端口死活不通。这些问题在在线环境下可能一条命令就解决了,但在离线环境里,每一个都得手动处理。
1.2 整体方案选型:为什么用 rpm 离线包而不是二进制
离线安装 Docker 有两条路可走:一是下载静态二进制包(docker-xx.tgz),解压后手动配置 systemd 服务;二是下载完整的 rpm 包集合,用yum localinstall或rpm -ivh安装。我强烈推荐第二种,原因有三:
第一,rpm 包会自动处理依赖关系。虽然离线环境下 yum 不能联网下载依赖,但只要你把依赖包也一并下载好放在同一个目录,yum localinstall *.rpm就能自动按正确顺序安装,省去手动解决依赖的麻烦。第二,rpm 安装会自动创建docker用户组、配置 systemd 服务文件、设置好默认的daemon.json路径,这些在二进制方案里全得手动做。第三,rpm 包支持yum remove干净卸载,二进制方案卸载时容易残留文件。
至于docker-compose,从 v2 版本开始官方只提供独立的二进制文件,直接下载对应架构的二进制放到/usr/local/bin/或/usr/libexec/docker/cli-plugins/即可。注意 v2 版本的 compose 命令是docker compose(作为 docker 的子命令),而不是独立的docker-compose命令,这一点后面会详细说。
整个方案的核心思路可以概括为:在外网机器上准备完整的离线安装包集合,通过物理介质传输到内网机器,然后按顺序安装并验证。听起来简单,但每一步都有细节需要注意。
1.3 准备工作:外网机器上的包下载策略
在外网机器上准备离线包时,最关键的是要保证目标机器的操作系统版本、CPU 架构和外网机器一致。CentOS 7 有 x86_64 和 aarch64 两种架构,Docker 的 rpm 包是不同的。你可以用uname -m确认架构,用cat /etc/centos-release确认系统版本。
下载 rpm 包有两种方式。第一种是用yumdownloader工具,它能自动把指定包及其所有依赖下载到本地目录:
# 安装 yumdownloader 工具 yum install -y yum-utils # 下载 docker-ce 及其所有依赖到指定目录 mkdir -p /tmp/docker-offline yumdownloader --resolve --destdir=/tmp/docker-offline docker-ce docker-ce-cli containerd.io docker-compose-plugin这里--resolve参数是关键,它会自动解析并下载所有依赖包。如果你不加这个参数,下载下来的 rpm 包在内网安装时会报依赖缺失。
第二种方式是直接从 Docker 官方仓库手动下载。Docker 的 rpm 包托管在https://download.docker.com/linux/centos/7/x86_64/stable/Packages/路径下,你可以用浏览器或wget逐个下载。这种方式适合你明确知道需要哪些包的情况,但容易漏掉依赖。
我个人的习惯是用yumdownloader --resolve下载,然后把整个目录打包成一个 tar 文件,通过 U 盘或内网文件服务器传到目标机器。打包命令很简单:
cd /tmp tar czvf docker-offline.tar.gz docker-offline/传输到内网后解压即可。这里有个小技巧:如果你不确定目标机器是否缺少某些基础依赖,可以在外网机器上先用yum deplist docker-ce查看完整的依赖树,确保没有遗漏。
注意:CentOS 7 的
container-selinux包版本有讲究,如果目标机器已经装了旧版本的 SELinux 策略包,可能需要先升级。建议在离线包中同时包含container-selinux的最新版本。
2. 核心细节解析与实操要点
2.1 rpm 包安装顺序与依赖处理
把离线包传到内网机器后,第一步是解压并查看包列表:
tar xzvf docker-offline.tar.gz cd docker-offline ls -lh *.rpm你会看到类似这样的文件列表:
containerd.io-1.6.21-3.1.el7.x86_64.rpm container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm docker-ce-24.0.5-1.el7.x86_64.rpm docker-ce-cli-24.0.5-1.el7.x86_64.rpm docker-ce-rootless-extras-24.0.5-1.el7.x86_64.rpm docker-compose-plugin-2.20.2-1.el7.x86_64.rpm docker-scan-plugin-0.23.0-1.el7.x86_64.rpm fuse-overlayfs-0.7.2-6.el7_8.x86_64.rpm fuse3-libs-3.6.1-4.el7.x86_64.rpm slirp4netns-0.4.3-4.el7_8.x86_64.rpm安装时直接用yum localinstall一次性安装所有包:
yum localinstall -y *.rpmyum localinstall会自动分析包之间的依赖关系并按正确顺序安装。如果你用rpm -ivh *.rpm,可能会因为安装顺序不对而报依赖错误。当然,如果你对包之间的依赖关系非常清楚,也可以手动按顺序安装:先装container-selinux,再装containerd.io,然后是docker-ce-cli,最后装docker-ce。
安装完成后,验证 Docker 是否安装成功:
docker version如果看到 Client 和 Server 的版本信息,说明安装成功。如果只看到 Client 信息,Server 部分报错,说明 Docker 守护进程没有启动,需要检查 systemd 服务状态。
2.2 docker-compose 的两种安装方式与版本选择
docker-compose的安装是另一个容易出问题的环节。从 Docker Compose v2 开始,官方推荐的方式是安装docker-compose-plugin,它会把 compose 作为 docker 的一个 CLI 插件安装到/usr/libexec/docker/cli-plugins/目录下。安装后你可以用docker compose(注意中间是空格)来调用。
如果你下载的离线包中包含了docker-compose-plugin,那么yum localinstall时会自动安装,不需要额外操作。验证方式:
docker compose version如果这个命令报错,说明插件没有正确安装。你可以手动检查插件目录:
ls -lh /usr/libexec/docker/cli-plugins/正常情况下应该能看到docker-compose这个二进制文件。如果没有,你可以从离线包中单独解压这个 rpm 包,把里面的二进制文件拷到对应目录:
rpm2cpio docker-compose-plugin-2.20.2-1.el7.x86_64.rpm | cpio -idmv cp usr/libexec/docker/cli-plugins/docker-compose /usr/libexec/docker/cli-plugins/ chmod +x /usr/libexec/docker/cli-plugins/docker-compose如果你更习惯用独立的docker-compose命令(v1 风格),也可以下载 compose 的独立二进制文件。GitHub 的 release 页面提供了各版本的二进制下载,选择对应架构的文件放到/usr/local/bin/并赋予执行权限即可:
chmod +x /usr/local/bin/docker-compose docker-compose --version不过我要提醒一句:v1 版本的 compose 已经停止维护,新项目建议直接用 v2 的插件方式。如果你维护的老项目用的是docker-compose命令,可以在/usr/local/bin/下创建一个软链接指向插件二进制:
ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose这样两种命令风格都能用。
2.3 系统配置:SELinux、防火墙与内核参数
Docker 安装完成后,还需要对系统做一些配置才能正常使用。这些配置在在线安装时可能被 yum 自动处理了,但离线环境下需要手动确认。
首先是 SELinux。CentOS 7 默认开启 SELinux,这会导致容器挂载宿主机目录时出现权限问题。你可以选择永久关闭 SELinux:
setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config或者保留 SELinux 但在 Docker 配置中启用selinux-enabled。我个人的建议是内网环境直接关闭 SELinux,省去很多权限排查的麻烦。如果安全要求高必须开启,那就要在daemon.json中配置"selinux-enabled": true,并确保container-selinux包已正确安装。
防火墙方面,CentOS 7 默认使用 firewalld。Docker 启动后会自动创建docker0网桥并配置 iptables 规则,但 firewalld 可能会干扰容器网络。你可以选择关闭 firewalld:
systemctl stop firewalld systemctl disable firewalld或者保留 firewalld 但把docker0网桥加入信任区域:
firewall-cmd --permanent --zone=trusted --add-interface=docker0 firewall-cmd --reload内核参数方面,Docker 需要开启 IP 转发:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf sysctl -p这个参数在大多数 CentOS 7 系统上默认是开启的,但有些安全加固过的系统会关闭它,导致容器无法访问外网。建议安装后主动检查一下。
2.4 配置国内镜像加速与数据目录迁移
内网环境虽然不能访问外网,但如果你所在的内网有私有镜像仓库,或者有内部代理可以访问部分外网资源,配置镜像加速能显著提升拉取速度。编辑/etc/docker/daemon.json:
{ "registry-mirrors": ["https://your-internal-mirror.example.com"], "data-root": "/data/docker", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } }这里>systemctl daemon-reload systemctl restart docker systemctl enable docker
验证配置是否生效:
docker info | grep -A5 "Registry Mirrors" docker info | grep "Docker Root Dir"3. 实操过程与核心环节实现
3.1 完整离线安装流程:从外网打包到内网验证
我把整个流程拆成两个阶段:外网准备阶段和内网安装阶段。外网准备阶段的目标是生成一个完整的离线安装包,内网安装阶段的目标是把这些包正确安装并配置好。
外网准备阶段,在一台与目标机器同架构、同系统版本的机器上执行:
# 1. 安装必要工具 yum install -y yum-utils createrepo # 2. 添加 Docker 官方仓库 yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo # 3. 创建离线包目录 mkdir -p /opt/docker-offline/packages # 4. 下载 Docker 及依赖 yumdownloader --resolve --destdir=/opt/docker-offline/packages \ docker-ce docker-ce-cli containerd.io docker-compose-plugin # 5. 下载一些常用工具(可选) yumdownloader --resolve --destdir=/opt/docker-offline/packages \ vim net-tools lsof # 6. 生成 repodata(可选,方便用 yum 本地源安装) createrepo /opt/docker-offline/packages # 7. 打包 cd /opt/docker-offline tar czvf docker-offline-full.tar.gz packages/这里我额外下载了vim、net-tools、lsof这几个工具,因为很多最小化安装的 CentOS 7 系统连这些基础工具都没有,排查问题时很不方便。createrepo生成的 repodata 可以让你在内网用yum --disablerepo=* --enablerepo=local的方式安装,比yum localinstall更灵活。
内网安装阶段,在目标机器上执行:
# 1. 解压离线包 tar xzvf docker-offline-full.tar.gz -C /opt/ # 2. 方式一:直接用 yum localinstall cd /opt/packages yum localinstall -y *.rpm # 方式二:配置本地 yum 源 cat > /etc/yum.repos.d/local.repo <<EOF [local] name=Local Repository baseurl=file:///opt/packages enabled=1 gpgcheck=0 EOF yum --disablerepo=* --enablerepo=local install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin # 3. 启动 Docker systemctl start docker systemctl enable docker # 4. 验证 docker version docker compose version docker run --rm hello-world最后一步docker run --rm hello-world需要本地有hello-world镜像。如果内网完全隔离,你需要提前在外网机器上docker pull hello-world,然后docker save成 tar 文件传到内网,再docker load进去。这是离线环境下验证 Docker 是否正常工作的标准流程。
3.2 离线镜像的导入导出:让容器真正跑起来
Docker 装好了只是第一步,真正要让业务跑起来,还需要把镜像也弄到内网。Docker 提供了save和load两个命令来处理镜像的离线传输。
在外网机器上,把需要的镜像保存成 tar 文件:
# 保存单个镜像 docker save -o nginx-1.24.tar nginx:1.24 # 保存多个镜像到一个文件 docker save -o all-images.tar nginx:1.24 redis:7.0 mysql:8.0 # 批量保存所有镜像 docker save -o all-images.tar $(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>")这里有个细节:docker save保存的镜像包含所有层,文件可能很大。如果你只需要传输到内网,可以用gzip压缩:
docker save nginx:1.24 | gzip > nginx-1.24.tar.gz在内网机器上导入:
# 导入 tar 文件 docker load -i nginx-1.24.tar # 导入 gzip 压缩的文件 gunzip -c nginx-1.24.tar.gz | docker load导入后可以用docker images确认镜像是否加载成功。注意docker load不会覆盖已有的同名镜像,如果内网已经有旧版本,需要先docker rmi删除再导入。
对于docker-compose项目,你还需要把项目文件也传到内网。通常包括docker-compose.yml、.env文件、配置文件目录、初始化 SQL 脚本等。建议把这些文件放在一个目录里一起打包:
tar czvf myapp-deploy.tar.gz docker-compose.yml .env config/ init-sql/在内网解压后,用docker compose up -d启动。如果 compose 文件中引用了镜像,确保这些镜像已经提前docker load进去了。
3.3 参数计算与资源规划:容器配置的取舍
在内网环境部署时,资源规划往往比在线环境更重要,因为一旦配置不当,调整起来很麻烦。我以一台 8 核 16G 内存的 CentOS 7 机器为例,说说容器资源限制的配置思路。
假设你要部署一个典型的 Web 应用栈:Nginx + 应用服务 + MySQL + Redis。在docker-compose.yml中,可以通过deploy.resources或mem_limit、cpus来限制资源。注意deploy.resources只在 Swarm 模式下生效,单机模式下要用mem_limit和cpus:
services: mysql: image: mysql:8.0 mem_limit: 4g cpus: 2.0 environment: MYSQL_ROOT_PASSWORD: ${DB_PASSWORD} volumes: - ./data/mysql:/var/lib/mysql restart: unless-stopped redis: image: redis:7.0 mem_limit: 1g cpus: 0.5 command: redis-server --maxmemory 800mb --maxmemory-policy allkeys-lru volumes: - ./data/redis:/data restart: unless-stopped app: image: myapp:latest mem_limit: 2g cpus: 1.5 depends_on: - mysql - redis restart: unless-stopped nginx: image: nginx:1.24 mem_limit: 512m cpus: 0.5 ports: - "80:80" - "443:443" volumes: - ./config/nginx.conf:/etc/nginx/nginx.conf:ro restart: unless-stopped这里的内存分配逻辑是:MySQL 给 4G(InnoDB 缓冲池默认 128M,建议根据数据量调整到 1-2G),Redis 给 1G(maxmemory设为 800M 留出余量),应用服务给 2G,Nginx 给 512M。总共约 7.5G,加上系统本身占用 2-3G,16G 内存的机器还有余量应对突发流量。
CPU 分配方面,MySQL 给 2 核,应用服务给 1.5 核,Redis 和 Nginx 各 0.5 核,总共 4.5 核,8 核机器留出 3.5 核给系统和突发负载。注意cpus参数是软限制,容器在空闲时不会占用这么多,只有在竞争时才会受限。
restart: unless-stopped这个策略很重要,它保证容器在异常退出时自动重启,但手动停止后不会自动拉起。在内网环境中,机器可能长期无人值守,这个配置能避免服务意外挂掉后没人处理。
3.4 实操现场记录:一次完整的部署过程
我拿最近做的一个项目举例,目标机器是一台内网 CentOS 7.9,8 核 16G,系统盘 100G,数据盘 500G。部署的是一个 Spring Boot + MySQL + Redis + Nginx 的应用。
第一步,在外网机器上准备离线包。我用的是 CentOS 7.9 的虚拟机,执行了前面说的yumdownloader命令,下载了 Docker 相关包和常用工具。同时把应用镜像docker save成 tar 文件,应用部署文件打包成 tar.gz。
第二步,通过内网文件服务器把三个文件传到目标机器:docker-offline-full.tar.gz(约 120M)、app-images.tar.gz(约 800M)、app-deploy.tar.gz(约 5M)。
第三步,在目标机器上解压安装 Docker:
tar xzvf docker-offline-full.tar.gz -C /opt/ cd /opt/packages yum localinstall -y *.rpm systemctl start docker systemctl enable docker docker version第四步,配置 Docker 数据目录到数据盘:
mkdir -p /data/docker cat > /etc/docker/daemon.json <<EOF { "data-root": "/data/docker", "log-driver": "json-file", "log-opts": { "max-size": "100m", "max-file": "3" } } EOF systemctl daemon-reload systemctl restart docker第五步,导入镜像:
gunzip -c app-images.tar.gz | docker load docker images第六步,解压部署文件并启动:
tar xzvf app-deploy.tar.gz -C /opt/app/ cd /opt/app docker compose up -d docker compose ps第七步,验证服务:
curl -I http://localhost docker compose logs -f app整个过程大约花了 40 分钟,其中大部分时间花在文件传输和镜像导入上。实际安装 Docker 只用了不到 5 分钟。
4. 常见问题与排查技巧实录
4.1 安装阶段的高频报错与解决
离线安装 Docker 时,最常见的报错集中在依赖缺失和版本冲突上。我整理了一个速查表:
| 报错信息 | 原因 | 解决方法 |
|---|---|---|
Error: Package: docker-ce-xxx requires containerd.io >= xxx | 缺少 containerd.io 包或版本过低 | 下载对应版本的 containerd.io rpm 包一并安装 |
Error: Package: container-selinux is obsoleted by docker-ce | container-selinux 版本冲突 | 先卸载旧版 container-selinux,再安装新版 |
Error: transaction check error: file /usr/bin/docker from install conflicts | 已安装旧版 Docker | 先yum remove docker docker-client docker-common清理 |
Failed to start docker.service: Unit not found | systemd 服务文件未安装 | 检查 docker-ce 包是否完整安装,重新安装 |
docker: command not found | PATH 未包含 Docker 二进制目录 | 检查/usr/bin/docker是否存在,确认 PATH 配置 |
其中container-selinux版本冲突是最常见的。CentOS 7 默认仓库里的container-selinux版本可能比较旧,而新版 Docker 需要更高版本。解决方法是下载最新版的container-selinuxrpm 包,先安装它再安装 Docker:
rpm -ivh container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm yum localinstall -y docker-ce*.rpm containerd.io*.rpm如果系统里已经装了旧版container-selinux,需要先升级:
rpm -Uvh container-selinux-2.119.2-1.911c772.el7_8.noarch.rpm另一个常见问题是docker-compose命令找不到。如果你装的是docker-compose-plugin,命令是docker compose而不是docker-compose。如果你习惯用后者,需要创建软链接:
ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/docker-compose4.2 运行阶段的网络与权限问题
Docker 装好之后,运行容器时可能遇到网络不通或权限不足的问题。这类问题在内网环境下排查起来比较麻烦,因为不能随便上网搜。
容器无法访问外网,通常是因为 IP 转发没开启或 iptables 规则被清空。检查方法:
sysctl net.ipv4.ip_forward iptables -t nat -L POSTROUTING -n如果ip_forward是 0,执行sysctl -w net.ipv4.ip_forward=1并写入/etc/sysctl.conf。如果 iptables 的POSTROUTING链中没有 Docker 的 MASQUERADE 规则,重启 Docker 服务会自动重建。
容器端口无法从宿主机访问,检查 Docker 的端口映射是否正确:
docker port <container_name> netstat -tlnp | grep <port>如果docker port显示映射了但netstat看不到监听,可能是 firewalld 拦截了。临时关闭 firewalld 测试:
systemctl stop firewalld如果关闭后能访问,说明需要配置 firewalld 规则。
挂载宿主机目录时权限拒绝,这是 SELinux 导致的。临时解决:
setenforce 0永久解决是修改/etc/selinux/config把SELINUX=enforcing改为SELINUX=disabled,然后重启。或者在挂载时加:z或:Z标签:
volumes: - ./data:/var/lib/mysql:z:z表示多个容器共享该目录,:Z表示私有。注意这两个标签只在 SELinux 开启时有效。
4.3 离线环境下的镜像管理技巧
内网环境下,镜像管理是个持续性的工作。你不能像在线环境那样随时docker pull,所以需要建立一套镜像导入导出的规范。
我的做法是在外网维护一个镜像仓库目录,每次需要更新镜像时,用脚本批量导出:
#!/bin/bash # export-images.sh IMAGES=( "nginx:1.24" "redis:7.0" "mysql:8.0" "myapp:latest" ) OUTPUT_DIR="/opt/image-exports" DATE=$(date +%Y%m%d) mkdir -p ${OUTPUT_DIR}/${DATE} for img in "${IMAGES[@]}"; do filename=$(echo ${img} | tr ':/' '--') docker save ${img} | gzip > ${OUTPUT_DIR}/${DATE}/${filename}.tar.gz echo "Exported ${img} to ${filename}.tar.gz" done # 生成校验文件 cd ${OUTPUT_DIR}/${DATE} md5sum *.tar.gz > checksums.md5在内网导入时,先校验文件完整性:
md5sum -c checksums.md5然后批量导入:
for f in *.tar.gz; do gunzip -c $f | docker load done这里有个经验:镜像的 tag 一定要规范,不要用latest,因为latest在不同时间导出的内容可能不同,容易搞混。建议用版本号或日期作为 tag,比如myapp:20240115。
另外,定期清理内网机器上不用的镜像也很重要。可以用docker image prune清理悬空镜像,用docker system prune清理停止的容器、无用网络和悬空镜像。但注意docker system prune -a会删除所有未被使用的镜像,执行前确认清楚。
4.4 独家避坑经验:那些文档里不会写的事
说几个我在实际项目中踩过的坑,都是文档里不会写的。
第一个坑:yum localinstall在最小化安装的系统上可能失败。CentOS 7 最小化安装时,yum本身可能缺少某些插件,导致localinstall命令不可用。这时候可以用rpm -ivh逐个安装,但要注意顺序。或者先安装yum-plugin-local包。
第二个坑:Docker 数据目录迁移后权限问题。如果你把>semanage fcontext -a -t container_var_lib_t "/data/docker(/.*)?" restorecon -Rv /data/docker
如果semanage命令不存在,需要先安装policycoreutils-python包。
第三个坑:docker compose的.env文件路径问题。docker compose默认从当前目录读取.env文件,如果你在别的目录执行docker compose -f /opt/app/docker-compose.yml up,.env文件不会被加载。解决方法是加--env-file参数指定路径,或者先cd到 compose 文件所在目录。
第四个坑:离线环境下时间同步问题。内网机器如果时间不准,会导致容器内的时间也不准,进而影响日志时间戳、证书验证等。建议在内网搭建 NTP 服务器,或者至少确保宿主机时间正确。Docker 容器默认使用宿主机时间,但如果你在容器里装了tzdata并设置了TZ环境变量,时区会按容器配置来。
第五个坑:docker save大镜像时内存占用高。docker save会把镜像的所有层打包,如果镜像很大(比如几个 G),可能会占用较多内存和磁盘空间。建议先docker pull到本地,用docker save时加gzip压缩,并且确保/tmp或输出目录有足够空间。
5. 内网 Docker 环境的长期维护建议
5.1 版本管理与升级策略
内网环境的 Docker 版本管理比在线环境更需要规划。因为升级需要重新走一遍离线安装流程,所以不能像在线环境那样随意yum update。
我的建议是:保持 Docker 版本与业务需求匹配,不盲目追新。Docker 24.0.x 是目前的稳定版本,支持到 2024 年底。如果你的业务没有用到新版本特性,没必要频繁升级。升级前先在外网测试环境验证,确认新版本与现有 compose 文件、镜像兼容后再在内网操作。
升级的步骤和首次安装类似:下载新版 rpm 包,传到内网,yum localinstall覆盖安装,然后重启 Docker 服务。注意升级前备份/etc/docker/daemon.json和 compose 文件,升级后检查配置是否被覆盖。
docker-compose-plugin的版本也要和 Docker 版本匹配。一般来说,compose 插件版本可以独立于 Docker 引擎版本,但建议使用官方推荐的组合。你可以在 Docker 官方文档的 release notes 中查到版本兼容性信息。
5.2 监控与日志:让问题可追溯
内网环境出了问题,排查起来比在线环境困难,所以监控和日志尤为重要。Docker 本身提供了docker events和docker logs命令,但长期运行需要更系统的方案。
我通常会在内网部署一个轻量的监控栈:cAdvisor收集容器指标,Prometheus存储数据,Grafana展示面板。这三个组件都可以用 Docker 跑,镜像提前docker save进去就行。docker-compose.yml大概长这样:
services: cadvisor: image: gcr.io/cadvisor/cadvisor:v0.47.0 volumes: - /:/rootfs:ro - /var/run:/var/run:ro - /sys:/sys:ro - /var/lib/docker/:/var/lib/docker:ro ports: - "8080:8080" restart: unless-stopped prometheus: image: prom/prometheus:v2.47.0 volumes: - ./config/prometheus.yml:/etc/prometheus/prometheus.yml:ro - ./data/prometheus:/prometheus ports: - "9090:9090" restart: unless-stopped grafana: image: grafana/grafana:10.1.0 volumes: - ./data/grafana:/var/lib/grafana ports: - "3000:3000" restart: unless-stopped日志方面,除了 Docker 的json-file驱动,还可以考虑journald驱动,把容器日志统一交给 systemd 管理。配置方法是在daemon.json中设置"log-driver": "journald",然后用journalctl -u docker查看。不过journald的日志查询不如json-file灵活,我一般还是用json-file配合日志轮转。
5.3 备份与恢复:数据安全底线
内网环境的数据备份往往被忽视,但一旦出问题就是大问题。Docker 环境需要备份的主要有三部分:镜像、容器数据卷、compose 文件。
镜像备份用docker save定期导出关键镜像,存到内网文件服务器或磁带库。数据卷备份可以用docker run --rm -v volume_name:/data -v /backup:/backup alpine tar czf /backup/volume_name.tar.gz -C /data .的方式打包。compose 文件和配置文件建议用 Git 管理,内网可以搭建 GitLab 或 Gitea 做版本控制。
恢复时,先docker load导入镜像,再解压数据卷备份到对应目录,最后docker compose up -d启动。注意数据卷的恢复要在容器停止状态下进行,否则可能数据不一致。
我个人的习惯是每周做一次全量备份,每天做一次增量备份。备份文件保留最近 4 周,过期的自动清理。这套流程用 cron 脚本就能实现,不需要额外的备份软件。
5.4 安全加固:内网也不能掉以轻心
很多人觉得内网环境安全要求低,Docker 配置比较随意。但实际上,内网一旦被突破,Docker 的 root 权限会成为攻击者的跳板。几个基本的安全加固措施:
第一,限制 Docker API 的访问。默认情况下 Docker 守护进程监听 Unix socket,这是安全的。但如果你开启了 TCP 监听(-H tcp://0.0.0.0:2375),任何能访问该端口的人都能控制 Docker。内网环境也不建议开启 TCP 监听,如果必须开启,一定要配置 TLS 证书认证。
第二,容器不要用 root 用户运行。在 Dockerfile 中用USER指令切换到非 root 用户,或者在 compose 文件中用user: "1000:1000"指定。这样即使容器被突破,攻击者也只有普通用户权限。
第三,限制容器的能力。默认情况下容器拥有不少 Linux capabilities,可以通过cap_drop和cap_add精细控制:
services: app: image: myapp:latest cap_drop: - ALL cap_add: - NET_BIND_SERVICE security_opt: - no-new-privileges:trueno-new-privileges防止容器内的进程通过 setuid 等方式提权,是个很实用的安全选项。
第四,定期更新镜像。内网环境容易忽略镜像更新,但旧镜像可能包含已知漏洞。建议每季度检查一次关键镜像的更新情况,及时导入新版本。
6. 从零到一:给新手的快速上手清单
6.1 五分钟检查清单
如果你第一次做内网离线安装,按这个清单逐项确认,能避开 90% 的坑:
- [ ] 确认目标机器架构(
uname -m)和系统版本(cat /etc/centos-release) - [ ] 在外网机器上下载完整 rpm 包(用
yumdownloader --resolve) - [ ] 下载
docker-compose-plugin或 compose 独立二进制 - [ ] 准备
hello-world镜像用于验证 - [ ] 传输离线包到内网(U 盘或文件服务器)
- [ ] 解压后
yum localinstall -y *.rpm - [ ] 启动 Docker 并设置开机自启
- [ ] 配置
daemon.json(数据目录、日志轮转、镜像加速) - [ ] 关闭 SELinux 或配置正确标签
- [ ] 配置防火墙或关闭 firewalld
- [ ] 导入业务镜像
- [ ] 用
docker compose up -d启动服务 - [ ] 验证服务可访问
6.2 常用命令速查
内网环境下,这些命令你会反复用到:
# 查看 Docker 版本和配置 docker version docker info # 查看镜像和容器 docker images docker ps -a # 导入导出镜像 docker save -o image.tar image:tag docker load -i image.tar # 查看容器日志 docker logs -f --tail 100 container_name # 进入容器 docker exec -it container_name /bin/bash # 查看容器资源占用 docker stats # 清理无用资源 docker system prune -a # compose 操作 docker compose up -d docker compose down docker compose ps docker compose logs -f docker compose restart6.3 我个人的经验总结
做了这么多内网部署项目,我最大的体会是:离线安装的核心不是技术难度,而是细致程度。在线安装时你不需要关心依赖关系、版本匹配、路径配置,因为 yum 和 Docker 官方脚本都帮你处理了。但离线环境下,每一个细节都需要你自己确认。
我的习惯是每次部署前先列一个清单,把需要下载的包、需要配置的参数、需要验证的步骤都写下来,部署时逐项打勾。这样虽然看起来麻烦,但能避免遗漏关键步骤导致的返工。另外,离线包建议多准备一份,放在不同的物理介质上,万一 U 盘坏了或者文件服务器挂了,还有备份可用。
还有一点:内网环境的文档记录很重要。每次部署后把实际操作的命令、遇到的问题、解决方法都记录下来,形成自己的知识库。下次遇到类似环境时,直接翻记录比重新摸索快得多。我现在的知识库里积累了十几个不同客户环境的部署记录,每次新项目都能找到参考。
最后分享一个小技巧:如果你经常需要做内网部署,可以做一个“万能离线包”,包含 Docker、compose、常用工具(vim、net-tools、lsof、curl、wget)以及几个基础镜像(alpine、busybox、nginx)。这个包大概 1-2G,放在 U 盘里随身带,到了现场直接解压安装,能省去很多准备时间。