目录

一、先搞懂:ConfigMap 到底是个什么东西?

1. 核心概念(一句话说清)

2. 为什么需要 ConfigMap?

3. 核心特性

二、ConfigMap 能干什么?(核心使用场景)

三、ConfigMap 3 种创建方式(小白也能上手)

方式 1:用 YAML 文件创建(最常用,适合复杂配置)

方式 2:用 --from-literal 命令行创建(适合简单键值对)

方式 3:用 --from-file 从文件创建(适合直接导入配置文件)

四、ConfigMap 2 种核心使用方式(实战必学)

方式 1:Volume 挂载(把 ConfigMap 变成容器内的文件)

实战示例:把 index.html 挂载到 httpd 容器

方式 2:环境变量注入(把 ConfigMap 变成容器内的环境变量)

实战示例:给 MySQL 容器注入 root 密码

五、实战:用 ConfigMap 给 MySQL 做初始化(你的项目场景)

需求背景

核心原理

步骤 1:编写 ConfigMap(存储初始化 SQL)

步骤 2:编写 MySQL Deployment,挂载 ConfigMap

步骤 3:通过 Argo CD 部署(GitOps 方式)

步骤 4:验证部署结果

六、ConfigMap 常见坑 & 最佳实践

1. 常见坑

2. 最佳实践

七、总结



一、先搞懂:ConfigMap 到底是个什么东西?

1. 核心概念(一句话说清)

ConfigMap 是 Kubernetes 中用来「存储配置信息」的核心资源,它的本质是一个「键值对(key-value)存储」,专门用来把应用的配置和容器镜像解耦,让同一个镜像可以在不同环境(开发 / 测试 / 生产)中复用不同配置。

2. 为什么需要 ConfigMap?

如果没有 ConfigMap,你会遇到这些痛点:

  • 把配置硬编码在镜像里:改个配置就要重新打包镜像、重新部署,效率极低
  • 配置和代码混在一起:不同环境(开发 / 生产)的数据库地址、端口不一样,要维护多套镜像
  • 敏感信息泄露:把密码、密钥写死在代码里,安全风险极高

ConfigMap 完美解决了这些问题:配置和镜像彻底分离,配置统一由 K8s 管理,容器运行时动态注入配置

3. 核心特性

  • 存储格式:支持纯文本、JSON、YAML、配置文件等任意格式,本质都是键值对
  • 大小限制:单个 ConfigMap 最大 1MiB,不适合存大量数据(大文件用存储卷)
  • 作用范围:默认属于某个命名空间,只能在同命名空间的 Pod 中使用
  • 使用方式:两种核心用法 ——挂载为文件到容器内注入为环境变量
  • 非加密存储:ConfigMap 不加密,敏感信息(密码、密钥)必须用 Secret,不能用 ConfigMap

二、ConfigMap 能干什么?(核心使用场景)

ConfigMap 几乎覆盖了容器化应用的所有配置需求,最常用的 3 个场景:

  1. 存储应用配置文件:比如 Nginx 的 nginx.conf、MySQL 的初始化 SQL、SpringBoot 的 application.yml,把配置文件存进 ConfigMap,挂载到容器内直接生效
  2. 注入环境变量:把配置项作为环境变量注入容器,应用启动时直接读取,比如数据库地址、端口、日志级别
  3. 容器启动命令参数:把配置作为容器启动命令的参数,动态调整容器行为

三、ConfigMap 3 种创建方式(小白也能上手)

方式 1:用 YAML 文件创建(最常用,适合复杂配置)

这是生产环境最推荐的方式,适合存储多行配置、配置文件等复杂内容。

# 1. 编写 ConfigMap YAML 文件
cat > game-demo.yaml <<'EOF'
apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo  # ConfigMap 名称,后续 Pod 要引用这个名字
data:
  # 键值对 1:单行配置
  player_initial_lives: "3"
  # 键值对 2:单行配置
  ui_properties_file_name: "tomcat"
  # 键值对 3:多行配置(用 | 符号,保留换行格式)
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5
EOF

# 2. 应用 YAML 创建 ConfigMap
kubectl create -f game-demo.yaml

# 3. 验证创建成功
kubectl get configmaps
kubectl describe configmaps game-demo

执行结果说明

  • kubectl get configmaps 会看到名为 game-demo 的 ConfigMap,DATA 列显示有 3 个键
  • kubectl describe 可以查看 ConfigMap 里的所有键值对详情

方式 2:用 --from-literal 命令行创建(适合简单键值对)

适合快速创建少量简单配置,直接在命令行写键值对,不用写 YAML。

# 创建名为 player 的 ConfigMap,包含 2 个键值对
kubectl create configmap player \
  --from-literal=username=player \
  --from-literal=age=18

# 验证
kubectl describe configmaps player

特点:简单快捷,适合临时创建、少量配置,不适合多行内容。


方式 3:用 --from-file 从文件创建(适合直接导入配置文件)

适合把本地已有的配置文件直接导入 ConfigMap,不用手动复制粘贴内容。

# 1. 先创建一个本地文件 index.html
echo "hello world!" > index.html

# 2. 从文件创建 ConfigMap,键名默认就是文件名
kubectl create configmap indexcontent --from-file=index.html

# 3. 验证(可以看到键名是 index.html,值是文件内容)
kubectl describe configmaps indexcontent

进阶用法:指定自定义键名

kubectl create configmap indexcontent --from-file=custom-index=index.html

四、ConfigMap 2 种核心使用方式(实战必学)

方式 1:Volume 挂载(把 ConfigMap 变成容器内的文件)

这是最常用的方式,适合把配置文件、脚本等内容挂载到容器指定目录,容器直接读取文件。

实战示例:把 index.html 挂载到 httpd 容器
# 1. 先创建包含 index.html 的 ConfigMap(前面已经创建过 indexcontent)
# 2. 编写 Pod YAML,挂载 ConfigMap
cat > cmvolume.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: configmapvolume
  labels:
    app: configmaptest
spec:
  containers:
  - name: test
    image: httpd
    imagePullPolicy: IfNotPresent
    # 容器内挂载点
    volumeMounts:
    - name: index  # 必须和下面 volumes 里的 name 一致
      mountPath: /usr/local/apache2/htdocs  # httpd 默认网页根目录
  # 定义存储卷,关联 ConfigMap
  volumes:
  - name: index  # 自定义卷名,和 volumeMounts 对应
    configMap:
      name: indexcontent  # 要挂载的 ConfigMap 名称
EOF

# 3. 应用 Pod
kubectl create -f cmvolume.yaml

# 4. 验证:进入容器查看文件
kubectl exec -it configmapvolume -- cat /usr/local/apache2/htdocs/index.html

执行结果:容器内 /usr/local/apache2/htdocs/ 目录下会生成 index.html 文件,内容就是 ConfigMap 里的 hello world!,httpd 启动后直接访问就能看到内容。

核心原理

  • volumes 段:把 ConfigMap 定义为一个存储卷,给卷起一个名字(比如 index
  • volumeMounts 段:把这个卷挂载到容器的指定目录,容器内就会生成对应的文件
  • 键名就是文件名,键值就是文件内容

方式 2:环境变量注入(把 ConfigMap 变成容器内的环境变量)

适合把配置项作为环境变量注入容器,应用启动时通过环境变量读取配置,比如数据库密码、端口等。

实战示例:给 MySQL 容器注入 root 密码
# 1. 创建包含密码的 ConfigMap
kubectl create configmap mysqlpw \
  --from-literal=password=ABCabc123

# 2. 编写 Pod YAML,注入环境变量
cat > cmenv.yaml <<'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: mysql
spec:
  containers:
  - name: mysqlname
    image: mysql
    imagePullPolicy: IfNotPresent
    env:
    # 定义环境变量 MYSQL_ROOT_PASSWORD
    - name: MYSQL_ROOT_PASSWORD
      valueFrom:
        configMapKeyRef:
          name: mysqlpw  # 引用的 ConfigMap 名称
          key: password  # 引用 ConfigMap 中的哪个键
EOF

# 3. 应用 Pod
kubectl create -f cmenv.yaml

# 4. 验证:进入容器查看环境变量
kubectl exec -it mysql -- env | grep MYSQL_ROOT_PASSWORD

执行结果:容器内会生成 MYSQL_ROOT_PASSWORD=ABCabc123 环境变量,MySQL 镜像会自动读取这个环境变量作为 root 密码,启动后直接用这个密码登录即可。


五、实战:用 ConfigMap 给 MySQL 做初始化(你的项目场景)

这是我的项目中实际用到的场景,我把完整流程拆解清楚,让你彻底明白每一步的作用。

需求背景

我们要在 K8s 中部署 MySQL 8.0,要求:

  1. 第一次启动时自动创建 hr_db 数据库
  2. 自动创建 departments(部门表)和 employees(员工表)
  3. 自动插入测试数据
  4. 配置和镜像分离,不用重新打包镜像

核心原理

MySQL 官方镜像有一个特性:容器第一次启动时,会自动执行 /docker-entrypoint-initdb.d/ 目录下的所有 .sql.sh 文件。我们只需要把初始化 SQL 存进 ConfigMap,然后把 ConfigMap 挂载到这个目录,MySQL 就会自动执行。


步骤 1:编写 ConfigMap(存储初始化 SQL)

yaml

# apps/mysql/mysql-deployment.yaml 中的 ConfigMap 部分
apiVersion: v1
kind: ConfigMap
metadata:
  name: mysql-init-scripts  # ConfigMap 名称,后续要引用
  namespace: ai-services
data:
  # 键名 init.sql,值是完整的初始化 SQL(用 | 保留换行)
  init.sql: |
    CREATE DATABASE IF NOT EXISTS hr_db;
    USE hr_db;
    -- 创建部门表
    CREATE TABLE IF NOT EXISTS departments (
        dept_id INT PRIMARY KEY,
        dept_name VARCHAR(50) NOT NULL,
        location VARCHAR(50)
    );
    -- 创建员工表
    CREATE TABLE IF NOT EXISTS employees (
        emp_id INT PRIMARY KEY,
        name VARCHAR(50) NOT NULL,
        role VARCHAR(50),
        salary DECIMAL(10, 2),
        dept_id INT,
        FOREIGN KEY (dept_id) REFERENCES departments(dept_id)
    );
    -- 插入测试数据
    INSERT INTO departments VALUES (1, 'Engineering', 'Building A'), (2, 'Sales', 'Building B'), (3, 'HR', 'Building A');
    INSERT INTO employees VALUES 
    (101, 'Alice', 'Engineer', 90000, 1),
    (102, 'Bob', 'Manager', 120000, 1),
    (103, 'Charlie', 'Salesperson', 70000, 2),
    (104, 'David', 'Recruiter', 60000, 3),
    (105, 'Eve', 'Engineer', 95000, 1);

说明

  • 这里的 init.sql 就是键名,后续挂载到容器后,会生成 /docker-entrypoint-initdb.d/init.sql 文件
  • 把 SQL 写在 ConfigMap 里,而不是硬编码在镜像里,后续改 SQL 只需要改 ConfigMap,不用重新部署 MySQL

步骤 2:编写 MySQL Deployment,挂载 ConfigMap

# apps/mysql/mysql-deployment.yaml 中的 Deployment 部分
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: ai-services
spec:
  selector:
    matchLabels:
      app: mysql
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
      - name: mysql
        image: mysql:8.0
        env:
        - name: MYSQL_ROOT_PASSWORD
          value: "native"
        - name: MYSQL_DATABASE
          value: "hr_db"
        ports:
        - containerPort: 3306
        volumeMounts:
        # 1. 挂载数据存储卷(持久化 MySQL 数据)
        - name: mysql-storage
          mountPath: /var/lib/mysql
        # 2. 挂载 ConfigMap 卷,到 MySQL 初始化目录
        - name: init-scripts
          mountPath: /docker-entrypoint-initdb.d
      volumes:
      # 1. 数据存储卷(PVC,持久化数据)
      - name: mysql-storage
        persistentVolumeClaim:
          claimName: mysql-pvc
      # 2. ConfigMap 卷,关联我们创建的 mysql-init-scripts
      - name: init-scripts
        configMap:
          name: mysql-init-scripts

核心挂载链路拆解(你之前困惑的点)

  1. ConfigMap 定义metadata.name: mysql-init-scripts(给 ConfigMap 起名字)
  2. volumes 段- name: init-scripts + configMap.name: mysql-init-scripts(把 ConfigMap 定义为一个存储卷,卷名 init-scripts
  3. volumeMounts 段- name: init-scripts + mountPath: /docker-entrypoint-initdb.d(把这个卷挂载到容器的 /docker-entrypoint-initdb.d 目录)
  4. 最终效果:容器内 /docker-entrypoint-initdb.d/ 目录下生成 init.sql 文件,MySQL 第一次启动自动执行这个 SQL,完成建库、建表、插数据

步骤 3:通过 Argo CD 部署(GitOps 方式)

把配置提交到 Git 仓库,Argo CD 自动同步到 K8s 集群:

bash

运行

cd ~/sre-agent-gitops
git add apps/mysql/
git commit -m "feat: Add MySQL deployment with HR schema"
git push origin main

# 创建 Argo CD Application,让 Argo 接管 MySQL
cat > ~/mysql-argocd.yaml <<'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: mysql-hr
  namespace: argocd
spec:
  project: default
  source:
    repoURL: 'https://github.com/Wang2978266434/sre-agent-gitops'
    targetRevision: HEAD
    path: apps/mysql
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: ai-services
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
EOF

kubectl apply -f ~/mysql-argocd.yaml

步骤 4:验证部署结果

# 1. 查看 MySQL Pod 是否正常运行
kubectl get pods -n ai-services

# 2. 进入 MySQL 容器,验证数据是否创建成功
kubectl exec -it <mysql-pod-name> -n ai-services -- mysql -uroot -pnative hr_db

# 3. 执行查询,验证表和数据
SELECT * FROM employees;

执行结果:会看到 5 条员工数据,说明 ConfigMap 挂载成功,SQL 自动执行完成。


六、ConfigMap 常见坑 & 最佳实践

1. 常见坑

  • 坑 1:ConfigMap 更新后,容器内不会自动热更新ConfigMap 更新后,已经运行的容器不会自动同步,需要重启 Pod 才能生效(K8s 1.16+ 支持部分热更新,但生产环境建议重启)
  • 坑 2:敏感信息用 ConfigMap 存储ConfigMap 是明文存储,密码、密钥等敏感信息必须用 Secret,绝对不能用 ConfigMap
  • 坑 3:ConfigMap 大小超过 1MiB单个 ConfigMap 最大 1MiB,大文件(比如日志、备份文件)用存储卷,不要用 ConfigMap
  • 坑 4:跨命名空间引用 ConfigMapConfigMap 属于命名空间资源,只能在同命名空间的 Pod 中使用,跨命名空间需要创建同名 ConfigMap

2. 最佳实践

  • 配置和镜像分离:所有配置都用 ConfigMap 管理,镜像只放业务代码,不写任何配置
  • 按环境区分 ConfigMap:开发、测试、生产环境用不同的 ConfigMap,同一个镜像复用
  • 用 Git 管理 ConfigMap:把 ConfigMap YAML 提交到 Git,通过 GitOps(Argo CD)管理,可追溯、可回滚
  • ConfigMap 命名规范:用 应用名-用途 命名,比如 mysql-init-scriptsnginx-config,见名知意
  • 避免过度使用:简单配置用环境变量,复杂配置用文件挂载,不要把所有配置都塞进 ConfigMap

七、总结

ConfigMap 是 K8s 中配置管理的核心工具,它的核心价值就是 **「解耦」**:把应用配置和容器镜像彻底分开,让容器化应用真正具备可移植性、可维护性。

  • 本质:键值对存储,用来存配置
  • 核心用法:挂载为文件、注入为环境变量
  • 实战价值:你的项目中用它给 MySQL 做初始化,就是最典型的生产级用法
  • 注意事项:敏感信息用 Secret,不要用 ConfigMap
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐