Kubernetes Secret & ConfigMap

一、Secret & ConfigMap 作用

Kubernetes上部署应用时,经常需要传配置信息给应用,如数据库配置,实现方案有多种,如:

  • 将配置信息放在应用的配置文件中。这种方式的坏处显而易见,每次配置更改均要重新打包镜像。
  • 在 Dockerfile 中指定环境变量,配置信息通过环境变量传入。这种方式的局限性在于每次修改配置都要修改yaml文件后重启容器,而且若大量的配置放到环境变量中,可读性低。另外,如果我们需要在不同的容器中引用相同的数据,如果应用程序是运行在集群上的时候,使用环境变量配置的方式是难以管理的。

Kubernetes中提供了Secret(用于比较私密的数据)和ConfigMap(用于非私密的数据)两种资源可以很好的解决上述问题。

二、Secret & ConfigMap 区别

Secret 和 ConfigMap 本质区别就是 Secret 的数据是用Base64编码混淆过的,对于比较机密的数据(如API密钥)使用 Secret 是一个很好的做法,但是对于一些非私密的数据(比如数据目录)用 ConfigMap 来保存即可。
安全性实际是扯淡的,Secret的实现,仅仅是ConfigMap把value用Base64 Encode一下。只要Decode就能还原数据,相当于明文存储。Base64不是加密算法,仅是编码。

三、Secret & ConfigMap 生成

Secret

创建:

echo_pwd_base64

①命令行创建
1
kubectl create secret generic mysecret --from-literal=test-db-username=YWRtaW4= --from-literal=test-db-password=MTIzNDU2

secret_create_cmd

②yaml文件创建:

推荐使用这种方式。

1
kubectl create -f mysecret.yaml

mysecret.yaml文件:

1
2
3
4
5
6
7
8
9
10
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: default
type: Opaque
data:
# base64
test-db-username: YWRtaW4=
test-db-password: MTIzNDU2

secret_create_file

更新及删除

mysecret.yaml文件新增一行数据

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: default
type: Opaque
data:
# base64
test-db-username: YWRtaW4=
test-db-password: MTIzNDU2
test-db-host: MTI3LjAuMC4x

更新:

1
kubectl replace -f mysecret.yaml

删除:

1
kubectl delete -f mysecret.yaml

secret_replace_delete

ConfigMap

①命令行创建

1
kubectl create configmap myconfigmap --from-literal=test-key1=testValue1 --from-literal=test-key2=testValue2

configmap_create_cmd

②yaml文件创建:

1
kubectl create -f myconfigmap.yaml

myconfigmap.yaml文件:

1
2
3
4
5
6
7
8
apiVersion: v1
kind: ConfigMap
metadata:
name: myconfigmap
namespace: default
data:
test-key1: testValue1
test-key2: testValue2

configmap_create_file

四、Secret & ConfigMap 配置

①配置到kubernetes集群的环境变量中:

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
spec:
containers:
name: test_pod
image: test_image
env:
name: mysceret-db-username
valueFrom:
secretKeyRef:
name: mysceret
key: test-db-username
name: mysceret-db-password
valueFrom:
secretKeyRef:
name: mysceret
key: test-db-password
name: myconfigmap-test-key1
valueFrom:
configMapKeyRef:
name: myconfigmap
key: test-key1
name: myconfigmap-test-key2
valueFrom:
configMapKeyRef:
name: myconfigmap
key: test-key2

②Secret挂载到pod的volume中:

1
2
3
4
5
6
7
8
9
10
11
12
spec:
containers:
name: test_pod
image: test_image
volumeMounts:
name: mysecret_volume
mountPath: "/data/secret"
readOnly: true
volumes:
name: mysecret_volume
secret:
secretName: mysecret

五、Secret & ConfigMap 实际应用

实际选用其中一种方式即可,为方便维护:

  • 采用文件创建
  • Volume方式映射,映射目录地址放到环境变量里

Secret的数据以stringData声明,value无需传base64编码后的数据,如下yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: v1
kind: Secret
metadata:
name: mysecret
namespace: default
type: Opaque
stringData:
DBConfig.properties: |-
datasource.primary.jdbcUrl=jdbc:mysql://127.0.0.1:3306?useUnicode=true&characterEncoding=utf8&useSSL=false
datasource.primary.username=root
datasource.primary.password=root
datasource.primary.driver-class-name=com.mysql.jdbc.Driver
RedisConfig.properteis: |-
redisConfig.masterName=mymaster
redisConfig.sentinelAddress=172.16.2.121:36379,172.16.2.122:36379,172.16.2.123:36379
redisConfig.maxActive=200
redisConfig.maxIdle=20
redisConfig.minIdle=10
redisConfig.maxWaitTime=10000
redisConfig.password=123456
redisConfig.timeout=5000
redisConfig.dbIndex=0

注:|- 是用于声明文件内容为多行
生成secret如下图:
secret_create_stringdata

应用参数读取,以SpringBoot为例,读取文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final String secretDirectory = "SECRET_DIRECTORY";
String secretFileDirStr = System.getenv(secretDirectory);
SpringApplicationBuilder builder = new SpringApplicationBuilder(MainApplication.class);
Properties p = new Properties();
try {
File secretFileDir = new File(secretFileDirStr);
for (File file : secretFileDir.listFiles()) {
if(!file.isDirectory()){
InputStream in = new BufferedInputStream(new FileInputStream(file));
p.load(in);
in.close();
}
}
} catch (IOException e) {
logger.error("get secretFile, IOException: {}", e);
return;
}
builder.properties(p).build();
builder.run(args);

调试如下:
secret_dev_param

六、扩展

1.在实际应用中, 维护K8s提供的Secret和ConfigMap时要合理分组,斟酌使用,可考虑如下方案替代。

应用配置信息可通过专门的统一配置中心来维护,敏感信息如数据库配置可通过权限管理解决。

统一配置中心方案有多种,如Zookeeper(服务注册中心,也可兼作配置中心)、Spring-Cloud-Config(Spring提供的分布式统一配置解决方案)、Disconf(百度开源,基于Zookeeper的分布式配置管理软件)、 Apollo(携程开源,分环境、支持热更、灰度、权限、发布版本管理)等。
Spring-Cloud-Config和Disconf方案未使用过,据说不错。比对使用过的Zookeeper和Apollo, 推荐Apollo。

2.企业尝试k8s(或其他dev/ops、ci/cd相关)时,开发阶段可引入rancher(企业容器管理平台,封装简化k8s操作),harbor(镜像仓库)等。

3.CI/CD流程中,代码审核通过后(merge finish),构建工具(如Jenkins pipeline)触发打出新镜像,推送至测试环境,测试通过后,标记成功并将镜像推送至线上发布(滚动更新)。