Tim Wang Tech Blog

如何在 Kubernetes 中有效使用 Secret、ConfigMap 和 Lease:详解及示例

简介

在 Kubernetes (k8s) 中,SecretConfigMapLease 是三种关键资源对象,它们分别用于处理敏感信息、配置数据和分布式系统中的 leader 选举。本文将详细介绍这三种对象,包括它们的使用场景、优缺点以及示例。

Secret

推荐使用场景

  • 存储和管理敏感信息,例如密码、OAuth 令牌、SSH 密钥等。
  • 注入到容器中的环境变量或挂载到文件系统,以便应用程序安全地访问敏感数据。

优势

  • 安全性:避免在配置文件中以明文形式存储敏感信息。
  • 灵活性:支持将 Secret 以多种方式提供给 Pod,例如环境变量或卷。
  • 加密存储:Kubernetes 支持配置加密存储 Secret 对象。

劣势

  • 配置复杂性:需要适当配置 RBAC 以确保 Secret 的安全访问。
  • 管理难度:在大规模集群中管理大量 Secret 可能较为复杂。

示例

创建一个 Secret 对象:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  # Base64 编码的 'admin'
  username: YWRtaW4=
  # Base64 编码的 '1f2d1e2e67df'
  password: MWYyZDFlMmU2N2Rm

在 Pod 中使用 Secret

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: mycontainer
    image: myimage
    env:
    - name: USERNAME
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: username
    - name: PASSWORD
      valueFrom:
        secretKeyRef:
          name: mysecret
          key: password

使用 Golang 访问 Secret

package main

import (
	"context"
	"encoding/base64"
	"fmt"
	"log"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
	config, err := rest.InClusterConfig()
	if err != nil {
		log.Fatalf("Error creating in-cluster config: %v", err)
	}
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error creating Kubernetes client: %v", err)
	}

	secret, err := clientset.CoreV1().Secrets("default").Get(context.TODO(), "mysecret", metav1.GetOptions{})
	if err != nil {
		log.Fatalf("Error getting Secret: %v", err)
	}

	for key, value := range secret.Data {
		decodedValue, err := base64.StdEncoding.DecodeString(string(value))
		if err != nil {
			log.Fatalf("Error decoding secret value: %v", err)
		}
		fmt.Printf("Secret key %s value: %s\n", key, string(decodedValue))
	}
}

ConfigMap

推荐使用场景

  • 存储非敏感配置信息:如配置文件、命令行参数、环境变量等。
  • 动态更新应用配置:通过更新 ConfigMap 对象,可以动态地改变应用程序的配置。

优势

  • 解耦配置和代码:可以将配置从应用程序代码中分离出来,使得应用程序更容易管理和部署。
  • 动态配置:支持在不重新构建镜像或重启 Pod 的情况下更新配置。
  • 灵活性:可以以文件或环境变量的形式将配置提供给应用程序。

劣势

  • 非敏感数据ConfigMap 不适合存储敏感信息,因为数据是明文存储的。
  • 潜在的配置更新延迟:某些情况下,配置更新不会立即生效,尤其是在使用卷的情况下。

例子

创建一个 ConfigMap 对象:

apiVersion: v1
kind: ConfigMap
metadata:
  name: myconfigmap
data:
  config.properties: |
    key1=value1
    key2=value2    

使用 ConfigMap 的 Pod 配置:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: mycontainer
      image: myimage
      volumeMounts:
        - name: config-volume
          mountPath: /etc/config
  volumes:
    - name: config-volume
      configMap:
        name: myconfigmap

使用代码访问ConfigMap的例子

package main

import (
	"context"
	"fmt"
	"log"

	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func main() {
	// 创建一个 Kubernetes 客户端配置
	config, err := rest.InClusterConfig()
	if err != nil {
		log.Fatalf("Error creating in-cluster config: %v", err)
	}

	// 创建一个 Kubernetes 客户端
	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		log.Fatalf("Error creating Kubernetes client: %v", err)
	}

	// 获取指定命名空间中的 ConfigMap
	namespace := "default"
	configMapName := "myconfigmap"
	configMap, err := clientset.CoreV1().ConfigMaps(namespace).Get(context.TODO(), configMapName, metav1.GetOptions{})
	if err != nil {
		log.Fatalf("Error getting ConfigMap: %v", err)
	}

	// 输出 ConfigMap 的数据
	fmt.Printf("ConfigMap %s data: %v\n", configMapName, configMap.Data)
}

Lease

引入时间

  • Lease 对象在 Kubernetes 1.13 版本中引入,作为 leader 选举机制的一部分。

推荐使用场景

  • Leader 选举:在分布式系统中进行 leader 选举,确保集群中只有一个活动的 leader 节点。
  • 协调共享资源的访问:例如在多个副本中协调任务的执行,确保只有一个副本在执行某些关键任务。

优势

  • 简化 Leader 选举:提供了一个原生的机制来进行 leader 选举,简化了在分布式系统中的实现。
  • 可靠性:使用 Kubernetes 内置的 API 和 etcd 存储,确保 Lease 信息的可靠性和一致性。
  • 集成性:与 Kubernetes 原生资源和控制器深度集成,方便使用和管理。

劣势

  • 复杂性:对于不熟悉分布式系统和 leader 选举机制的开发者,理解和正确使用 Lease 可能会有一定的学习曲线。
  • 额外开销:在使用 Lease 进行 leader 选举时,会有额外的 API 调用和 etcd 存储操作,可能会带来一定的性能开销。

例子

创建一个 Lease 对象:

apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
  name: mylease
  namespace: default
spec:
  holderIdentity: mypod
  leaseDurationSeconds: 30

使用 Lease 进行 leader 选举的示例代码(伪代码):

from kubernetes import client, config
import time

config.load_kube_config()
coordination_v1 = client.CoordinationV1Api()

def try_acquire_lease():
    lease = coordination_v1.read_namespaced_lease(name='mylease', namespace='default')
    if lease.spec.holderIdentity is None or lease.spec.leaseDurationSeconds < time.time() - lease.spec.acquireTime.timestamp():
        lease.spec.holderIdentity = 'mypod'
        lease.spec.acquireTime = client.V1MicroTime(datetime.datetime.utcnow())
        coordination_v1.replace_namespaced_lease(name='mylease', namespace='default', body=lease)
        return True
    return False

while not try_acquire_lease():
    time.sleep(5)

print("I am the leader now")

当然,以下是对这段话的优化版本:


通过上述示例,我们清晰地看到了 SecretConfigMapLease 在 Kubernetes 中的独特用途和配置方法。每种资源都设计有特定的应用场景:

  • Secret 用于安全地管理敏感信息,如密码和密钥,保护它们不被暴露。
  • ConfigMap 旨在存储配置数据,使得应用程序设置与代码分离,便于配置的动态更新。
  • Lease 提供了一种机制,用于在分布式环境中高效进行 Leader 选举和管理资源共享

根据不同的需求,选择恰当的资源对象,可以有效地管理和维护 Kubernetes 集群中的配置和状态。这不仅提升了集群的运维效率,也加强了安全性和可靠性。

Reference