一、适用场景
本文档适用于以下场景:
应用运行在 Kubernetes 集群中,需要让 Pod 访问某个外部共享目录,例如 Windows SMB/CIFS 共享目录。
操作方式为:
- 先在指定 Kubernetes 节点上挂载外部共享目录;
- 再通过
hostPath将节点本地目录挂载到 Pod 内; - 使用
nodeSelector限制 Pod 只能调度到已完成挂载的节点上。
二、整体架构说明
外部共享目录:
//<共享服务器IP>/<共享目录路径>
挂载到 Kubernetes 节点本地目录:
/<本地挂载目录>
再通过 Kubernetes hostPath 挂载到 Pod 内:
/<Pod内访问目录>
整体关系如下:
Windows / SMB 共享目录
↓
Kubernetes 节点本地挂载目录
↓
Pod 内目录
示例:
//<共享服务器IP>/<共享目录路径>
↓
<Node节点>:/<本地挂载目录>
↓
Pod:/<Pod内访问目录>
三、前置准备
1. 确认共享目录信息
需要提前确认以下信息:
| 项目 | 示例 |
|---|---|
| 共享服务器地址 | <共享服务器IP> |
| 共享目录路径 | //<共享服务器IP>/<共享目录路径> |
| 共享账号 | <共享账号> |
| 共享密码 | <共享密码> |
| Kubernetes 节点 IP | <节点IP1>、<节点IP2> |
| 节点本地挂载目录 | /<本地挂载目录> |
| Pod 内挂载目录 | /<Pod内访问目录> |
2. 确认需要绑定的 Kubernetes 节点
查看节点列表:
kubectl get nodes -o wide
根据内网 IP 找到对应节点:
kubectl get nodes -o wide | grep <节点IP>
如果节点名就是 IP,后续可以直接使用该 IP 作为节点名。
四、在 Kubernetes 节点上挂载 SMB/CIFS 共享目录
以下操作需要在所有目标节点上执行。
例如目标节点为:
<节点IP1>
<节点IP2>
需要分别登录每台节点执行。
1. 安装 CIFS 工具
CentOS / TencentOS / RedHat 系统:
yum install -y cifs-utils
Ubuntu / Debian 系统:
apt-get update
apt-get install -y cifs-utils
2. 创建本地挂载目录
mkdir -p /<本地挂载目录>
示例:
mkdir -p /share-data
检查目录:
ls -ld /<本地挂载目录>
3. 创建 SMB 认证文件
建议不要直接把账号密码写到 /etc/fstab 中,而是放到独立认证文件。
cat > /root/.smbcred <<'EOF'
username=<共享账号>
password=<共享密码>
domain=<域名或工作组>
EOF
chmod 600 /root/.smbcred
如果没有域名,可以写成:
cat > /root/.smbcred <<'EOF'
username=<共享账号>
password=<共享密码>
EOF
chmod 600 /root/.smbcred
4. 手动测试挂载
mount -t cifs "//<共享服务器IP>/<共享目录路径>" /<本地挂载目录> \
-o credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm
示例格式:
mount -t cifs "//<共享服务器IP>/<共享目录路径>" /share-data \
-o credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm
5. 验证挂载结果
df -hT | grep <共享服务器IP>
mount | grep <共享服务器IP>
ls -l /<本地挂载目录>
正常情况下,df -hT 中应该能看到文件系统类型为:
cifs
如果可以看到共享目录里的文件,说明挂载成功。
6. SMB 版本兼容处理
如果 vers=3.0 挂载失败,可以尝试:
umount /<本地挂载目录>
然后改用:
vers=2.1
或:
vers=2.0
例如:
mount -t cifs "//<共享服务器IP>/<共享目录路径>" /<本地挂载目录> \
-o credentials=/root/.smbcred,iocharset=utf8,vers=2.1,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm
实际使用哪个版本,以测试成功为准。
五、配置开机自动挂载
确认手动挂载成功后,再配置 /etc/fstab。
1. 备份 fstab
cp /etc/fstab /etc/fstab.bak.$(date +%F_%H%M%S)
2. 追加自动挂载配置
cat >> /etc/fstab <<'EOF'
//<共享服务器IP>/<共享目录路径> /<本地挂载目录> cifs credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm,_netdev,nofail 0 0
EOF
示例格式:
cat >> /etc/fstab <<'EOF'
//<共享服务器IP>/<共享目录路径> /share-data cifs credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm,_netdev,nofail 0 0
EOF
说明:
| 参数 | 说明 |
|---|---|
_netdev | 表示该挂载依赖网络 |
nofail | 避免共享目录异常导致系统启动失败 |
iocharset=utf8 | 支持中文路径和中文文件名 |
file_mode=0777 | 文件权限 |
dir_mode=0777 | 目录权限 |
noperm | 客户端不做额外权限校验 |
3. 测试自动挂载配置
mount -a
检查:
df -hT | grep <共享服务器IP>
ls -l /<本地挂载目录>
六、给 Kubernetes 节点打标签
为了保证 Pod 只调度到已经完成共享目录挂载的节点,需要给这些节点打标签。
1. 确认当前集群
执行前先确认当前 kubectl 连接的是正确集群:
kubectl config current-context
2. 查看目标节点
kubectl get nodes -o wide | egrep '<节点IP1>|<节点IP2>'
3. 给节点打标签
kubectl label node <节点名称1> <标签key>=<标签value> --overwrite
kubectl label node <节点名称2> <标签key>=<标签value> --overwrite
示例:
kubectl label node <节点名称1> app-sharedata=true --overwrite
kubectl label node <节点名称2> app-sharedata=true --overwrite
如果节点名就是 IP,可以这样写:
kubectl label node <节点IP1> app-sharedata=true --overwrite
kubectl label node <节点IP2> app-sharedata=true --overwrite
4. 检查节点标签
kubectl get nodes -l <标签key>=<标签value> -o wide
示例:
kubectl get nodes -l app-sharedata=true -o wide
七、修改 Kubernetes Deployment YAML
1. 添加 nodeSelector
在 Deployment 的:
spec:
template:
spec:
下面添加:
nodeSelector:
<标签key>: "<标签value>"
示例:
nodeSelector:
app-sharedata: "true"
作用:
限制该 Pod 只能调度到带有该标签的节点上。
2. 添加 volumeMounts
在容器配置中添加:
volumeMounts:
- name: sharedata
mountPath: /<Pod内访问目录>
示例:
volumeMounts:
- name: sharedata
mountPath: /share-data
3. 添加 volumes
在 spec.template.spec 下,与 containers 同级添加:
volumes:
- name: sharedata
hostPath:
path: /<本地挂载目录>
type: Directory
示例:
volumes:
- name: sharedata
hostPath:
path: /share-data
type: Directory
八、Deployment YAML 通用模板
apiVersion: v1
kind: Service
metadata:
name: <应用名称>
namespace: <命名空间>
labels:
app: <应用名称>
spec:
selector:
app: <应用名称>
ports:
- name: http
port: <Service端口>
targetPort: <容器端口>
protocol: TCP
type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: <应用名称>
namespace: <命名空间>
labels:
app: <应用名称>
spec:
replicas: <副本数>
selector:
matchLabels:
app: <应用名称>
template:
metadata:
labels:
app: <应用名称>
annotations:
cloud.tencent.com/inject-java: 'true'
cloud.tencent.com/otel-service-name: <应用名称>
spec:
nodeSelector:
<标签key>: "<标签value>"
containers:
- name: <应用名称>
image: <镜像地址>:<镜像版本>
imagePullPolicy: IfNotPresent
env:
- name: profile
value: "<环境名称>"
- name: JAVA_OPTION
value: "-Dfile.encoding=UTF-8 -XX:+UseG1GC"
- name: XMX
value: "<最大堆内存>"
- name: XMS
value: "<初始堆内存>"
ports:
- containerPort: <容器端口>
protocol: TCP
resources:
requests:
cpu: <请求CPU>
memory: <请求内存>
limits:
cpu: <限制CPU>
memory: <限制内存>
volumeMounts:
- name: sharedata
mountPath: /<Pod内访问目录>
volumes:
- name: sharedata
hostPath:
path: /<本地挂载目录>
type: Directory
imagePullSecrets:
- name: <镜像仓库Secret名称>
九、发布前检查
发布前需要确认所有目标节点都已经成功挂载共享目录。
在每台目标节点执行:
df -hT /<本地挂载目录>
正常应该显示文件系统类型为:
cifs
继续检查:
mount | grep <共享服务器IP>
ls -l /<本地挂载目录>
如果 df -hT /<本地挂载目录> 显示的是:
xfs
ext4
而不是:
cifs
说明当前目录只是节点本地目录,并没有真正挂载到共享目录。
十、发布应用
kubectl apply -f <应用YAML文件名>.yaml
查看 Pod:
kubectl get pod -n <命名空间> -o wide
确认 Pod 所在节点必须是已打标签的节点。
十一、验证 Pod 内挂载
进入 Pod:
kubectl exec -it -n <命名空间> deploy/<Deployment名称> -- sh
查看挂载目录:
ls -l /<Pod内访问目录>
查看文件系统类型:
df -hT /<Pod内访问目录>
可以测试写入:
echo "k8s mount test $(date)" > /<Pod内访问目录>/k8s-mount-test.txt
cat /<Pod内访问目录>/k8s-mount-test.txt
测试完成后删除:
rm -f /<Pod内访问目录>/k8s-mount-test.txt
十二、注意事项
1. 容器内目录不需要提前存在
即使镜像中没有:
/<Pod内访问目录>
Kubernetes 在挂载时也会自动处理。
真正必须提前存在的是节点上的:
/<本地挂载目录>
并且该目录需要已经成功挂载外部共享目录。
2. 不建议使用 DirectoryOrCreate
建议使用:
type: Directory
不建议使用:
type: DirectoryOrCreate
原因是:
如果共享目录没有挂载成功,DirectoryOrCreate 可能会在节点上自动创建一个空目录。
这样 Pod 虽然能启动,但实际读写的是节点本地目录,不是共享目录,容易造成生产文件写错位置。
3. 多节点时每台节点都必须挂载
如果同一个标签打在多台节点上,例如:
app-sharedata=true
那么 Pod 可能会调度到任意一台带该标签的节点。
因此,每一台带该标签的节点都必须提前完成共享目录挂载。
4. hostPath 不是共享存储
hostPath 只是把节点上的本地路径挂进 Pod。
如果 Pod 被调度到不同节点,看到的是对应节点上的本地路径。
所以必须配合:
nodeSelector
或:
nodeAffinity
来限制 Pod 调度范围。
十三、回滚操作
1. 回滚 Deployment
kubectl rollout undo deployment/<Deployment名称> -n <命名空间>
查看回滚状态:
kubectl rollout status deployment/<Deployment名称> -n <命名空间>
2. 删除节点标签
kubectl label node <节点名称1> <标签key>-
kubectl label node <节点名称2> <标签key>-
示例:
kubectl label node <节点名称1> app-sharedata-
kubectl label node <节点名称2> app-sharedata-
3. 卸载共享目录
在节点上执行:
umount /<本地挂载目录>
如果提示 busy,查看占用进程:
lsof +f -- /<本地挂载目录>
或者:
fuser -vm /<本地挂载目录>
4. 删除 fstab 自动挂载配置
先备份:
cp /etc/fstab /etc/fstab.bak.$(date +%F_%H%M%S)
编辑:
vi /etc/fstab
删除对应挂载行:
//<共享服务器IP>/<共享目录路径> /<本地挂载目录> cifs credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm,_netdev,nofail 0 0
然后执行:
mount -a
确认没有异常。
十四、操作流程汇总
| 步骤 | 操作 | 执行位置 |
|---|---|---|
| 1 | 确认共享服务器、共享目录、账号密码 | 运维人员 |
| 2 | 确认需要绑定的 Kubernetes 节点 | kubectl 客户端 |
| 3 | 安装 cifs-utils | 目标节点 |
| 4 | 创建本地挂载目录 | 目标节点 |
| 5 | 创建 SMB 认证文件 | 目标节点 |
| 6 | 手动挂载共享目录 | 目标节点 |
| 7 | 验证挂载类型是否为 cifs | 目标节点 |
| 8 | 配置 /etc/fstab 自动挂载 | 目标节点 |
| 9 | 给目标节点打 Kubernetes 标签 | kubectl 客户端 |
| 10 | 修改 Deployment YAML | Git 仓库 / 本地 |
| 11 | 发布 Deployment | kubectl 客户端 |
| 12 | 进入 Pod 验证目录 | kubectl 客户端 |
十五、关键命令模板
1. 节点挂载命令
mount -t cifs "//<共享服务器IP>/<共享目录路径>" /<本地挂载目录> \
-o credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm
2. fstab 配置模板
//<共享服务器IP>/<共享目录路径> /<本地挂载目录> cifs credentials=/root/.smbcred,iocharset=utf8,vers=3.0,uid=0,gid=0,file_mode=0777,dir_mode=0777,noperm,_netdev,nofail 0 0
3. 节点标签命令
kubectl label node <节点名称> <标签key>=<标签value> --overwrite
4. YAML 核心配置
nodeSelector:
<标签key>: "<标签value>"
volumeMounts:
- name: sharedata
mountPath: /<Pod内访问目录>
volumes:
- name: sharedata
hostPath:
path: /<本地挂载目录>
type: Directory
十六、常见问题
1. Pod 启动失败,提示 hostPath 目录不存在
原因:节点上没有创建本地挂载目录,或者 Pod 被调度到了未挂载共享目录的节点。
处理:
ls -ld /<本地挂载目录>
df -hT /<本地挂载目录>
kubectl get pod -n <命名空间> -o wide
2. Pod 能启动,但目录里没有共享文件
原因:节点目录存在,但没有真正挂载到 SMB 共享目录。
处理:
df -hT /<本地挂载目录>
如果不是 cifs,需要重新挂载共享目录。
3. Pod 被调度到了错误节点
原因:节点标签配置错误,或者 Deployment 没有配置 nodeSelector。
处理:
kubectl get nodes --show-labels | grep <标签key>
kubectl get pod -n <命名空间> -o wide
kubectl describe pod <Pod名称> -n <命名空间>
4. 中文路径无法访问
处理方式:
挂载参数中需要包含:
iocharset=utf8
同时确认 Linux 系统环境支持 UTF-8。
可以检查:
locale
5. 共享目录重启后丢失
原因:没有写入 /etc/fstab,或者 /etc/fstab 配置错误。
处理:
mount -a
df -hT /<本地挂载目录>
确认 /etc/fstab 中存在正确配置。