您现在的位置是:首页 > 文章详情

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

日期:2018-12-13点击:297

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

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

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

有序索引Pod

稳定的网络ID

有序并行的Pod管理

滚动更新

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

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

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

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

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

配置HostVM的Daemon Set

Mongo Pod的Service Account和ClusterRole Binding

为Pod提供永久性存储SSDs的Storage Class

访问Mongo容器的Headless Service

Mongo Pods Stateful Set

GCP Internal LB:从Kubernetes集群外部访问MongoDB(可选)

使用Ingress访问Pod(可选)

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

Daemon Set for HostVM Configuration

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 - o errexit set - o pipefail set - o nounset # Disable hugepages echo 'never' >/sys/kernel/mm/transparent_hugepage/enabled echo 'never' > /sys/kernel/mm/transparent_hugepage/defrag

Configuration for ServiceAccount, Storage Class, Headless SVC and StatefulSet

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:mongosubjects: - 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: - mongo -"--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-Pod Anti-Affinity确保在同一个工作节点上不会安排2个Mongo Pod,从而使其能够适应节点故障。 此外,建议将节点保留在不同的可用区中,以便集群能够抵御区域故障。
当前部署的Service Account具有管理员权限。 但是,它应该仅限于DB的命名空间。
上面提到的两个配置文件也可以在这里[4]找到。

部署MongoDB集群

kubectl apply -f configure-node.yml kubectl apply -f mongo.yml

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

root$ kubectl -n mongo get all NAME DESIRED CURRENT AGE statefulsets/mongo 3 3 3m NAME READY STATUS RESTARTS AGE po/mongo-0 2/2 Running 0 3m po/mongo-1 2/2 Running 0 2m po/mongo-2 2/2 Running 0 1m NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S AGE svc/mongo ClusterIP None <none> 27017/TCP 3m

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

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

kubectl run my-shell --rm -i --tty --image ubuntu -- bash root@my-shell-68974bb7f7-cs4l9:/# dig mongo.mongo +search +noall +answer ; <<>>DiG9.11.3-1ubuntu1.1-Ubuntu <<>> mongo.mongo +search +noall +answer;; global options:+cmd mongo.mongo.svc.cluster.local.30 IN A 10.56.7.10 mongo.mongo.svc.cluster.local30 IN A 10.56.8.11 mongo.mongo.svc.cluster.local. 30 IN A 10.56.1.4

服务的DNS规则是<服务名称>.<服务的命名空间>,因此,在我们的例子中看到的是mongo.mongo。

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

root@my-shell-68974bb7f7-cs4l9:/# nslookup 10.56.6.17 17.6.56.10.in-addr.arpa name = mongo-0.mongo.mongo.svc.cluster.local. root@my-shell-68974bb7f7-cs4l9:/# nslookup 10.56.7.1010.7.56.10.in-addr.arpa name = mongo-1.mongo. mongo.svc.cluster.local. root@my-shell-68974bb7f7-cs4l9:/# nslookup 10.56.8.1111.8. 56.10.in-addr.arpa name = 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部署内部负载平衡或使用Ingress Controller(如NGINX或Traefik)创建一个内部Ingress。
GCP Internal LB SVC Configuration(可选)

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提供给MongoClient URI。

root$ kubectl - n mongo get svc NAME TYPE CLUSTER - IP EXTERNAL - IP PORT ( S ) AGE mongo ClusterIP None <none> 27017 / TCP 15m mongo - 0 LoadBalancer 10.59 . 252.157 10.20 . 20.2 27017 : 30184 / TCP 9m mongo - 1 LoadBalancer 10.59 . 252.235 10.20 . 20.3 27017 : 30343 / TCP 9m mongo - 2 LoadBalancer 10.59.254.199 10.20.20.4 27017:31298/ TCP 9m

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

通过Ingress访问Pod(可选)

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

... spec: rules:- host: mongo.example.com http: paths: - path:'/mongo-0' backend: hostNames: - mongo-0 serviceName: mongo # There is no extra service. This is the headless service. servicePort: '27017'

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

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

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


image

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


image

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


现在我们扩展mongo Pods的Stateful Set以检查新的Mongo容器是否被添加到ReplicaSet。
 root$ kubectl - n mongo scale statefulsets mongo -- replicas = 4 statefulset "mongo" scaled root$ kubectl - n mongo get pods - o wide NAME READY STATUS RESTARTS AGE IP NODE mongo - 0 2 / 2 Running 0 25m 10.56 . 6.17 gke - k8 - demo - demo - k8 - pool - 1 - 45712bb7 - vfqs mongo - 1 2 / 2 Running 0 24m 10.56.7.10 gke - k8 - demo - demo - k8 - pool - 1 - c6901f2e - trv5 mongo - 2 2 / 2 Running 0 23m 10.56.8.11 gke - k8 - demo - demo - k8 - pool - 1 - c7622fba - qayt mongo - 3 2 / 2 Running 0 3m 10.56.1.4 gke - k8 - demo - demo - k8 - pool - 1 - 85308bb7 - 89a4

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

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

 root$ kubectl - n mongo get pvc NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE mongo - persistent - storage - mongo - 0 Bound pvc - 337fb7d6 - 9f8f - 11e8 - bcd6 - 42010a940024 11G RWO fast 49m mongo - persistent - storage - mongo - 1 Bound pvc - 53375e31 - 9f8f - 11e8 - bcd6 - 42010a940024 11G RWO fast 49m mongo - persistent - storage - mongo - 2 Bound pvc - 6cee0f97 - 9f8f - 11e8 - bcd6 - 42010a940024 11G RWO fast 48m mongo - persistent - storage - mongo - 3 Bound pvc - 3e89573f - 9f92 - 11e8 - bcd6 - 42010a940024 11G RWO fast 

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

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


image

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

进一步的考虑

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

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

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

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

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

原文发布时间为:2018-12-13
本文作者: kelvinji2009 译
本文来自云栖社区合作伙伴“ 数据和云”,了解相关信息可以关注“OraNews”微信公众号

原文链接:https://yq.aliyun.com/articles/679351
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章