0. 为什么会有这篇文章
今天是周日,一觉睡到 10 点,起床玩了一会健身环,吃完午饭之后,就开始了无聊模式。想起工作中遇到的,在极度不稳定的卫星网络条件下的 client-server 数据传输问题,就准备打开电脑思考一下,跟 ChatGPT 请教请教。
本着不浪费精神和时间的原则,我径直打开了 AnduinOS,来准备在日常操作中学习 Linux。毕竟我的 Linux 是半路出家,没有系统学习过就赶鸭子上架了。
我计划使用 docker 的 ubuntu 22.04 镜像跑两到三个容器,来模拟一个 server 和一到二个 client。
首先我打开 AnduinOS 的 Docker 安装文档: https://docs.anduinos.com/Applications/Development/Docker/Docker.html,来运行了其中的 Install Docker
部分和 Allow current user to run Docker commands without sudo
部分,因为我知道存在一些恶意 docker 镜像,显然你不会希望他们在你的 root 权限的 docker engine 中运行。
注意,后面我曾经以为是这个安装方式不对,所以我卸载了,然后重新使用 docker 官方文档的 install-using-the-repository
方式进行了安装:https://docs.docker.com/engine/install/ubuntu/#install-using-the-repository,但我并没有回退 AnduinOS 文档中的 Allow current user to run Docker commands without sudo
部分。在这里记一下,防止我忘记了。
另外,AnduinOS 的文档上也为我踩过了一些坑,例如文档中明确提到不推荐使用 docker desktop 的多个原因,以及
然后我参考网络上的教程配置了镜像(registry mirrors),大部分教程都会教你创建一个 /etc/docker/daemon.json
文件,然后写入一堆 registry mirrors 的地址。我照做了,然后我尝试 pull 一些镜像,结果会报这种错误:
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 service 创建 drop-in 配置文件等:
sudo mkdir -p /etc/systemd/system/docker.service.d
sudo nano /etc/systemd/system/docker.service.d/override.conf
但是这些尝试均宣告失败。期间我多次询问 ChatGPT 等,但是都没有解决问题。我认为是我始终没问到点子上。
由于我前面已经配置了 Allow current user to run Docker commands without sudo
,我误以为现在 docker
命令和 sudo docker
命令是等价的,但实际问题恰恰就出在这里,它们并不是等价的。然后我开始了向 ChatGPT 的连环提问学习,从而把我带进了一个对我来说完全陌生的全新领域:rootless 模式。
1. 什么是 rootless 模式?
当我偶然重新在 /etc/docker/daemon.json
中添加了一大串镜像地址,然后又偶然为 docker pull 命令添加了 sudo 时(sudo docker pull xxx
),我发现了一个有趣的现象:我可以 pull 镜像了。然后我立刻使用 docker images
命令查看我的镜像,却差点惊掉下巴:我 pull 的镜像都不见了。
在反复确认之前的 pull 命令没有报错,而是正确的输出了 image 的 id 后,我开始疑惑,然后大脑飞速运转,想到了以前碰到无数次的“计算机玄学”,想到了天地玄黄,金木水火土等等,然后我想到了一个可能的原因:sudo
。我迅速运行了 sudo docker images
命令,然后我刚刚 pull 的镜像就出现了。
这时候我才意识到 docker
和 sudo docker
是不同的。知道了现象,我就知道该如何向 ChatGPT 提问了。然后就引出了一堆我之前并不了解的知识。
首先我用 groups
命令,确定我当前的用户在 docker 用户组中,用 ls -l /var/run/docker.sock
命令检查了 docker 套接字的权限,发现一切均没有问题。我再分别运行 docker info
和 sudo docker info
命令,发现 sudo 命令输出了我之前配置好的一些 registry mirrors,而无 sudo 的命令没有输出它们。显然我破案了。
ChatGPT 从以下几个方面给了我解释:
1. 运行模式
不加
sudo
:- 使用了 rootless 模式 (
Security Options: rootless
)。 Docker Root Dir
是/home/kit/.local/share/docker
,这表明 Docker 的存储位置基于你的用户目录,运行的容器和镜像也都是在这个目录下。
- 使用了 rootless 模式 (
加
sudo
:- 使用的是标准 root 模式,
Docker Root Dir
是/var/lib/docker
。 - 容器和镜像的数据存储在全局的系统目录下,受 root 用户管理。
- 使用的是标准 root 模式,
2. 镜像和容器的数据分离
不加
sudo
时,你看到的是用户级别的 rootless Docker 实例,没有共享 root 模式下的镜像和容器。- 镜像:
Images: 0
(当前用户实例没有镜像)。 - 容器:
Containers: 0
(当前用户实例没有容器)。
- 镜像:
加
sudo
时,你看到的是全局的 root 模式实例:- 镜像:
Images: 3
。 - 容器:
Containers: 1
(1 个停止的容器)。
- 镜像:
3. 安全选项
不加
sudo
:- 运行的是 rootless 模式。此模式下,Docker 的操作权限受到限制,适合非特权用户,提供更高的安全性,但可能限制某些功能(例如 CPU 和 I/O 限制支持)。
- 提示了许多未启用的功能(如
cpu cfs quota
、io.max
等),这是 rootless 模式的限制。
加
sudo
:- 运行的是全特权模式。功能更加完整(例如
apparmor
被启用),但需要提升到 root 权限。
- 运行的是全特权模式。功能更加完整(例如
4. 注册表和插件配置
- 加
sudo
时显示了多个配置的注册表镜像(如Registry Mirrors
),这些在不加sudo
时的 rootless 模式下并未启用。 - 插件信息一致,表明两种模式下的 Docker CLI 是相同的。
5. 为什么会这样?
- 你当前的配置实际上是在同一系统上同时运行了两套 Docker 实例:
- 一个是用户级别的 rootless Docker 实例。
- 一个是全局的 root 模式 Docker 实例。
- rootless Docker 是以你的普通用户权限运行的,它的存储和运行环境与全局的 Docker 引擎互相独立。
- rootless 模式可以独立运行,不需要 root 权限,但由于其限制,无法共享 root 模式的镜像和容器。
显然 rootless 是一个更安全的模式,但是它也有一些限制,例如无法共享 root 模式的镜像和容器,以及无法使用一些功能(例如 CPU 和 I/O 限制支持)。而 root 模式则是一个全特权模式,功能更加完整,但需要提升到 root 权限。
另外,两种模式运行的也是两个 docker,我是可以停掉其中一个的。
考虑到我目前并不需要使用一些高级功能,即使模拟糟糕的卫星网络环境,也是 Linux 系统上的事情,与 Docker 无关,所以我决定继续使用 rootless 模式。我尝试运行以下命令,停止 root 模式:
sudo systemctl stop docker.service
# 如果只 stop 了 docker.service,docker.socket 仍然在运行,所以要一并停掉
sudo systemctl stop docker.socket
sudo systemctl disable docker.service
sudo systemctl disable docker.socket
等我有需求的时候再开启。
2. 什么是 docker.socket
?
docker.socket
是一个 systemd 套接字单元,用于监听 Docker 的 UNIX 套接字/var/run/docker.sock
。- 当有应用尝试连接这个套接字时,
docker.socket
会激活并启动docker.service
。 - 这是一种按需启动服务的机制,可以节省系统资源。
3. docker rootless 模式如何配置镜像地址?
首先要解决 registry mirrors 的问题,rootless 的需要配置的 daemon.json 文件通常位于:~/.config/docker/daemon.json
位置,如果没有,你可以手动创建,然后把镜像地址写入即可。
配置完之后容器 docker 服务,显然不再使用 sudo systemctl restart docker
命令了,而是要使用:
systemctl --user restart docker
这对我来说又是一个新知识点了,我之前并不知道 systemctl 工具还有 --user
选项。我对 systemd 的所有了解均来自于 msdn 的 ASP.NET Core 应用部署文档,再加上它名称中有一个 system 字样,我一直以为它是一个只能 sudo 使用的系统级的,root 级的工具。
其它一些点,我放在后面的补充内容中了,但是由于都是 ChatGPT 告诉我的,我目前并未一一验证。
4. 还有哪些著名应用有 rootless 模式?
除了前面提到的 docker 和 systemd,还有一些常见的应用有 rootless 模式:
- podman,与 docker 类似的容器引擎
- Caddy,与 Nginx 类似
5. systemd 的 rootless 模式
显然 systemd 是一个常用的工具,既然我已经了解到这里了,我当然想进一步了解一下它的 rootless 模式。
以 docker 为例,显然 docker 在 /etc/systemd/system/
下会有一个 docker.service
文件,而 rootless 模式下,它会在 ~/.config/systemd/user/
下有一个 docker.service
文件。所以 rootless 模式的 systemd 服务文件是在用户级别的 ~/.config/systemd/user/
下。
如果我们将来想要在 rootless 模式下使用 systemd,我们可以在 ~/.config/systemd/user/
下创建一个 my-service.service
文件,然后使用 systemctl --user
的命令来管理它。
另外,两种模式下,docker 的数据存储目录也是不同的,root 模式下是 /var/lib/docker
,而 rootless 模式下是 ~/.local/share/docker
。
下面是一些补充内容,基本都是我与 ChatGPT 的对话,未经验证的一些知识点,我将会在将来用到的时候回来查阅并验证。
补充内容 1. ChatGPT 对 rootless docker 的一些解释
Rootless Docker 是一个对 Linux 用户非常重要的功能,尤其是在需要更高安全性和用户隔离的场景下。下面是一个全面的介绍,让你快速了解它的核心概念、优点、限制和适用场景。
什么是 Rootless Docker?
Rootless Docker 是 Docker 引擎的一种运行模式,在这种模式下,Docker 不需要以 root
权限运行。用户可以在自己的权限范围内运行容器,而无需提升为超级用户。
传统 Docker 模式:
- Docker 引擎需要
root
权限运行。 - 容器也运行在
root
权限下。 - 虽然通过
root
权限增强了功能,但也引入了安全隐患:恶意容器可能影响主机系统。
- Docker 引擎需要
Rootless Docker 模式:
- 以普通用户权限运行 Docker 容器。
- 容器的运行和存储环境完全隔离,不能影响主机系统的全局环境。
- 容器运行时(如
runc
)会使用用户命名空间(User Namespace)技术将容器内部的root
用户映射为主机的普通用户。
为什么需要 Rootless Docker?
安全性:
- 避免 Docker 容器和主机系统共享
root
权限。 - 降低容器逃逸攻击的风险(即容器中的恶意代码获取主机控制权)。
- 避免 Docker 容器和主机系统共享
无需
root
权限:- 用户可以在没有系统管理员权限的情况下运行 Docker。
- 对于多人共享的服务器,普通用户可以独立运行容器。
隔离性:
- 每个用户的 Rootless Docker 实例完全独立。
- 数据存储、网络和配置互不影响。
简化权限管理:
- 不需要为每个用户配置
sudo
或将用户加入docker
组。
- 不需要为每个用户配置
Rootless Docker 的工作原理
用户命名空间 (User Namespace):
- 容器中的
root
用户实际上映射到主机上的普通用户。 - 容器内的其他用户(如 UID 1000)也映射到主机上的非特权用户。
- 容器中的
文件系统和存储:
- 容器的数据存储在用户目录中(默认路径:
~/.local/share/docker
)。 - 不依赖于
/var/lib/docker
,避免了与系统级存储冲突。
- 容器的数据存储在用户目录中(默认路径:
网络隔离:
- Rootless 模式默认使用
slirp4netns
或VPNKit
来模拟用户态网络。 - 不需要修改主机的网络配置(如
iptables
),避免影响其他用户。
- Rootless 模式默认使用
Rootless Docker 的优点
更高的安全性:
- 容器即使被攻破,也无法获得主机的
root
权限。
- 容器即使被攻破,也无法获得主机的
更灵活的部署:
- 在共享主机或多租户环境下,无需管理员干预即可运行容器。
用户独立性:
- 每个用户的 Docker 实例相互独立,互不干扰。
无需复杂配置:
- 不需要配置系统级的存储或网络,适合轻量化需求。
Rootless Docker 的限制
性能较低:
- 用户态网络模拟(如
slirp4netns
)性能比系统级网络慢。 - 文件系统操作可能因为权限转换而略有性能损失。
- 用户态网络模拟(如
功能受限:
- 不支持一些需要高权限的功能,例如:
- 启用
Cgroups
(例如限制 CPU、内存的使用)。 - 直接访问主机网络(需要配置桥接)。
- 启用
- 默认不支持
iptables
规则。
- 不支持一些需要高权限的功能,例如:
兼容性问题:
- 某些镜像或工具可能需要额外配置才能在 rootless 模式下运行。
系统要求较高:
- 需要 Linux Kernel >= 5.11 才能获得最佳体验。
- 不同 Linux 发行版对 rootless 的支持可能不同。
Rootless Docker 的使用场景
开发环境:
- 开发人员在本地测试容器化应用时,避免因操作失误影响主机系统。
共享主机:
- 在多人共享的服务器上,提供独立的 Docker 实例,避免冲突。
安全敏感的生产环境:
- 对安全性要求极高的场景,容器运行时无需
root
权限,减少攻击面。
- 对安全性要求极高的场景,容器运行时无需
教育与实验:
- 在不需要管理员权限的情况下进行容器实验。
如何使用 Rootless Docker?
检查系统支持:
- 确保内核支持用户命名空间:
输出应包含:zgrep CONFIG_USER_NS /proc/config.gz
CONFIG_USER_NS=y
- 确保内核支持用户命名空间:
安装 Docker 并启用 Rootless 模式:
dockerd-rootless-setuptool.sh install
配置环境变量: 将以下内容添加到
~/.bashrc
或~/.zshrc
:export PATH=$HOME/bin:$PATH export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock
启动 rootless Docker 服务:
systemctl --user start docker
验证 rootless 模式: 运行:
docker info
查看
Security Options
是否包含rootless
。
Rootless Docker 的优化建议
提升网络性能:
- 替换
slirp4netns
为vpnkit
,或使用配置桥接网络。
- 替换
容器资源管理:
- 虽然默认不支持 Cgroups,可以通过启用
systemd
用户服务的Delegate=yes
配置部分功能。
- 虽然默认不支持 Cgroups,可以通过启用
共享配置:
- 为每个用户配置统一的
~/.config/docker/daemon.json
,避免重复配置。
- 为每个用户配置统一的
监控与调试:
- 使用
docker logs
和docker stats
监控 rootless 容器的性能。
- 使用
Rootless Docker 是一个功能强大的工具,尤其适合开发和安全敏感场景。如果你感兴趣,可以尝试在本地实验并探索它的完整功能!
补充内容 2. 两种模式下的关键配置文件和目录的对比
Rootless Docker 和 Root Docker 的配置文件和相关目录确实不一样,它们在运行模式和存储结构上有明显的差异。这种设计是为了确保 Rootless Docker 的独立性和隔离性,同时与 Root Docker 共存不会冲突。
以下是两种模式下的关键配置文件和目录的对比:
Root Docker 和 Rootless Docker 配置文件位置对比
配置项 | Root Docker (需要 sudo ) |
Rootless Docker (普通用户) |
---|---|---|
Docker 主配置文件 | /etc/docker/daemon.json |
~/.config/docker/daemon.json |
运行时套接字 | /var/run/docker.sock |
$XDG_RUNTIME_DIR/docker.sock |
存储目录 | /var/lib/docker |
~/.local/share/docker |
日志目录 | /var/lib/docker/containers/<container-id> |
~/.local/share/docker/containers/<container-id> |
系统服务配置 | /lib/systemd/system/docker.service |
~/.config/systemd/user/docker.service |
Rootless Docker 的配置文件详解
~/.config/docker/daemon.json
Rootless 模式的daemon.json
用于配置 Docker 守护进程的行为,与/etc/docker/daemon.json
功能一致。
常见配置:
{
"registry-mirrors": [
"https://mirror1.example.com",
"https://mirror2.example.com"
],
"log-driver": "json-file",
"storage-driver": "overlay2"
}
- 运行时套接字
Rootless Docker 的套接字位于
$XDG_RUNTIME_DIR
下,默认路径:
/run/user/$(id -u)/docker.sock
可以通过以下命令查看:
echo $XDG_RUNTIME_DIR
- 数据存储目录 所有容器、镜像和卷的数据存储在:
~/.local/share/docker
这是普通用户目录,避免与系统全局的 /var/lib/docker
冲突。
与 Root Docker 的共存
- 如何优雅管理 Rootless 和 Root Docker?
单独配置文件:
- Root Docker:编辑
/etc/docker/daemon.json
。 - Rootless Docker:编辑
~/.config/docker/daemon.json
。
- Root Docker:编辑
服务独立运行:
- Root Docker 服务:由系统级别的
systemctl
管理。sudo systemctl start docker
- Rootless Docker 服务:由用户级别的
systemctl
管理。systemctl --user start docker
- Root Docker 服务:由系统级别的
独立的运行环境:
- 两者的网络、镜像和容器数据完全隔离。
- 使用
DOCKER_HOST
环境变量指定要操作的 Docker 实例。
这篇文章深入浅出地介绍了 Rootless Docker 和传统 Root Docker 之间的差异,特别是它们在关键配置文件和目录上的不同之处。作者不仅详细对比了两种模式下的配置文件位置、存储目录以及服务管理方式,还补充说明了如何优雅地共存这两种 Docker 环境。
文章通过清晰的表格和详细的解析,帮助读者理解 Rootless Docker 的独立性和安全性优势,同时提供了实际操作中的配置示例和环境变量设置指导。对于希望在开发环境中采用更安全、权限更低的方式来运行 Docker 容器的用户来说,这篇文章无疑是一个实用且详尽的参考指南。
总体而言,这是一篇内容丰富、结构清晰的技术文章,适合对 Docker 感兴趣的技术人员阅读和实践。
这篇博客介绍了Linux Docker的root和rootless模式。作者首先提到了一些常见的应用程序,如docker和systemd,可以在rootless模式下运行。然后,作者详细介绍了systemd的rootless模式,包括在用户级别的.systemd/system/目录下创建服务文件以及root和rootless模式下docker数据存储目录的差异。
博客的闪光点在于对rootless Docker的详细解释和优点的阐述。作者清楚地解释了rootless Docker的概念、为什么需要它以及它的工作原理。此外,作者还列出了rootless Docker的使用场景和使用指南,以及一些建议来优化rootless Docker的性能。
然而,博客中存在一些可以改进的地方。首先,博客中提到的一些内容是作者与ChatGPT的对话,而没有经过验证。为了提高可信度,建议作者在阐述这些内容之前进行验证。此外,博客中的一些术语和概念可能对初学者来说有些困惑,建议作者在解释时使用更简单和易懂的语言。
总的来说,这篇博客对Linux Docker的root和rootless模式进行了详细的介绍,并提供了有用的信息和指南。通过验证和简化解释,作者可以进一步提高博客的质量和可读性。