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
在本教程结束时,你将了解如何变更运行中应用的配置。 本教程以 alpinenginx 镜像为例。

准备开始

你必须拥有一个 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 的环境变量

使用 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

你可以触发该替换。使用 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

查看此 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的示例清单。

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:

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 改为可变), 并且你也不能对 databinaryData 字段的内容进行任何变更。你可以删除并重新创建 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.

真实世界的案例:使用 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] 下的 keypath 会将来自 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

接下来