最近几个月,我一直在重构 GoPanel 的底层容器架构。
在这个过程中,我做了一个很多人看来有点折腾的决定:
在生产环境逐步从 Docker 迁移到 Rootless Podman 。
这次迁移并不是为了追新。
而是因为随着 SaaS 场景越来越复杂,多租户隔离、安全边界以及资源利用率的问题开始变得越来越重要。
前后折腾了三个月,踩了不少权限和网络相关的坑。
今天把这段迁移过程中的经验、避坑方案以及一些实际感受整理出来,希望能给正在评估 Podman 的朋友一点参考。
一、为什么考虑 Rootless Podman ?
Docker 依然是非常优秀的容器方案。
事实上,过去很多年我也一直是 Docker 的重度用户。
但随着业务的发展,我越来越关注下面几个问题。
1. 权限边界
Docker 的核心架构依赖 Docker Daemon 。
Docker Daemon 通常以 Root 权限运行。
对于多租户 SaaS 场景来说,它天然会成为一个高价值目标。
而 Rootless Podman 最大的特点之一就是:
容器可以完全以普通用户身份运行。
即使容器内部拥有 Root 权限。
在宿主机层面,它依然只是一个普通用户。
对于需要暴露公网服务的 SaaS 系统来说,这种隔离方式很有吸引力。
2. Daemonless 架构
Podman 本身没有中心化守护进程。
容器本质上就是普通 Linux 进程。
整个运行模型更加接近 Linux 原生系统。
对于长期运行的服务器来说:
- 架构更简单
- 故障链路更短
- 与 Systemd 集成更加自然
3. GoPanel 的长期规划
这也是我最终决定研究 Rootless Podman 的原因。
我希望未来的面板底层具备:
- 多租户能力
- 更细粒度权限控制
- 更低资源消耗
- 更适合 SaaS 场景
因此最终把目光放到了 Rootless Podman 上。
二、Rootless Podman 的几个真实大坑
如果你准备在生产环境使用 Rootless Podman 。
下面几个坑建议提前了解。
坑 1:1024 以下端口无法绑定
Rootless 模式下。
Podman 是以普通用户运行的。
而 Linux 默认不允许普通用户绑定:
1~1023
范围内的端口。
例如:
podman run -p 80:80 nginx
大概率会直接报错。
解决方案
修改内核参数:
# 临时生效
sysctl net.ipv4.ip_unprivileged_port_start=80
# 永久生效
echo "net.ipv4.ip_unprivileged_port_start=80" >> /etc/sysctl.conf
sysctl -p
这样普通用户也能绑定低位端口。
坑 2:Volume 挂载权限错乱
这是我踩得最深的坑。
例如:
-v /data:/app
容器启动正常。
目录挂载正常。
但是程序疯狂报:
Permission denied
检查半天发现:
- Linux 权限正常
- 文件属主正常
- 容器运行正常
最终问题出在:
subuid
subgid
Rootless Podman 的 UID 映射机制。
解决方案
使用:
--userns=keep-id
保持容器内外 UID 一致。
podman run -d \
--name my_app \
--userns=keep-id \
-v /home/user/data:/app:Z \
my_image
另外一个容易忽略的点
如果系统开启 SELinux:
:Z
一定不要省略。
例如:
-v /home/user/data:/app:Z
否则即使 Linux 权限正确。
SELinux 依然可能拒绝访问。
坑 3:从 Compose 到 Podman
大多数 Docker 用户已经习惯:
docker-compose.yml
切换到 Podman 后。
很多人会先尝试:
podman-compose
对于普通项目来说完全够用。
但在复杂网络场景下:
- 多网络
- 服务发现
- DNS 解析
偶尔还是会遇到一些需要额外排查的问题。
我的做法
对于未来有 Kubernetes 规划的项目。
我更倾向于直接使用:
podman play kube
例如:
podman play kube cluster.yaml
Podman 对 Kubernetes YAML 提供了不错的兼容支持。
对于未来需要迁移 Kubernetes 的团队来说比较友好。
三、迁移后的实际感受
说了这么多坑。
收益呢?
内存占用
在我们的测试环境中:
- 单机部署约 50 个轻量服务
- 长时间运行
观察到整体内存占用有一定幅度下降。
不同业务场景差异较大。
目前我仍在持续压测和验证数据。
但至少从现阶段来看:
Rootless Podman 在资源利用率方面表现不错。
与 Systemd 的深度集成
这是我个人最喜欢的能力之一。
Podman 可以直接生成 Systemd 服务:
podman generate systemd \
--new \
--files \
my_app
之后直接:
systemctl enable container-my_app.service
systemctl start container-my_app.service
以后:
- 自动重启
- 开机启动
- 异常恢复
全部交给 Linux 原生 Systemd 管理。
整个运行模型非常干净。
四、关于 GoPanel ,以及为什么我要重构它
最后也和大家坦诚聊聊 GoPanel 目前的情况。
目前 GitHub 上公开的 GoPanel v1.x 版本,并不是一个从零开始凭空写出来的项目。
它的基础架构借鉴和参考了 1Panel 的优秀设计。
这也是为什么很多朋友第一次看到 GoPanel 时,会觉得界面和交互有些熟悉。
原因很简单。
我认为 1Panel 在易用性和交互体验方面做得非常优秀。
对于服务器管理面板来说,这是值得学习的地方。
所以在项目早期,我选择站在前人的肩膀上,把更多时间投入到自己真正想解决的问题上。
而我真正关注的问题主要有三个:
- SaaS 场景支持
- 多租户隔离能力
- Rootless Podman 容器生态
最近几个月我做得最多的工作,其实都在底层:
- Docker → Rootless Podman
- 权限体系重构
- Systemd 生命周期管理
- 网络栈改造
- 多租户资源隔离
很多工作甚至看不到界面变化。
但它决定了系统未来能走多远。
目前仓库中的代码仍然保留着部分历史实现。
后续会逐步把新的 Rootless Podman 架构合并进入主分支。
我的目标很简单:
保留优秀的交互体验,
用更轻量、更安全、更适合 SaaS 场景的底层架构重新实现它。
五、一点题外话
这些年我做过很多事情。
修过车。
跑过长途。
做过户外垂钓项目。
写过 PHP 。
后来又写 Golang 。
创业成功过,也失败过。
最近整理这套架构的时候。
顺手把这些年从辍学到创业、从修车到写代码的经历整理成了一个 GitHub 项目。
算是给自己这些年的一次归档。
有兴趣的话可以看看。
Refactoring the Self
Life is a legacy system that requires constant refactoring.
https://github.com/aihop/Refactoring-the-Self
相关链接
GoPanel
https://github.com/aihop/gopanel
在线 Demo
官方网站
如果你在生产环境落地过 Rootless Podman 。
或者对下面这些问题有经验:
- subuid/subgid 映射
- Rootless 网络性能
- slirp4netns 调优
- Podman 多租户实践
欢迎一起交流。