为什么一个跑命令行的站长还需要 Portainer
如果你和我一样,服务器上跑着十几二十个容器,那你一定经历过这种时刻:半夜被监控告警叫醒,只想看一眼某个容器的日志,结果要先 docker ps -a 找到容器名,再 docker logs --tail 200,再回想上次那条 docker run 到底挂了几个端口、几个 volume。命令用熟了当然也能干活,但问题在于——记忆不是配置的可靠载体。三个月前你写的那行 docker run,三个月后你还记得清吗?
Portainer 解决的正是这个问题。它不是什么黑科技,本质就是一个跑在宿主机上的 Web 界面,通过挂载 docker.sock 拿到 Docker 的全部控制权,把「容器、镜像、网络、卷、Stack、日志、控制台」这些平时靠命令行的操作可视化出来。对个人站长来说,它的价值有三个:
- 可视化查看状态:一台机器上几十个容器,谁在跑、谁重启过、吃多少内存,一眼扫完,不用记命令。
- 保存编排能力:通过 Stack(本质就是 docker-compose)把服务的完整定义存下来,比手工
docker run考古强太多。 - 应急操作方便:手机浏览器打开就能重启容器、看日志、进控制台,出门在外也能救火。
但我要先把话说明白:Portainer 只是管理层,不能替代你对 Docker 的理解。它把 docker.sock 的权限等同于 root,配置不当等于把服务器钥匙挂在公网。这篇的重点一半在装它,另一半在安全地收口它。
前置检查:你确实需要它吗
先说结论:如果你只跑 1~3 个容器,且服务器本身有 SSH 和监控,Portainer 属于「锦上添花」,可以不上。如果你的情况符合下面任意一条,那它值得装:
- 容器数量超过 5 个,且分散在多个 compose 项目里;
- 有多台 VPS 需要统一管理(Portainer 支持接入远程 Docker 环境);
- 团队/家人偶尔需要操作,但不是每个人都有 SSH 权限;
- 你经常需要在手机上处理简单的容器重启、看日志。
部署:用 Docker 跑 Portainer,别用二进制
Portainer 官方推荐的部署方式就是它自己跑在 Docker 里。先建一个专用卷持久化它的数据,否则容器重建后账号密码、接入配置全丢:
docker volume create portainer_data然后启动。注意这里我不用官方那条最简命令,而是加了几处加固(后面安全章节会解释为什么):
docker run -d \
--name portainer \
--restart=unless-stopped \
-p 127.0.0.1:9443:9443 \
-v /var/run/docker.sock:/var/run/docker.sock:ro \
-v portainer_data:/data \
portainer/portainer-ce:latest逐行说明,这里每一行都是踩过坑换来的:
--restart=unless-stopped:服务器重启后自动拉起。用always也行,但unless-stopped更符合直觉——你手动停了它就别自己爬起来。-p 127.0.0.1:9443:9443:关键。只绑定本地回环,公网直接访问不到 9443,只能通过 SSH 隧道或 Nginx 反代进来。-v /var/run/docker.sock:...:ro:把 docker.sock 挂进去是 Portainer 能工作的前提,加:ro是尽量的最小权限(但要知道 ro 并不等于完全安全,见下文)。-v portainer_data:/data:数据持久化。
9443 是 HTTPS 端口,Portainer 自带自签名证书。首次访问会提示证书不受信任,浏览器点「继续」即可。首次进去要在 5 分钟内设置管理员账号,否则会进入「等待会话超时」状态,只能重启容器重新计时。
第一次访问与初始化
因为绑到了 127.0.0.1,本地直接开 https://127.0.0.1:9443 是不行的(那是服务器的回环)。两种进入方式:
方式一:SSH 隧道(推荐,最简单)
ssh -L 9443:127.0.0.1:9443 root@你的服务器IP然后本地浏览器打开 https://127.0.0.1:9443,流量走 SSH 加密隧道,绝对安全。
方式二:Nginx 反代(适合长期使用,但要配合鉴权,见安全章节)
初始化时 Portainer 会问你「环境」,本地这台选 Docker Standalone,然后点 Connect 即可。它会自动发现本机的 Docker。
日常怎么用:三个我天天用的功能
1. Containers 页看全局
进去后 Containers 列表会显示每个容器的状态、镜像、端口映射、创建时间。比起 docker ps,它多了几个实用能力:
- 点容器名进去能看 Logs(实时日志,可暂停、可搜索)和 Stats(CPU/内存/网络实时曲线);
- Console 按钮可以直接进容器的 shell,不用
docker exec -it; - 每个容器都有 Recreate,改完环境变量后一键重建,不用手写 run 命令。
2. Stacks 保存服务定义
Stacks 是 Portainer 最有价值的部分。你可以把以前散落的 docker run 变成一个 compose 文件贴进去,Portainer 负责 up 和 down。比如一个 Typecho 站点:
services:
db:
image: mariadb:11
restart: unless-stopped
environment:
MARIADB_ROOT_PASSWORD: 换成你的强密码
MARIADB_DATABASE: typecho
volumes:
- db_data:/var/lib/mysql
web:
image: typecho/typecho:latest
restart: unless-stopped
depends_on:
- db
volumes:
- web_data:/var/www/html
volumes:
db_data:
web_data:存成 Stack 之后,服务的完整定义就「进版本库」了——以后迁移服务器,直接把这个 Stack 贴到新机器,不用再考古。这是我从命令行迁到 Portainer 后最大的收益。
3. Images 自动清理
Images 页能按「未使用」筛选,一键删掉那些被新版本顶替的旧镜像。注意别手贱删掉正在被容器引用的镜像(它会拒绝,但心里要有数)。
安全收口:这一步不做,前面全白搭
挂载 docker.sock 意味着谁能操作 Portainer,谁就拥有这台机器的 root。可以这样理解:控制台里点一下就能跑一个挂载了宿主机 / 的容器,然后为所欲为。所以安全收口是这套方案的生死线。
第一层:绝不让 9443 暴露公网
前面命令里已经绑了 127.0.0.1。如果你图省事绑成了 0.0.0.0:9443,建议立刻改回来:docker rm -f portainer 后用正确命令重建(数据在卷里,不丢)。
如果确实需要远程访问,用 Nginx 反代 + HTTPS + 基本认证,且只对固定 IP 开放:
server {
listen 443 ssl http2;
server_name portainer.你的域名;
# 只允许你自己的家用 IP,其余一律拒绝
allow 1.2.3.4;
deny all;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
location / {
proxy_pass https://127.0.0.1:9443;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}注意 proxy_pass 用的是 https://,因为 Portainer 自己就是 HTTPS。还有 Upgrade/Connection 两行不能少,否则 Console 进不去(WebSocket 需要)。
第二层:依赖它自己的强密码
Portainer 管理员密码至少 12 位,别用生成器偷懒到 8 位。它没有两步验证(社区版),所以密码强度就是最后一道门。
第三层:定期看 Access 日志
Nginx 那层的 access.log 会记录每次登录尝试。如果你在日志里看到陌生 IP 反复访问 /api/auth,说明有人在爆破,赶紧配合 fail2ban 封掉。
四个必踩的坑
坑一:docker.sock 的 ro 并不是安全保证
很多人以为挂载时加了 :ro 就只读、安全了。事实是:Docker 的 API 本身没有只读概念,即便 socket 是只读挂载,只要能调用 POST /containers/create,你依然能创建一个特权容器去改宿主机。所以 :ro 只是「防止 Portainer 主动写 socket 文件」,不是权限隔离。真正的隔离手段是不暴露它、加反代鉴权。
坑二:容器重建后数据全没,因为忘了挂卷
如果你用的是 -v portainer_data:/data 之外的临时存储,一旦 docker rm 重建,账号和接入环境全丢,又要重新初始化。命名卷一定要建。
坑三:Stack 里的相对路径找不到
Portainer 的 Stack 默认工作目录和你在命令行跑 compose 不一样。如果你在 compose 里写了 ./conf/nginx.conf 这类相对路径挂载,很可能挂到 /data/compose/数字/ 下面,找不到文件。稳妥做法:一律用绝对路径或命名卷。
坑四:端口冲突导致起不来
如果你本机已经有服务占了 8000/9000,而 Portainer 的 Stack 又要映射这些端口,就会启动失败。先 ss -tlnp 看占用,再改端口映射。
什么时候该放弃 Portainer
说了这么多好话,也要说反面。下面这种情况我不建议用 Portainer:
- 纯命令行爱好者:如果你已经有一套脚本化的
docker compose工作流,Portainer 反而多一层。 - 强调最小攻击面:多一个 Web 服务就多一个攻击面。挂 docker.sock 的组件越少越安全。
- 多机 + 复杂编排:如果规模到了多节点编排,Portainer 的 Swarm/K8s 管理能力还不如直接用原生工具。
小结
Portainer 对个人站长的意义,不是「炫技」,而是把散落在 shell 历史里的运维知识沉淀成可视、可复用、可迁移的资产。装上它,你半夜救火时不用再翻三个月前的命令;迁移服务器时,Stack 一贴就走。但请记住它手上握着 root 权限——绑 127.0.0.1、加 SSH 隧道或 IP 白名单、用强密码,这三件事不做,其他都是给自己挖坑。
如果你已经在用 Portainer,欢迎在评论区分享你的收口方案;如果是第一次装,建议先在测试机验证一遍 Stack 迁移流程,再上生产。