1 - 通过 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 # 如果在任务执行期间未被处理
2 - 使用 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 更新配置的示例。
3 - 使用边车(Sidecar)容器
本文适用于使用新的内置边车容器特性的用户。
边车容器并不是一个新概念,正如在博客文章中所提到的那样。 Kubernetes 允许在一个 Pod 中运行多个容器来实现这一概念。然而,作为一个普通容器运行边车容器存在许多限制, 这些限制通过新的内置边车容器支持得到了解决。
Kubernetes v1.29 [beta]
(enabled by default: true)
教程目标
- 理解边车容器的需求
- 能够排查边车容器的问题
- 了解如何"注入"边车容器到任意的工作负载中
准备开始
你必须拥有一个 Kubernetes 的集群,且必须配置 kubectl 命令行工具让其与你的集群通信。 建议运行本教程的集群至少有两个节点,且这两个节点不能作为控制平面主机。 如果你还没有集群,你可以通过 Minikube 构建一个你自己的集群,或者你可以使用下面的 Kubernetes 练习环境之一:
你的 Kubernetes 服务器版本必须不低于版本 1.29. 要获知版本信息,请输入kubectl version
.
边车容器概述
边车容器是与主应用程序容器在同一 Pod 内一起运行的辅助容器。这些容器通过提供额外的服务或功能(如日志记录、监控、安全或数据同步)来增强或扩展主应用容器的功能, 而无需直接修改主应用程序代码。你可以在边车容器概念页面中阅读更多相关内容。
边车容器的概念并不新鲜,有许多不同的实现方式。除了你(定义 Pod 的人)希望运行的边车容器外, 一些插件也会在 Pod 开始运行之前对其进行修改, 以添加额外的边车容器。这些额外边车容器的注入机制通常是变更 Webhook(Mutating Webhook)。 例如,服务网格插件可能会注入一个配置双向 TLS(Mutual TLS)和传输中加密的边车容器。
虽然边车容器的概念并不新鲜,但 Kubernetes 对这一特性的原生实现却是新的。 与每一项新特性一样,采用这一特性可能会带来某些挑战。
本教程探讨了终端用户和边车容器作者可能遇到的挑战及其解决方案。
内置边车容器的优势
使用 Kubernetes 对边车容器的原生支持可以带来以下几个好处:
- 你可以配置原生边车容器在 Init 容器之前启动。
- 内置边车容器可以编写为确保它们最后终止。一旦所有常规容器完成并终止,边车容器将接收到
SIGTERM
信号。 如果边车容器未能体面关闭,系统将使用SIGKILL
信号终止它。 - 在 Job 中,当 Pod 配置
restartPolicy: OnFailure
或restartPolicy: Never
时, 原生边车容器不会阻止 Pod 完成。而对于传统边车容器,需要特别处理这种情况。 - 同样在 Job 中,即使 Pod 的
restartPolicy: Never
时常规容器不会重启, 内置边车容器仍会在完成后继续重启。
更多详情请参见与 Init 容器的区别。
采用内置边车容器
从 Kubernetes 1.29 版本开始,SidecarContainers
特性门控处于 Beta 阶段,
并默认启用。某些集群可能禁用了此特性,或者安装了与该特性不兼容的软件。
当这种情况发生时,Pod 可能会被拒绝,或者边车容器可能阻止 Pod 启动,导致 Pod 无法使用。 这种情况下很容易检测到问题,因为 Pod 会卡在初始化阶段。然而,通常不清楚是什么原因导致了问题。
以下是在使用边车容器处理工作负载时可以考虑的因素和排查步骤。
确保特性门控已启用
首先,确保 API 服务器和节点都在 Kubernetes v1.29 及更高版本上运行。 如果节点运行的是早期版本且未启用该特性,集群中的该特性将无法正常工作。
注意
此特性可以在 1.28 版本的节点上启用。然而,内置边车容器的终止行为在 1.28 版本中有所不同, 不建议将边车的行为调整为 1.28 中的行为。但是,如果唯一的关注点是启动顺序, 上述陈述可以改为:运行 1.28 版本并启用了特性门控的节点。
你应该确保控制平面内的 API 服务器和所有节点都启用了特性门控。
一种检查特性门控是否启用的方法是运行如下命令:
-
对于 API 服务器:
kubectl get --raw /metrics | grep kubernetes_feature_enabled | grep SidecarContainers
-
对于单个节点:
kubectl get --raw /api/v1/nodes/<node-name>/proxy/metrics | grep kubernetes_feature_enabled | grep SidecarContainers
如果你看到类似这样的内容:
kubernetes_feature_enabled{name="SidecarContainers",stage="BETA"} 1
表示该特性已启用。
检查第三方工具和变更 Webhook
如果你在验证特性时遇到问题,这可能表明某个第三方工具或变更 Webhook 出现了问题。
当 SidecarContainers
特性门控启用后,Pod 在其 API 中会新增一个字段。
某些工具或变更 Webhook 可能是基于早期版本的 Kubernetes API 构建的。
如果工具使用各种修补策略将未知字段原样传递,这不会有问题。然而,有些工具会删除未知字段; 如果你使用的是这些工具,必须使用 v1.28+ 版本的 Kubernetes API 客户端代码重新编译它们。
检查这一点的方法是使用 kubectl describe pod
命令查看已通过变更准入的 Pod。
如果任何工具删除了新字段(如 restartPolicy: Always
),你将不会在命令输出中看到它。
如果你遇到了此类问题,请告知工具或 Webhook 的作者使用修补策略来修改对象,而不是进行完整的对象更新。
注意
变更 Webhook 可能会根据某些条件更新 Pod。 因此,边车容器可能对某些 Pod 有效,但对其他 Pod 无效。
边车的自动注入
如果你使用的是自动注入边车的软件,可以采取几种策略来确保能够使用原生边车容器。 所有这些策略通常都是你可以选择的选项,以决定注入边车的 Pod 是否会落在支持该特性的节点上。
例如,可以参考 Istio 社区中的这次讨论。 讨论中探讨了以下选项:
- 标记支持边车的节点上的 Pod。你可以使用节点标签和节点亲和性来标记支持边车容器的节点以及落在这些节点上的 Pod。
- 注入时检查节点兼容性。在边车注入过程中,可以使用以下策略来检查节点兼容性:
- 查询节点版本并假设版本 1.29+ 上启用了特性门控。
- 查询节点 Prometheus 指标并检查特性启用状态。
- 假设节点与 API 服务器的版本差异在支持的版本范围内。
- 可能还有其他自定义方法来检测节点兼容性。
- 开发通用边车注入器(Sidecar Injector)。通用边车注入器的想法是在注入一个普通容器的同时注入一个原生边车容器,并在运行时决定哪个容器会生效。
通用边车注入器虽然浪费资源(因为它会两次计算请求量),但在某些特殊情况下可以视为可行的解决方案。
- 一种方法是在原生边车容器启动时检测节点版本,如果不支持边车特性则立即退出。
- 考虑运行时特性检测设计:
- 定义一个空目录(Empty Dir)以便容器之间通信。
- 注入一个 Init 容器,我们称之为
NativeSidecar
,并设置restartPolicy=Always
。 NativeSidecar
必须在空目录中写入一个文件,表示首次运行并立即退出,退出码为0
。
- `NativeSidecar` 在重启时(当支持原生边车时)检查空目录中是否已存在该文件,并进行更改 —— 表示已支持原生边车容器并正在运行。
- 注入一个普通容器,我们称之为
OldWaySidecar
。 OldWaySidecar
启动时检查空目录中是否存在文件。- 如果文件表示
NativeSidecar
未运行,则假设边特性不支持,并按边车的方式工作。 - 如果文件表示
NativeSidecar
正在运行,则根据 Pod 的restartPolicy
决定行为: - 如果 Pod 的
restartPolicy=Always
,则不做任何操作并永远休眠。 - 如果 Pod 的
restartPolicy!=Always
,则立即退出,退出码为0
。
接下来
- 了解有关边车容器的更多信息。