当前位置: 首页 > news >正文

K8s 集群部署中间件 - yaml 版本(二)

K8s 集群部署中间件 - yaml 版本(二)


文章目录

  • K8s 集群部署中间件 - yaml 版本(二)
  • 1. 部署 mysql
    • 1. ConfigMap
    • 2. Secret
    • 3. StatefulSet
    • 4. Service
  • 2. 部署 Nacos
    • 1 ConfigMap
    • 2. StatefulSet
    • 3. Service
  • 疑问与总结
    • 1. 单独创建 PVC 与 通过 volumeClaimTemplates 创建 PVC 的区别?
    • 2. 独立创建 PVC 可以被共享,为什么不适合有状态服务?


  • 不同的应用,需要采取不同的部署方式:
    • deployment:无状态应用部署,比如微服务,提供多副本等功能。
    • statefulSet: 有状态应用部署,比如 redis,提供稳定的存储、网络等功能。
      • 稳定的网络身份:StatefulSet 为每个 Pod 分配了一个持久且唯一的网络标识符,这对于 MySQL 这类需要固定主机名或网络地址以维持主从复制关系的数据库服务至关重要。
      • 持久化存储:StatefulSet 易于与持久化存储卷结合使用,确保数据库数据的持久保存,即便是在 Pod 重启或重新调度后。
      • 适合有状态应用:StatefulSet 是为有状态应用设计的,如数据库和消息队列,它提供了必要的支持来维护这些应用的状态。
    • DaemonSet:守护型应用部署,比如日志收集组件,在每个机器都运行一份。
    • Job/CronJob:定时任务部署,比如垃圾清理组件,可以在指定时间运行。

1. 部署 mysql

  • k8s 部署 mysql 我们需要编写的文件以4下个:
    • ConfigMap:用于存储 mysql 相关配置。kind:ConfigMap
    • Secret:用于存储 mysql 用户密码。kind:Secret
    • StatefulSet:构建 mysql 服务。kind:StatefulSet
    • Service:向集群内提供 mysql 服务访问入口。由于我们需要使用工具连接该 mysql,所以除了向集群内提供服务,还要向集群外提供连接服务。
      • 向 k8s 集群内部提供服务的 service,kind:service,ClusterIP:None,type: ClusterIP(默认值,可省略)。提供 DNS 解析与服务发现。让集群内部组件能直接发现所有 MySQL 节点,k8s 会为无头服务自动创建 DNS 记录(格式:服务名.命名空间.svc.cluster.local),当其他 Pod(如应用服务、MySQL 从节点)解析该 DNS 时,会返回 所有匹配该服务标签选择器的 MySQL Pod 的真实 IP 列表(而非单一虚拟 IP)。
      • 向集群外提供连接服务的 service,kind:service,type:NodePort。
    • Ingress:向集群外提供服务访问入口。Ingress 具体分为:(mysql 不配置 ingerss)
      • 通常不建议给 MySQL 这类数据库服务配置 Ingress,核心原因是 Ingress 本质是为 HTTP/HTTPS 流量设计的 “路由网关”,而 MySQL 基于 TCP 协议通信,两者适配性差,且存在安全和性能隐患。由于我需要通过工具连接 mysql ,所以上面用的 type: NodePort。无需给 mysql 提供 ingress 向外提供服务。该模式在集群节点开放一个高位端口(30000-32767),仅允许特定 IP(如运维电脑、跳板机)通过 “节点 IP:NodePort” 访问,配合防火墙限制 IP 白名单。
      • Ingress:是 Kubernetes 中的一种资源对象,它定义了外部访问集群内服务的规则。可以将其理解为一个智能的 “流量路由器”,根据接收到的 HTTP/HTTPS 请求的不同规则,将流量转发到集群内不同的服务上。kind:Ingress
      • Ingress-Controller:是实际负责执行 Ingress 资源中定义的路由规则的组件。它是一个运行在 Kubernetes 集群中的服务,通常以 Pod 的形式存在,不断监听 Ingress 资源的变化,并根据最新的规则来配置和更新负载均衡器或代理服务器。kind:Service

1. ConfigMap

  • 我们在启动 mysql 服务时(通过容器启动),通常需要创建 my.cnf 文件,根据我们的需求设置一些参数,现在将这些配置抽取到 ConfigMap。

  • my.cnf 文件:

    [client]
    #设置客户端默认字符集utf8mb4
    default-character-set=utf8mb4
    [mysql]
    #设置服务器默认字符集为utf8mb4
    default-character-set=utf8mb4
    [mysqld]
    #配置服务器的服务号,具备日后需要集群做准备
    server-id = 1
    #开启MySQL数据库的二进制日志,用于记录用户对数据库的操作SQL语句,具备日后需要集群做准备
    log-bin=mysql-bin
    #设置清理超过30天的日志,以免日志堆积造过多成服务器内存爆满。2592000秒等于30天的秒数
    binlog_expire_logs_seconds = 2592000
    #解决MySQL8.0版本GROUP BY问题
    sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'
    #允许最大的连接数
    max_connections=1000
    # 禁用符号链接以防止各种安全风险
    symbolic-links=0
    # 设置东八区时区
    default-time_zone = '+8:00'
    
  • 抽取成 ConfigMap:mysql-configMap.yaml

    apiVersion: v1
    kind: ConfigMap
    metadata:# ConfigMap 名称,用于挂载引用name: mysql-config  # 与 MySQL 部署同命名空间namespace: mortal-system  
    data:# 键为配置文件名,值为完整的配置内容(包含 client、mysql、mysqld 三个部分)my.cnf: |[client]# 设置客户端默认字符集为 utf8mb4(支持 emoji 等特殊字符)default-character-set=utf8mb4[mysql]# 设置 MySQL 命令行客户端默认字符集为 utf8mb4default-character-set=utf8mb4[mysqld]# 配置服务器的服务号,为日后集群做准备server-id = 1# 开启 MySQL 数据库的二进制日志,记录用户操作 SQL 语句,为日后集群做准备log-bin=mysql-bin# 设置清理超过 30 天的日志,避免日志堆积导致服务器内存爆满(2592000= 30 天)binlog_expire_logs_seconds = 2592000# 解决 MySQL8.0 版本 GROUP BY 问题sql_mode='STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION'# 允许最大的连接数max_connections=1000# 禁用符号链接以防止各种安全风险symbolic-links=0# 设置东八区时区default-time_zone = '+8:00'# 服务器端默认字符集(与客户端保持一致,避免乱码)character-set-server=utf8mb4# 数据库字符集对应的排序规则collation-server=utf8mb4_unicode_ci
    
  • 创建 ConfigMap 资源:

    kubectl apply -f mysql-configMap.yaml
    
  • 查看:

    在这里插入图片描述

2. Secret

  • 我们创建一个 Secret 用来存储 MySQL root 用户的密码:mysql-secret.yaml

    apiVersion: v1
    kind: Secret
    metadata:# Secret 名称,用于部署时引用name: mysql-secret# 与 MySQL 部署在同一命名空间namespace: mortal-system
    # 通用类型,用于存储任意键值对
    type: Opaque
    data:# root 用户密码(需要 base64 编码)# 生成方式:echo -n "你的root密码" | base64# 示例:明文为 "root",编码后为 cm9vdA==root-password: cm9vdA==
    
  • 创建 Secret 资源:mysql-secret.yaml

    kubectl apply -f mysql-secret.yaml
    
  • 查看创建的服务:

    在这里插入图片描述

  • 查看解码后的密码:

    在这里插入图片描述

3. StatefulSet

  • 部署mysql 服务:mysql-k8s.yaml,采用 NFS 进行存储。

    # 持久化存储声明模板(PVC Template):为每个副本自动创建独立存储
    apiVersion: apps/v1
    kind: StatefulSet
    metadata:name: mysqlnamespace: mortal-system
    spec:serviceName: mysql-headless  # 关联 service 上的服务名称,要一致# 单副本(扩展集群时修改此处)replicas: 1selector:matchLabels:app: mysqltemplate:metadata:labels:app: mysqlspec:imagePullSecrets: - name: harbor-credscontainers:- name: mysql# 替换为你的 MySQL 镜像image: mortal.harbor.com/mortal-system/mysql:8.3.0ports:- containerPort: 3306env:- name: MYSQL_ROOT_PASSWORDvalueFrom:secretKeyRef:name: mysql-secretkey: root-password- name: TZvalue: "Asia/Shanghai"# 配置文件挂载(ConfigMap)volumeMounts:- name: mysql-configmountPath: /etc/mysql/conf.d/my.cnfsubPath: my.cnf# 数据存储卷(与下面的 volumeClaimTemplates 对应)- name: mysql-data  mountPath: /var/lib/mysql# 健康检查livenessProbe:exec:command: ["mysqladmin", "ping", "-uroot", "-p$(MYSQL_ROOT_PASSWORD)"]initialDelaySeconds: 30periodSeconds: 10readinessProbe:exec:command: ["mysqladmin", "ping", "-uroot", "-p$(MYSQL_ROOT_PASSWORD)"]initialDelaySeconds: 5periodSeconds: 5volumes:# 配置文件卷(引用 ConfigMap- name: mysql-configconfigMap:name: mysql-config# 存储声明模板:为每个副本自动创建 PVC(名称格式:mysql-data-<statefulset-name>-<ordinal>)volumeClaimTemplates:- metadata:name: mysql-dataspec:accessModes: [ "ReadWriteOnce" ]resources:requests:storage: 10Gi# 使用我们创建的sc nfs-scstorageClassName: "nfs-sc"
  • 部署并查看启动情况:

    kubectl apply -f mysql-k8s.yaml
    kubectl get pod -A
    

    在这里插入图片描述

4. Service

  • 部署 Service:mysql-headless.yaml

    apiVersion: v1
    kind: Service
    metadata:# 服务名称,集群内可通过此名称访问name: mysql-headless# 与 MySQL 实例同命名空间namespace: mortal-system
    spec:clusterIP: None  # 无头服务核心标识selector:# 匹配标签为 app=mysql 的 Pod(与 StatefulSet 保持一致)app: mysqlports:# 集群内访问端口(其他服务通过此端口连接)- port: 3306# 指向 Pod 内部的 MySQL 端口(容器暴露的端口)targetPort: 3306# 仅集群内部可访问(默认类型,可省略)type: ClusterIP---
    #  外部访问 ServiceNodePort 供应用连接,如 Navicat)
    apiVersion: v1
    kind: Service
    metadata:name: mysql-servicenamespace: mortal-system
    spec:type: NodePortports:- port: 3306targetPort: 3306nodePort: 30306selector:app: mysql
    
  • 部署并查看启动情况:

    kubectl apply -f mysql-headless.yaml
    kubectl get service -n mortal-system
    

    在这里插入图片描述

  • 测试连接:

    在这里插入图片描述

2. 部署 Nacos

  • k8s 部署 nacos 我们需要编写的文件以4下个:我们采用是 mysql 作为 nacos 的存储。
    • ConfigMap:用于存储 nacos 连接 mysql 相关配置。kind:ConfigMap
    • StatefulSet:构建 nacos 服务。kind:StatefulSet
    • Service:向集群内提供 nacos 服务访问入口。由于我们需要连接该 nacos 控制台,所以除了向集群内提供服务,还要向集群外提供连接服务。
      • 向 k8s 集群内部提供服务的 service,kind:service,ClusterIP:None,type: ClusterIP(默认值,可省略)。提供 DNS 解析与服务发现。为 Nacos 集群节点提供固定 DNS 解析(nacos-0.xxx),支持集群内部 Leader 选举、数据同步。
      • 向集群外提供连接服务的 service,kind:service,type:NodePort。把 Nacos 控制台 / API 暴露到 K8s 集群外部,提供负载均衡(外部请求转发到任意 Nacos 节点)。

1 ConfigMap

  • 部署 Nacos 的 ConfigMap ,配置 Nacos 连接 mysql 进行存储的一些配置,以及 Nacos 的一些安全配置。

    apiVersion: v1
    kind: ConfigMap
    metadata:name: nacos-confignamespace: mortal-system
    data:application.properties: |spring.datasource.platform=mysqldb.num=1# 替换为你的 MySQL 服务名、数据库名、用户名、密码db.url.0=jdbc:mysql://mysql-headless:3306/nacos_config?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTCdb.user=rootdb.password=root# 生产环境建议改为 true 并配置密钥nacos.core.auth.enabled=falsenacos.server.ip=0.0.0.0nacos.naming.data.warmup=true# 禁用单节点模式(集群必须设为 false)nacos.core.singleton=false# 指定 raft 协议数据目录(需持久化)nacos.raft.dataDir=/home/nacos/data/raft# 显式指定集群名称nacos.naming.cluster.name=mortal-cluster# 显式指定集群通信端口nacos.server.rpc.port=9849nacos.inetutils.prefer-hostname-over-ip=true# 显式指定集群内节点的通信地址(域名形式)nacos.naming.serverAddr=nacos-0.nacos-headless.mortal-system.svc.cluster.local:8848,nacos-1.nacos-headless.mortal-system.svc.cluster.local:8848,nacos-2.nacos-headless.mortal-system.svc.cluster.local:8848
    
    • 还需要对 Nacos 数据库进行初始化,创建 12 张表,根据ConfigMap配置,数据库名为 nacos_config。具体 sql 之前 的文章有写到,或者查看官方文档。

2. StatefulSet

apiVersion: apps/v1
kind: StatefulSet
metadata:name: nacosnamespace: mortal-system
spec:serviceName: nacos-headlessreplicas: 3selector:matchLabels:app: nacostemplate:metadata:labels:app: nacosspec:containers:- name: nacosimage: mortal.harbor.com/mortal/nacos:2.1.1ports:- containerPort: 8848name: http- containerPort: 9848name: client-rpc- containerPort: 9849name: server-rpcenv:- name: NACOS_REPLICASvalue: "3"- name: NACOS_SERVERSvalue: "nacos-0.nacos-headless.mortal-system.svc.cluster.local:8848 nacos-1.nacos-headless.mortal-system.svc.cluster.local:8848 nacos-2.nacos-headless.mortal-system.svc.cluster.local:8848"- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name- name: NACOS_SERVER_PORTvalue: "8848"volumeMounts:- name: nacos-configmountPath: /home/nacos/conf/application.propertiessubPath: application.properties- name: nacos-datamountPath: /home/nacos/data# 挂载 ConfigMap 配置volumes:- name: nacos-configconfigMap:name: nacos-config  # 与前面 ConfigMap 名称一致# PVC 模板:自动创建 3 个 PVC,关联 nfs-sc 存储类volumeClaimTemplates:- metadata:name: nacos-data  # 与上面 volumeMounts.name 对应spec:accessModes: [ "ReadWriteMany" ]  # NFS 支持多节点读写(适配集群)storageClassName: "nfs-sc"  # 关键:指定已存在的 NFS 存储类名resources:requests:storage: 5Gi  # 每个节点请求 10Gi 存储(3节点共 30Gi,SC 需支持)
  • NACOS_SERVERS:为必填项,供集群选举、数据同步使用。如果替换成 IP 需要替换成 物理机IP (不建议替换成 IP)。建议使用如上的 保留无头服务域名:nacos-0.nacos-headless.mortal-system.svc.cluster.local:8848。

    • nacos-0:StatefulSet 实例的名称(固定格式:StatefulSet名称-序号)。
    • nacos-headless:绑定的无头服务(Headless Service)名称(你前面定义的 Service 名)。
    • mortal-system:命名空间(Nacos 部署所在的 K8s 命名空间)。
    • svc:K8s 服务的 DNS 子域(所有 Service 都会注册到这个子域下)。
    • cluster.local:K8s 集群的默认域名(可通过集群配置修改,默认就是这个)。
    • :8848:Nacos 容器的通信端口(和 StatefulSet 中定义的 containerPort: 8848 对应)。
  • 为什么要这么写?核心依据是 K8s 的两个核心特性:

    • StatefulSet 的「固定实例命名」特性。普通 Deployment 部署的 Pod 名称是随机的(比如 nacos-7f98d765c4-2xqzk),重启后名称和 IP 都会变;而 StatefulSet 会给每个实例分配 固定的名称和序号:
      • 当你设置 replicas: 3 时,K8s 会自动创建 3 个 Pod,名称固定为 nacos-0、nacos-1、nacos-2(序号从 0 开始);
      • 即使 Pod 重启、重建(比如节点故障迁移),实例名称依然是 nacos-0,不会变。
    • 无头服务(Headless Service)的「DNS 解析」特性:你前面定义的 nacos-headless 是「无头服务」(clusterIP: None),它和普通 Service 的区别是:
      • 普通 Service 会分配一个集群 IP,通过负载均衡转发请求到 Pod;
      • 无头服务 不分配集群 IP,而是直接在 K8s DNS 中为每个关联的 StatefulSet Pod 注册一条 DNS 记录:{Pod名称}.{无头服务名}.{命名空间}.svc.cluster.local;
      • 这条 DNS 记录会自动解析到对应 Pod 的 实际 IP(不管 Pod 重启后 IP 怎么变,DNS 都会动态更新解析结果)。

3. Service

# 无头服务(供 Nacos 集群内部通信,固定 DNS 解析)
apiVersion: v1
kind: Service
metadata:name: nacos-headlessnamespace: mortal-system
spec:clusterIP: None  # 无头服务核心标识(无集群 IP,生成 Pod 独立 DNS)ports:- port: 8848targetPort: 8848name: http  # 控制台/API 端口- port: 9848targetPort: 9848name: client-rpc  # 客户端通信端口- port: 9849targetPort: 9849name: server-rpc  # 集群节点通信端口selector:app: nacos---
# 供外部访问
apiVersion: v1
kind: Service
metadata:name: nacos-servicenamespace: mortal-system
spec:type: NodePort  # 暴露到 K8s 节点 IP,外部可访问ports:- port: 8848targetPort: 8848nodePort: 30848  # 外部访问端口(范围 30000-32767,可自定义)selector:app: nacos  # 关联 Nacos Pod
  • 部署效果图如下:
    在这里插入图片描述
    在这里插入图片描述

  • Naocs控制台集群节点状态:

    在这里插入图片描述

  • 微服务成功注册进 Nacos:

    在这里插入图片描述

疑问与总结

1. 单独创建 PVC 与 通过 volumeClaimTemplates 创建 PVC 的区别?

  • 创建 PVC 有两种 方式:PVC 只会和 一个 PV 进行绑定。
    • 使用 kind:PersistentVolumeClaim 来单独创建 PVC。
      • 与 pod / Deployment 等通过 vloumes.persistenVolumeClaim.claimName:[PVC name] 进行关联。

      • 该 PVC 是独立的 k8s 资源,其生命周期与使用它的 pod / Deployment 等解耦。即时 pod 被删除,PVC 与其绑定的 PV 仍会保留,除非手动删除。

      • 当单独创建 PVC 设置 accessModes:ReadWriteMany, 和单独创建 PV 设置 accessModes:ReadWriteMany 进行绑定时,创建的资源可以被多个 pod 共享。注意:如果 accessModes 不一致则PVC 会一直处于 Pending 状态,无法与 PV 绑定。或者通过单独创建 PVC 设置 accessModes:ReadWriteMany 和 通过 StorageClass 自动创建 PV,也可实现多个 pod 共享。

        访问模式缩写含义适用场景
        ReadWriteManyRWX多 Pod 可同时读写共享静态资源(如前端代码)、日志收集
        ReadWriteOnceRWO仅一个节点上的 Pod 可读写(单节点独占)数据库、有状态服务(独立存储)
        ReadOnlyManyROX多 Pod 可同时只读共享只读数据(如配置文件、镜像)
        • 适合无状态服务。
  • 部署服务时,如kind:StatefulSet,通过 volumeClaimTemplates 来创建 PVC。
    • 通过 volumeClaimTemplates 定义 “存储模板”,Kubernetes 会自动为每个 StatefulSet 副本创建对应的 PVC 和 PV。
    • PVC 的生命周期与 StatefulSet 的副本强绑定。当 StatefulSet 扩容时,会基于模板创建新的 PVC 和 PV。当缩容时,对应的 PV 和 PVC 不会自动删除(默认保留,防止数据丢失)。当 StatefulSet 被删除时, PVC 和 PV 也会保留,需手动清除。
    • 适用于有状态服务,如数据库,分布式中间件(Nacos等),需要每个实例有独立且持久的存储。

2. 独立创建 PVC 可以被共享,为什么不适合有状态服务?

  • 资源数据可以被共享,不是更能体现数据的一致性吗,为什么却不适合 有状态服务呢?
    • 有状态服务的 “状态” 本质:并非简单 “数据一致”,而是 “实例身份与数据的强绑定”,比如说:
      • MySQL 主从集群中,主库和从库的数据虽然最终一致,但各自有独立的写入路径、日志(binlog)和缓存,不能直接共享同一份存储(否则主从同步会失效,甚至产生数据冲突)。
      • Nacos 集群中,每个节点需要存储本地元数据、raft 协议日志等,这些数据是节点私有的,不能共享(否则集群共识机制会混乱)。
    • 共享存储只能保证 “数据文件相同”,但无法解决分布式服务的 “实例独立性”(如节点身份、本地状态、读写冲突等)。
    • 如果有状态服务共享的 PVC 和 PV,可能带来的风险有:
      • 并发写入冲突:多个实例同时读写同一份数据文件(如数据库表、配置文件),会导致数据损坏(如 MySQL 的 InnoDB 引擎无法在共享存储上安全运行多实例)。
      • 锁机制失效:分布式服务的本地锁(如文件锁)在共享存储中可能失效,引发数据一致性问题。
    • 而在无状态服务中,实例之间完全对等,没有 “身份” 差异(删除任意实例不影响服务)。不需要持久化本地状态(或状态可通过外部服务恢复,如从 Redis 拉取配置)。所以 独立创建 PVC 更适合 无状态服务。
    • 独立创建 PVC 也适合 只读场景(如共享静态资源)。
    • 所以有状态服务的 “状态” 不仅是数据本身,还包括实例的身份和本地逻辑,这些必须与独立存储绑定才能正常工作。所以不适合有状态服务。

其他中间件持续更新中…

http://www.dtcms.com/a/588800.html

相关文章:

  • zmaiFy音频转录介绍
  • 学校资源网站建设目标关于做电商网站导流项目
  • 【论文阅读与项目复现】Hypothesis Generation with Large Language Models
  • win7下asp.net网站发布软件开发文档编写
  • socket编程——使用UDP实现的一个回显功能
  • 侠客行・iOS 26 Liquid Glass TabBar 破阵记
  • G882磁力仪方向调整
  • 站长友情链接网上卖货的平台有哪些
  • 弱函数:嵌入式回调的最佳实践
  • 如何在实验室服务器上搭建python虚拟环境?安装conda并配置虚拟环境
  • 【开发者导航】轻量可微调且开源的大语言模型家族:LLaMA
  • 北京网站建立公司创意包装设计网站
  • INSERT INTO … SELECT … 常见问答(含样例)
  • 做图素材的网站有哪些昆明做网站公司有哪些
  • 移动端网站定制搞笑网站模板
  • 网站后台的数据库怎么做工业产品设计要学什么
  • 你去湖北省住房城乡建设厅网站查软件开发好学吗
  • 北京手机网站设计公司益阳建设公司网站
  • 网站建设ip微信小程序 做网站
  • 单位网站建设的必要性网站如何被收录
  • 狗贩子怎么做网站卖狗成都网站建设餐饮
  • 如何开发网站自己做站长哪个网站可以做编程题
  • 深圳制作公司网站wordpress 显示微信二维码
  • 微信网站建设费记什么科目中山网站搭建
  • 有关小城镇建设的网站网站是怎么建设的
  • 国外一直小猫做图标的网站centos lnmp wordpress
  • 网站备案不注销有什么后果网站免费注册域名
  • 建网站的重要性内蒙古互联网公司
  • 网站建设价格一览表wordpress主页显示博客
  • 没有ipc备案的网站重庆网站设计找重庆最佳科技