十三、K8s自定义资源Operator

十三、K8s自定义资源Operator

文章目录

  • 十三、K8s自定义资源Operator
    • 1、什么是Operator
      • [1.1 Operator定义](#1.1 Operator定义)
      • [1.2 Operator使用场景:](#1.2 Operator使用场景:)
      • [1.3 Operator组成](#1.3 Operator组成)
      • [1.4 Operator和Helm对比](#1.4 Operator和Helm对比)
    • [2、在 K8s 集群中安装 Redis 集群](#2、在 K8s 集群中安装 Redis 集群)
      • [2.1 安装 Operator 和 CRD](#2.1 安装 Operator 和 CRD)
      • [2.2 安装集群](#2.2 安装集群)
      • [2.3 设置 Redis 集群密码](#2.3 设置 Redis 集群密码)
      • [2.4 在 K8s 外部访问 Redis 集群](#2.4 在 K8s 外部访问 Redis 集群)
      • [2.5 卸载集群](#2.5 卸载集群)
    • [3、使用 Operator 安装 MySQL 集群](#3、使用 Operator 安装 MySQL 集群)
      • [3.1 安装 Operator 和 CRD](#3.1 安装 Operator 和 CRD)
      • [3.2 创建 NDB Cluster](#3.2 创建 NDB Cluster)
      • [3.3 访问测试](#3.3 访问测试)
      • [3.4 集群外部访问](#3.4 集群外部访问)
      • [3.5 删除集群](#3.5 删除集群)

1、什么是Operator

1.1 Operator定义

Operator是一种用于扩展K8s API的自定义控制器,可以实现在原生资源对象上进行自定义资源类型。通过Operator,也可以实现将复杂的任务转化对K8s资源的操作,让应用程序的管理和维护更加简单和规范。

1.2 Operator使用场景:

  • 简化复杂应用的管理:像管理K8s资源一样管理复杂的系统
  • 一致性操作:通过资源定义决定行为,各个环境可以统一配置
  • 扩展集群能力:自定义资源类型,扩展集群的调度能力

1.3 Operator组成

  • CRD:Custom Resource Definitions,Operator使用k8s的CRD来定义新的资源类型,新的类型可像核心资源被管理,比如定义一个叫做Database的CRD,可以用于一键启动一个数据库实例。
  • Controller:Operator控制器,该控制器监视自定义资源的状态,并根据用户的配置自动执行相应的操作,比如创建一个数据库,执行一次备份任务等。

1.4 Operator和Helm对比

  • Helm:
  • 适合简单的应用程序或微服务,尤其是那些不需要复杂运维任务的应用
  • 开发简单,无需深入了解开发知识
  • Operator:
    • 适合复杂的应用程序(如数据库、消息队列、缓存系统等),除部署外,也需要一些额外任务的应用,比如备份等
    • 开发稍微复杂,需要了解相关开发知识

2、在 K8s 集群中安装 Redis 集群

Redis Cluster 是 Redis 的分布式部署模式,可以让多个 Redis 实例以集群的方式运行,从而提供高可用性、水平扩展和数据冗余的能力。Redis Cluster 通过分片(Sharding)和复制(Replication)技术,确保数据可以在多个节点之间分布,并且在节点故障时能够自动恢复。

推荐 Operator:https://operatorhub.io/operator/redis-operator

仓库地址:https://github.com/OT-CONTAINER-KIT/redis-operator

国内仓库:https://gitee.com/dukuan/redis-operator

官方文档:https://ot-redis-operator.netlify.app/docs/

架构

2.1 安装 Operator 和 CRD

复制代码
# 添加 Operator 的 Helm 仓库:
[root@k8s-master01 ~]# helm repo add ot-helm https://ot-container-kit.github.io/helm-charts/

# 安装 Operator 和 CRD:
[root@k8s-master01 ~]# helm install redis-operator ot-helm/redis-operator --namespace ot-operators --create-namespace
NAME: redis-operator
LAST DEPLOYED: Sun Jun 29 08:16:50 2025
NAMESPACE: ot-operators
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
Thank you for installing redis-operator.

Your release is named redis-operator.

To learn more about the release, try:

  $ helm status redis-operator
  $ helm get all redis-operator


# 查看创建的 CRD:
[root@k8s-master01 ~]# kubectl get crd | grep redis
redis.redis.redis.opstreelabs.in                      2025-06-29T00:16:46Z
redisclusters.redis.redis.opstreelabs.in              2025-06-29T00:16:47Z
redisreplications.redis.redis.opstreelabs.in          2025-06-29T00:16:47Z
redissentinels.redis.redis.opstreelabs.in             2025-06-29T00:16:48Z

# 查看 Pod:
[root@k8s-master01 ~]# kubectl get po -n ot-operators
NAME                              READY   STATUS    RESTARTS   AGE
redis-operator-68b7b4c9f8-dtjrr   1/1     Running   0          3m40s

# 如果无法连外网,可以使用国内的仓库进行离线安装,已同步国内的镜像仓库
# crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/redis-operator:v0.20.2

2.2 安装集群

复制代码
# 创建 Redis 集群的资源文件:
[root@k8s-master01 ~]# vim redis-cluster.yaml 
[root@k8s-master01 ~]# cat redis-cluster.yaml 
apiVersion: redis.redis.opstreelabs.in/v1beta2
kind: RedisCluster
metadata:
  name: redis-cluster
spec:
  clusterSize: 3
  clusterVersion: v7
  persistenceEnabled: true
  kubernetesConfig:
    image: crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/redis:v7.0.15
    imagePullPolicy: IfNotPresent
  storage:
    volumeClaimTemplate:
      spec:
        storageClassName: nfs-csi       # 生产上不建议使用NFS
        accessModes: ["ReadWriteOnce"]
        resources:
          requests:
            storage: 1Gi

[root@k8s-master01 ~]# kubectl create -f redis-cluster.yaml -n public-service

# 查看pod状态
[root@k8s-master01 ~]# kubectl get po -n public-service
NAME                       READY   STATUS    RESTARTS      AGE
redis-cluster-follower-0   1/1     Running   0             33s
redis-cluster-follower-1   1/1     Running   0             26s
redis-cluster-follower-2   1/1     Running   0             20s
redis-cluster-leader-0     1/1     Running   2 (83s ago)   89s
redis-cluster-leader-1     1/1     Running   0             60s
redis-cluster-leader-2     1/1     Running   0             39s

# 查看 RedisCluster 状态:
[root@k8s-master01 ~]# kubectl get rediscluster -n public-service
NAME            CLUSTERSIZE   READYLEADERREPLICAS   READYFOLLOWERREPLICAS
redis-cluster   3             3                     3

# 查看 Redis 集群状态:
[root@k8s-master01 ~]# kubectl exec -ti redis-cluster-leader-0 -n public-service -- bash
redis-cluster-leader-0:/data$ redis-cli
127.0.0.1:6379> CLUSTER info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:3
cluster_my_epoch:1
cluster_stats_messages_ping_sent:274
cluster_stats_messages_pong_sent:271
cluster_stats_messages_sent:545
cluster_stats_messages_ping_received:268
cluster_stats_messages_pong_received:274
cluster_stats_messages_meet_received:3
cluster_stats_messages_received:545
total_cluster_links_buffer_limit_exceeded:0


# 查看集群节点状态:
127.0.0.1:6379> CLUSTER NODES
e7f1e66cd4f329292494b180eef228544a6173d5 172.16.85.199:6379@16379,redis-cluster-leader-0 myself,master - 0 1751207832000 1 connected 0-5460
5513c90d5bfc92e29400207f2892d546950e9056 172.16.58.226:6379@16379,redis-cluster-follower-1 slave efb4a3641bb8b244a020a49741da746e406d7c9a 0 1751207832000 2 connected
540db0e0a90acfb0a334beef8bb28ed72a3b9a26 172.16.85.202:6379@16379,redis-cluster-follower-2 slave 96d23f632eb74396ccbe6cd100461e7fc8a466c2 0 1751207832749 3 connected
01c8b60089fc8807e579aa704c95be98774e5a4d 172.16.85.201:6379@16379,redis-cluster-follower-0 slave e7f1e66cd4f329292494b180eef228544a6173d5 0 1751207831739 1 connected
96d23f632eb74396ccbe6cd100461e7fc8a466c2 172.16.85.200:6379@16379,redis-cluster-leader-2 master - 0 1751207833762 3 connected 10923-16383
efb4a3641bb8b244a020a49741da746e406d7c9a 172.16.58.225:6379@16379,redis-cluster-leader-1 master - 0 1751207833000 2 connected 5461-10922

# 创建 Redis Key 测试
redis-cluster-leader-0:/data$ redis-cli -c
127.0.0.1:6379> set a test
-> Redirected to slot [15495] located at 172.16.85.200:6379
OK
172.16.85.200:6379> get a
"test"

2.3 设置 Redis 集群密码

复制代码
# 使用 Secret 存储 Redis 密码
[root@k8s-master01 ~]# vim secret.yaml
[root@k8s-master01 ~]# cat secret.yaml 
---
apiVersion: v1
kind: Secret
metadata:
  name: redis-secret
stringData:
  password: yunwei
type: Opaque

[root@k8s-master01 ~]# kubectl create -f secret.yaml -n public-service

# 更新 Redis 集群配置:
[root@k8s-master01 ~]# vim redis-cluster.yaml 
[root@k8s-master01 ~]# cat redis-cluster.yaml 
....
  kubernetesConfig:
    image: crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/redis:v7.0.15
    imagePullPolicy: IfNotPresent
    redisSecret:
      name: redis-secret
      key: password
....

# 重新创建集群(删除重建才会生效)
[root@k8s-master01 ~]# kubectl delete -f redis-cluster.yaml -n public-service
[root@k8s-master01 ~]# kubectl create -f redis-cluster.yaml -n public-service

# 查看pod
[root@k8s-master01 ~]# kubectl get po -n public-service
NAME                       READY   STATUS    RESTARTS   AGE
redis-cluster-follower-0   1/1     Running   0          17s
redis-cluster-follower-1   1/1     Running   0          12s
redis-cluster-follower-2   1/1     Running   0          6s
redis-cluster-leader-0     1/1     Running   0          33s
redis-cluster-leader-1     1/1     Running   0          28s
redis-cluster-leader-2     1/1     Running   0          22s

# 登录测试:
[root@k8s-master01 ~]# kubectl exec -ti redis-cluster-follower-0 -n public-service -- bash
redis-cluster-follower-0:/data$ redis-cli -c -a yunwei
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:6379> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:3
cluster_my_epoch:1
cluster_stats_messages_ping_sent:91
cluster_stats_messages_pong_sent:96
cluster_stats_messages_meet_sent:1
cluster_stats_messages_sent:188
cluster_stats_messages_ping_received:96
cluster_stats_messages_pong_received:92
cluster_stats_messages_received:188
total_cluster_links_buffer_limit_exceeded:0

2.4 在 K8s 外部访问 Redis 集群

如果需要在 K8s 外部访问 Redis 集群(生产环境不推荐),就要把 Redis 集群暴露出去,此时可以把 Service 更改为 NodePort:

复制代码
[root@k8s-master01 ~]# cat redis-cluster.yaml 
....
  kubernetesConfig:
    service:
      serviceType: NodePort  # 改成NodePort即可
    image: crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/redis:v7.0.15
    imagePullPolicy: IfNotPresent
    redisSecret:
      name: redis-secret
      key: password

2.5 卸载集群

复制代码
# 直接通过 yaml 文件删除即可:

[root@k8s-master01 ~]# kubectl delete -f redis-cluster.yaml -n public-service

3、使用 Operator 安装 MySQL 集群

MySQL NDB Cluster 是一个分布式、高可用的数据库系统,适用于需要高并发读写、低延迟和高可用性的应用场景。MySQL NDB Cluster 基于 NDB(Network Database)存储引擎,并通过多个节点协同工作来提供数据的分布存储和故障恢复能力。

MySQL 集群安装推荐 Operator:https://operatorhub.io/operator/ndb-operator

官方文档:https://dev.mysql.com/doc/ndb-operator/8.4/en/

仓库地址:https://github.com/mysql/mysql-ndb-operator

国内仓库:https://gitee.com/dukuan/mysql-ndb-operator

架构

组件介绍:

  • 管理节点:Management Node,负责管理和配置整个 NDB Cluster。保存了 NDB 集群的配置信息,包括 Data Node、SQL Node。管理节点不直接参与数据存储或事务处理,主要负责集群的管理和监控,确保集群的正常运行。
  • 数据节点:Data Node,数据节点是集群中实际存储数据的节点,负责存储一部分数据,并且数据会在多个 Data Node 之间进行分区和复制,以实现高可用性和负载均衡。
  • SQL 节点:SQL Node,SQL Node 是用户与 NDB Cluster 交互的主要入口,提供了标准的 MySQL SQL 接口,允许用户通过 SQL 查询、插入、更新和删除数据。
  • NDBAPI:可以直接与 NDB 存储引擎进行交互的接口。

3.1 安装 Operator 和 CRD

复制代码
# 下载部署文件:
[root@k8s-master01 ~]# git clone https://gitee.com/dukuan/mysql-ndb-operator.git

# 创建 Operator:
[root@k8s-master01 ~]# cd mysql-ndb-operator/
[root@k8s-master01 mysql-ndb-operator]# kubectl apply -f deploy/manifests/ndb-operator.yaml -n ndb-operator

# 查看 Pod
[root@k8s-master01 mysql-ndb-operator]# kubectl get po -n ndb-operator
NAME                                           READY   STATUS    RESTARTS   AGE
ndb-operator-app-5f8547d4b-hps2g               1/1     Running   0          8m35s
ndb-operator-webhook-server-78d5d44bb5-92c77   1/1     Running   0          8m35s


# 如果无法连外网,可以使用国内的仓库进行离线安装,已同步国内的镜像仓库
# crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/community-ndb-operator:9.1.0-1.6.0

3.2 创建 NDB Cluster

复制代码
# 创建一个 NDB Cluster:
[root@k8s-master01 ~]# vim ndb-cluster.yaml
[root@k8s-master01 ~]# cat ndb-cluster.yaml 
apiVersion: mysql.oracle.com/v1
kind: NdbCluster
metadata:
  name: example-ndb
spec:
  image: crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/community-cluster:9.1.0
  redundancyLevel: 1        # 指定数据副本的数量,生产环境大于等于 2
  dataNode:
    nodeCount: 1            # 指定数据副本的数量,生产环境大于等于 2
    pvcSpec:
      storageClassName: nfs-csi
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi
  mysqlNode:
    nodeCount: 1            # 指定数据副本的数量,生产环境大于等于 2
    pvcSpec:
      storageClassName: nfs-csi
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

# 创建集群:
[root@k8s-master01 ~]# kubectl create ns ndb-cluster

[root@k8s-master01 ~]# kubectl create -f ndb-cluster.yaml -n ndb-cluster


# 查看 Pod 状态:
[root@k8s-master01 ~]# kubectl get po -n ndb-cluster
NAME                   READY   STATUS    RESTARTS   AGE
example-ndb-mgmd-0     1/1     Running   0          4m57s
example-ndb-mysqld-0   1/1     Running   0          75s
example-ndb-ndbmtd-0   1/1     Running   0          4m48s

# 查看集群状态:
[root@k8s-master01 ~]# kubectl get ndbcluster -n ndb-cluster
NAME          REPLICA   MANAGEMENT NODES   DATA NODES   MYSQL SERVERS   AGE    UP-TO-DATE
example-ndb   1         Ready:1/1          Ready:1/1    Ready:1/1       5m2s   True

3.3 访问测试

复制代码
# 集群创建后,会有一些 Service 可以访问到集群内部:
[root@k8s-master01 ~]# kubectl get svc -n ndb-cluster
NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
example-ndb-mgmd     ClusterIP   10.104.25.157   <none>        1186/TCP   5m37s
example-ndb-mysqld   ClusterIP   10.101.140.23   <none>        3306/TCP   115s
example-ndb-ndbmtd   ClusterIP   None            <none>        1186/TCP   5m28s

# 首先登录到 SQL 节点,然后使用 ndb_mgm 查看集群状态
[root@k8s-master01 ~]# kubectl exec -ti example-ndb-mysqld-0 -n ndb-cluster -- bash
Defaulted container "mysqld-container" out of: mysqld-container, ndb-pod-init-container (init), mysqld-init-container (init)
bash-5.1# ndb_mgm -c example-ndb-mgmd
-- NDB Cluster -- Management Client --
ndb_mgm> SHOW
Connected to management server at example-ndb-mgmd port 1186 (using cleartext)
Cluster Configuration
---------------------
[ndbd(NDB)]	1 node(s)
id=2	@172.16.58.230  (mysql-9.1.0 ndb-9.1.0, Nodegroup: 0, *)

[ndb_mgmd(MGM)]	1 node(s)
id=1	@172.16.85.210  (mysql-9.1.0 ndb-9.1.0)

[mysqld(API)]	6 node(s)
id=147 (not connected, accepting connect from any host)
id=148	@172.16.85.211  (mysql-9.1.0 ndb-9.1.0)
id=149 (not connected, accepting connect from example-ndb-mysqld-1.example-ndb-mysqld.ndb-cluster.svc.cluster.local)
id=150 (not connected, accepting connect from example-ndb-mysqld-2.example-ndb-mysqld.ndb-cluster.svc.cluster.local)
id=151 (not connected, accepting connect from any host)
id=152 (not connected, accepting connect from any host)

# 也可以通过 MySQL 客户端链接集群,首先查看集群的访问密码:
[root@k8s-master01 ~]# base64 -d <<< $(kubectl -n ndb-cluster get secret example-ndb-mysqld-root-password -o jsonpath={.data.password})
pFWE8AwPocQ5qiGR

# 登录 MySQL:
bash-5.1# mysql -h example-ndb-mysqld -uroot -ppFWE8AwPocQ5qiGR
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 111
Server version: 9.1.0-cluster MySQL Cluster Community Server - GPL

Copyright (c) 2000, 2024, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| ndbinfo            |
| performance_schema |
| sys                |
+--------------------+
5 rows in set (0.01 sec)

mysql> 

3.4 集群外部访问

如果想要在集群外部访问,可以创建一个 SQL Node NodePort 类型的 Service:

复制代码
[root@k8s-master01 ~]# vim ndbcluster-svc-nodeport.yaml
[root@k8s-master01 ~]# cat ndbcluster-svc-nodeport.yaml 
apiVersion: v1
kind: Service
metadata:
   name: example-ndb-mysqld-nodeport
   namespace: ndb-cluster
spec:
   ports:
   - name: mysqld-service-port-0
     port: 3306
     protocol: TCP
     targetPort: 3306
   selector:
     mysql.oracle.com/node-type: mysqld
     mysql.oracle.com/v1: example-ndb
   sessionAffinity: None
   type: NodePort
   
   
# 创建该 Service:
[root@k8s-master01 ~]# kubectl create -f ndbcluster-svc-nodeport.yaml

# 查看端口:
[root@k8s-master01 ~]# kubectl get -f ndbcluster-svc-nodeport.yaml
NAME                          TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
example-ndb-mysqld-nodeport   NodePort   10.110.189.195   <none>        3306:30314/TCP   11s

接下来即可通过节点 IP:30314 访问该集群。

3.5 删除集群

复制代码
# 直接删除资源即可:
[root@k8s-master01 ~]# kubectl delete -f ndb-cluster.yaml -n ndb-cluster

此博客来源于:https://edu.51cto.com/lecturer/11062970.html