【本文目标】
在minikube上部署MongoDB和Mongo-express,MongoDB作为内部项目(internal service),Mongo-express会连接到Mongo服务上,Mongo-express会对外暴露。
注:本文用到的yaml配置文件有大神在git上已经上传了,可以直接使用,github地址:https://github.com/Einsteinish/mongo-mongoexpress-minikube
【前置文章】
- 【k8s学习】Kubernetes学习——核心组件和架构
- 【k8s学习】minikube、kubectl、yaml配置文件的介绍
【运行环境 】
- MacOS
- minikube version: v1.25.2
- hyperkit: 0.20200908
(--vm-driver可以是docker或hyperkit,在启动minikube以及部署mongo db上都没有问题,这里用hyperkit的主要原因是docker在MacOS系统上minikube对外暴露服务有点问题。详见第3.5章节。)
1. 架构
用到的Component:
2个Deployment / Pod
2个Service
1个ConfigMap
1个Secret
架构:
可以看到:
-
2个Deployment / Pod
:即MongoDB和Mongo-express -
2个Service
:internal service(MongoDB)和external service(Mongo-express),外部用户可以用Node的IP+external Service的Port访问到Mongo-express,进而连接到internal service中的MongoDB。 -
1个ConfigMap
,用来配置MongoDB的地址:DB Url,供Mongo-express的Deployment.yaml当作环境变量读取。 -
1个Secret
,用来配置MongoDB的用户名(DB User)和密码(DB Pwd),供Mongo-express的Deployment.yaml当作环境变量读取。
运行环境
运行在minikube上,并且集群中没有部署项目:
2. MongoDB部署
2.1 MongoDB docker hub
首先我们想要运行的是MongoDB最新版,docker hub地址:https://hub.docker.com/_/mongo
下拉页面,可以看到默认的Port是:27017
。
其次是查看环境变量,可以看到我们需要定义MongoDB的username和password,变量名称为:MONGO_INITDB_ROOT_USERNAME
, MONGO_INITDB_ROOT_PASSWORD
:
:
2.2 定义Secret
我们在定义的时候,不会直接把username和password在mongodb-deployment.yaml中hardcode,因为mongodb-deployment.yaml通常会checkin到Git repository,如果直接写那么所有人都能看到,这样不安全。所以我们会使用Kubernetes的Secret组件,这样用户名和密码就存在Kubernetes中,而不是Git repository中。
基于以上原因,我们先定义Secret组件,配置文件叫:mongodb-secret.yaml:
apiVersion: v1
kind: Secret
metadata:
name: mongodb-secret
type: Opaque
data:
mongo-root-username: dXNlcm5hbWU=
mongo-root-password: cGFzc3dvcmQ=
-
apiVerion
和kind
这两行声明是必须的,因为要定义Secret组件,那么kind就是Secret。 -
metadata
中的name是Secret运行后的名字,可以自由定义。 -
type
是Secret的type,Opaque是最常见的type,base64编码格式的Secret,用来存储密码、秘钥等。 -
data
就是Secret的内容。data需要用base64加密后,再cp到这里。
我们可以用命令先把想要的明文加密成密文后,再放到上述的data中。
通过mongodb-secret.yaml先创建出Secret组件:
可以看到Secret组件创建成功:kubectl apply -f mongodb-secret.yaml
2.3 开始定义mongodb-deployment.yaml文件
在Deployment文件中会引用到2.2中定义的Secret。
apiVersion: apps/v1
kind: Deployment
metadata:
name: mondodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
通过配置文件创建deployment:
2.4 定义MongoDB的internal service:
我们接着上述的Deployment定义,把MongoDB service也放在同一个文件中,用---
分割开(上述mongo-deployment.yaml改名为mongodb.yaml):
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
-
apiVersion
和kind
:最前面的两行声明,kind表示定义的组件是Service。 -
metadata
和name
表示Service的名称,可自行取名。 -
selector
:和上述Deployment定义中的Pod联系起来。(selector中的和上述template中的metadata中的labels对应)。 -
ports
:port
表示Service的端口,targetPort
表示Pod中Container容器的端口,与上述的Deployment中的Pod中的Container的containerPort要一致。
port和targetPort可以不一致。
通过mongodb.yaml创建Service,可以看到它会先提示Deployment没有改动,然后再提示Service创建了。
查看Pod和Service具体信息,可以看到Service的Endpoint信息,指向的IP是Pod的IP:
【总结】至此,MongoDB相关的组件都创建好了:
- 我们先创建了
Secret
组件,用来存放username和password。 - 再创建了
Deployment
,在用户名和密码这块,引用了Secret组件中的key。在Deployment中定义了mongodb的Pod,并且声明了container容器的Port=27017。 - 最后在和Deployment定义的同一个配置文件中定义了
Service
,通过selector来联系Deployment中的Pod,通过Ports中的targetPort来联系Deployment中的Pod中的Container port。并且同时也通过Ports中的port来暴露自身的端口号,同样是27017。
3. MongoDB-express部署
3.1 MongoDB-express docker hub
MongoDB-express docker hub地址:https://hub.docker.com/_/mongo-express
可以下拉查看基础信息,默认的port为:- MongoDB地址(即internal service):
ME_CONFIG_MONGODB_SERVER
- 用户名:
ME_CONFIG_MONGODB_ADMINUSERNAME
- 密码:
ME_CONFIG_MONGODB_ADMINPASSWORD
3.2 定义ConfigMap
上述三个变量(MongoDB地址,用户名和密码),其中用户名和密码我们可以从Secret组件中读取(查看2.2章),剩下来的MongoDB地址,没有必要加密,但为什么不放在mongodb-express.yaml中是因为,可能MongoDB以后换地址了,那么作为读取他的mongodb-express项目,不想要重新打包并部署,所以我们使用组件ConfigMap
来定义MongoDB地址。
新建配置mongodb-configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-configmap
data:
database_url: mongodb-service
创建ConfigMap组件:
3.3 定义MongoDB-express Deployment:
新建mongodb-express.yaml
文件,用来定义Deployment和Service,首先是Deployment,可以看到定义了Pod中的容器的containerPort
为8081,三个变量其中用户名和密码从Secret
组件中读取(valueFrom定义的是secretKeyRef
),而server信息,是从ConfigMap
组件中读取(valueFrom定义的是configMapKeyRef
。)
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongo-express
labels:
app: mongo-express
spec:
replicas: 1
selector:
matchLabels:
app: mongo-express
template:
metadata:
labels:
app: mongo-express
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_ADMINUSERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: ME_CONFIG_MONGODB_ADMINPASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
- name: ME_CONFIG_MONGODB_SERVER
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
创建Deployment:
3.4 定义MongoDB-express的external service
我们在上述的mongodb-express.yaml中追加Service组件的定义,用---
分隔,这里和上述定义mongodb的internal service略有不同的是,我们在mongodb-express中需要定义external service,以便外部的客户可以通过浏览器访问express dashboard页面。所以我们在spec中需要额外定义service的type为LoadBalancer,表示给这个service一个外部的IP以便可以接收外部的请求。
注:内部的internal service没有定义type,不定义type的时候,默认为ClusterIP
,也是load balanced,即如果有两个mongodb的Pod,我们访问mongo-service,那么也会做负载均衡跳到两个Pod中的一个。
所以type定义为LoadBlancer是在负载均衡的基础上再做了暴露Service IP的作用。
另外,除了Service自身的端口port
,以及需要转发的Pod中的container的端口targetPort
外,我们还需要定义第三种端口,即nodePort
,这个端口表示的意思是给外部IP地址访问时用的端口,一般来说范围在30000~32767之间。在例子中,我们定义为30000:
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-express-service
spec:
selector:
app: mongodb-express
type: LoadBalancer
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30000
创建Service:
查看Service:
- mongodb的internal service类型为ClusterIP,它会分配内部IP(cluster-ip,集群内IP,即内部IP)给这个Service。并且如果是internal service,port这一列显示的就是它的端口。
- mongo-express-service的external service类型则为LoadBalancer,同样的也会分配内部IP,除此之外,还会分配外部IP(external-ip,即Node本身的IP)给这个Service。如果是真实的kubernetes集群,这里也会显示IP格式的地址,因为我跑在minkube上,所以有一点不一样,显示的是
(表示还没有外部IP,在minikube上需要额外配)。并且如果是LoadBalancer类型的Service,port这里会有两个端口,前者是Service的端口,即cluster-ip的端口,后者是外部端口,即external-ip的端口。
如上述所说,在minikube的external-ip中,显示的是
,表示还没有分配外部IP,所以我们需要手动分配:
这个命令表示给service mongo-express-service分配一个public的IP地址,这个是minikube only的,所以你看到的是minikube的命令,而不是kubectl:
minikube service mongo-express-service
3.5 MacOS,vm-driver=docker,对外暴露有点问题
我的系统是MacOS,然后minikube选择的虚拟driver是docker,在service expose的过程中出现了一些问题,发现命令停在了以下状态并且无法正确expose:网上看了一圈说是MacOS上的minikube用docker作虚拟driver的时候,network会有点问题,参考:https://github.com/kubernetes/minikube/issues/11193,解决办法是可以安装https://github.com/yuzhaopeng/minikube-mac-network,中文文档有:https://zhuanlan.zhihu.com/p/446915171,(这个解决办法我没有试过)。
**但我觉得太麻烦了,直接改个虚拟driver,用hyperkit
代替docker。
3.6 安装新的vm-driver:hyperkit
hyperkit是轻量级虚拟化工具包。先安装hyperkit:**
安装完毕:brew install hyperkit
启动minikube,这次使用的是vm-driver=hyperkit,而不是docker:
minikube start --vm-driver=hyperkit
3.7 使用hyperkit作为vm-driver后,再次启动,并对外暴露服务
启动好了之后,再重新kubectl apply -f
在组件(secret, configmap, deployment, pod, replicaset, service)都创建好了之后,再次使用minikube的命令进行对外暴露:
minikube service mongo-express-service --url
返回:http://192.168.64.4:30000
使用minikube service命令查看所有的服务:
minikube service list
4. 测试
在mongo-express UI页面上新增一个database:这个请求:首先会跳到Mongo Express External Service
--> 然后再转发到Mongo Express Pod
上 --> 再通过Pod上的url转到Mongo DB Internal Serivce
--> 再转发到Mongo DB Pod
参考:
https://www.youtube.com/watch?v=X48VuDVv0do
https://www.bogotobogo.com/DevOps/Docker/Docker_Kubernetes_MongoDB_MongoExpress.php
https://github.com/Einsteinish/mongo-mongoexpress-minikube