kubernetes笔记: subpath
by 伊布
k8s允许我们将不同类型的volume挂载到容器的特定目录下。例如,我们可以将configmap的数据以volume的形式挂到容器下。
定义一个configmap,其中的数据以 key:value 的格式体现。
apiVersion: v1
kind: ConfigMap
metadata:
name: special-config
namespace: default
data:
special.level: very
special.type: |-
property.1=value-1
property.2=value-2
property.3=value-3
创建一个Pod,其挂载上面定义的cm,并在启动时查看挂载目录 /etc/config/
下有哪些文件。
apiVersion: v1
kind: Pod
metadata:
name: dapi-test-pod
spec:
containers:
- name: test-container
image: k8s.gcr.io/busybox
command: [ "/bin/sh", "-c", "ls /etc/config/" ]
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
# Provide the name of the ConfigMap containing the files you want
# to add to the container
name: special-config
restartPolicy: Never
将上面定义的Pod扔到k8s上,查看日志:
special.level
special.type
这里有几点需要注意。
- 挂载目录下的文件名称,即为cm定义里的
key
值。 - 挂载目录下的文件的内容,即为cm定义里的
value
值。value可以多行定义,这在一些稍微复杂的场景下特别有用,比如 my.cnf。 - 如果挂载目录下原来有文件,挂载后将不可见(AUFS)。
有的时候,我们希望将文件挂载到某个目录,但希望只是挂载该文件,不要影响挂载目录下的其他文件。有办法吗?
可以用subPath
: Path within the volume from which the container's volume should be mounted
。Volume中待挂载的子目录。
subPath 的目的是为了在单一Pod中多次使用同一个volume而设计的。
例如,像下面的LAMP,可以将同一个volume下的 mysql 和 html目录,挂载到不同的挂载点上,这样就不需要为 mysql 和 html 单独创建volume了。
apiVersion: v1
kind: Pod
metadata:
name: my-lamp-site
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_ROOT_PASSWORD
value: "rootpasswd"
volumeMounts:
- mountPath: /var/lib/mysql
name: site-data
subPath: mysql
- name: php
image: php:7.0-apache
volumeMounts:
- mountPath: /var/www/html
name: site-data
subPath: html
volumes:
- name: site-data
persistentVolumeClaim:
claimName: my-lamp-site-data
那怎么解决我们的问题呢?很简单,如果subPath不是目录,而是一个文件,那么就可以达到单独挂载的目的了。
containers:
- volumeMounts:
- name: demo-config
mountPath: /etc/special.type
subPath: special.type
volumes:
- name: demo-config
configMap:
name: special-config
来看看实现。重点应该是在t.Mode()&os.ModeDir
,即根据volume中的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)
}
}
Ref:
Subscribe via RSS