Kubernetes部署MongoDB集群(三):用户数据库服务配置公网访问
本文为系列第三部分,使用第二部分创建好的userdb
可以从公网访问,方法是通过Kubernetes
Service暴露userdb pod到公网上。
整个系列:
- 安装MongoDB Ops Manager
- 创建用户数据库(replicaset)
- 用户数据库服务配置公网访问
- openssl生成自签名CA证书和server证书
- 打开用户数据库TLS通信加密和Auth授权
官方文档是通过NodePort的方式暴露给公网,而我们这里是通过创建service的方式完成,好处是这种方式不必管理pod和node之间的端口映射,也不必担心pod被调度到不同node上之后IP的改变。不知道官方使用NodePort进行服务暴露是基于怎样的考虑。 # Connect to a MongoDB Database Resource from Outside Kubernetes
在此之前,先保证replicaset中spec.security.tls.enabled
为false,否则各server之间的auth可能会让配置变得复杂。
命令行创建LoadBalancer Service
$ kubectl expose pod/userdb-0 --type="LoadBalancer" --port 27017 -n mongodb
service/userdb-0 exposed
$ kubectl expose pod/userdb-1 --type="LoadBalancer" --port 27017 -n mongodb
service/userdb-1 exposed
$ kubectl expose pod/userdb-2 --type="LoadBalancer" --port 27017 -n mongodb
service/userdb-2 exposed
可以在kubernetes dashboard看到我们创建了三个service: userdb-0, userdb-1, userdb-2,同时有三个公网IP。
这种暴露方式在pod更新之后可能会出现service匹配不到pod的情况,因为statefulset是用controller-revision-hash
来标记pod的版本。所以要编辑一下这几个service中spec.selector部分,把controller-revision-hash
一行去掉:
...
spec:
ports:
- protocol: TCP
port: 27017
targetPort: 27017
nodePort: 30560
selector:
app: userdb-svc
controller: mongodb-enterprise-operator
controller-revision-hash: userdb-676c9c444 # DELETE THIS LINE!
pod-anti-affinity: userdb
statefulset.kubernetes.io/pod-name: userdb-0
通过yaml创建Service
新建userdb0service.yaml文件:
apiVersion: v1
kind: Service
metadata:
name: userdb-0-svc-ext
namespace: mongodb
labels:
app: userdb-svc
controller: mongodb-enterprise-operator
pod-anti-affinity: userdb
statefulset.kubernetes.io/pod-name: userdb-0
spec:
type: LoadBalancer
ports:
- protocol: TCP
port: 27017
targetPort: 27017
selector:
app: userdb-svc
controller: mongodb-enterprise-operator
pod-anti-affinity: userdb
statefulset.kubernetes.io/pod-name: userdb-0
直接apply:
$ kubectl apply -f userdb0service.yaml
service/userdb-0-svc-ext created
过一会儿,userdb-0-svc-ext
就会被分配一个公网IP,然后可以在域名服务商那里将此IP绑定到域名。此处假设域名为userdb<X>.com
。同理,我们将另外两个pod
userdb1
和userdb2
也通过LoadBalancer暴露给公网。
客户端访问和getaddrinfo ENOTFOUND
错误
至此,我们可以通过mongo
shell来访问userdb
了,查看下三个service的IP(以<ip0>``<ip1>``<ip2>
代替):
$ mongo "mongodb://<ip0>:27017,<ip1>:27017,<ip2>:27017/"
MongoDB shell version v4.4.2
connecting to: mongodb://<ip0>:27017,<ip1>:27017,<ip2>:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("6b780874-6ecb-4510-b7e9-61bff04a4711") }
MongoDB server version: 4.2.2
WARNING: shell and server versions do not match
---
The server generated these startup warnings when booting:
2020-12-10T08:32:10.037+0000 I STORAGE [initandlisten]
2020-12-10T08:32:10.037+0000 I STORAGE [initandlisten] ** WARNING: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine
2020-12-10T08:32:10.037+0000 I STORAGE [initandlisten] ** See http://dochub.mongodb.org/core/prodnotes-filesystem
2020-12-10T08:32:10.848+0000 I CONTROL [initandlisten]
2020-12-10T08:32:10.848+0000 I CONTROL [initandlisten] ** WARNING: Access control is not enabled for the database.
2020-12-10T08:32:10.848+0000 I CONTROL [initandlisten] ** Read and write access to data and configuration is unrestricted.
2020-12-10T08:32:10.848+0000 I CONTROL [initandlisten]
---
MongoDB Enterprise userdb:SECONDARY>
似乎有些问题,为什么connect的是SECONDARY节点?mongo默认的readPreference是primary。如果用MongoDB
Compass连接,则会出现连接失败的问题,错误是: getaddrinfo ENOTFOUND userdb-0.userdb-svc.mongodb.svc.cluster.local
搜索了很久,这篇文章 # Connecting from external sources to MongoDB replica set in Kubernetes fails with getaddrinfo ENOTFOUND error but standalone works解释了这种现象。
client会逐个连接connectionstring指定的这三个ip:port
地址(种子列表)直到成功,然后使用isMaster
确定这个节点他是否是master,并且得到replica
set的成员列表。然后,client会弃用connectionstring中的种子列表,而使用获取的成员列表连接到每个成员。这个成员列表一般来说与rs.conf()
命令获取的列表一致。而在Kubernetes集群中,获取的地址都是内网地址(xxx.svc.cluster.local
),因此client连接不成功,会报错getaddrinfo ENOTFOUND
。在登入的shell中运行rs.conf()
印证了这一点:
MongoDB Enterprise userdb:SECONDARY> rs.conf()
{
"_id" : "userdb",
"version" : 1,
"protocolVersion" : NumberLong(1),
"writeConcernMajorityJournalDefault" : true,
"members" : [
{
"_id" : 0,
"host" : "userdb-0.userdb-svc.mongodb.svc.cluster.local:27017",
...
},
{
"_id" : 1,
"host" : "userdb-1.userdb-svc.mongodb.svc.cluster.local:27017",
...
},
{
"_id" : 2,
"host" : "userdb-2.userdb-svc.mongodb.svc.cluster.local:27017",
...
}
],
"settings" : {
...
}
}
此外,从client的行为分析,Linux下的mongo shell和MongoDB
Compass的实现行为不一致:mongo
shell可以连接MongoDB成功,但可能显示为SECONDARY
;而MongoDB
Compass会报错,连接失败。
解决这个问题,需要在userdb.yaml中使用spec.connectivity.replicaSetHorizons
指定公网访问的地址。而使用此选项又必须打开spec.security.tls
:
security:
tls:
enabled: true
connectivity:
replicaSetHorizons:
- "userdb": "userdb0.com:27017"
- "userdb": "userdb1.com:27017"
- "userdb": "userdb2.com:27017"
打开spec.security.tls
比较麻烦,需要先生成每个server的证书,证书上绑定了自己的域名或IP,然后才能再更新userdb
replicaset的yaml文件打开TLS。
我们先来生成MongoDB所需要的证书: Kubernetes部署MongoDB集群(四):openssl生成自签名CA证书和server证书