如果挂载目录下原来有文件,挂载后将被覆盖掉。有的时候,我们希望将文件挂载到某个目录,但希望只是挂载该文件,不要影响挂载目录下的其他文件。有办法吗?

可以用subPath,subPath的目的是为了在单一Pod中多次使用同一个volume而设计的。示例:

比如我们要通过ConfigMap的形式挂载 Nginx 的配置文件:

1.保存下面文件为:nginx.conf

user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
 worker_connections 1024;
}
http {
 include /etc/nginx/mime.types;
 default_type application/octet-stream;
 log_format main '$remote_addr - $remote_user [$time_local] "$request" '
 '$status $body_bytes_sent "$http_referer" '
 '"$http_user_agent" "$http_x_forwarded_for"';
 access_log /var/log/nginx/access.log main;
 sendfile on;
 #tcp_nopush on;
 keepalive_timeout 65;
 #gzip on;
 include /etc/nginx/conf.d/*.conf;
}

2.通过文件创建ConfigMap对象:

$ kubectl create configmap confnginx --from-file=nginx.conf

3.创建一个 nginx 的 Pod,通过上面的 configmap 挂载 nginx.conf 配置文件,保存为 nginx.yaml:

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
 name: ngtest
spec:
 template:
 metadata:
 labels:
 app: nginx
 spec:
 containers:
 - name: nginx
 image: nginx:1.7.9
 ports:
 - containerPort: 80
 volumeMounts:
 - name: nginx-config
 mountPath: /etc/nginx/nginx.conf
 subPath: nginx.conf
 volumes:
 - name: nginx-config
 configMap:
 name: confnginx

4.创建上面的Deployment:

$ kubectl apply -f nginx.yaml

5.验证: 下面是我们生成的 Pod,看状态可以看出已经正常运行了

$ kubectl get pods
NAME READY STATUS RESTARTS AGE
...
ngtest-7df9b74f98-btlgp 1/1 Running 0 19m
...

现在我们进入容器中查看下 nginx.conf 文件:

$ kubectl exec -it ngtest-7df9b74f98-btlgp /bin/bash
root@ngtest-7df9b74f98-btlgp:/# ls /etc/nginx/
conf.d koi-utf mime.types scgi_params win-utf
fastcgi_params koi-win nginx.conf uwsgi_params
root@ngtest-7df9b74f98-btlgp:/# cat /etc/nginx/nginx.conf
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
 worker_connections 1024;
}
http {
 include /etc/nginx/mime.types;
 default_type application/octet-stream;
 log_format main '$remote_addr - $remote_user [$time_local] "$request" '
 '$status $body_bytes_sent "$http_referer" '
 '"$http_user_agent" "$http_x_forwarded_for"';
 access_log /var/log/nginx/access.log main;
 sendfile on;
 #tcp_nopush on;
 keepalive_timeout 65;
 gzip on;
 include /etc/nginx/conf.d/*.conf;
}

可以看到 nginx.conf 文件正是我们上面的 ConfigMap 对象中的内容,验证成功。

6.原理: 下面是绑定 subPath 的源码部分,我们可以看到下面的 t.Model()&os.ModeDir 部分,如果 subPath 是一个文件夹的话就会去创建这个文件夹,如果是文件的话就可以进行单独挂载了。

func doBindSubPath(mounter Interface, subpath Subpath, kubeletPid int) (hostPath string, err error) {
 ...
 // Create target of the bind mount. A directory for directories, empty file
 // for everything else.
 t, err := os.Lstat(subpath.Path)
 if err != nil {
 return "", fmt.Errorf("lstat %s failed: %s", subpath.Path, err)
 }
 if t.Mode() & os.ModeDir > 0 {
 if err = os.Mkdir(bindPathTarget, 0750); err != nil && !os.IsExist(err) {
 return "", fmt.Errorf("error creating directory %s: %s", bindPathTarget, err)
 }
 } else {
 // "/bin/touch <bindDir>".
 // A file is enough for all possible targets (symlink, device, pipe,
 // socket, ...), bind-mounting them into a file correctly changes type
 // of the target file.
 if err = ioutil.WriteFile(bindPathTarget, []byte{}, 0640); err != nil {
 return "", fmt.Errorf("error creating file %s: %s", bindPathTarget, err)
 }
 }
 ...
}