1 - 示例:配置 java 微服务
1.1 - 使用 MicroProfile、ConfigMaps、Secrets 实现外部化应用配置
在本教程中,你会学到如何以及为什么要实现外部化微服务应用配置。 具体来说,你将学习如何使用 Kubernetes ConfigMaps 和 Secrets 设置环境变量, 然后在 MicroProfile config 中使用它们。
准备开始
创建 Kubernetes ConfigMaps 和 Secrets
在 Kubernetes 中,为 docker 容器设置环境变量有几种不同的方式,比如: Dockerfile、kubernetes.yml、Kubernetes ConfigMaps、和 Kubernetes Secrets。 在本教程中,你将学到怎么用后两个方式去设置你的环境变量,而环境变量的值将注入到你的微服务里。 使用 ConfigMaps 和 Secrets 的一个好处是他们能在多个容器间复用, 比如赋值给不同的容器中的不同环境变量。
ConfigMaps 是存储非机密键值对的 API 对象。 在互动教程中,你会学到如何用 ConfigMap 来保存应用名字。 ConfigMap 的更多信息,你可以在这里找到文档。
Secrets 尽管也用来存储键值对,但区别于 ConfigMaps 的是:它针对机密/敏感数据,且存储格式为 Base64 编码。 secrets 的这种特性使得它适合于存储证书、密钥、令牌,上述内容你将在交互教程中实现。 Secrets 的更多信息,你可以在这里找到文档。
从代码外部化配置
外部化应用配置之所以有用处,是因为配置常常根据环境的不同而变化。 为了实现此功能,我们用到了 Java 上下文和依赖注入(Contexts and Dependency Injection, CDI)、MicroProfile 配置。 MicroProfile config 是 MicroProfile 的功能特性, 是一组开放 Java 技术,用于开发、部署云原生微服务。
CDI 提供一套标准的依赖注入能力,使得应用程序可以由相互协作的、松耦合的 beans 组装而成。 MicroProfile Config 为 app 和微服务提供从各种来源,比如应用、运行时、环境,获取配置参数的标准方法。 基于来源定义的优先级,属性可以自动的合并到单独一组应用可以通过 API 访问到的属性。 CDI & MicroProfile 都会被用在互动教程中, 用来从 Kubernetes ConfigMaps 和 Secrets 获得外部提供的属性,并注入应用程序代码中。
很多开源框架、运行时支持 MicroProfile Config。 对于整个互动教程,你都可以使用开放的库、灵活的开源 Java 运行时,去构建并运行云原生的 apps 和微服务。 然而,任何 MicroProfile 兼容的运行时都可以用来做替代品。
教程目标
- 创建 Kubernetes ConfigMap 和 Secret
- 使用 MicroProfile Config 注入微服务配置
示例:使用 MicroProfile、ConfigMaps、Secrets 实现外部化应用配置
1.2 - 互动教程 - 配置 java 微服务
2 - 通过 ConfigMap 更新配置
本页提供了通过 ConfigMap 更新 Pod 中配置信息的分步示例,
本教程的前置任务是配置 Pod 以使用 ConfigMap。
在本教程结束时,你将了解如何变更运行中应用的配置。
本教程以 alpine
和 nginx
镜像为例。
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你需要有 curl 命令行工具,用于从终端或命令行界面发出 HTTP 请求。
如果你没有 curl
,可以安装此工具。请查阅你本地操作系统的文档。
教程目标
- 通过作为卷挂载的 ConfigMap 更新配置
- 通过 ConfigMap 更新 Pod 的环境变量
- 在多容器 Pod 中通过 ConfigMap 更新配置
- 在包含边车容器的 Pod 中通过 ConfigMap 更新配置
通过作为卷挂载的 ConfigMap 更新配置
使用 kubectl create configmap
命令基于字面值创建一个
ConfigMap:
kubectl create configmap sport --from-literal=sport=football
下面是一个 Deployment 清单示例,其中 ConfigMap sport
作为卷挂载到 Pod 的唯一容器中。
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-volume
labels:
app.kubernetes.io/name: configmap-volume
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: configmap-volume
template:
metadata:
labels:
app.kubernetes.io/name: configmap-volume
spec:
containers:
- name: alpine
image: alpine:3
command:
- /bin/sh
- -c
- while true; do echo "$(date) My preferred sport is $(cat /etc/config/sport)";
sleep 10; done;
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: sport
创建此 Deployment:
kubectl apply -f https://k8s.io/examples/deployments/deployment-with-configmap-as-volume.yaml
检查此 Deployment 的 Pod 以确保它们已就绪(通过选择算符进行匹配):
kubectl get pods --selector=app.kubernetes.io/name=configmap-volume
你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
configmap-volume-6b976dfdcf-qxvbm 1/1 Running 0 72s
configmap-volume-6b976dfdcf-skpvm 1/1 Running 0 72s
configmap-volume-6b976dfdcf-tbc6r 1/1 Running 0 72s
在运行这些 Pod 之一的每个节点上,kubelet 获取该 ConfigMap 的数据,并将其转换为本地卷中的文件。 然后,kubelet 按照 Pod 模板中指定的方式将该卷挂载到容器中。 在该容器中运行的代码从文件中加载信息,并使用它将报告打印到标准输出。 你可以通过查看该 Deployment 中其中一个 Pod 的日志来检查此报告:
# 选择一个属于该 Deployment 的 Pod,并查看其日志
kubectl logs deployments/configmap-volume
你应该会看到类似以下的输出:
Found 3 pods, using pod/configmap-volume-76d9c5678f-x5rgj
Thu Jan 4 14:06:46 UTC 2024 My preferred sport is football
Thu Jan 4 14:06:56 UTC 2024 My preferred sport is football
Thu Jan 4 14:07:06 UTC 2024 My preferred sport is football
Thu Jan 4 14:07:16 UTC 2024 My preferred sport is football
Thu Jan 4 14:07:26 UTC 2024 My preferred sport is football
编辑 ConfigMap:
kubectl edit configmap sport
在出现的编辑器中,将键 sport
的值从 football
变更为 cricket
。
保存你的变更。kubectl 工具会相应地更新 ConfigMap(如果报错,请重试)。
以下是你编辑后该清单可能的样子:
apiVersion: v1
data:
sport: cricket
kind: ConfigMap
# 你可以保留现有的 metadata 不变。
# 你将看到的值与本例的值不会完全一样。
metadata:
creationTimestamp: "2024-01-04T14:05:06Z"
name: sport
namespace: default
resourceVersion: "1743935"
uid: 024ee001-fe72-487e-872e-34d6464a8a23
你应该会看到以下输出:
configmap/sport edited
查看属于此 Deployment 的 Pod 之一的日志(并跟踪最新写入的条目):
kubectl logs deployments/configmap-volume --follow
几秒钟后,你应该会看到日志输出中的如下变化:
Thu Jan 4 14:11:36 UTC 2024 My preferred sport is football
Thu Jan 4 14:11:46 UTC 2024 My preferred sport is football
Thu Jan 4 14:11:56 UTC 2024 My preferred sport is football
Thu Jan 4 14:12:06 UTC 2024 My preferred sport is cricket
Thu Jan 4 14:12:16 UTC 2024 My preferred sport is cricket
当你有一个 ConfigMap 通过 configMap
卷或 projected
卷映射到运行中的 Pod,
并且你更新了该 ConfigMap 时,运行中的 Pod 几乎会立即更新。
但是,你的应用只有在编写为轮询变更或监视文件更新时才能看到变更。
启动时一次性加载其配置的应用将不会注意到变更。
说明:
从更新 ConfigMap 的那一刻到将新的键投射到 Pod 的那一刻,整个延迟可能与 kubelet 同步周期相同。 另请参阅挂载的 ConfigMap 会被自动更新。
通过 ConfigMap 更新 Pod 的环境变量
使用 kubectl create configmap
命令基于字面值创建一个
ConfigMap:
kubectl create configmap fruits --from-literal=fruits=apples
下面是一个 Deployment 清单的示例,包含一个通过 ConfigMap fruits
配置的环境变量。
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-env-var
labels:
app.kubernetes.io/name: configmap-env-var
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: configmap-env-var
template:
metadata:
labels:
app.kubernetes.io/name: configmap-env-var
spec:
containers:
- name: alpine
image: alpine:3
env:
- name: FRUITS
valueFrom:
configMapKeyRef:
key: fruits
name: fruits
command:
- /bin/sh
- -c
- while true; do echo "$(date) The basket is full of $FRUITS";
sleep 10; done;
ports:
- containerPort: 80
创建此 Deployment:
kubectl apply -f https://k8s.io/examples/deployments/deployment-with-configmap-as-envvar.yaml
检查此 Deployment 的 Pod 以确保它们已就绪(通过选择算符进行匹配):
kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
configmap-env-var-59cfc64f7d-74d7z 1/1 Running 0 46s
configmap-env-var-59cfc64f7d-c4wmj 1/1 Running 0 46s
configmap-env-var-59cfc64f7d-dpr98 1/1 Running 0 46s
ConfigMap 中的键值对被配置为 Pod 容器中的环境变量。 通过查看属于该 Deployment 的某个 Pod 的日志来检查这一点。
kubectl logs deployment/configmap-env-var
你应该会看到类似以下的输出:
Found 3 pods, using pod/configmap-env-var-7c994f7769-l74nq
Thu Jan 4 16:07:06 UTC 2024 The basket is full of apples
Thu Jan 4 16:07:16 UTC 2024 The basket is full of apples
Thu Jan 4 16:07:26 UTC 2024 The basket is full of apples
编辑 ConfigMap:
kubectl edit configmap fruits
在出现的编辑器中,将键 fruits
的值从 apples
变更为 mangoes
。
保存你的变更。kubectl 工具会相应地更新 ConfigMap(如果报错,请重试)。
以下是你编辑后该清单可能的样子:
apiVersion: v1
data:
fruits: mangoes
kind: ConfigMap
# 你可以保留现有的 metadata 不变。
# 你将看到的值与本例的值不会完全一样。
metadata:
creationTimestamp: "2024-01-04T16:04:19Z"
name: fruits
namespace: default
resourceVersion: "1749472"
你应该看到以下输出:
configmap/fruits edited
查看此 Deployment 的日志,并观察几秒钟的输出:
# 如上所述,输出不会有变化
kubectl logs deployments/configmap-env-var --follow
请注意,即使你编辑了 ConfigMap,输出仍然没有变化:
Thu Jan 4 16:12:56 UTC 2024 The basket is full of apples
Thu Jan 4 16:13:06 UTC 2024 The basket is full of apples
Thu Jan 4 16:13:16 UTC 2024 The basket is full of apples
Thu Jan 4 16:13:26 UTC 2024 The basket is full of apples
说明:
尽管 ConfigMap 中的键的取值已经变更,Pod 中的环境变量仍然显示先前的值。 这是因为当源数据变更时,在 Pod 内运行的进程的环境变量不会被更新; 如果你想强制更新,需要让 Kubernetes 替换现有的 Pod。新 Pod 将使用更新的信息来运行。
你可以触发该替换。使用 kubectl rollout
为 Deployment 执行上线操作:
# 触发上线操作
kubectl rollout restart deployment configmap-env-var
# 等待上线操作完成
kubectl rollout status deployment configmap-env-var --watch=true
接下来,检查 Deployment:
kubectl get deployment configmap-env-var
你应该会看到类似以下的输出:
NAME READY UP-TO-DATE AVAILABLE AGE
configmap-env-var 3/3 3 3 12m
检查 Pod:
kubectl get pods --selector=app.kubernetes.io/name=configmap-env-var
上线操作会导致 Kubernetes 为 Deployment 新建一个 ReplicaSet; 这意味着现有的 Pod 最终会终止,并创建新的 Pod。几秒钟后,你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
configmap-env-var-6d94d89bf5-2ph2l 1/1 Running 0 13s
configmap-env-var-6d94d89bf5-74twx 1/1 Running 0 8s
configmap-env-var-6d94d89bf5-d5vx8 1/1 Running 0 11s
说明:
请等待旧的 Pod 完全终止后再进行下一步。
查看此 Deployment 中某个 Pod 的日志:
# 选择属于 Deployment 的一个 Pod,并查看其日志
kubectl logs deployment/configmap-env-var
你应该会看到类似以下的输出:
Found 3 pods, using pod/configmap-env-var-6d9ff89fb6-bzcf6
Thu Jan 4 16:30:35 UTC 2024 The basket is full of mangoes
Thu Jan 4 16:30:45 UTC 2024 The basket is full of mangoes
Thu Jan 4 16:30:55 UTC 2024 The basket is full of mangoes
这个场景演示了在 Pod 中如何更新从 ConfigMap 派生的环境变量。ConfigMap 值的变更在随后的上线操作期间被应用到 Pod。如果 Pod 由于其他原因(例如 Deployment 扩容)被创建, 那么新 Pod 也会使用最新的配置值; 如果你不触发上线操作,你可能会发现你的应用在运行过程中混用了新旧环境变量值。
在多容器 Pod 中通过 ConfigMap 更新配置
使用 kubectl create configmap
命令基于字面值创建一个
ConfigMap:
kubectl create configmap color --from-literal=color=red
下面是一个 Deployment 清单的示例,该 Deployment 管理一组 Pod,每个 Pod 有两个容器。
这两个容器共享一个 emptyDir
卷并使用此卷进行通信。第一个容器运行 Web 服务器(nginx
)。
在 Web 服务器容器中共享卷的挂载路径是 /usr/share/nginx/html
。
第二个辅助容器基于 alpine
,对于这个容器,emptyDir
卷被挂载在 /pod-data
。
辅助容器生成一个 HTML 文件,其内容基于 ConfigMap。Web 服务器容器通过 HTTP 提供此 HTML 文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-two-containers
labels:
app.kubernetes.io/name: configmap-two-containers
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: configmap-two-containers
template:
metadata:
labels:
app.kubernetes.io/name: configmap-two-containers
spec:
volumes:
- name: shared-data
emptyDir: {}
- name: config-volume
configMap:
name: color
containers:
- name: nginx
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
- name: alpine
image: alpine:3
volumeMounts:
- name: shared-data
mountPath: /pod-data
- name: config-volume
mountPath: /etc/config
command:
- /bin/sh
- -c
- while true; do echo "$(date) My preferred color is $(cat /etc/config/color)" > /pod-data/index.html;
sleep 10; done;
创建此 Deployment:
kubectl apply -f https://k8s.io/examples/deployments/deployment-with-configmap-two-containers.yaml
检查此 Deployment 的 Pod 以确保它们已就绪(通过选择算符进行匹配):
kubectl get pods --selector=app.kubernetes.io/name=configmap-two-containers
你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
configmap-two-containers-565fb6d4f4-2xhxf 2/2 Running 0 20s
configmap-two-containers-565fb6d4f4-g5v4j 2/2 Running 0 20s
configmap-two-containers-565fb6d4f4-mzsmf 2/2 Running 0 20s
公开 Deployment(kubectl
工具会为你创建 Service):
kubectl expose deployment configmap-two-containers --name=configmap-service --port=8080 --target-port=80
使用 kubectl
转发端口:
# 此命令将在后台运行
kubectl port-forward service/configmap-service 8080:8080 &
访问服务:
curl http://localhost:8080
你应该会看到类似以下的输出:
Fri Jan 5 08:08:22 UTC 2024 My preferred color is red
编辑 ConfigMap:
kubectl edit configmap color
在出现的编辑器中,将键 color
的值从 red
变更为 blue
。
保存你的变更。kubectl 工具会相应地更新 ConfigMap(如果报错,请重试)。
以下是你编辑后该清单可能的样子:
apiVersion: v1
data:
color: blue
kind: ConfigMap
# 你可以保留现有的 metadata 不变。
# 你将看到的值与本例的值不会完全一样。
metadata:
creationTimestamp: "2024-01-05T08:12:05Z"
name: color
namespace: configmap
resourceVersion: "1801272"
uid: 80d33e4a-cbb4-4bc9-ba8c-544c68e425d6
循环访问服务 URL 几秒钟。
# 当你满意时可以取消此操作 (Ctrl-C)
while true; do curl --connect-timeout 7.5 http://localhost:8080; sleep 10; done
你应该会看到如下的输出变化:
Fri Jan 5 08:14:00 UTC 2024 My preferred color is red
Fri Jan 5 08:14:02 UTC 2024 My preferred color is red
Fri Jan 5 08:14:20 UTC 2024 My preferred color is red
Fri Jan 5 08:14:22 UTC 2024 My preferred color is red
Fri Jan 5 08:14:32 UTC 2024 My preferred color is blue
Fri Jan 5 08:14:43 UTC 2024 My preferred color is blue
Fri Jan 5 08:15:00 UTC 2024 My preferred color is blue
在包含边车容器的 Pod 中通过 ConfigMap 更新配置
要重现上述场景,可以使用边车容器作为辅助容器来写入 HTML 文件。
由于边车容器在概念上是一个 Init 容器,因此保证会在主要 Web 服务器容器启动之前启动。
这确保了当 Web 服务器准备好提供服务时,HTML 文件始终可用。
请参阅启用边车容器以使用此特性。
如果你从前一个场景继续操作,你可以在此场景中重用名为 color
的 ConfigMap。
如果你是独立执行此场景,请使用 kubectl create configmap
命令基于字面值创建一个
ConfigMap:
kubectl create configmap color --from-literal=color=blue
以下是一个 Deployment 清单示例,该 Deployment 管理一组 Pod,每个 Pod 有一个主容器和一个边车容器。
这两个容器共享一个 emptyDir
卷并使用此卷来通信。主容器运行 Web 服务器(NGINX)。
在 Web 服务器容器中共享卷的挂载路径是 /usr/share/nginx/html
。
第二个容器是基于 Alpine Linux 作为辅助容器的边车容器。对于这个辅助容器,emptyDir
卷被挂载在 /pod-data
。
边车容器写入一个 HTML 文件,其内容基于 ConfigMap。Web 服务器容器通过 HTTP 提供此 HTML 文件。
apiVersion: apps/v1
kind: Deployment
metadata:
name: configmap-sidecar-container
labels:
app.kubernetes.io/name: configmap-sidecar-container
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: configmap-sidecar-container
template:
metadata:
labels:
app.kubernetes.io/name: configmap-sidecar-container
spec:
volumes:
- name: shared-data
emptyDir: {}
- name: config-volume
configMap:
name: color
containers:
- name: nginx
image: nginx
volumeMounts:
- name: shared-data
mountPath: /usr/share/nginx/html
initContainers:
- name: alpine
image: alpine:3
restartPolicy: Always
volumeMounts:
- name: shared-data
mountPath: /pod-data
- name: config-volume
mountPath: /etc/config
command:
- /bin/sh
- -c
- while true; do echo "$(date) My preferred color is $(cat /etc/config/color)" > /pod-data/index.html;
sleep 10; done;
创建此 Deployment:
kubectl apply -f https://k8s.io/examples/deployments/deployment-with-configmap-and-sidecar-container.yaml
检查此 Deployment 的 Pod 以确保它们已就绪(通过选择算符进行匹配):
kubectl get pods --selector=app.kubernetes.io/name=configmap-sidecar-container
你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
configmap-sidecar-container-5fb59f558b-87rp7 2/2 Running 0 94s
configmap-sidecar-container-5fb59f558b-ccs7s 2/2 Running 0 94s
configmap-sidecar-container-5fb59f558b-wnmgk 2/2 Running 0 94s
公开 Deployment(kubectl
工具会为你创建一个 Service):
kubectl expose deployment configmap-sidecar-container --name=configmap-sidecar-service --port=8081 --target-port=80
使用 kubectl
转发端口:
# 此命令将在后台运行
kubectl port-forward service/configmap-sidecar-service 8081:80 &
访问服务:
curl http://localhost:8081
你应该看到类似以下的输出:
Sat Feb 17 13:09:05 UTC 2024 My preferred color is blue
编辑 ConfigMap:
kubectl edit configmap color
在出现的编辑器中,将键 color
的值从 blue
变更为 green
。
保存你的变更。kubectl 工具会相应地更新 ConfigMap(如果报错,请重试)。
以下是你编辑后该清单可能的样子:
apiVersion: v1
data:
color: green
kind: ConfigMap
# 你可以保留现有的 metadata 不变。
# 你将看到的值与本例的值不会完全一样。
metadata:
creationTimestamp: "2024-02-17T12:20:30Z"
name: color
namespace: default
resourceVersion: "1054"
uid: e40bb34c-58df-4280-8bea-6ed16edccfaa
循环访问服务 URL 几秒钟。
# 当你满意时可以取消此操作 (Ctrl-C)
while true; do curl --connect-timeout 7.5 http://localhost:8081; sleep 10; done
你应该会看到如下的输出变化:
Sat Feb 17 13:12:35 UTC 2024 My preferred color is blue
Sat Feb 17 13:12:45 UTC 2024 My preferred color is blue
Sat Feb 17 13:12:55 UTC 2024 My preferred color is blue
Sat Feb 17 13:13:05 UTC 2024 My preferred color is blue
Sat Feb 17 13:13:15 UTC 2024 My preferred color is green
Sat Feb 17 13:13:25 UTC 2024 My preferred color is green
Sat Feb 17 13:13:35 UTC 2024 My preferred color is green
通过作为卷挂载的不可变 ConfigMap 更新配置
说明:
不可变 ConfigMap 专门用于恒定且预期不会随时间变化的配置。 将 ConfigMap 标记为不可变可以提高性能,因为 kubelet 不会监视变更。
如果你确实需要进行变更,你应计划:
- 变更 ConfigMap 的名称,并转而运行引用新名称的 Pod
- 替换集群中之前运行使用旧值的 Pod 的所有节点
- 在任何之前加载过旧 ConfigMap 的节点上重新启动 kubelet
以下是一个不可变 ConfigMap的示例清单。
apiVersion: v1
data:
company_name: "ACME, Inc." # 虚构的公司名称
kind: ConfigMap
immutable: true
metadata:
name: company-name-20150801
创建不可变 ConfigMap:
kubectl apply -f https://k8s.io/examples/configmap/immutable-configmap.yaml
下面是一个 Deployment 清单示例,其中不可变 ConfigMap company-name-20150801
作为卷挂载到 Pod 的唯一容器中。
apiVersion: apps/v1
kind: Deployment
metadata:
name: immutable-configmap-volume
labels:
app.kubernetes.io/name: immutable-configmap-volume
spec:
replicas: 3
selector:
matchLabels:
app.kubernetes.io/name: immutable-configmap-volume
template:
metadata:
labels:
app.kubernetes.io/name: immutable-configmap-volume
spec:
containers:
- name: alpine
image: alpine:3
command:
- /bin/sh
- -c
- while true; do echo "$(date) The name of the company is $(cat /etc/config/company_name)";
sleep 10; done;
ports:
- containerPort: 80
volumeMounts:
- name: config-volume
mountPath: /etc/config
volumes:
- name: config-volume
configMap:
name: company-name-20150801
创建此 Deployment:
kubectl apply -f https://k8s.io/examples/deployments/deployment-with-immutable-configmap-as-volume.yaml
检查此 Deployment 的 Pod 以确保它们已就绪(通过选择算符进行匹配):
kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
你应该看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
immutable-configmap-volume-78b6fbff95-5gsfh 1/1 Running 0 62s
immutable-configmap-volume-78b6fbff95-7vcj4 1/1 Running 0 62s
immutable-configmap-volume-78b6fbff95-vdslm 1/1 Running 0 62s
Pod 的容器引用 ConfigMap 中所定义的数据,并使用它将报告打印到标准输出。 你可以通过查看 Deployment 中某个 Pod 的日志来检查此报告:
# 选择属于该 Deployment 的一个 Pod,并查看其日志
kubectl logs deployments/immutable-configmap-volume
你应该会看到类似以下的输出:
Found 3 pods, using pod/immutable-configmap-volume-78b6fbff95-5gsfh
Wed Mar 20 03:52:34 UTC 2024 The name of the company is ACME, Inc.
Wed Mar 20 03:52:44 UTC 2024 The name of the company is ACME, Inc.
Wed Mar 20 03:52:54 UTC 2024 The name of the company is ACME, Inc.
说明:
一旦 ConfigMap 被标记为不可变,就无法撤销此变更,也无法修改 data
或 binaryData
字段的内容。
为了修改使用此配置的 Pod 的行为,你需要创建一个新的不可变 ConfigMap,并编辑 Deployment
以定义一个稍有不同的 Pod 模板,引用新的 ConfigMap。
通过使用下面所示的清单创建一个新的不可变 ConfigMap:
apiVersion: v1
data:
company_name: "Fiktivesunternehmen GmbH" # 虚构的公司名称
kind: ConfigMap
immutable: true
metadata:
name: company-name-20240312
kubectl apply -f https://k8s.io/examples/configmap/new-immutable-configmap.yaml
你应该看到类似以下的输出:
configmap/company-name-20240312 created
检查新建的 ConfigMap:
kubectl get configmap
你应该看到输出会同时显示新旧 ConfigMap:
NAME DATA AGE
company-name-20150801 1 22m
company-name-20240312 1 24s
修改 Deployment 以引用新的 ConfigMap。
编辑 Deployment:
kubectl edit deployment immutable-configmap-volume
在出现的编辑器中,更新现有的卷定义以使用新的 ConfigMap。
volumes:
- configMap:
defaultMode: 420
name: company-name-20240312 # 更新此字段
name: config-volume
你应该看到以下输出:
deployment.apps/immutable-configmap-volume edited
这将触发一次上线操作。等待所有先前的 Pod 终止并且新的 Pod 处于就绪状态。
监控 Pod 的状态:
kubectl get pods --selector=app.kubernetes.io/name=immutable-configmap-volume
NAME READY STATUS RESTARTS AGE
immutable-configmap-volume-5fdb88fcc8-29v8n 1/1 Running 0 13s
immutable-configmap-volume-5fdb88fcc8-52ddd 1/1 Running 0 14s
immutable-configmap-volume-5fdb88fcc8-n5jx4 1/1 Running 0 15s
immutable-configmap-volume-78b6fbff95-5gsfh 1/1 Terminating 0 32m
immutable-configmap-volume-78b6fbff95-7vcj4 1/1 Terminating 0 32m
immutable-configmap-volume-78b6fbff95-vdslm 1/1 Terminating 0 32m
最终,你应该会看到类似以下的输出:
NAME READY STATUS RESTARTS AGE
immutable-configmap-volume-5fdb88fcc8-29v8n 1/1 Running 0 43s
immutable-configmap-volume-5fdb88fcc8-52ddd 1/1 Running 0 44s
immutable-configmap-volume-5fdb88fcc8-n5jx4 1/1 Running 0 45s
查看此 Deployment 中某个 Pod 的日志:
# 选择属于此 Deployment 的一个 Pod,并查看其日志
kubectl logs deployment/immutable-configmap-volume
你应该会看到类似下面的输出:
Found 3 pods, using pod/immutable-configmap-volume-5fdb88fcc8-n5jx4
Wed Mar 20 04:24:17 UTC 2024 The name of the company is Fiktivesunternehmen GmbH
Wed Mar 20 04:24:27 UTC 2024 The name of the company is Fiktivesunternehmen GmbH
Wed Mar 20 04:24:37 UTC 2024 The name of the company is Fiktivesunternehmen GmbH
建议一旦所有 Deployment 都迁移到使用新的不可变 ConfigMap,删除旧的 ConfigMap。
kubectl delete configmap company-name-20150801
总结
在 Pod 上作为卷挂载的 ConfigMap 所发生的变更将在后续的 kubelet 同步后无缝生效。
配置为 Pod 环境变量的 ConfigMap 所发生变更将在后续的 Pod 上线操作后生效。
一旦 ConfigMap 被标记为不可变,就无法撤销此变更(你不能将不可变的 ConfigMap 改为可变),
并且你也不能对 data
或 binaryData
字段的内容进行任何变更。你可以删除并重新创建 ConfigMap,
或者你可以创建一个新的不同的 ConfigMap。当你删除 ConfigMap 时,
运行中的容器及其 Pod 将保持对引用了现有 ConfigMap 的任何卷的挂载点。
清理现场
终止正在运行的 kubectl port-forward
命令。
删除以上教程中所创建的资源:
kubectl delete deployment configmap-volume configmap-env-var configmap-two-containers configmap-sidecar-container immutable-configmap-volume
kubectl delete service configmap-service configmap-sidecar-service
kubectl delete configmap sport fruits color company-name-20240312
kubectl delete configmap company-name-20150801 # 如果在任务执行期间未被处理
3 - 使用 ConfigMap 来配置 Redis
这篇文档基于配置 Pod 以使用 ConfigMap 这个任务,提供了一个使用 ConfigMap 来配置 Redis 的真实案例。
教程目标
- 使用 Redis 配置的值创建一个 ConfigMap
- 创建一个 Redis Pod,挂载并使用创建的 ConfigMap
- 验证配置已经被正确应用
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
要获知版本信息,请输入kubectl version
.
- 此页面上显示的示例适用于
kubectl
1.14 及以上的版本。 - 理解配置 Pod 以使用 ConfigMap。
真实世界的案例:使用 ConfigMap 来配置 Redis
按照下面的步骤,使用 ConfigMap 中的数据来配置 Redis 缓存。
首先创建一个配置模块为空的 ConfigMap:
cat <<EOF >./example-redis-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: example-redis-config
data:
redis-config: ""
EOF
应用上面创建的 ConfigMap 以及 Redis Pod 清单:
kubectl apply -f example-redis-config.yaml
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml
检查 Redis pod 清单的内容,并注意以下几点:
- 由
spec.volumes[1]
创建一个名为config
的卷。 spec.volumes[1].configMap.items[0]
下的key
和path
会将来自example-redis-config
ConfigMap 中的redis-config
键公开在config
卷上一个名为redis.conf
的文件中。- 然后
config
卷被spec.containers[0].volumeMounts[1]
挂载在/redis-master
。
这样做的最终效果是将上面 example-redis-config
配置中 data.redis-config
的数据作为 Pod 中的 /redis-master/redis.conf
公开。
apiVersion: v1
kind: Pod
metadata:
name: redis
spec:
containers:
- name: redis
image: redis:5.0.4
command:
- redis-server
- "/redis-master/redis.conf"
env:
- name: MASTER
value: "true"
ports:
- containerPort: 6379
resources:
limits:
cpu: "0.1"
volumeMounts:
- mountPath: /redis-master-data
name: data
- mountPath: /redis-master
name: config
volumes:
- name: data
emptyDir: {}
- name: config
configMap:
name: example-redis-config
items:
- key: redis-config
path: redis.conf
检查创建的对象:
kubectl get pod/redis configmap/example-redis-config
你应该可以看到以下输出:
NAME READY STATUS RESTARTS AGE
pod/redis 1/1 Running 0 8s
NAME DATA AGE
configmap/example-redis-config 1 14s
回顾一下,我们在 example-redis-config
ConfigMap 保留了空的 redis-config
键:
kubectl describe configmap/example-redis-config
你应该可以看到一个空的 redis-config
键:
Name: example-redis-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
redis-config:
使用 kubectl exec
进入 pod,运行 redis-cli
工具检查当前配置:
kubectl exec -it redis -- redis-cli
查看 maxmemory
:
127.0.0.1:6379> CONFIG GET maxmemory
它应该显示默认值 0:
1) "maxmemory"
2) "0"
同样,查看 maxmemory-policy
:
127.0.0.1:6379> CONFIG GET maxmemory-policy
它也应该显示默认值 noeviction
:
1) "maxmemory-policy"
2) "noeviction"
现在,向 example-redis-config
ConfigMap 添加一些配置:
apiVersion: v1
kind: ConfigMap
metadata:
name: example-redis-config
data:
redis-config: |
maxmemory 2mb
maxmemory-policy allkeys-lru
应用更新的 ConfigMap:
kubectl apply -f example-redis-config.yaml
确认 ConfigMap 已更新:
kubectl describe configmap/example-redis-config
你应该可以看到我们刚刚添加的配置:
Name: example-redis-config
Namespace: default
Labels: <none>
Annotations: <none>
Data
====
redis-config:
----
maxmemory 2mb
maxmemory-policy allkeys-lru
通过 kubectl exec
使用 redis-cli
再次检查 Redis Pod,查看是否已应用配置:
kubectl exec -it redis -- redis-cli
查看 maxmemory
:
127.0.0.1:6379> CONFIG GET maxmemory
它保持默认值 0:
1) "maxmemory"
2) "0"
同样,maxmemory-policy
保留为默认设置 noeviction
:
127.0.0.1:6379> CONFIG GET maxmemory-policy
返回:
1) "maxmemory-policy"
2) "noeviction"
配置值未更改,因为需要重新启动 Pod 才能从关联的 ConfigMap 中获取更新的值。 让我们删除并重新创建 Pod:
kubectl delete pod redis
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/main/content/en/examples/pods/config/redis-pod.yaml
现在,最后一次重新检查配置值:
kubectl exec -it redis -- redis-cli
查看 maxmemory
:
127.0.0.1:6379> CONFIG GET maxmemory
现在,它应该返回更新后的值 2097152:
1) "maxmemory"
2) "2097152"
同样,maxmemory-policy
也已更新:
127.0.0.1:6379> CONFIG GET maxmemory-policy
现在它反映了期望值 allkeys-lru
:
1) "maxmemory-policy"
2) "allkeys-lru"
删除创建的资源,清理你的工作:
kubectl delete pod/redis configmap/example-redis-config
接下来
- 了解有关 ConfigMap 的更多信息。
- 学习通过 ConfigMap 更新配置的示例。