【docker】cifs卷改smb密码后容器起不来的解决方法

文章目录

背景

NAS 那边把 SMB 共享账号的密码改了,然后服务器上一个挂载了这个共享目录的容器就怎么都起不来了。一开始我以为改个卷参数就行,结果发现 Docker 根本不让改卷的参数;要删卷重建,它又提示卷正在被容器使用,一个个删容器太麻烦。折腾了一圈,最后找到了不删容器、不删卷 的改法,顺手记录一下。特别要提醒的是,改成功以后 Portainer 里显示的还是旧密码,别被它骗了。

我的环境如下(IP、账号、卷名均已脱敏):

配置项 详情
宿主机 Ubuntu 服务器,192.168.1.101
Docker 24.0.2(Standalone)
管理工具 Portainer
NAS //192.168.1.10/myshare,SMB 协议 vers=2.0
Docker 卷 myshare,local 驱动,type=cifs

问题现象

当初这个卷是用下面这样的命令创建的,SMB 的账号密码直接写在卷参数里:

bash 复制代码
docker volume create --driver local \
  --opt type=cifs \
  --opt device=//192.168.1.10/myshare \
  --opt o=username=smbuser,password=OLD_PASSWORD,vers=2.0 \
  myshare

NAS 改密码后,在 Portainer 里启动容器,直接报错:

复制代码
error while mounting volume '/var/lib/docker/volumes/myshare/_data': failed to mount local volume:
mount //192.168.1.10/myshare:/var/lib/docker/volumes/myshare/_data,
data: username=smbuser,password=********,vers=2.0: permission denied

原因很好理解:Docker 每次启动容器都要先用卷参数里的账号密码去 mount 这个 SMB 共享,而参数里还是旧密码,NAS 当然不认。

为什么不能直接改

  • Docker 没有修改 volume 参数的命令,Portainer 里卷详情页的 Volume options 也只能看不能改;
  • 标准做法是 docker volume rm 后重建,但只要还有容器引用这个卷(哪怕容器是停止状态 ),就会报 volume is in use,得先把相关容器全删掉。

好在 local 驱动会把每个卷的挂载参数存成宿主机上的一个文件,改它就行。

解决步骤

1. 找到并备份卷的参数文件

local 驱动的卷参数保存在 /var/lib/docker/volumes/<卷名>/opts.json,先看一眼、再备份:

bash 复制代码
sudo cat /var/lib/docker/volumes/myshare/opts.json
sudo cp /var/lib/docker/volumes/myshare/opts.json /root/myshare-opts.json.bak

正常会看到一行 JSON:

json 复制代码
{"MountType":"cifs","MountOpts":"username=smbuser,password=OLD_PASSWORD,vers=2.0","MountDevice":"//192.168.1.10/myshare"}

2. 先手动验证新密码能不能挂上(建议)

改配置之前先确认新密码本身是对的,免得白忙活:

bash 复制代码
sudo mkdir -p /mnt/test
sudo mount -t cifs //192.168.1.10/myshare /mnt/test -o 'username=smbuser,password=NEW_PASSWORD,vers=2.0'
ls /mnt/test          # 能看到共享里的文件就说明密码没问题
sudo umount /mnt/test

如果手动挂载也报错,执行 sudo dmesg | tail -20 看内核日志:出现 STATUS_LOGON_FAILURE 说明账号/密码不对;如果是协议相关报错,可以试试把 vers=2.0 换成 vers=3.0 或加上 sec=ntlmssp。

3. 修改 opts.json 中的密码

用 sed 一行替换:

bash 复制代码
sudo sed -i 's/password=OLD_PASSWORD/password=NEW_PASSWORD/' /var/lib/docker/volumes/myshare/opts.json
sudo cat /var/lib/docker/volumes/myshare/opts.json

确认输出里 MountOpts 已经是新密码,并且 JSON 是完整的一行。

⚠️ 我在这里踩过坑 :一开始用的是 sudo vim 编辑,保存时提示:

复制代码
/var/lib/docker/volumes/myshare/opts.json [+][RO]
E45: 'readonly' option is set (add ! to override)

明明加了 sudo 还是只读。这是 vim 把文件以只读方式打开了(文件本身没有写权限,或者之前残留了 .opts.json.swp 交换文件、打开时选了 Read-Only)。root 下用 :wq! 强制保存即可;实在不放心就 :q! 退出,改用上面的 sed。

另外,如果新密码里有 /、& 这类字符,sed 需要转义,不如用 vim 手动改;密码里不能有逗号,因为逗号是 mount 参数的分隔符。

4. 重启 Docker(关键!)

这一步最容易漏。 我改完 opts.json 直接去启动容器,还是一样的 permission denied。原因是 dockerd 只在启动时把 opts.json 读进内存,运行中改文件它是不知道的。

bash 复制代码
sudo systemctl restart docker

⚠️ 重启 Docker 会让这台机器上的所有容器跟着停止/重启,建议挑空闲时间操作,并提前通知同事。

5. 启动原来的容器

bash 复制代码
docker start <容器名>
docker ps | grep <容器名>

看到容器状态是 Up 就成功了,原来的容器、原来的卷都不用动。

重点:Portainer 里的显示不会更新

问题解决后,我回到 Portainer 打开卷详情页一看,Volume options 里显示的居然还是旧密码:

复制代码
device  //192.168.1.10/myshare
o       username=smbuser,password=OLD_PASSWORD,vers=2.0
type    cifs

而宿主机上 cat opts.json 明明已经是新密码了,容器也确实是用新密码挂上的。

原因是这两处读的根本不是同一个地方:

位置 数据来源 作用
opts.json /var/lib/docker/volumes/myshare/opts.json local 驱动实际 mount 时使用的参数
Portainer / docker volume inspect 中的 Options Docker 卷元数据库 /var/lib/docker/volumes/metadata.db 创建卷时记录下来的参数,只用于展示

我们只改了 opts.json,没动 metadata.db,所以:

  • 显示是旧的,但实际挂载用的是新的,这只是展示不一致,不影响使用;
  • 判断是否改成功,以容器能否正常启动、宿主机 mount | grep myshare 的结果为准,不要看 Portainer;
  • metadata.db 是二进制的 BoltDB 文件,不建议手动修改。如果一定要让界面显示也一致,只能删容器 → 删卷 → 按原名重建卷 → 重建容器。

另外还有个小细节:Portainer 卷详情里的 Created 时间会变成你编辑文件的时间。这是因为 local 卷的"创建时间"取的是卷目录的修改时间,编辑 opts.json 刷新了它,并不代表卷被重建了,不用慌。

其他可选方案

  • 方案 A:删卷重建(标准做法),能删容器的话最干净,Portainer 显示也会同步:

    bash 复制代码
    docker rm <所有引用该卷的容器>
    docker volume rm myshare
    docker volume create --driver local \
      --opt type=cifs \
      --opt device=//192.168.1.10/myshare \
      --opt o=username=smbuser,password=NEW_PASSWORD,vers=2.0 \
      myshare
    # 然后按原参数重建容器;用 compose/stack 部署的重新 deploy 即可
  • 方案 B:让 NAS 管理员把密码改回去,如果允许的话,这是最省事的。

防患于未然:别把密码写在卷参数里

这次的坑本质是密码明文硬编码在 Docker 卷参数里,带来两个问题:一是 Portainer 里有权限的人都能看到明文密码;二是改密码时卷参数改不了。

更好的做法是在宿主机上放一个凭据文件:

bash 复制代码
sudo tee /root/.smbcred > /dev/null <<'EOF'
username=smbuser
password=YOUR_PASSWORD
EOF
sudo chmod 600 /root/.smbcred

创建卷时引用它:

bash 复制代码
docker volume create --driver local \
  --opt type=cifs \
  --opt device=//192.168.1.10/myshare \
  --opt o=credentials=/root/.smbcred,vers=2.0 \
  myshare

以后 NAS 再改密码,只需要改 /root/.smbcred 这个文件,Docker 那边什么都不用动,Portainer 里也不会再暴露密码。

总结

Docker CIFS 卷的 SMB 密码变了,不想删容器的话,改 /var/lib/docker/volumes/<卷名>/opts.json 里的密码,然后一定要 systemctl restart docker ,原来的容器就能直接起来。改完后 Portainer 里显示的仍是旧密码,那是元数据库里的展示数据,不影响实际挂载,以容器能否启动为准。长期来看,建议把 SMB 凭据挪到 credentials= 文件里,下次改密码就不用再折腾 Docker 了。

相关推荐
时早玖沐1 小时前
放弃 QQ 官方机器人:用 SnowLuma + AstrBot 自建 QQ 机器人
docker·开源·agent
云飞云共享云桌面1 小时前
1台SolidWorks服务器如何承载10个设计并发?
运维·服务器·网络·3d·制造
未来之窗软件服务2 小时前
WAF负载均衡+蜜罐混淆安全体系-东方仙盟
运维·安全·负载均衡·仙盟创梦ide·东方仙盟·阿雪技术观·网关开发
Linux-lucky2 小时前
46-Linux学习之旅之LVS负载均衡与keepalived高可用
linux·运维·学习·ubuntu·负载均衡·lvs
Jenni see2 小时前
服装厂裁剪自动化怎么搞?自动铺布+激光裁床+六轴机械手裁片分拣,省料省人的实战指南
运维·自动化
阿标在干嘛3 小时前
容器编排的“场景适配”:我们为什么在部分服务中回归了Docker Compose?
运维·docker·容器
墨理学AI4 小时前
虚拟机导致的服务器交换内容占用过大(服务器卡顿)问题 快速解决
运维·服务器
熊猫钓鱼>_>5 小时前
平行视界兼容模式与魔法变量配置避坑【鸿蒙心迹】
运维·服务器·开发语言·笔记·华为·harmonyos·openharmony