什么是configMap
Kubernetes中的ConfigMap 是用于存储非敏感数据的API对象,用于将配置数据与应用程序的镜像分离。ConfigMap可以包含键值对、文件或者环境变量等配置信息,应用程序可以通过挂载ConfigMap来访问其中的数据,从而实现应用配置的动态管理。ConfigMap的使用有助于提高应用程序的可移植性、可伸缩性和可维护性。
简单来讲, configmap 是用来存储app的配置或者容器的环境变量的, 避免这些配置hard code 在容器内。
但是configMap 并不适合存放敏感数据(例如帐号密码) , 敏感数据应该存放在secret manager中。
configMap的创建
1. by kubectl command line
我们可以用 kubectl create configMap -h 查看官方的一些示例
Aliases:
configmap, cm
Examples:
# Create a new config map named my-config based on folder bar
kubectl create configmap my-config --from-file=path/to/bar
# Create a new config map named my-config with specified keys instead of file basenames on disk
kubectl create configmap my-config --from-file=key1=/path/to/bar/file1.txt --from-file=key2=/path/to/bar/file2.txt
# Create a new config map named my-config with key1=config1 and key2=config2
kubectl create configmap my-config --from-literal=key1=config1 --from-literal=key2=config2
# Create a new config map named my-config from the key=value pairs in the file
kubectl create configmap my-config --from-file=path/to/bar
# Create a new config map named my-config from an env file
kubectl create configmap my-config --from-env-file=path/to/foo.env --from-env-file=path/to/bar.env
可见, 用命令行创建configMap的都必须提供一个or 若干文件,甚至可以是文件夹, 而且真正的内容都要写在文件中的。
而-from-literal 可以在不提供文件的情况下直接在命令指定key value
例子1 基于文件夹
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI/test$ cd ..
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ ls test/
db1.config db2.config
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ cat test/db1.config
db1.hostname=db1
db1.port=3309
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ cat test/db2.config
db2.hostname=db1
db2.port=3309
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl create cm test-dir-config --from-file=test/
configmap/test-dir-config created
上面我创建了两个config files 在test 文件夹中, 并用folder 名创建了1个configMap item
查看 configMap 的items
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl get cm
NAME DATA AGE
kube-root-ca.crt 1 172d
test-dir-config 2 2m28s
可以见到 test-dir-config 被创建出来了
查看 configMap的内容
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl describe cm test-dir-config
Name: test-dir-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
db1.config:
----
db1.hostname=db1
db1.port=3309
db2.config:
----
db2.hostname=db1
db2.port=3309
BinaryData
====
Events: <none>
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$
可以到这个configMap item 中有两个item, 注意的是
db.hostname 这种细节不是key
keys 只有两个
db1.config
db2.config
里面的内容的整体是key 对应的value!
例子2 基于某个文件
我们在另1个folder test2 created 了1个文件pgsql.yml
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ ls test2
pgsql.yml
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ cat test2/pgsql.yml
hostname:
db3.hostname
port:
3310
注意格式是yaml的
这时我们创建1个configmap item
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl create cm db3.config --from-file=test2/pgsql.yml
configmap/db3.config created
查看configMap item
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl get cm
NAME DATA AGE
db3.config 1 67s
kube-root-ca.crt 1 172d
test-dir-config 2 52m
新建的configMap item 名字是db3.config
查看内容
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl describe cm db3.config
Name: db3.config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
pgsql.yml:
----
hostname:
db3.hostname
port:
3310
BinaryData
====
Events: <none>
key 是 pgsql.yml
这时我们再创建1个configMap item
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl create cm db4.config --from-file=db4-config=test2/pgsql.yml
configmap/db4.config created
注意这里的 --from-fille=db4-config=test2/pgsql.yaml
db4-config 是指定key的名字, overwrite掉 文件名作为key
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl get cm
NAME DATA AGE
db3.config 1 5m33s
db4.config 1 85s
kube-root-ca.crt 1 172d
test-dir-config 2 57m
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl describe cm db4.config
Name: db4.config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
db4-config:
----
hostname:
db3.hostname
port:
3310
BinaryData
====
Events: <none>
再查看内容:
这时的key 已经是 db4-config了
例子3 直接在命令行带上key value 的值
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl create cm db5.config --from-literal=hostname=db5.hostname --from-literal=port=3311
configmap/db5.config created
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl get cm
NAME DATA AGE
cloud-user-config 1 33d
db3.config 1 36m
db4.config 1 32m
db5.config 2 3s
kube-root-ca.crt 1 172d
test-dir-config 2 88m
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/CLI$ kubectl describe cm db5.config
Name: db5.config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
port:
----
3311
hostname:
----
db5.hostname
BinaryData
====
Events: <none>
注意这句命令创建了 1个 configmap item, 但是里面有两对KV
分别是
key: hostname
key: port
2. by yaml file
在项目中, 更常用定义资源应该是yaml file 格式
为了实现与上面命令同样的效果, 我们创建了1个yaml file, 内容如下
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/yaml$ ls
db6-config.yaml
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/yaml$ cat db6-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: db6.config
data:
hostname: db6.hostname
port: "3311"g
apply 资源
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/yaml$ kubectl apply -f db6-config.yaml
configmap/db6.config created
查看configMap items
configmap/db6.config created
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/yaml$ kubectl get cm
NAME DATA AGE
db3.config 1 85m
db4.config 1 81m
db5.config 2 48m
db6.config 2 33s
kube-root-ca.crt 1 172d
test-dir-config 2 137m
查看内容
gateman@MoreFine-S500:~/projects/coding/k8s-s/configMap/yaml$ kubectl describe cm db6.config
Name: db6.config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
hostname:
----
db6.hostname
port:
----
3311
BinaryData
====
Events: <none>
可见 效果时间一样的
configMap的使用
在yaml 里大概有两种使用方法
- 利用configMapKeyRef 读取配置
- 利用volumes 把配置写入1个文件
1. 利用configMapKeyRef 读取配置
例子:
我们首先准备1个springboot serice - cloud order
令其的actuator/info 接口可以return 当前环境的所有环境变量
@Component
@Slf4j
public class AppVersionInfo implements InfoContributor {
@Value("${pom.version}") // https://stackoverflow.com/questions/3697449/retrieve-version-from-maven-pom-xml-in-code
private String appVersion;
@Autowired
private String hostname;
@Autowired
private InfoService infoservice;
@Value("${spring.datasource.url}")
private String dbUrl;
@Override
// https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-endpoints-info
public void contribute(Info.Builder builder) {
log.info("AppVersionInfo: contribute ...");
builder.withDetail("app", "Cloud Order Service")
.withDetail("version", appVersion)
.withDetail("hostname",hostname)
.withDetail("dbUrl", dbUrl)
.withDetail("description", "This is a simple Spring Boot application to for cloud order.")
.withDetail("SystemVariables", infoservice.getSystemVariables());
}
}
@Service
public class InfoService {
public String getHostName() {
return "unknown";
}
public HashMap<String, String> getSystemVariables() {
Map<String, String> envVariables = System.getenv();
//envVariables.forEach((key, value) -> systemVariables.put(key, value));
return new HashMap<>(envVariables);
}
}
然后我们利用yaml 创建1个configMap
cloud-order-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: cloud-order-app-tag-config
data:
APP_TAG: cloud-order-app-tag
里面指定一了1个KV 对
部署
kubectl apply -f cloud-order-configmap.yaml
然后再编写 deployment 的yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels: # label of this deployment
app: cloud-order # custom defined
author: nvd11
name: deployment-cloud-order # name of this deployment
namespace: default
spec:
replicas: 3 # desired replica count, Please note that the replica Pods in a Deployment are typically distributed across multiple nodes.
revisionHistoryLimit: 10 # The number of old ReplicaSets to retain to allow rollback
selector: # label of the Pod that the Deployment is managing,, it's mandatory, without it , we will get this error
# error: error validating data: ValidationError(Deployment.spec.selector): missing required field "matchLabels" in io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector ..
matchLabels:
app: cloud-order
strategy: # Strategy of upodate
type: RollingUpdate # RollingUpdate or Recreate
rollingUpdate:
maxSurge: 25% # The maximum number of Pods that can be created over the desired number of Pods during the update
maxUnavailable: 25% # The maximum number of Pods that can be unavailable during the update
template: # Pod template
metadata:
labels:
app: cloud-order # label of the Pod that the Deployment is managing. must match the selector, otherwise, will get the error Invalid value: map[string]string{"app":"bq-api-xxx"}: `selector` does not match template `labels`
spec: # specification of the Pod
containers:
- image: europe-west2-docker.pkg.dev/jason-hsbc/my-docker-repo/cloud-order:1.0.2 # image of the container
imagePullPolicy: IfNotPresent
name: container-cloud-order
env: # set env varaibles
- name: APP_ENVIRONMENT # name of the environment variable
value: prod # value of the environment variable
- name: APP_TAG
valueFrom: # value from config map
configMapKeyRef:
name: cloud-order-app-tag-config # name of the configMap item
key: APP_TAG # key from the config map item
restartPolicy: Always # Restart policy for all containers within the Pod
terminationGracePeriodSeconds: 10 # The period of time in seconds given to the Pod to terminate gracefully
注意, 我们在环境变量那里 新增了1个item , 环境变量的名字是 APP_TAG, 而 环境变量的value 是从 configmap 里获取的
部署成功后
测试api /actuator/info
ateman@MoreFine-S500:~/projects/coding/k8s-s/service-case/cloud-order$ curl http://www.jp-gcp-vms.cloud:8085/cloud-order/actuator/info | jq .
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 1777 0 1777 0 0 3067 0 --:--:-- --:--:-- --:--:-- 3063
{
"app": "Cloud Order Service",
"version": "1.0.2",
"hostname": "deployment-cloud-order-c56db7848-lt5wc",
"dbUrl": "jdbc:mysql://192.168.0.42:3306/demo_cloud_order?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true",
"description": "This is a simple Spring Boot application to for cloud order.",
"SystemVariables": {
"PATH": "/usr/java/openjdk-17/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
"CLUSTERIP_CLOUD_ORDER_SERVICE_HOST": "10.98.117.97",
"CLUSTERIP_BQ_API_SERVICE_SERVICE_PORT": "8080",
"KUBERNETES_PORT": "tcp://10.96.0.1:443",
"JAVA_HOME": "/usr/java/openjdk-17",
"KUBERNETES_SERVICE_HOST": "10.96.0.1",
"LANG": "C.UTF-8",
"CLUSTERIP_CLOUD_ORDER_PORT": "tcp://10.98.117.97:8080",
"CLUSTERIP_BQ_API_SERVICE_PORT_8080_TCP": "tcp://10.100.68.154:8080",
"CLUSTERIP_CLOUD_ORDER_PORT_8080_TCP": "tcp://10.98.117.97:8080",
"CLUSTERIP_BQ_API_SERVICE_PORT_8080_TCP_PROTO": "tcp",
"PWD": "/app",
"JAVA_VERSION": "17.0.2",
"_": "/usr/java/openjdk-17/bin/java",
"CLUSTERIP_CLOUD_ORDER_PORT_8080_TCP_ADDR": "10.98.117.97",
"KUBERNETES_PORT_443_TCP": "tcp://10.96.0.1:443",
"KUBERNETES_PORT_443_TCP_ADDR": "10.96.0.1",
"CLUSTERIP_CLOUD_ORDER_PORT_8080_TCP_PORT": "8080",
"CLUSTERIP_BQ_API_SERVICE_PORT": "tcp://10.100.68.154:8080",
"CLUSTERIP_BQ_API_SERVICE_PORT_8080_TCP_PORT": "8080",
"KUBERNETES_PORT_443_TCP_PROTO": "tcp",
"APP_ENVIRONMENT": "prod",
"KUBERNETES_SERVICE_PORT": "443",
"CLUSTERIP_CLOUD_ORDER_SERVICE_PORT": "8080",
"CLUSTERIP_BQ_API_SERVICE_PORT_8080_TCP_ADDR": "10.100.68.154",
"APP_TAG": "cloud-order-app-tag",
"HOSTNAME": "deployment-cloud-order-c56db7848-lt5wc",
"CLUSTERIP_CLOUD_ORDER_PORT_8080_TCP_PROTO": "tcp",
"CLUSTERIP_BQ_API_SERVICE_SERVICE_HOST": "10.100.68.154",
"KUBERNETES_PORT_443_TCP_PORT": "443",
"KUBERNETES_SERVICE_PORT_HTTPS": "443",
"SHLVL": "1",
"HOME": "/root"
}
}
查看 APP_TAG 的那一行, 的确能正确地获取configmap 里的配置信息!
2. 利用volumes 把配置写入1个文件
在这个例子中
我们再利用yaml 创建另1个configMap
apiVersion: v1
kind: ConfigMap
metadata:
name: cloud-order-os-version-config
data:
OS_VERSION: ubuntu-x.x
部署
gateman@MoreFine-S500:~/projects/coding/k8s-s/service-case/cloud-order$ kubectl apply -f cloud-order-configmap2.yaml
configmap/cloud-order-os-version-config created
修改 cloud order service 的deployment yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels: # label of this deployment
app: cloud-order # custom defined
author: nvd11
name: deployment-cloud-order # name of this deployment
namespace: default
spec:
replicas: 3 # desired replica count, Please note that the replica Pods in a Deployment are typically distributed across multiple nodes.
revisionHistoryLimit: 10 # The number of old ReplicaSets to retain to allow rollback
selector: # label of the Pod that the Deployment is managing,, it's mandatory, without it , we will get this error
# error: error validating data: ValidationError(Deployment.spec.selector): missing required field "matchLabels" in io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector ..
matchLabels:
app: cloud-order
strategy: # Strategy of upodate
type: RollingUpdate # RollingUpdate or Recreate
rollingUpdate:
maxSurge: 25% # The maximum number of Pods that can be created over the desired number of Pods during the update
maxUnavailable: 25% # The maximum number of Pods that can be unavailable during the update
template: # Pod template
metadata:
labels:
app: cloud-order # label of the Pod that the Deployment is managing. must match the selector, otherwise, will get the error Invalid value: map[string]string{"app":"bq-api-xxx"}: `selector` does not match template `labels`
spec: # specification of the Pod
containers:
- image: europe-west2-docker.pkg.dev/jason-hsbc/my-docker-repo/cloud-order:1.0.2 # image of the container
imagePullPolicy: IfNotPresent
name: container-cloud-order
env: # set env varaibles
- name: APP_ENVIRONMENT # name of the environment variable
value: prod # value of the environment variable
- name: APP_TAG
valueFrom: # value from config map
configMapKeyRef:
name: cloud-order-app-tag-config # name of the configMap item
key: APP_TAG # key from the config map item
volumeMounts: # volume mount
- name: config-volume
mountPath: /app/config
volumes:
- name: config-volume
configMap:
name: cloud-order-os-version-config # name of the config map
items:
- key: OS_VERSION # key of the config map item
path: os-version.conf # name of the file, it will only contain the content of the key
restartPolicy: Always # Restart policy for all containers within the Pod
terminationGracePeriodSeconds: 10 # The period of time in seconds given to the Pod to terminate gracefully
注意我们在这里新增了1个卷 把configmap cloud-order-os-version-config 指定某个key的value 保存到1个文件中 os-version.conf
而在container 的配置中, 我们 吧这个卷mount 在 /app/config 路径下
重新部署:
gateman@MoreFine-S500:~/projects/coding/k8s-s/service-case/cloud-order$ kubectl apply -f deployment-cloud-order.yaml
deployment.apps/deployment-cloud-order configured
gateman@MoreFine-S500:~/projects/coding/k8s-s/service-case/cloud-order$ kubectl get pods
NAME READY STATUS RESTARTS AGE
deployment-bq-api-service-6f6ffc7866-8djx9 1/1 Running 0 26h
deployment-bq-api-service-6f6ffc7866-g4854 1/1 Running 9 (3d21h ago) 45d
deployment-bq-api-service-6f6ffc7866-lwxt7 1/1 Running 11 (3d21h ago) 47d
deployment-bq-api-service-6f6ffc7866-mxwcq 1/1 Running 8 (3d21h ago) 45d
deployment-cloud-order-7cb8466d48-2kcnn 1/1 Running 0 8s
deployment-cloud-order-7cb8466d48-57w6n 1/1 Running 0 7s
deployment-cloud-order-7cb8466d48-d6jk7 1/1 Running 0 5s
进入容器:
gateman@MoreFine-S500:~$ kubectl exec -it deployment-cloud-order-7cb8466d48-d6jk7 /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
bash-4.4# cd /app/config
bash-4.4# ls
os-version.conf
bash-4.4# cat os-version.conf
ubuntu-x.x
可以见到configmap的内容的确放入了 容器内的对应的目录下