Kubernetes入门实践(ConfigMap/Secret)
Kubernetes中用于管理配置信息的两种对象: ConfigMap和Secret,可使用它们来灵活地配置和定制应用。应用程序有很多类别的配置信息,从数据安全的角度看可分为明文配置和机密配置,明文配置就是不保密,可以任意修改,如服务端口、运行参数、文件路径等等,机密配置涉及敏感信息需要保密,不能随便查看,比如密码、密钥和证书等
创建ConfigMap
用kubectl create
来创建一个它的YAML样板,可以使用缩写"cm":
$ minikube kubectl -- create cm info --dry-run=client -o yaml
apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: null
name: info
ConfigMap中没有spec这样的字段,只有apiVersion、kind和metadata,因为ConfigMap存储的是配置数据,是静态的字符串,并非容器,所以就无需spec字段,但应当给它定义一个data字段,就要加一个参数--from-literal
,表示从字面值生成一些数据:
$ minikube kubectl -- create cm info --from-literal=k=v --dry-run=client -o yaml
apiVersion: v1
data:
k: v
kind: ConfigMap
metadata:
creationTimestamp: null
name: info
这里的--fron-literal
指定了k=v,原因是其中数据都是key-value结构,编辑这个样板:
apiVersion: v1
data:
count: '10'
debug: 'on'
path: '/etc/systemd'
greeting: |
say hello to kubernetes.
kind: ConfigMap
metadata:
name: info
使用apply来创建ConfigMap对象,使用get cm
来查看,使用describe cm
来查看详细信息:
$ minikube kubectl -- apply -f cm.yml
configmap/info created
$ minikube kubectl -- get cm
NAME DATA AGE
info 4 40s
$ minikube kubectl -- describe cm info
Name: info
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
count:
----
10
debug:
----
on
greeting:
----
say hello to kubernetes.
path:
----
/etc/systemd
BinaryData
====
Events: <none>
现在这个配置信息已经存入etcd数据库了,后续可以被其他API对象使用
创建Secret
与ConfigMap相似,但细分了很多类:
- 访问私有镜像仓库的认证信息
- 身份识别的凭证信息
- HTTPS通信的证书和私钥
- 一般的机密信息(格式由用户自行解释)
创建样板:
$ minikube kubectl -- create secret generic user --from-literal=name=root --dry-run=client -o yaml
apiVersion: v1
data:
name: cm9vdA==
kind: Secret
metadata:
creationTimestamp: null
name: user
Secret对象和ConfigMap比较相似,只是kind字段由ConfigMap变成了Secret,data里同样是key-value数据
可以发现Secret中的name是做了base64编码的,不让用户直接看到原来的内容,现在来直接编辑这个样板:
apiVersion: v1
data:
name: cm9vdA== # root
pwd: MTIzNDU2 # 123456
db: bXlzcWw= # mysql
kind: Secret
metadata:
name: user
创建和查看对象:
$ minikube kubectl -- apply -f secret.yml
secret/user created
$ minikube kubectl -- get secret
NAME TYPE DATA AGE
default-token-cg2ws kubernetes.io/service-account-token 3 5d12h
user Opaque 3 67s
$ minikube kubectl -- describe secret
Name: default-token-cg2ws
Namespace: default
Labels: <none>
Annotations: kubernetes.io/service-account.name: default
kubernetes.io/service-account.uid: 4d3b6e91-89b6-4345-85ae-e9e8b1e9073f
Type: kubernetes.io/service-account-token
Data
====
ca.crt: 1111 bytes
namespace: 7 bytes
token: eyJhbGciOiJSUzI1NiIsImtpZCI6Ik1JWmxuY0Jac1B1OXFXcmNLQ3pONmstcVZqdzQwMHJ0UmY4R3BWYWxIQ1UifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImRlZmF1bHQtdG9rZW4tY2cyd3MiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGVmYXVsdCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjRkM2I2ZTkxLTg5YjYtNDM0NS04NWFlLWU5ZThiMWU5MDczZiIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.U87mqQq1jAb9JCyxfoFG0D-_UF1qZAszbIwnR-a_HNPTq4-Wnn3xSlMkc6ZUSHOQ2YytTuyi0KVYlxl6uzqJiJ78n_EnjLb_4r-gzVaRFBchtSND4RmM0TDG_uvIZMie4rhF8-E3O2h-iPv6jV9Jj-lA_6HoK3v1Fngucq_jzQX1MWwRhS-95OD-fDtukNotX29E7K-P0t__vFWFX4dVm00FAxIXkvPhkIKwfR1qazsg-o9ykalA9LxUoiJxRKzyCSS9MPCOdal8Dik_2CXAcX6NcijcXWTQkmbLdSK0Alm4CiKiYTInQyVo_hlLMMmS19NhhzpVWtLtomzrvDNfTA
Name: user
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
db: 5 bytes
name: 4 bytes
pwd: 6 bytes
可以看到信息是保密的,describe并不难直接看到信息
使用方法
在Kubernetes中ConfigMap和Secret的应用方式有两种: 环境变量和加载文件,让其在Pod运行时产生效果
环境变量
在Pod的containers字段中有一个env,定义了Pod里容器能够看到的环境变量,还可以用valueFrom,从ConfigMap或者Secret对象中取值,这样就实现了把配置信息以环境变量的形式注入Pod,也就是配置与应用的解耦
由于valueFrom字段在YAML里的嵌套层次比较深,初次使用可以看kubectl explain
对它的说明:
$ minikube kubectl -- explain pod.spec.containers.env.valueFrom
KIND: Pod
VERSION: v1
RESOURCE: valueFrom <Object>
DESCRIPTION:
Source for the environment variable's value. Cannot be used if value is not
empty.
EnvVarSource represents a source for the value of an EnvVar.
FIELDS:
configMapKeyRef <Object>
Selects a key of a ConfigMap.
fieldRef <Object>
Selects a field of the pod: supports metadata.name, metadata.namespace,
`metadata.labels['<KEY>']`, `metadata.annotations['<KEY>']`, spec.nodeName,
spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
resourceFieldRef <Object>
Selects a resource of the container: only resources limits and requests
(limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu,
requests.memory and requests.ephemeral-storage) are currently supported.
secretKeyRef <Object>
Selects a key of a secret in the pod's namespace
valueFrom
指定了环境变量值的来源,可以是configMapKeyRet(从ConfigMap中获取配置信息)或者secretKeyRef,然后要再进一步指定应用的ConfigMap/Secret的name和它里面的key:
apiVersion: v1
kind: Pod
metadata:
name: env-pod
spec:
containers:
- env:
- name: COUNT
valueFrom:
configMapKeyRef:
name: info
key: count
- name: GREETING
valueFrom:
configMapKeyRef:
name: info
key: greeting
- name: USERNAME
valueFrom:
secretKeyRef:
name: user
key: name
- name: PASSWORD
valueFrom:
secretKeyRef:
name: user
key: pwd
image: busybox
name: busy
imagePullPolicy: IfNotPresent
command: ["/bin/sleep","300"]
这个Pod的名字是env-pod,镜像是busybox,执行命令sleep睡眠300秒
env中定义了4个环境变量: COUNT、GREETING、USERNAME、PASSWORD
对于明文配置数据,COUNT、GREETING引用的是ConfigMap对象,所以使用字段configMapKeyRef
,里面name是ConfigMap对象的名字,也就是之前创建的info,而key字段分别是info对象里的count和greeting
同样对于机密配置数据,USERNAME、PASSWORD引用的是Secret对象,使用字段secretKeyRef
,再用name指定Secret对象的名字user,用key字段应用里面的name和pwd
显然,Pod与ConfigMap、Secret的松耦合关系,它们不是直接嵌套包含,而是使用KeyRef
字段间接引用对象,同一段配置信息就可以在不同的对象之间共享
接下来创建Pod,并使用exec进行验证:
$ minikube kubectl -- apply -f env-pod.yml
pod/env-pod created
使用exec打开一个shell,验证环境变量:
$ minikube kubectl -- exec -it env-pod -- sh
/ # echo $COUNT
10
/ # echo $GREETING
say hello to kubernetes.
/ # echo $USERNAME
root
/ # echo $PASSWORD
123456
成功输出了在两个YAML里定义的配置信息,就是说Pod对象成功组合了ConfigMap和Secret对象
查看状态信息:
$ minikube kubectl -- get pod
NAME READY STATUS RESTARTS AGE
env-pod 1/1 Running 0 4m39s
Volume
Kubernetes为Pod定义了一个volume的概念,可以翻译成存储卷,如果将Pod理解成一个虚拟机,那么Volume就相当于虚拟机里的磁盘,可以为Pod挂载多个volume,里面存放供Pod访问的数据,这种方式类似docker run -v
在Pod里要挂载只要在spec中增加一个volumns字段,然后再定义卷的名字和引用的ConfigMap/Secret中即可,要注意的是volume属于Pod,不属于容器,所以它和字段containers属于同级,都属于spec
编辑YAML文件:
apiVersion: v1
kind: Pod
metadata:
name: env-pod
spec:
volumes:
- name: cm-vol
configMap:
name: info
- name: sec-vol
secret:
secretName: user
containers:
- volumeMounts:
- mountPath: /tmp/cm-items
name: cm-vol
- mountPath: /tmp/sec-items
name: sec-vol
image: busybox
name: busy
imagePullPolicy: IfNotPresent
command: ["/bin/sleep","300"]
定义两个volume,分别引用ConfigMap和Secret,名字是cm-vol和sec-vol,有了volume的定义后就可以在容器里挂载,这就要使用volumeMounts
字段,正如其字面含义,将定义好的volume挂载到容器的某个路径,所以需要在里面用mountPath和name明确地指定挂载路径和volume的名字,使用volume的好处是统一抽象了所有的存储,不仅现在支持ConfigMap/Secret,以后还能支持临时卷、持久卷、动态卷、快照卷等许多形式的存储,扩展性良好
创建Pod:
$ minikube kubectl -- apply -f env-pod.yml
pod/vol-pod created
创建之后,使用kubectl exec
进入Pod,配置信息:
$ minikube kubectl -- exec -it vol-pod -- sh
/ # ls /tmp/cm-items/
count debug greeting path
/ # ls /tmp/sec-items/
db name pwd
/ # cat /tmp/cm-items/greeting
say hello to kubernetes.
/ # cat /tmp/sec-items/pwd
123456/
ConfigMap和Secret都变成了目录的形式,而它们里面的key-value变成了一个个文件,文件名就是key
环境变量用法简单,更适合存放简短的字符串,而Volume更适合存在大数据量的配置文件,在Pod中加载成文件后让应用直接读取使用