emptyDir + initContainer实现ConfigMap的动态更新(K8s相关)

devtools/2024/10/22 10:35:16/

1. 絮絮叨叨

  • K8s部署服务时,一般都需要使用ConfigMap定义一些配置文件
  • 例如,部署分布式SQL引擎Presto,会在ConfigMap中定义coordinator、worker所需的配置文件
  • node.properties为例,node.environmentnode.data-dir的值将由Helm的values.yaml进行渲染
    data:node.properties: |# node.id由系统使用uuid自动赋值node.environment={{ .Values.environment }}node.data-dir={{ .Values.server.data_dir }}
    
  • 工作过程中,笔者接到了一个新的需求:node.id需要与podName保持一致,而非毫无规律的UUID
  • 如果是毫无规律的UUID,将nodeId与pod映射需要一个复杂的过程,笔者的做法一般是:
    1. 通过Presto的worker lists实现nodeId与pod IP的映射在这里插入图片描述
    2. 通过kubectl -n ${namespace} get pod -owide | grep ${IP}实现pod IP与pod的映射,从而最终实现nodeId与pod的映射(图片可能上下文不一致,忽略即可)在这里插入图片描述
  • 所谓的podName其实就是K8s中的metadata.name,也是kubectl -n ${namespace} get pod展示的NAME在这里插入图片描述

2. 一些前期调研

2.1 初始想法

拿到这个需求后,自己的想法很简单,以StatefulSet方式部署的coordinator为例:

  1. 更新node.properties的定义

    data:node.properties: |node.id=NODE_IDnode.environment={{ .Values.environment }}node.data-dir={{ .Values.server.data_dir }}
    
  2. 定义 POD_NAME env,其值为metadata.name

    env:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name
    
  3. 通过container中的command,获取env、使用sed命令更新node.properties中的NODE_ID关键字

    command: [ 'sh', '-c', 'sed -i "s/NODE_ID/$POD_NAME/g" /opt/presto/etc/node.properties' ]
    
  • 存在的问题: 服务部署失败,提示Read-only file system
    sed: couldn't open temporary file /opt/onequery/etc/sedQau48c: Read-only file system
    
  • 甚至尝试cat、更新后再写入文件,仍然失败
    # command
    (cat /opt/onequery/etc/node.properties | sed "s/NODE_ID/$POD_NAME/" > /opt/onequery/etc/node.properties)
    # 报错信息
    /bin/sh: 1: cannot create /opt/onequery/etc/node.properties: Read-only file system
    

2.2 ConfigMap能否智能地动态更新? —— No

  • 由于当时对Helm values.yaml如何实现ConfigMap的更新机制不了解,笔者期望ConfigMap能智能加载容器的环境变量,自己实现更新
    • 保持上面的env定义不变,将node.properties的定义修改成如下样式
      data:node.properties: |node.id=$(POD_NAME)node.environment={{ .Values.environment }}node.data-dir={{ .Values.server.data_dir }}
      
    • 期望ConfigMap像Shell脚本一样,动态加载env
  • 笔者试验后发现,node.properties中node.id=$(POD_NAME)原封不动、未被修改

2.3 学习官方文档 —— 未解

  • 阅读K8s官方文档,发现有关ConfigMap的文档,主要是描述如何定义、使用ConfigMap,并未提及如何动态更新ConfigMap
    • 《ConfigMaps》:如何定义ConfigMap,两种使用方式:
      • ConfigMap映射为文件,一般都是作为etc/config/目录下的配置文件
      • 基于ConfigMap定义环境变量env,ConfigMap中的key-value及时一个环境变量
    • 《Configure a Pod to Use a ConfigMap》中的使用描述更加详细

3. 终极解决方案:emptyDir + initContainer

  • 通过read-only file system这个错误薪资,笔者找到了类似问题的交流帖:
    • Error config Map volume mount read-only file system error
    • k8s 使用 statefulset 如何在 pod 内获取序号并设置到环境变量里
  • 最终,笔者使用emptyDir + initContainer,实现了ConfigMap的动态更新
  • PS: 最近在处理其他问题的过程中,笔者还发现了一个有完整实现的交流贴:How to get environment variable in configMap

3.1 具体实现方案

  1. 定义volumes
    volumes:# 加载原始的ConfigMap- name: config-template-volumeconfigMap:name: onequery-coordinator# 定义一个emtyDir- name: config-volumeemptyDir: {}
    
  2. 创建initContainer,在initContainer中实现nodeId的动态更新
    initContainers:- name: config-templateimage: "{{ .Values.Images.onequery }}"# 将ConfigMap拷贝到emptyDir映射的路径,并使用sed命令、基于环境变量动态修改NODE_ID为podNamecommand: [ 'sh', '-c', 'cp /etc/config-templates/* /opt/onequery/etc && (cat /etc/config-templates/node.properties | sed "s/NODE_ID/$POD_NAME/" > /opt/onequery/etc/node.properties)' ]volumeMounts:# 定义volumeMounts,分别将ConfigMap、emptyDir映射到container的指定路径- name: config-template-volumemountPath: /etc/config-templates- name: config-volumemountPath: /opt/onequery/etcenv:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name
    
  3. coordinator对应的container中,定义volumeMount、与initContainer保持一致(也可以不一致)。启动后的coordinator container,可以访问写入emptyDir的、动态更新后的node.properties文件
    volumeMounts:- name: config-volumemountPath: /opt/onequery/etc
    

4. 后记

4.1 关于emptyDir卷

  • K8s官方文档对emptyDir卷的描述可知:emptyDir的生命周期与pod一致,初始时为一个空目录,特别适合在pod的不同容器中共享文件
  • 上面的实现方案中,即使initContainer、coordinator container中emptyDir卷的挂载路径不同,也可以共同读写emptyDir中的文件

4.2 volumes vs volumeMounts

  • volumes(存储卷):是K8s中的一种资源,用于将持久化存储与Pod绑定,与Pod具有相同的生命周期
  • volumeMounts(挂载点):
    • 在容器描述符中定义volumeMount,可以将 volume 挂载到容器中的指定路径
    • 在容器中读写上述路径下的文件,实际就是读写持久化存储中相应路径下的文件
  • 举个例子,阐述二者之间的关系:
    • volumes定义如下,将log-path与宿主机的/data00/basic_log/svc_logs/presto目录绑定
      volumes:- name: log-pathhostPath:path: /data00/basic_log/svc_logs/presto/logtype: DirectoryOrCreate # 支持动态创建目录
      
    • 在container中定义volumeMount,使得容器中的/opt/data/var/log路径对应
      volumeMounts:- name: log-path # volume namemountPath: /opt/data/var/log# 将基于环境变量POD_NAME、在宿主机磁盘上动态创建子目录,最终coordinator-0在宿主机上的路径为/data00/basic_log/svc_logs/presto/log/coordinator/coordinator-0subPathExpr: coordinator/$(POD_NAME) 
      
    • coordinator运行起来后,会在/opt/data/var/log下生成launcher.log。相应地,其所在宿主机的/data00/basic_log/svc_logs/presto/log/coordinator/coordinator-0目录下将出现launcher.log文件
    • 在容器中对launcher.log的读写操作,实际就是在读写宿主机中对应目录的launcher.log

http://www.ppmy.cn/devtools/57047.html

相关文章

基于STM32+华为云IOT设计的智能冰箱(华为云IOT)

文章目录 一、前言1.1 项目介绍【1】项目开发背景【2】设计实现的功能【3】项目硬件模块组成【4】摘要 1.2 设计思路1.3 系统功能总结1.4 开发工具的选择【1】设备端开发【2】上位机开发 二、部署华为云物联网平台2.1 物联网平台介绍2.2 开通物联网服务2.3 创建产品&#xff08…

《后端程序猿 · 基于 Lettuce 实现缓存容错策略》

📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗 🌻 近期刚转战 CSDN,会严格把控文章质量,绝不滥竽充数,如需交流&#xff…

Log4j日志框架讲解(全面,详细)

目录 Log4j概述 log4j的架构(组成) Loggers Appenders Layouts 快速入门 依赖 java代码 日志的级别 log4j.properties 自定义Logger 总结: Log4j概述 Log4j是Apache下的一款开源的日志框架,通过在项目中使用 Log4J&…

Nginx详解-安装配置等

目录 一、引言 1.1 代理问题 1.2 负载均衡问题 1.3 资源优化 1.4 Nginx处理 二、Nginx概述 三、Nginx的安装 3.1 安装Nginx 3.2 Nginx的配置文件 四、Nginx的反向代理【重点】 4.1 正向代理和反向代理介绍 4.2 基于Nginx实现反向代理 4.3 关于Nginx的location路径…

使用 Java Swing 和 XChart 创建多种图表

在现代应用程序开发中,数据可视化是一个关键部分。本文将介绍如何使用 Java Swing 和 XChart 库创建各种类型的图表。XChart 是一个轻量级的图表库,支持多种类型的图表,非常适合在 Java 应用中进行快速的图表绘制。 1、环境配置 在开始之前&…

用pycharm进行python爬虫的步骤

使用 pycharm 进行 python 爬虫的步骤:下载并安装 pycharm。创建一个新项目。安装 requests 和 beautifulsoup 库。编写爬虫脚本,包括获取页面内容、解析 html 和提取数据的代码。运行爬虫脚本。保存和处理提取到的数据。 用 PyCharm 进行 Python 爬虫的…

如何切换IP地址 怎么修改ip地址

修改IP地址的方法因地区而异。在中国,通常可以通过以下几种方式修改IP地址: 1. 领导者IP代理:代理软件可以帮助你通过代理服务器访问互联网,从而改变你的IP地址。你可以在网上搜索领导者IP代理软件,并按照软件提示进行…

Python UUID模块:深入理解与使用技巧

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…