多读书多实践,勤思考善领悟

kubernetes Pod挂载单个文件的办法

本文于1908天之前发表,文中内容可能已经过时。

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

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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对象:

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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:

1
$ kubectl apply -f nginx.yaml

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

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
$ 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 是一个文件夹的话就会去创建这个文件夹,如果是文件的话就可以进行单独挂载了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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)
}
}
...
}