k8s部署MongoDB集群
环境介绍
MongoDB介绍
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统,在高负载的情况下,添加更多的节点,可以保证服务器性能
MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成,文档类似于 JSON 对象,字段值可以包含其他文档,数组及文档数组
部署规划
本文使用StatefulSet部署MongoDB集群,同时每个MongoDB实例使用nfs实现持久化存储,从而部署无单点故障、高可用、可动态扩展的MongoDB集群
部署架构如下:
部署MongoDB集群
禁用巨页
运行一个 DaemonSet 的控制器来管理节点禁用巨页,因为 MongoDB 是建议关闭掉 Transparent Hugepage 的,否则可能导致性能下降,内存锁,甚至系统重启等问题,当然最好的还是只调整 MongoDB 的 Pod 所在的节点
[root@k8s01 mongodb]# cat hostvm-ds.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: hostvm-configurer
labels:
app: startup-script
spec:
selector:
matchLabels:
app: startup-script
template:
metadata:
labels:
app: startup-script
spec:
hostPID: true
containers:
- name: hostvm-configurer
image: cnych/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
[root@k8s01 mongodb]# kubectl apply -f hostvm-ds.yaml
daemonset.apps/hostvm-configurer created
[root@k8s01 mongodb]# kubectl get po -owide|grep hostvm
hostvm-configurer-8hb5j 1/1 Running 0 113s 10.244.2.54 k8s03 <none> <none>
hostvm-configurer-r47fn 1/1 Running 0 113s 10.244.1.91 k8s02 <none> <none>
hostvm-configurer-zbwlw 1/1 Running 0 113s 10.244.0.56 k8s01 <none> <none>
部署mongodb集群
部署 ServiceAccount、Headless SVC 和 StatefulSet
[root@k8s01 mongodb]# cat mongo.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: mongo
namespace: tool-env
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: mongo
subjects:
- kind: ServiceAccount
name: mongo
namespace: tool-env
roleRef:
kind: ClusterRole
name: cluster-admin
apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: Service
metadata:
name: mongo
namespace: tool-env
labels:
name: mongo
spec:
ports:
- port: 27017
targetPort: 27017
clusterIP: None
selector:
role: mongo
---
apiVersion: v1
kind: Service
metadata:
name: mongo-client
namespace: tool-env
labels:
name: mongo
spec:
ports:
- port: 27017
targetPort: 27017
nodePort: 32717
type: NodePort
selector:
role: mongo
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongo
namespace: tool-env
spec:
serviceName: mongo
replicas: 3
selector:
matchLabels:
role: mongo
environment: staging
template:
metadata:
labels:
role: mongo
environment: staging
replicaset: MainRepSet
spec:
affinity:
podAntiAffinity: # 添加 Pod 反亲和性,将副本打散在不同的节点
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:4.0
command:
- mongod
- "--wiredTigerCacheSizeGB"
- "0.25"
- "--bind_ip"
- "0.0.0.0"
- "--replSet"
- MainRepSet
- "--smallfiles"
- "--noprealloc"
ports:
- containerPort: 27017
volumeMounts:
- name: mongo-data
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 #根据自己namespace
value: "tool-env"
- name: KUBERNETES_MONGO_SERVICE_NAME
value: "mongo"
volumeClaimTemplates:
- metadata:
name: mongo-data
spec:
accessModes: [ "ReadWriteOnce" ]
storageClassName: managed-nfs-storage # 提供一个可用的 Storageclass
resources:
requests:
storage: 10Gi
[root@k8s01 mongodb]# kubectl apply -f mongo.yaml
serviceaccount/mongo created
clusterrolebinding.rbac.authorization.k8s.io/mongo created
service/mongo created
service/mongo-client created
statefulset.apps/mongo created
这里给 Mongo 的 Pod 添加了一个 sidecar 容器,主要用于副本集的配置,该 sidecar 会每5s检查一次新成员,通过几个环境变量配置指定了 Pod 的标签、命名空间和 Service,为了保证应用的稳定性,通过 podAntiAffinity 指定了 Pod 的反亲和性,这样可以保证不会有两个副本出现在同一个节点上
部署完成后可以通过如下命令检查应用运行状态
[root@k8s01 mongodb]# kubectl -n tool-env get all
NAME READY STATUS RESTARTS AGE
pod/mongo-0 2/2 Running 0 11m
pod/mongo-1 2/2 Running 0 9m35s
pod/mongo-2 2/2 Running 0 7m9s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/mongo ClusterIP None <none> 27017/TCP 11m
NAME READY AGE
statefulset.apps/mongo 3/3 11m
验证集群
[root@k8s01 mongodb]# kubectl exec -it -n tool-env mongo-0 bash -c mongo
root@mongo-0:/# mongo
MongoDB shell version v4.0.19
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
.......
MainRepSet:SECONDARY> rs.status
function () {
return db._adminCommand("replSetGetStatus");
}
MainRepSet:SECONDARY> rs.status()
{
"set" : "MainRepSet",
"date" : ISODate("2020-08-08T14:32:31.132Z"),
"myState" : 2,
"term" : NumberLong(1),
"syncingTo" : "mongo-1.mongo.tool-env.svc.cluster.local:27017",
"syncSourceHost" : "mongo-1.mongo.tool-env.svc.cluster.local:27017",
"syncSourceId" : 1,
"heartbeatIntervalMillis" : NumberLong(2000),
"optimes" : {
"lastCommittedOpTime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"readConcernMajorityOpTime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"appliedOpTime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"durableOpTime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
}
},
"lastStableCheckpointTimestamp" : Timestamp(1596897114, 1),
"members" : [
{
"_id" : 0,
"name" : "mongo-2.mongo.tool-env.svc.cluster.local:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 171,
"optime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-08-08T14:32:24Z"),
"optimeDurableDate" : ISODate("2020-08-08T14:32:24Z"),
"lastHeartbeat" : ISODate("2020-08-08T14:32:30.035Z"),
"lastHeartbeatRecv" : ISODate("2020-08-08T14:32:29.871Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1596896964, 2),
"electionDate" : ISODate("2020-08-08T14:29:24Z"),
"configVersion" : 4
},
{
"_id" : 1,
"name" : "mongo-1.mongo.tool-env.svc.cluster.local:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 171,
"optime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"optimeDurable" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-08-08T14:32:24Z"),
"optimeDurableDate" : ISODate("2020-08-08T14:32:24Z"),
"lastHeartbeat" : ISODate("2020-08-08T14:32:30.034Z"),
"lastHeartbeatRecv" : ISODate("2020-08-08T14:32:30.369Z"),
"pingMs" : NumberLong(0),
"lastHeartbeatMessage" : "",
"syncingTo" : "mongo-2.mongo.tool-env.svc.cluster.local:27017",
"syncSourceHost" : "mongo-2.mongo.tool-env.svc.cluster.local:27017",
"syncSourceId" : 0,
"infoMessage" : "",
"configVersion" : 4
},
{
"_id" : 2,
"name" : "mongo-0.mongo.tool-env.svc.cluster.local:27017",
"health" : 1,
"state" : 2,
"stateStr" : "SECONDARY",
"uptime" : 196,
"optime" : {
"ts" : Timestamp(1596897144, 1),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-08-08T14:32:24Z"),
"syncingTo" : "mongo-1.mongo.tool-env.svc.cluster.local:27017",
"syncSourceHost" : "mongo-1.mongo.tool-env.svc.cluster.local:27017",
"syncSourceId" : 1,
"infoMessage" : "",
"configVersion" : 4,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
"ok" : 1,
"operationTime" : Timestamp(1596897144, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1596897144, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
Mongo集群扩容
如需要对mongo扩容,只需要调整statefulset的replicas即可
[root@k8s01 mongodb]# kubectl scale statefulset mongo --replicas=4 -n test-env
Mongo使用/访问
mongo cluster访问默认连接为:
mongodb://mongo1,mongo2,mongo3:27017/dbname_?
在kubernetes中最常用的FQDN连接服务的连接为:
appName.$HeadlessServiceName.$Namespace.svc.cluster.local
因为我们采用statefulset部署的pod,所以命名均有规则,所以实际上如果连接4副本的mongodb cluster,上面的默认连接该为(默认为namespace之外):
mongodb://mongo-0.mongo.tool-env.svc.cluster.local:27017,mongo-1.mongo.tool-env.svc.cluster.local:27017,mongo-2.mongo.tool-env.svc.cluster.local:27017,mongo-3.mongo.tool-env.svc.cluster.local:2701