在本地的 Kubernetes 环境中搭建 RocketChat(使用 Local Volume)
简而言之
我将在本地的Kubernetes环境中搭建名为RocketChat的类似Slack的开源聊天工具。
在RocketChat中,使用了mongoDB,并且本次使用LocalVolume作为所使用的卷。数据将保存在运行mongoDB的节点的本地存储中,形式简单。
以这种状态来看的话,无法应对节点故障、无法使用mongoDB的副本集(包括复制和自动故障转移功能),等等存在一些问题。不过,本次计划将按照这种方式进行。
稍后,我打算在文章中提到使用NFS而不是LocalVolume的步骤。
构成
将如下步骤在workerNode1上创建用于RocketChat和mongoDB的POD。
在创建之前,请先创建一个NodePort,并在浏览器中通过http连接到Node的IP地址和设置的NodePort上的端口。

前面提到的条件
操作系统:Ubuntu 16.04.6 LTS
vagrant@master:~$ kubectl version
Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.1", GitCommit:"b7394102d6ef778017f2ca4046abbaa23b8
8c290", GitTreeState:"clean", BuildDate:"2019-04-08T17:11:31Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/amd
64"}
Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.2", GitCommit:"66049e3b21efe110454d67df4fa62b08ea7
9a19b", GitTreeState:"clean", BuildDate:"2019-05-16T16:14:56Z", GoVersion:"go1.12.5", Compiler:"gc", Platform:"linux/amd
64"}
创作了SC
首先,我们将创建一个与LocalVolume兼容的StorageClass。
由于LocalVolume不支持动态卷配置功能Dynamic Volume Provisioning,因此无法使用。
因此,如果要使用LocalVolume,则需要选择provisioner为“kubernetes.io/no-provisioner”。
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: local-storage
provisioner: kubernetes.io/no-provisioner
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl create -f sc_chat.yaml
storageclass.storage.k8s.io/local-storage created
vagrant@master:/vagrant/yaml_file$ kubectl get sc
NAME PROVISIONER AGE
local-storage kubernetes.io/no-provisioner 6s
制作PV和PVC
我们创建 PersistentVolume。
通过 nodeAffinity,将 PV 设置为在 node1 上创建。
访问模式设置为 ReadWriteOnce,可以在单个节点上进行读写操作。
apiVersion: v1
kind: PersistentVolume
metadata:
name: local-pv
spec:
capacity:
storage: 2Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: local-storage
local:
path: /chat_data
nodeAffinity:
required:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl create -f pv_chat.yaml
persistentvolume/local-pv created
vagrant@master:/vagrant/yaml_file$ kubectl get pv -o wide
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
local-pv 2Gi RWO Retain Available local-storage 6s
接下来,我们将创建PersistentVolumeClaim。
在storageClassName中指定我们之前创建的SC的metadata.name。
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-claim
spec:
accessModes:
- ReadWriteOnce
storageClassName: local-storage
resources:
requests:
storage: 2Gi
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl create -f pvc_chat.yaml
persistentvolumeclaim/local-claim created
vagrant@master:/vagrant/yaml_file$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
local-claim Bound local-pv 2Gi RWO local-storage 3s
创建一个MongoDB的POD
spec.replicas: 1 で、レプリカセットをシングルノードで作成します。
スタンドアロンモードでの作成も可能ですが、レプリカセットを使うことでトランザクション機能が使用できるようになります。
从4.0版本开始,MongoDB提供了对副本集进行多文档事务处理的能力。
https://docs.mongodb.com/manual/release-notes/4.0/
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
spec:
replicas: 1 #シングルノードで作成
selector:
matchLabels:
app: mondb
template:
metadata:
labels:
app: mondb
spec:
containers:
- name: mongodb
image: mongo:4.0.18-xenial
args: ["--smallfiles","--replSet","rs0","--oplogSize","128"]
env:
volumeMounts:
- name: vol
mountPath: /data/db
volumes:
- name: vol
persistentVolumeClaim:
claimName: local-claim
spec.template.spec.containers.argsで指定している引数は以下の通りです。
“–smallfiles”:少ない容量で作成
“–replSet”,”rs0″:レプリカセットをrs0という名前で作成
“–oplogSize”:オペログのサイズ
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl apply -f mongodb.yaml
deployment.apps/mongodb-deployment created
vagrant@master:/vagrant/yaml_file$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mongodb-deployment-7b75b6447b-hn5xt 1/1 Running 0 6s 10.244.1.248 node1 <none> <none>
vagrant@master:/vagrant/yaml_file$ kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
mongodb-deployment 1/1 1 1 13s mongodb mongo:4.0.18-xenial app=mondb
初始化MongoDB的复制集
レプリカセットの初期化を行います。
作成したmongoDBのメンバーへ接続し、rs.initiate()コマンドで初期化します。
これを忘れてRocketChatのPODを作成しようとすると、エラーが出て正常に作成できません。
・mongoDBに接続
# mongo 10.244.1.248
・初期化前確認
> rs.status()
{
"operationTime" : Timestamp(0, 0),
"ok" : 0,
"errmsg" : "no replset config has been received",
"code" : 94,
"codeName" : "NotYetInitialized",
"$clusterTime" : {
"clusterTime" : Timestamp(0, 0),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
・初期化実行
> rs.initiate()
{
"info2" : "no configuration specified. Using a default configuration for the set",
"me" : "mongodb-deployment-7b75b6447b-hn5xt:27017",
"ok" : 1,
"operationTime" : Timestamp(1588334257, 1),
"$clusterTime" : {
"clusterTime" : Timestamp(1588334257, 1),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
・実行後、Enterを押すとプロンプトが変化します。
旧)>
新)rs0:PRIMARY>
・初期化後確認
rs0:PRIMARY> rs.status()
{
"set" : "rs0",
"date" : ISODate("2020-05-01T11:57:52.337Z"),
"myState" : 1,
***長いので省略***
"members" : [
{
"_id" : 0,
"name" : "mongodb-deployment-7b75b6447b-hn5xt:27017",
"health" : 1,
"state" : 1,
"stateStr" : "PRIMARY",
"uptime" : 945,
"optime" : {
"ts" : Timestamp(1588334257, 7),
"t" : NumberLong(1)
},
"optimeDate" : ISODate("2020-05-01T11:57:37Z"),
"syncingTo" : "",
"syncSourceHost" : "",
"syncSourceId" : -1,
"infoMessage" : "",
"electionTime" : Timestamp(1588334257, 2),
"electionDate" : ISODate("2020-05-01T11:57:37Z"),
"configVersion" : 1,
"self" : true,
"lastHeartbeatMessage" : ""
}
],
"ok" : 1,
"operationTime" : Timestamp(1588334257, 7),
"$clusterTime" : {
"clusterTime" : Timestamp(1588334257, 7),
"signature" : {
"hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
"keyId" : NumberLong(0)
}
}
}
创建 Cluster IP
在spec.selector.app中,将mongoDB的spec.selector.matchLabels.app列出。
另外,将spec.ports.port指定为27017。
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
type: ClusterIP
selector:
app: mondb
ports:
- protocol: TCP
port: 27017
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl apply -f service_chat.yaml
service/mongodb-service created
vagrant@master:/vagrant/yaml_file$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
mongodb-service ClusterIP 10.244.181.86 <none> 27017/TCP 7s app=mondb
RocketChatの作成
・MONGO_URL:连接到MongoDB的URL
→ 在spec.template.spec.env.value中设置为mongodb://<ClusterIP服务名>:<ClusterIP端口>/<用于RocketChat的MongoDB数据库名称>。
・根网址:RocketChat的URL
→根据环境的不同可能会有所变化,在这里我们设置为http://localhost:3000/。
・MONGO_OPLOG_URL:用于存储MongoDB的OPLOG
→在spec.template.spec.env.value中设置为mongodb://<ClusterIP的服务名称>:<ClusterIP的端口>/local。
apiVersion: apps/v1
kind: Deployment
metadata:
name: rocket-deployment
spec:
replicas: 1
selector:
matchLabels:
app: rocket
template:
metadata:
labels:
app: rocket
spec:
containers:
- name: rocket
image: rocket.chat:3.0.12
ports:
- containerPort: 3000
env:
- name: MONGO_URL
value: mongodb://mongodb-service:27017/rocket
- name: ROOT_URL
value: http://localhost:3000/
- name: MONGO_OPLOG_URL
value: mongodb://mongodb-service:27017/local
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- node1
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl apply -f rocketchat.yaml
deployment.apps/rocket-deployment created
vagrant@master:/vagrant/yaml_file$
vagrant@master:/vagrant/yaml_file$ kubectl get deployment -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
mongodb-deployment 1/1 1 1 18m mongodb mongo:4.0.18-xenial app=mondb
rocket-deployment 1/1 1 1 4s rocket rocket.chat:3.0.12 app=rocket
vagrant@master:/vagrant/yaml_file$ kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mongodb-deployment-7b75b6447b-hn5xt 1/1 Running 0 18m 10.244.1.248 node1 <none> <none>
rocket-deployment-77596dcf9f-c9w9z 1/1 Running 0 6s 10.244.1.251 node1 <none> <none>
确认结果
确认日志后,可以确认服务器正在运行。
vagrant@master:/vagrant/yaml_file$ kubectl logs rocket-deployment-77596dcf9f-c9w9z
Setting default file store to GridFS
LocalStore: store created at
LocalStore: store created at
LocalStore: store created at
ufs: temp directory created at "/tmp/ufs"
Loaded the Apps Framework and loaded a total of 0 Apps!
Updating process.env.MAIL_URL
Using GridFS for custom sounds storage
Using GridFS for custom emoji storage
Browserslist: caniuse-lite is outdated. Please run next command `npm update`
➔ System ➔ startup
➔ +--------------------------------------------------+
➔ | SERVER RUNNING |
➔ +--------------------------------------------------+
➔ | |
➔ | Rocket.Chat Version: 3.0.12 |
➔ | NodeJS Version: 12.14.0 - x64 |
➔ | MongoDB Version: 4.0.18 |
➔ | MongoDB Engine: wiredTiger |
➔ | Platform: linux |
➔ | Process Port: 3000 |
➔ | Site URL: http://172.16.20.12:3000/ |
➔ | ReplicaSet OpLog: Enabled |
➔ | Commit Hash: 15ac7670be |
➔ | Commit Branch: HEAD |
➔ | |
➔ +--------------------------------------------------+
创建NodePort
我们将按照以下的指导方针来记录spec.ports。
从Kubernetes的发现和负载均衡资源(第一部分)中引用
spec.ports[x].port ClusterIPで受け付けるPort番号
spec.ports[x].targetPort 転送先のコンテナのPort番号
spec.ports[x].nodePort 全Kubernetes NodeのIP Adressで受け付けるPort番号
这次我们制作了如下所示的内容。
apiVersion: v1
kind: Service
metadata:
name: rocket-service
spec:
type: NodePort
selector:
app: rocket
ports:
- protocol: TCP
port: 3000
targetPort: 3000
nodePort: 31000
执行结果
vagrant@master:/vagrant/yaml_file$ kubectl apply -f service_nodeport_chat.yaml
service/rocket-service created
vagrant@master:/vagrant/yaml_file$ kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
mongodb-service ClusterIP 10.244.181.86 <none> 27017/TCP 23m app=mondb
rocket-service NodePort 10.244.175.43 <none> 3000:31000/TCP 2s app=rocket
服务连接
我会从浏览器上连接到 http://172.16.20.12:31000/。


请以本机汉语进行再创作,请提供一个选项:
・MongoDB相关
在Kubernetes上构建独立的MongoDB服务器的方法
我能理解的系列:MongoDB复制
尝试MongoDB Replica Sets时的备忘录
・RocketChat
官方参考资料
官方安装文档