在Kubernetes上扩展MongoDB,这样可以吗?

预告:12月22日,ACOUG联合创始人及多位核心专家将欢聚一堂,为“关键词解析2018技术圈的那些事儿”为主题的分享讨论,欢迎大家踊跃报名参会。

详情:【感恩,回馈,展望】2018ACOUG年会盛大来袭!

在Kubernetes上扩展MongoDB,这样可以吗?

Kubernetes主要用于无状态应用程序。但是,在1.3版本中引入了PetSets,之后它们演变为StatefulSets。官方文档将StatefulSets描述为“StatefulSets旨在与有状态应用程序和分布式系统一起使用”。

对此最好的用例之一是对数据存储服务进行编排,例如MongoDB,ElasticSearch,Redis,ZooKeeper等。

我们可以把StatefulSets的特性归纳如下:

有序索引Pod

稳定的网络ID

有序并行的Pod管理

滚动更新

这些细节可以在这里[1]找到。

StatefulSets的一个非常明显的特征是提供稳定网络ID,与HeadlessServices[2]一起使用时,功能可以更加强大。

我们在Kubernetes文档中随时可以查看的信息上不会花费很多时间,让我们专注于运行和扩展MongoDB集群。

你需要一个可以运行的Kubernetes群集并启用RBAC(推荐)。在本教程中,我将使用GKE集群,但是,AWSEKS或Microsoft的AKS或Kops管理的Kubernetes也是可行的替代方案。

我们将为MongoDB集群部署以下组件:

配置HostVM的DaemonSet

MongoPod的ServiceAccount和ClusterRoleBinding

为Pod提供永久性存储SSDs的StorageClass

访问Mongo容器的HeadlessService

MongoPodsStatefulSet

GCPInternalLB:从Kubernetes集群外部访问MongoDB(可选)

使用Ingress访问Pod(可选)

值得注意的是,每个MongoDBPod都会运行一个Sidecar,以便动态配置副本集。Sidecar每5秒检查一次新成员。

DaemonSetforHostVMConfiguration

kind:DaemonSet

apiVersion:extensions/v1beta1

metadata:

name:hostvm-configurer

labels:

app:startup-script

spec:

template:

metadata:

labels:

app:startup-script

spec:

hostPID:true

containers:

-name:hostvm-configurer-container

image:gcr.io/google-containers/startup-script:v1

securityContext:

privileged:true

env:

-name:STARTUP_SCRIPT

value:|

#!/bin/bash

set-oerrexit

set-opipefail

set-onounset

#Disablehugepages

echo'never'>/sys/kernel/mm/transparent_hugepage/enabled

echo'never'>/sys/kernel/mm/transparent_hugepage/defrag

ConfigurationforServiceAccount,StorageClass,HeadlessSVCandStatefulSet

apiVersion:v1

kind:Namespace

metadata:

name:mongo

---

apiVersion:v1

kind:ServiceAccount

metadata:

name:mongo

namespace:mongo

---

apiVersion:rbac.authorization.k8s.io/v1beta1

kind:ClusterRoleBinding

metadata:

name:mongo

subjects:

-kind:ServiceAccount

name:mongo

namespace:mongo

roleRef:

kind:ClusterRole

name:cluster-admin

apiGroup:rbac.authorization.k8s.io

---

apiVersion:storage.k8s.io/v1beta1

kind:StorageClass

metadata:

name:fast

provisioner:kubernetes.io/gce-pd

parameters:

type:pd-ssd

fsType:xfs

allowVolumeExpansion:true

---

apiVersion:v1

kind:Service

metadata:

name:mongo

namespace:mongo

labels:

name:mongo

spec:

ports:

-port:27017

targetPort:27017

clusterIP:None

selector:

role:mongo

---

apiVersion:apps/v1beta1

kind:StatefulSet

metadata:

name:mongo

namespace:mongo

spec:

serviceName:mongo

replicas:3

template:

metadata:

labels:

role:mongo

environment:staging

replicaset:MainRepSet

spec:

affinity:

podAntiAffinity:

preferredDuringSchedulingIgnoredDuringExecution:

-weight:100

podAffinityTerm:

labelSelector:

matchExpressions:

-key:replicaset

operator:In

values:

-MainRepSet

topologyKey:kubernetes.io/hostname

terminationGracePeriodSeconds:10

serviceAccountName:mongo

containers:

-name:mongo

image:mongo

command:

-mongod

-"--wiredTigerCacheSizeGB"

-"0.25"

-"--bind_ip"

-"0.0.0.0"

-"--replSet"

-MainRepSet

-"--smallfiles"

-"--noprealloc"

ports:

-containerPort:27017

volumeMounts:

-name:mongo-persistent-storage

mountPath:/data/db

resources:

requests:

cpu:1

memory:2Gi

-name:mongo-sidecar

image:cvallance/mongo-k8s-sidecar

env:

-name:MONGO_SIDECAR_POD_LABELS

value:"role=mongo,environment=staging"

-name:KUBE_NAMESPACE

value:"mongo"

-name:KUBERNETES_MONGO_SERVICE_NAME

value:"mongo"

volumeClaimTemplates:

-metadata:

name:mongo-persistent-storage

annotations:

volume.beta.kubernetes.io/storage-class:"fast"

spec:

accessModes:["ReadWriteOnce"]

storageClassName:fast

resources:

requests:

storage:10Gi

关键点:

应该使用适当的环境变量仔细配置Mongo的Sidecar,以及为Pod提供的标签,和为deployment和service的命名空间。有关Sidecar容器的详细信息,请点击此处[3]。

默认缓存大小的指导值是:“50%的RAM减去1GB,或256MB”。鉴于所请求的内存量为2GB,此处的WiredTiger缓存大小已设置为256MB。

Inter-PodAnti-Affinity确保在同一个工作节点上不会安排2个MongoPod,从而使其能够适应节点故障。此外,建议将节点保留在不同的可用区中,以便集群能够抵御区域故障。

当前部署的ServiceAccount具有管理员权限。但是,它应该仅限于DB的命名空间。

上面提到的两个配置文件也可以在这里[4]找到。

部署MongoDB集群

kubectlapply-fconfigure-node.yml

kubectlapply-fmongo.yml

你可以通过以下命令查看所有组件状况:

root$kubectl-nmongogetall

NAMEDESIREDCURRENTAGE

statefulsets/mongo333m

NAMEREADYSTATUSRESTARTSAGE

po/mongo-02/2Running03m

po/mongo-12/2Running02m

po/mongo-22/2Running01m

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE

svc/mongoClusterIPNone<none>27017/TCP3m/<none>

如你所见,该服务没有Cluster-IP,也没有External-IP,它是Headless服务。此服务将直接解析为StatefulSets的Pod-IP。

让我们来验证一下DNS解析。我们在集群中启动了一个交互式shell:

kubectlrunmy-shell--rm-i--tty--imageubuntu--bash

root@my-shell-68974bb7f7-cs4l9:/#digmongo.mongo+search+noall+answer

;<<>>DiG9.11.3-1ubuntu1.1-Ubuntu<<>>mongo.mongo+search+noall+answer

;;globaloptions:+cmd

mongo.mongo.svc.cluster.local.30INA10.56.7.10

mongo.mongo.svc.cluster.local.30INA10.56.8.11

mongo.mongo.svc.cluster.local.30INA10.56.1.4

服务的DNS规则是.,因此,在我们的例子中看到的是mongo.mongo。

IPs(10.56.6.17,10.56.7.10,10.56.8.11)是我们的MongoStatefulSets的PodIPs。这可以通过在集群内部运行nslookup来测试。

root@my-shell-68974bb7f7-cs4l9:/#nslookup10.56.6.17

17.6.56.10.in-addr.arpaname=mongo-0.mongo.mongo.svc.cluster.local.

root@my-shell-68974bb7f7-cs4l9:/#nslookup10.56.7.10

10.7.56.10.in-addr.arpaname=mongo-1.mongo.mongo.svc.cluster.local.

root@my-shell-68974bb7f7-cs4l9:/#nslookup10.56.8.11

11.8.56.10.in-addr.arpaname=mongo-2.mongo.mongo.svc.cluster.local.

如果你的应用程序部署在Kubernetes的群集中,那么它可以通过以下方式访问节点:

Node-0:mongo-0.mongo.mongo.svc.cluster.local:27017

Node-1:mongo-1.mongo.mongo.svc.cluster.local:27017

Node-2:mongo-2.mongo.mongo.svc.cluster.local:27017

如果要从集群外部访问Mongo节点,你可以为每个Pod部署内部负载平衡或使用IngressController(如NGINX或Traefik)创建一个内部Ingress。

GCPInternalLBSVCConfiguration(可选)

apiVersion:v1

kind:Service

metadata:

annotations:

cloud.google.com/load-balancer-type:Internal

name:mongo-0

namespace:mongo

spec:

ports:

-

port:27017

targetPort:27017

selector:

statefulset.kubernetes.io/pod-name:mongo-0

type:LoadBalancer

为mongo-1和mongo-2也部署2个此类服务。

你可以将内部负载均衡的IP提供给MongoClientURI。

root$kubectl-nmongogetsvc

NAMETYPECLUSTER-IPEXTERNAL-IPPORT(S)AGE

mongoClusterIPNone<none>27017/TCP15m/<none>

mongo-0LoadBalancer10.59.252.15710.20.20.227017:30184/TCP9m

mongo-1LoadBalancer10.59.252.23510.20.20.327017:30343/TCP9m

mongo-2LoadBalancer10.59.254.19910.20.20.427017:31298/TCP9m

mongo-0/1/2的外部IP是新创建的TCP负载均衡器的IP。这些是您的子网或对等网络,如果有的话。

通过Ingress访问Pod(可选)

也可以使用诸如Nginx之类的IngressController来定向到MongoStatefulSets的流量。确保Ingress服务是内部服务,而不是通过PublicIP公开。Ingress对象的配置看起来像这样:

...

spec:

rules:

-host:mongo.example.com

http:

paths:

-path:'/mongo-0'

backend:

hostNames:

-mongo-0

serviceName:mongo#Thereisnoextraservice.Thisistheheadlessservice.

servicePort:'27017'

请务必注意,您的应用程序至少应该知道一个当前处于启动状态的Mongo节点,这样可以发现所有其他节点。

我在本地mac上使用Robo3T作为mongo客户端。连接到其中一个节点后并运行rs.status(),您可以查看副本集的详细信息,并检查是否已配置其他2个Pod并自动连接到副本集。

rs.status()查看副本集名称和成员个数

在Kubernetes上扩展MongoDB,这样可以吗?

每个成员都可以看到FQDN和状态。此FQDN只能从群集内部访问。

在Kubernetes上扩展MongoDB,这样可以吗?

每个secondary成员正在同步到mongo-0,mongo-0是当前的primary。

现在我们扩展mongoPods的StatefulSet以检查新的Mongo容器是否被添加到ReplicaSet。

root$kubectl-nmongoscalestatefulsetsmongo--replicas=4

statefulset"mongo"scaled

root$kubectl-nmongogetpods-owide

NAMEREADYSTATUSRESTARTSAGEIPNODE

mongo-02/2Running025m10.56.6.17gke-k8-demo-demo-k8-pool-1-45712bb7-vfqs

mongo-12/2Running024m10.56.7.10gke-k8-demo-demo-k8-pool-1-c6901f2e-trv5

mongo-22/2Running023m10.56.8.11gke-k8-demo-demo-k8-pool-1-c7622fba-qayt

mongo-32/2Running03m10.56.1.4gke-k8-demo-demo-k8-pool-1-85308bb7-89a4

可以看出,所有四个Pod都部署到不同的GKE节点,因此我们的Pod-AntiAffinity策略工作正常。

扩展操作还将自动提供持久卷,该卷将充当新Pod的数据目录。

root$kubectl-nmongogetpvc

NAMESTATUSVOLUMECAPACITYACCESSMODESSTORAGECLASSAGE

mongo-persistent-storage-mongo-0Boundpvc-337fb7d6-9f8f-11e8-bcd6-42010a94002411GRWOfast49m

mongo-persistent-storage-mongo-1Boundpvc-53375e31-9f8f-11e8-bcd6-42010a94002411GRWOfast49m

mongo-persistent-storage-mongo-2Boundpvc-6cee0f97-9f8f-11e8-bcd6-42010a94002411GRWOfast48m

mongo-persistent-storage-mongo-3Boundpvc-3e89573f-9f92-11e8-bcd6-42010a94002411GRWOfast28m

要检查名为mongo-3的Pod是否已添加到副本集,我们将在同一节点上再次运行rs.status()并观察其差异。

在Kubernetes上扩展MongoDB,这样可以吗?

对于同一个的Replicaset,成员数现在为4。

在Kubernetes上扩展MongoDB,这样可以吗?

新添加的成员遵循与先前成员相同的FQDN方案,并且还与同一主节点同步。

进一步的考虑

给MongoPod的NodePool打上合适的label并确保在StatefulSets和HostVM配置的DaemonSets的Spec中指定适当的NodeAffinity会很有帮助。这是因为DaemonSet将调整主机操作系统的一些参数,并且这些设置应仅限于MongoDBPod。没有这些设置,对其他应用程序可能会更好。

在GKE中给NodePool打Label非常容易,可以直接从GCP控制台进行。

虽然我们在Pod的Spec中指定了CPU和内存限制,但我们也可以考虑部署VPA(VerticalPodAutoscaler)。

可以通过实施网络策略或服务网格(如Istio)来控制从集群内部到我们的数据库的流量。

如果你已经看到这里,我相信你已经浏览了整个博文。我试图整理很多分散的信息并将其作为一个整体呈现。我的目标是为您提供足够的信息,以便开始使用Kubernetes上的StatefulSets,并希望你们中的许多人觉得它很有用。我们非常欢迎您提出反馈、意见或建议。:)

相关链接:

https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/

https://kubernetes.io/docs/concepts/services-networking/service/#headless-services

https://github.com/cvallance/mongo-k8s-sidecar

https://github.com/Thakurvaibhav/k8s/tree/master/databases/mongodb

原文链接:https://medium.com/@thakur.vaibhav23/scaling-mongodb-on-kubernetes-32e446c16b82

资源下载

2018DTCC

,数据库大会PPT

2018DTC

,2018DTC大会PPT

DBALIFE

,“DBA的一天”海报

DBA04

,DBA手记4电子书

122ARCH

,Oracle12.2体系结构图

2018OOW

,OracleOpenWorld资料

PRELECTION

,大讲堂讲师课程资料

近期文章

企业数据架构的云化智能重构和变革(含大会PPT)

Oracle研发总裁ThomasKurian加盟GoogleCloud

变与不变:Undo构造一致性读的例外情况

Oracle18c新特性:动态ContainerMap增强ApplicationContainer灵活性

Oracle18c新特性:Schema-Only帐号提升应用管理安全性

Oracle18c新特性:多租户舰队CDBFleet(含PPT)

为什么看了那么多灾难,还是过不好备份这一关?


分享到:


相關文章: