为什么用 `service` 启动 Docker 容器总出错,而 `systemctl` 却能轻松搞定?

382 阅读3分钟

在日常使用 Docker 的过程中,你是否遇到过这样的问题:当使用 service 启动 Docker 后,尝试启动容器却报错,提示类似以下信息:

Error response from daemon: driver failed programming external connectivity on endpoint mysql
(iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 3306 -j DNAT --to-destination 172.17.0.2:3306 ! -i docker0:
iptables: No chain/target/match by that name. (exit status 1))

然而,当你换用 systemctl 启动 Docker 服务后,容器却可以正常启动。这到底是为什么?本文将带你深入剖析其中的原因,并提供实用的解决方案。


service vs systemctl:启动机制的不同

servicesystemctl 是 Linux 系统中管理服务的两种工具。尽管它们的目标一致,但在实现方式上却有显著差异。

1. service 的工作原理

  • service 属于传统的 SysVinit 工具,直接调用 /etc/init.d/ 下的脚本来启动服务。
  • 它没有严格的依赖管理机制,启动服务时不会检查与之相关的系统状态,例如网络是否就绪、防火墙是否启动。

2. systemctl 的工作原理

  • systemctl 是基于现代 systemd 的工具,能够通过 .service 单元文件精确管理服务。
  • 在启动服务前,systemctl 会按照 .service 文件中的依赖关系加载所有必要的系统组件。例如,Docker 的 .service 文件会显式声明对 network-online.target(网络就绪)和 iptables 的依赖,确保这些条件满足后再启动服务。

为什么用 service 启动 Docker 会报错?

当使用 service 启动 Docker 时,可能出现以下问题:

  1. iptables 未正确加载 Docker 需要通过 iptables 配置容器的网络规则,而报错信息中:

    iptables: No chain/target/match by that name.
    

    表明 Docker 尝试添加端口转发规则时,iptablesDOCKER 链尚未创建。这通常是由于 iptables 模块未加载或初始化不完全导致的。

  2. 网络依赖未满足 service 启动 Docker 时,可能系统网络尚未完全就绪(例如 network-online.target 未完成初始化),导致容器的网络功能无法正常工作。

  3. 缺乏依赖管理 service 启动机制较为简单,无法像 systemctl 那样精确加载依赖的服务或模块,可能引发各种初始化问题。


如何解决这个问题?

1. 使用 systemctl 启动 Docker

在现代 Linux 系统中,建议始终使用 systemctl 来管理服务。启动 Docker 的命令如下:

systemctl start docker

此外,还可以设置 Docker 开机自启:

systemctl enable docker

2. 检查 Docker 的 .service 文件

确保 Docker 的 .service 文件中正确定义了网络依赖。你可以通过以下命令查看:

systemctl cat docker

通常 .service 文件中会包含以下内容:

[Unit]
Wants=network-online.target
After=network-online.target

3. 确保 iptables 模块加载

如果问题仍然存在,可以手动加载 iptables 模块:

modprobe ip_tables

检查当前的 iptables 规则:

iptables -L -t nat

4. 如果必须使用 service

若你的系统环境限制只能使用 service,在启动 Docker 前,确保网络和防火墙规则已就绪:

service networking restart
service docker restart

总结

通过本文的分析,可以得出以下结论:

  1. 优先使用 systemctl 启动 Docker 服务,它能够确保依赖加载正确,避免网络和防火墙初始化的问题。
  2. 如果必须使用 service,则需要手动初始化网络和 iptables,确保 Docker 的运行环境完备。
  3. 遇到问题时,可通过检查日志(如 /var/log/syslogjournalctl -u docker)定位根本原因。

写在最后

现代 Linux 系统管理更趋向于依赖 systemd,它提供了更智能的服务管理能力。对于习惯使用传统工具的开发者来说,尽快熟悉 systemctl 的用法是必不可少的一步。希望本文能帮助你更轻松地管理 Docker 服务,少踩坑,多提效!