解决 Ubuntu 上 Docker 安装与网络问题的实践笔记
在 Ubuntu(Noble 版本)上安装 Docker 时,我遇到了两个常见的网络问题:apt-get update
失败和无法拉取 Docker 镜像。通过逐步排查和配置,最终成功运行 docker run hello-world
。这篇笔记整理了我的解决过程,重点讲解了禁用 IPv6 和为 Docker 守护进程配置代理的原理与操作,帮助读者理解并复现。
问题背景
我按照 Docker 官方文档的 Install using the apt repository 方法,在 Ubuntu 上安装 Docker。使用的命令如下:
bash
# 更新软件源并安装依赖
sudo apt-get update
sudo apt-get install ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
# 添加 Docker GPG 密钥
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
# 添加 Docker 软件源
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
# 安装 Docker
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
安装过程顺利,但运行测试命令时遇到问题:
bash
sudo docker run hello-world
报错如下:
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
此外,最初运行 sudo apt-get update
时也遇到错误,提示无法连接 Docker 仓库。
问题 1:apt-get update
失败
现象
运行 sudo apt-get update
时,出现以下错误:
错误:1 https://download.docker.com/linux/ubuntu noble InRelease
Could not handshake: Error in the pull function. [IP: 2600:9000:2804:5800:3:db06:4200:93a1 443]
W: 无法下载 https://download.docker.com/linux/ubuntu/dists/noble/InRelease
原因分析
错误信息中的 IP 地址(2600:9000:...
)是一个 IPv6 地址,表明系统尝试通过 IPv6 连接 Docker 仓库,但失败了。可能的原因包括:
- 网络对 IPv6 支持不稳定:某些网络环境(如国内部分运营商)对 IPv6 的支持不完善,导致连接超时或失败。
- Docker 仓库的 IPv6 配置问题:服务器可能优先返回 IPv6 地址,但实际连接不可靠。
解决方案:禁用 IPv6
为了强制系统使用 IPv4,我临时禁用了 IPv6:
bash
sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
然后再次运行:
bash
sudo apt-get update
这次更新成功,没有报错。
为什么禁用 IPv6?
- IPv6 vs IPv4:IPv6 是下一代互联网协议,但许多网络和服务器对 IPv6 的支持仍不完善。如果系统优先尝试 IPv6 连接,而目标服务器的 IPv6 不可靠,就会导致超时或连接失败。
- 命令解析 :
sysctl -w net.ipv6.conf.all.disable_ipv6=1
:禁用所有网络接口的 IPv6。sysctl -w net.ipv6.conf.default.disable_ipv6=1
:为新创建的网络接口禁用 IPv6。- 这些命令通过修改内核参数(
/proc/sys/net/ipv6/conf/...
)临时禁用 IPv6,重启后会失效。
- 适用场景:如果你的网络环境不支持 IPv6,或者目标服务器的 IPv6 连接不稳定,禁用 IPv6 是快速有效的解决方法。
注意事项
-
临时性 :上述命令仅在当前会话有效。如果需要永久禁用 IPv6,需编辑
/etc/sysctl.conf
:bashsudo nano /etc/sysctl.conf
添加:
net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1
保存后应用:
bashsudo sysctl -p
-
风险:禁用 IPv6 可能影响依赖 IPv6 的服务,建议在确认网络环境后使用。
问题 2:无法拉取 hello-world
镜像
现象
安装 Docker 后,运行 sudo docker run hello-world
报错:
Unable to find image 'hello-world:latest' locally
docker: Error response from daemon: Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connection (Client.Timeout exceeded while awaiting headers)
原因分析
此错误表明 Docker 无法从 Docker Hub(registry-1.docker.io
)拉取镜像,原因是连接超时。进一步排查发现:
-
我使用了代理(
http://127.0.0.1:7897
),通过以下命令验证:bashcurl -v https://registry-1.docker.io/v2/
输出显示
curl
通过代理成功连接(返回401 Unauthorized
,正常现象,因为需要认证)。 -
但是,Docker 守护进程(
dockerd
)并未使用代理,导致无法访问 Docker Hub。
为什么需要为 Docker 守护进程配置代理?
- Docker 架构 :Docker 采用客户端-服务器架构。运行
docker run
时,客户端(docker
命令)与守护进程(dockerd
)通信,守护进程负责拉取镜像和运行容器。 - 环境变量隔离 :用户的代理环境变量(
HTTP_PROXY
、HTTPS_PROXY
)只对客户端命令生效,守护进程默认不继承这些设置。 - 网络限制:在国内,Docker Hub 的访问可能因网络限制而失败,代理或镜像加速器是常见解决方案。
解决方案:为 Docker 守护进程配置代理
我通过以下步骤为 Docker 守护进程配置了代理:
-
创建代理配置文件:
bashsudo mkdir -p /etc/systemd/system/docker.service.d sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
添加以下内容:
[Service] Environment="HTTP_PROXY=http://127.0.0.1:7897" Environment="HTTPS_PROXY=http://127.0.0.1:7897" Environment="NO_PROXY=localhost,127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12,172.29.0.0/16,::1"
- 解析 :
HTTP_PROXY
和HTTPS_PROXY
:指定代理地址(127.0.0.1:7897
是本地代理服务地址)。NO_PROXY
:定义不需要代理的地址范围(如本地地址和常见内网 IP 段),避免本地通信走代理。- 文件路径
/etc/systemd/system/docker.service.d/
:Systemd 允许为服务添加自定义配置,http-proxy.conf
会覆盖 Docker 的默认服务设置。
- 解析 :
-
重新加载并重启 Docker:
bashsudo systemctl daemon-reload sudo systemctl restart docker
daemon-reload
:通知 Systemd 重新加载配置文件。restart docker
:重启 Docker 服务以应用新配置。
-
验证服务状态:
bashsudo systemctl status docker
确认服务状态为
active (running)
。 -
测试拉取镜像:
bashsudo docker run hello-world
这次命令成功运行,输出:
Hello from Docker! This message shows that your installation appears to be working correctly. ...
理解代理配置
-
为什么单独配置守护进程?
Docker 守护进程是一个独立运行的后台服务(通过 Systemd 管理),它不读取用户的 shell 环境变量(如
export HTTPS_PROXY
)。因此,必须通过 Systemd 配置文件显式设置代理。 -
NO_PROXY 的作用
NO_PROXY
防止本地通信(如守护进程与客户端之间的通信)被代理拦截。例如,localhost
和127.0.0.1
是 Docker 客户端与守护进程通信的常用地址,192.168.0.0/16
等内网地址常用于容器网络。 -
适用场景
如果你的网络环境需要代理访问外部资源(如国内访问 Docker Hub),或者企业网络有代理要求,必须为守护进程配置代理。
最终结果
通过禁用 IPv6 和为 Docker 守护进程配置代理,我成功解决了网络问题,docker run hello-world
正常运行。输出表明 Docker 客户端、守护进程和 Docker Hub 的通信都正常。
经验总结与建议
-
禁用 IPv6 的场景:
- 当遇到类似
Could not handshake
的错误,且 IP 地址是 IPv6 时,禁用 IPv6 是快速排查方法。 - 注意检查网络环境,确认是否需要长期禁用 IPv6。
- 当遇到类似
-
代理配置的通用性:
- 如果使用代理,确保客户端和守护进程都配置正确。
NO_PROXY
需根据网络环境调整,包含所有本地和内网地址。
-
国内用户优化:
-
考虑配置镜像加速器(如阿里云、腾讯云)以提高拉取速度:
bashsudo nano /etc/docker/daemon.json
添加:
json{ "registry-mirrors": ["https://mirror.ccs.tencentyun.com"] }
重启 Docker:
bashsudo systemctl restart docker
-
-
权限优化:
-
为避免每次使用
sudo
,将用户添加到docker
组:bashsudo usermod -aG docker $USER
注销后重新登录即可。
-
-
进一步学习:
- 尝试运行复杂容器:
docker run -it ubuntu bash
。 - 探索 Docker Hub 和官方文档:https://docs.docker.com/get-started/。
- 尝试运行复杂容器:
结语
这次安装 Docker 的过程让我深入理解了网络配置对 Docker 的影响。禁用 IPv6 解决了软件源更新的问题,而为守护进程配置代理确保了镜像拉取的成功。希望这篇笔记能帮助你在遇到类似问题时快速定位和解决,同时对 Docker 的网络机制有更深的认识!
这篇博客简洁明了,涵盖了问题的现象、原因、解决方案以及背后的原理,适合初学者和有一定经验的用户参考。如果你有其他需求(例如添加图片、代码高亮,或调整语气),请告诉我!