kubegres 源码解析(一) 简介和部署

摘要

Kubegress 是一个 Kubernetes operator, 允许部署一个或多个PostgreSql pods集群,并启用开箱即用的数据复制和故障转移。考虑到Kubernetes管理状态集的生命周期和数据复制的复杂性,它让使用 PostgresSql 变得简单. 本文的主要目的, 是在 minikube 下尝试对 kubegres 进行部署, 先把项目跑起来, 分析 Kubegres 的配置文件, 它创建了哪些 resource? 这些 resource 的组织方式是怎样的, 相互之间的关系如何? 数据库主从, 读写分离如何实现? 故障转移和数据备份如何进行? 尝试建立一个框架式的印象, 有助于在源码分析时理清思路. 

特性

  • 它可以管理一个或多个 Postgres 实例的集群。每个 Postgres 实例集群都使用 YAML 中的 "kind: Kubegres" 创建。每个集群都是独立的,并由其独一无二的名称和命名空间来识别。
  • 它创建了一个启用了流复制的 PostgreSql 服务器集群:它创建了一个 Primary PostgreSql pod 和一些Replica PostgreSql pod,并将 primary 的数据库实时复制到 Replica pod。
  • 它管理故障转移:如果Primary PostgreSql 崩溃了,它会自动将 Replica PostgreSq 提升为 Primary。
  • 它有一个数据备份选项,允许在一个给定的卷中定期转储 PostgreSql 数据。
  • 它提供了一个非常简单的YAML,具有专门针对 PostgreSql 的属性。
  • 它是有弹性的,有超过85个自动测试案例,并且已经在生产中运行了。

与众不同之处

Kubegres 与 Kubernetes 的生命周期完全集成,因为它作为一个用 Go 编写的操作程序运行。
与其他开源 Postgres 运营商相比,它的代码库是最小的。它拥有管理 Kubernetes 上 PostgreSql 集群所需的最小而强大的功能。项目小而简单。
推荐 Kubegres 的 5 个主要原因。
  • 标准: 为了管理复制、故障转移和备份,Kubegres 100% 依赖于 PostgreSql 容器中绑定的PostgreSql 标准库。它不带有任何自定义或第三方库来管理这些功能。
  • 学习曲线小: 如果你已经熟悉了标准的 PostgreSql 库,你可以轻松地理解和管理 Kubegres。
  • 安全:我们评估的所有其他开源项目都需要他们自己的定制 Docke 容器,这些容器带有定制库。为了减少由额外的依赖关系引起的攻击面,我们决定只依赖由 Docker 官方图像团队创建和维护的 PostgreSql 容器。
  • 可移植性:上述方法允许 Kubegres 与任何衍生自这些 PostgreSql 容器的容器兼容。
  • 纯 Go:Kubegres 完全是用 Go 编写的,没有使用其他语言(如 Python,......)。而且它使用了最新的 Kubebuilder 第三版,该版本由官方的 Kubernetes API(SIG)维护。

部署

安装 Kubegres operator

1
kubectl apply -f https://raw.githubusercontent.com/reactive-tech/kubegres/v1.16/kubegres.yaml
 
网络不佳的, 可以修改 kubegres.yaml 源文件, 使用 bitnami/kube-rbac-proxy:0.13.0 替换 gcr.io/kubebuilder/kube-rbac-proxy:v0.13.0 完成部署.

StorageClass

1
2
3
kubectl get sc
NAME                 PROVISIONER                RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
standard (default)   k8s.io/minikube-hostpath   Delete          Immediate           false                  10d
 
使用的是 Kubernetes 集群默认的 StorageClass.
1
2
3
4
5
6
7
8
9
10
11
12
kubectl describe sc standard
Name:            standard
IsDefaultClass:  Yes
Annotations:     kubectl.kubernetes.io/last-applied-configuration={"apiVersion":"storage.k8s.io/v1","kind":"StorageClass","metadata":{"annotations":{"storageclass.kubernetes.io/is-default-class":"true"},"labels":{"addonmanager.kubernetes.io/mode":"EnsureExists"},"name":"standard"},"provisioner":"k8s.io/minikube-hostpath"}
,storageclass.kubernetes.io/is-default-class=true
Provisioner:           k8s.io/minikube-hostpath
Parameters:            <none>
AllowVolumeExpansion:  <unset>
MountOptions:          <none>
ReclaimPolicy:         Delete
VolumeBindingMode:     Immediate
Events:                <none>
 
通过将名为 standard 的 StorageClass 对象的 Annotations 中设置 "storageclass.kubernetes.io/is-default-class":"true", 将 standard 设置为默认 StorageClass.
在 minikube 中, 使用的存储插件是, "provisioner":"k8s.io/minikube-hostpath". hostPath 将主机节点文件系统上的文件或目录挂载到 Pod 中, 这违背了 "hostPath 仅做只读使用" 和 "一个PV一块盘" 的使用原则, 仅应该作为测试使用.
一般数据库应用应该使用 local 类型的插件, Kubegres 并没有与哪一种具体的 local 插件做绑定, 在实际部署时可以进行定制.

创建 Secret

Secret 属于 Projected Volume, 将加密数据放到 etcd 中, 通过在 Pod 中挂载 Volume 的方式提供访问, 并可以自动更新, 但是更新可能会有延迟, 所以在使用 Secret 的时间敏感相关业务中要增加重试和超时机制.
1
vim my-postgres-secret.yaml
1
2
3
4
5
6
7
8
9
apiVersion: v1
kind: Secret
metadata: 
  name: mypostgres-secret 
  namespace: default
type: Opaque
stringData: 
  superUserPassword: postgresSuperUserPsw 
  replicationUserPassword: postgresReplicaPsw
1
kubectl apply -f my-postgres-secret.yaml

创建 PostgresSQL 实例集群

我们已经在 kubegres.yaml 中定义了 Kubegres Kind, 现在是要使用它的时候了, 具体地说, 我们要创建一个 Kubegres resource. "A resource is a use of a Kind". 下面的 Kubegres YAML 文件包含了让下面的集群跑起来的最小配置需求:
  • 使用 Docker 官方维护的 PostgreSql Docker container 创建的 PostgreSql pods 集群
  • "replica: 3", Kubegres 会创建一个 Primary PostgresSql pod 和 两个 Replica PostgresSql pods
  • 数据将从 Primary PostgreSql pod 实时地复制到两个 Replica PostgreSql pods
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
vim my-postgres.yaml
 
apiVersion: kubegres.reactive-tech.io/v1
kind: Kubegres
metadata:
  name: mypostgres
  namespace: default
 
spec:
 
   replicas: 3
   image: postgres:14.1
 
   database:
      size: 200Mi
 
   env:
      - name: POSTGRES_PASSWORD
        valueFrom:
           secretKeyRef:
              name: mypostgres-secret
              key: superUserPassword
 
      - name: POSTGRES_REPLICATION_PASSWORD
        valueFrom:
           secretKeyRef:
              name: mypostgres-secret
              key: replicationUserPassword
 
这里直接将 mypostgres-secret 中的 Password 用作了环境变量.
在 "spec.database" 中, 除了 "size", 即存储空间的大小(注意 MiB 和 MB 的区别), 还可以在此指定 "storageClassName" 和 "volumeMount". 因为这里没有指定, 所以一律采用默认值. 只要 storageClass 有值了, Kubernetes 就会基于此为每一个 Postgres Pod 自动创建 PV 和 PVC, 是为 Dynamic Provisioning.

部署 Kubegres

1
kubectl apply -f my-postgres.yaml
 
至此, 一个包含 3 个 PostgreSql 实例的集群就跑起来了, 每个 PostgreSql 实例就是一个 pod.
1
2
3
4
5
kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
mypostgres-1-0   1/1     Running   0          118m
mypostgres-2-0   1/1     Running   0          118m
mypostgres-3-0   1/1     Running   0          117m
 
同时, Kubegres 会记录每个 PostgreSql 集群的事件, 这些事件将有助于调试.
1
kubectl get events

PostgreSql 集群内部

示意图:

可以查看一下已经创建的各种 resource:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
kubectl get pod,statefulset,svc,configmap,pv,pvc -o wide
NAME                 READY   STATUS    RESTARTS   AGE    IP           NODE       NOMINATED NODE   READINESS GATES
pod/mypostgres-1-0   1/1     Running   0          126m   10.244.0.4   minikube   <none>           <none>
pod/mypostgres-2-0   1/1     Running   0          126m   10.244.0.5   minikube   <none>           <none>
pod/mypostgres-3-0   1/1     Running   0          126m   10.244.0.6   minikube   <none>           <none>
 
NAME                            READY   AGE    CONTAINERS     IMAGES
statefulset.apps/mypostgres-1   1/1     126m   mypostgres-1   postgres:14.1
statefulset.apps/mypostgres-2   1/1     126m   mypostgres-2   postgres:14.1
statefulset.apps/mypostgres-3   1/1     126m   mypostgres-3   postgres:14.1
 
NAME                         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE    SELECTOR
service/kubernetes           ClusterIP   10.96.0.1    <none>        443/TCP    10d    <none>
service/mypostgres           ClusterIP   None         <none>        5432/TCP   126m   app=mypostgres,replicationRole=primary
service/mypostgres-replica   ClusterIP   None         <none>        5432/TCP   126m   app=mypostgres,replicationRole=replica
 
NAME                             DATA   AGE
configmap/base-kubegres-config   7      126m
configmap/kube-root-ca.crt       1      10d
 
NAME                                                        CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                STORAGECLASS   REASON   AGE    VOLUMEMODE
persistentvolume/pvc-4cf2cea9-608d-4d1c-a469-f8face387e91   200Mi      RWO            Delete           Bound    default/postgres-db-mypostgres-3-0   standard                126m   Filesystem
persistentvolume/pvc-6d292ccf-cb85-42c4-b182-c7e85c758068   200Mi      RWO            Delete           Bound    default/postgres-db-mypostgres-1-0   standard                126m   Filesystem
persistentvolume/pvc-b958ca70-0598-4b53-bcd6-53ff43eb767f   200Mi      RWO            Delete           Bound    default/postgres-db-mypostgres-2-0   standard                126m   Filesystem
 
NAME                                               STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE    VOLUMEMODE
persistentvolumeclaim/postgres-db-mypostgres-1-0   Bound    pvc-6d292ccf-cb85-42c4-b182-c7e85c758068   200Mi      RWO            standard       126m   Filesystem
persistentvolumeclaim/postgres-db-mypostgres-2-0   Bound    pvc-b958ca70-0598-4b53-bcd6-53ff43eb767f   200Mi      RWO            standard       126m   Filesystem
persistentvolumeclaim/postgres-db-mypostgres-3-0   Bound    pvc-4cf2cea9-608d-4d1c-a469-f8face387e91   200Mi      RWO            standard       126m   Filesystem
 
Kubegres 创建了 3 个 pod, "mypostgres-1-0", "mypostgres-2-0", "mypostgres-3-0". 每个 pod 都运行了一个 PostgreSql DB 并且关联同名的 StatefulSet. 理论上每个 pod 会部署在不同的 Node 之上, 不过这是 minikube 所以例外. 题外话, pod 的均匀部署可以通过 3 个方式实现, 1. podAntiAffinity, 2. SelectorSpreadPriority 同 Service 的其他 pod 尽量不在一个 Node 上, 3. Hash.
进一步, Kubegres 创建了两个 Headless Service, "mypostgres" 和 "mypostgres-replica", 也就意味着, 访问 "mypostgres.default.svc.cluster.local' 返回的将是 "mypostgres-1-0" 的 IP 地址. 通过这两个 Headless Service 也就实现了读写分离和负载均衡, 负载均衡策略默认是轮询.
Kubegres 还创建了一个 ConfigMap, 名叫 "base-kubegres-config", ConfigMap 也属于 ProjectedVolume. 在 Kubegres 运行的每个 Namespace, 它都会创建这么一个 ConfigMap.
最后, 针对每一个 Postgres Pod, Kubegres 都使用 StorageClass 创建了一个 PV 和 一个 PVC.
Kubegres 使用预定义的模板来创建这些 Kubernetes resources.

连接客户端 apps 到 PostgreSql

基于 Kubegres YAML 创建的集群, 在集群中的 client app 可以使用下面的参数访问 PostgreSql 数据库:
  • host: mypostgres
  • port: 5432
  • username: postgres
  • password: [value of mypostgres-secret/superUserPassword]
Host
使用 mypostgres 访问 Primary PostgreSql, 使用 mypostgres-replica 访问 Replica PostgreSql.如果 Primary PostgreSql 发生宕机, 故障转移机制将会把一个 Replica PostgreSql 提升为 Primary, 这个过程对客户端透明.
Port
默认使用 5432, 可以自定义.
Username and Password
默认的 "Postgres" 是一个 super user. 自用还是建议添加用户并做好权限控制.

删除 Postgres 集群

1
kubectl delete kubegres mypostgres

  

该操作会删除为集群 mypostgres 创建的所有 resources, 除了 PV 和 PVC, 因为 PV 中存储着数据库, 所以需要手动删除.
上面的操作, 不会删除 Kubegres 这个 Kind, 还有针对这个 Kubegres Kind 的 controller, 通过以下操作将 Kubegres Operator 完整删除
1
kubectl delete -f https://raw.githubusercontent.com/reactive-tech/kubegres/v1.16/kubegres.yaml

备份

可以选择开启备份选项, 对 Primary PostgreSql database 执行定时备份.

创建一个 PVC
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
vim my-backup-pvc.yaml
 
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-backup-pvc
  namespace: default
spec:
  storageClassName: "standard"
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Mi
       
kubectl apply -f my-backup-pvc.yaml
开启备份选项
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
vim my-postgres.yaml
 
apiVersion: kubegres.reactive-tech.io/v1
kind: Kubegres
metadata:
  name: mypostgres
  namespace: default
 
spec:
 
   replicas: 3
   image: postgres:14.1
   port: 5432
 
   database:
      size: 200Mi
 
   backup:
       schedule: "0 */1 * * *"
       pvcName: my-backup-pvc
       volumeMount: /var/lib/backup
 
   env:
      - name: POSTGRES_PASSWORD
        valueFrom:
           secretKeyRef:
              name: mySecretResource
              key: superUserPassword
 
      - name: POSTGRES_REPLICATION_PASSWORD
        valueFrom:
           secretKeyRef:
              name: mySecretResource
              key: replicationUserPassword
               
               
kubectl apply -f my-postgres.yaml

总结

简单地过了一遍 Kubegres 的部署, 对于需要自定义的 YAML 文件和它所创建的各种 resources 有个大概印象, 从下一篇开始尝试对 Kubegres 进行源码解析, 以学习其功能实现和代码组织.
posted @   Herbert_Kwok  阅读(85)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· 葡萄城 AI 搜索升级:DeepSeek 加持,客户体验更智能
· 什么是nginx的强缓存和协商缓存
· 一文读懂知识蒸馏
点击右上角即可分享
微信分享提示