版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第KubeSphere中部署Wiki系统wiki.js并启用中文全文检索目录背景准备storageclass部署PostgreSQL数据库准备用户名密码配置准备数据库初始化脚本准备存储部署PostgreSQL数据库创建供其他Pod访问的Service完成PostgreSQL部署部署wiki.js准备用户名密码配置准备数据库连接配置创建数据库用户和数据库准备wiki.js的yaml部署文件创建集群内访问wiki.js的Service创建集群外访问的Ingress执行部署配置wiki.js支持中文全文检索授予wikijs用户临时超管权限启用数据库的中文分词能力取消wikijs用户的临时超管权限创建支持中文分词的配置ConfigMap更新wikijs的Deployment配置wiki.js,启用基于PostgreSQL的全文检索总结
背景
wiki.js是优秀的开源Wiki系统,相较于xwiki,功能目前性上比xwiki不够完善,但也在不断进步。Wiki写作、分享、权限管理功能还是有的,胜在UI设计很漂亮,能满足小团队的基本知识管理需求。
以下工作是在KubeSphere3.2.1+Helm3已经部署好的情况下进行的。
部署KuberSphere的方法官网有很详细的文档介绍,这里不再赘叙。/docs/instal
准备storageclass
我们使用OpenEBS作为存储,OpenEBS默认安装的LocalStorageSlass在Pod销毁后自动删除,不适合用于我的MySQL存储,我们在LocalStorageClass基础上稍作修改,创建新的StorageClass,允许Pod销毁后,PV内容继续保留,手动决定怎么处理。
apiVersion:v1
items:
-apiVersion:storage.k8s.io/v1
kind:StorageClass
metadata:
annotations:
cas.openebs.io/config:|
-name:StorageType
value:"hostpath"
-name:BasePath
value:"/var/openebs/localretain/"
openebs.io/cas-type:local
storageclass.beta.kubernetes.io/is-default-class:"false"
storageclass.kubesphere.io/supported-access-modes:'["ReadWriteOnce"]'
name:localretain
provisioner:openebs.io/local
reclaimPolicy:Retain
volumeBindingMode:WaitForFirstConsumer
kind:List
metadata:
resourceVersion:""
selfLink:""
部署PostgreSQL数据库
我们团队其他项目中也需要使用PostgreSQL,为了提高PostgreSQL数据库的利用率和统一管理,我们独立部署PostgreSQL,并在安装wiki.js时,配置为使用外部数据库。
准备用户名密码配置
我们使用Secret保存PostgreSQL用户密码等敏感信息。
kind:Secret
apiVersion:v1
metadata:
name:postgres-prod
data:
POSTGRES_PASSWORD:xxxx
type:Opaque
以上POSTGRES_PASSWORD自行准备,为base64编码的数据。
准备数据库初始化脚本
使用ConfigMap保存数据库初始化脚本,在数据库创建时,将ConfigMap中的数据库初始化脚本挂载到/docker-entrypoint-initdb.d,容器初始化时会自动执行该脚本。
apiVersion:v1
kind:ConfigMap
metadata:
name:wikijs-postgres-init
data:
init.sql:|-
CREATEDATABASEwikijs;
CREATEUSERwikijswithpassword'xxxx';
GRANTCONNECTONDATABASEwikijstowikijs;
GRANTUSAGEONSCHEMApublicTOwikijs;
GRANTSELECT,update,INSERT,deleteONALLTABLESINSCHEMApublicTOwikijs;
ALTERDEFAULTPRIVILEGESINSCHEMApublicGRANTSELECTONTABLESTOwikijs;
以上wikijs用户的密码自行准备,明文保存。
准备存储
我们使用KubeSphere默认安装的OpenEBS来提供存储服务。可以通过创建PVC来提供持久化存储。
这里声明一个10G的PVC。
kind:PersistentVolumeClaim
apiVersion:v1
metadata:
name:postgres-prod-data
finalizers:
-kubernetes.io/pvc-protection
spec:
accessModes:
-ReadWriteOnce
resources:
requests:
storage:10Gi
storageClassName:localretain
volumeMode:Filesystem
部署PostgreSQL数据库
在前面的步骤准备好各种配置信息和存储后,就可以开始部署PostgreSQL服务了。
我们的Kubernetes没有配置存储阵列,使用的是OpenEBS作为存储,采用Deployment方式部署PostgreSQL。
apiVersion:apps/v1
kind:Deployment
metadata:
labels:
app:postgres-prod
name:postgres-prod
spec:
replicas:1
selector:
matchLabels:
app:postgres-prod
template:
metadata:
labels:
app:postgres-prod
spec:
containers:
-name:db
imagePullPolicy:IfNotPresent
image:'abcfy2/zhparser:12-alpine'
ports:
-name:tcp-5432
protocol:TCP
containerPort:5432
envFrom:
-secretRef:
name:postgres-prod
volumeMounts:
-name:postgres-prod-data
readOnly:false
mountPath:/var/lib/postgresql/data
-name:wikijs-postgres-init
readOnly:true
mountPath:/docker-entrypoint-initdb.d
volumes:
-name:postgres-prod-data
persistentVolumeClaim:
claimName:postgres-prod-data
-name:wikijs-postgres-init
configMap:
name:wikijs-postgres-init
创建供其他Pod访问的Service
apiVersion:v1
kind:Service
metadata:
name:postgres-prod
spec:
selector:
app:postgres-prod
ports:
-protocol:TCP
port:5432
targetPort:tcp-5432
完成PostgreSQL部署
测试略
部署wiki.js
准备用户名密码配置
我们使用Secret保存wiki.js用于连接数据库的用户名密码等敏感信息。
apiVersion:v1
kind:Secret
metadata:
name:wikijs
data:
DB_USER:d2lraWpz
DB_PASS:xxxx
type:Opaque
以上DB_PASS自行准备,为base64编码的数据。
准备数据库连接配置
我们使用ConfigMap保存wiki.js的数据库连接信息。
apiVersion:v1
kind:ConfigMap
metadata:
name:wikijs
data:
DB_TYPE:postgres
DB_HOST:postgres-prod.infra
DB_PORT:"5432"
DB_NAME:wikijs
HA_ACTIVE:"true"
创建数据库用户和数据库
如果PostgreSQL数据库里没有创建wikijs用户和数据,需要手工完成一下工作:
通过『数据库工具』连接PostgreSQL数据库,执行一下SQL语句,完成数据库和用户的创建、授权。
CREATEDATABASEwikijs;
CREATEUSERwikijswithpassword'xxxx';
GRANTCONNECTONDATABASEwikijstowikijs;
GRANTUSAGEONSCHEMApublicTOwikijs;
GRANTSELECT,update,INSERT,deleteONALLTABLESINSCHEMApublicTOwikijs;
ALTERDEFAULTPRIVILEGESINSCHEMApublicGRANTSELECTONTABLESTOwikijs;
以上wikijs的密码自行修改。
准备wiki.js的yaml部署文件
采用Deployment方式部署wiki.js的yaml文件如下:
#wikijs-deploy.yaml
apiVersion:apps/v1
kind:Deployment
metadata:
labels:
app:wikijs
name:wikijs
spec:
replicas:1
selector:
matchLabels:
app:wikijs
template:
metadata:
labels:
app:wikijs
spec:
containers:
-name:wikijs
image:'requarks/wiki:2'
ports:
-name:http-3000
protocol:TCP
containerPort:3000
envFrom:
-secretRef:
name:wikijs
-configMapRef:
name:wikijs
创建集群内访问wiki.js的Service
#wikijs-svc.yaml
apiVersion:v1
kind:Service
metadata:
name:wikijs
spec:
selector:
app:wikijs
ports:
-protocol:TCP
port:3000
targetPort:http-3000
创建集群外访问的Ingress
#wikijs-ing.yaml
kind:Ingress
apiVersion:networking.k8s.io/v1
metadata:
name:wikijs
spec:
ingressClassName:nginx
rules:
-host:
http:
paths:
-path:/
pathType:ImplementationSpecific
backend:
service:
name:wikijs
port:
number:3000
以上host域名需要自行配置。
执行部署
$kubectlapply-fwikijs-deploy.yaml
$kubectlapply-fwikijs-svc.yaml
$kubectlapply-fwikijs-ing.yaml
配置wiki.js支持中文全文检索
wiki.js的全文检索支持基于PostgreSQL的检索,也支持Elasticsearch等,相对来说,PostgreSQL比较轻量级,本项目中,我们使用PostgreSQL的全文检索。
但是,因为PostgreSQL不支持中文分词,需要额外安装插件并配置启用中文分词,下面描述了为wiki.js启动基于PostgreSQL数据库中文分词的全文检索。
授予wikijs用户临时超管权限
通过数据库管理工具登录有超管权限的PostgreSQL用户,临时授予wiki.js用户临时超管权限,便于启动中文分词功能。
ALTERUSERwikijsWITHSUPERUSER;
启用数据库的中文分词能力
使用数据库管理工具登录PostgreSQL数据库的wikijs用户,执行以下命令,启动数据库的中文分词功能。
CREATEEXTENSIONpg_trgm;
CREATEEXTENSIONzhparser;
CREATETEXTSEARCHCONFIGURATIONpg_catalog.chinese_zh(PARSER=zhparser);
ALTERTEXTSEARCHCONFIGURATIONchinese_zhADDMAPPINGFORn,v,a,i,e,lWITHsimple;
--忽略标点影响
ALTERROLEwikijsSETzhparser.punctuation_ignore=ON;
--短词复合
ALTERROLEwikijsSETzhparser.multi_short=ON;
--测试一下
selectts_debug('chinese_zh','青春是最美好的年岁,青春是最灿烂的日子。每一个人的青春都无比宝贵,宝贵的青春只有与奋斗为伴才最闪光、最出彩。');
取消wikijs用户的临时超管权限
登录PostgreSQL数据库wikijs用户,取消wikijs用户的超管权限。
ALTERUSERwikijsWITHNOSUPERUSER;
创建支持中文分词的配置ConfigMap
#zh-parse.yaml
kind:ConfigMap
apiVersion:v1
metadata:
name:wikijs-zhparser
data:
definition.yml:|-
key:postgres
title:Database-PostgreSQL
description:AdvancedPostgreSQL-basedsearchengine.
author:requarks.io
logo:https://static.requarks.io/logo/postgresql.svg
website:https://www.requarks.io/
isAvailable:true
props:
dictLanguage:
type:String
title:DictionaryLanguage
hint:Languagetousewhencreatingandqueryingtextsearchvectors.
default:english
enum:
-simple
-danish
-dutch
-english
-finnish
-french
-german
-hungarian
-italian
-norwegian
-portuguese
-romanian
-russian
-spanish
-swedish
-turkish
-chinese_zh
order:1
engine.js:|-
consttsquery=require('pg-tsquery')()
conststream=require('stream')
constPromise=require('bluebird')
constpipeline=Pmisify(stream.pipeline)
/*globalWIKI*/
module.exports={
asyncactivate(){
if(WIKI.config.db.type!=='postgres'){
thrownewWIKI.Error.SearchActivationFailed('MustusePostgreSQLdatabasetoactivatethisengine!')
asyncdeactivate(){
WIKI.(`(SEARCH/POSTGRES)Droppingindextables...`)
awaitWIKI.models.knex.schema.dropTable('pagesWords')
awaitWIKI.models.knex.schema.dropTable('pagesVector')
WIKI.(`(SEARCH/POSTGRES)Indextableshavebeendropped.`)
*INIT
asyncinit(){
WIKI.(`(SEARCH/POSTGRES)Initializing...`)
//-CreateSearchIndex
constindexExists=awaitWIKI.models.knex.schema.hasTable('pagesVector')
if(!indexExists){
WIKI.(`(SEARCH/POSTGRES)CreatingPagesVectortable...`)
awaitWIKI.models.knex.schema.createTable('pagesVector',table={
table.increments()
table.string('path')
table.string('locale')
table.string('title')
table.string('description')
table.specificType('tokens','TSVECTOR')
table.text('content')
//-CreateWordsIndex
constwordsExists=awaitWIKI.models.knex.schema.hasTable('pagesWords')
if(!wordsExists){
WIKI.(`(SEARCH/POSTGRES)CreatingWordsSuggestionIndex...`)
awaitWIKI.models.knex.raw(`
CREATETABLE"pagesWords"ASSELECTwordFROMts_stat(
'SELECTto_tsvector(''simple'',"title")||to_tsvector(''simple'',"description")||to_tsvector(''simple'',"content")FROM"pagesVector"'
awaitWIKI.models.knex.raw('CREATEEXTENSIONIFNOTEXISTSpg_trgm')
awaitWIKI.models.knex.raw(`CREATEINDEX"pageWords_idx"ON"pagesWords"USINGGIN(wordgin_trgm_ops)`)
WIKI.(`(SEARCH/POSTGRES)Initializationcompleted.`)
*QUERY
*@param{String}qQuery
*@param{Object}optsAdditionaloptions
asyncquery(q,opts){
try{
letsuggestions=[]
letqry=`
SELECTid,path,locale,title,description
FROM"pagesVector",to_tsquery(,)query
WHERE(query@@"tokens"ORpathILIKE)
letqryEnd=`ORDERBYts_rank(tokens,query)DESC`
letqryParams=[this.config.dictLanguage,tsquery(q),`%${q.toLowerCase()}%`]
if(opts.locale){
qry=`${qry}ANDlocale=`
qryParams.push(opts.locale)
if(opts.path){
qry=`${qry}ANDpathILIKE`
qryParams.push(`%${opts.path}`)
constresults=awaitWIKI.models.knex.raw(`
${qry}
${qryEnd}
`,qryParams)
if(results.rows.length5){
constsuggestResults=awaitWIKI.models.knex.raw(`SELECTword,word-ASrankFROM"pagesWords"WHEREsimilarity(word,)0.2ORDERBYrankLIMIT5;`,[q,q])
suggestions=suggestResults.rows.map(r=r.word)
return{
results:results.rows,
suggestions,
totalHits:results.rows.length
}catch(err){
WIKI.logger.warn('SearchEngineError:')
WIKI.logger.warn(err)
*CREATE
*@param{Object}pagePagetocreate
asynccreated(page){
awaitWIKI.models.knex.raw(`
INSERTINTO"pagesVector"(path,locale,title,description,"tokens")VALUES(
,,,,(setweight(to_tsvector('${this.config.dictLanguage}',),'A')||setweight(to_tsvector('${this.config.dictLanguage}',),'B')||setweight(to_tsvector('${this.config.dictLanguage}',),'C'))
`,[page.path,page.localeCode,page.title,page.description,page.title,page.description,page.safeContent])
*UPDATE
*@param{Object}pagePagetoupdate
asyncupdated(page){
awaitWIKI.models.knex.raw(`
UPDATE"pagesVector"SET
title=,
description=,
tokens=(setweight(to_tsvector('${this.config.dictLanguage}',),'A')||
setweight(to_tsvector('${this.config.dictLanguage}',),'B')||
setweight(to_tsvector('${this.config.dictLanguage}',),'C'))
WHEREpath=ANDlocale=
`,[page.title,page.description,page.title,page.description,page.safeContent,page.path,page.localeCode])
*DELETE
*@param{Object}pagePagetodelete
asyncdeleted(page){
awaitWIKI.models.knex('pagesVector').where({
locale:page.localeCode,
path:page.path
}).del().limit(1)
*RENAME
*@param{Object}pagePagetorename
asyncrenamed(page){
awaitWIKI.models.knex('pagesVector').where({
locale:page.localeCode,
path:page.path
}).update({
locale:page.destinationLocaleCode,
path:page.destinationPath
*REBUILDINDEX
asyncrebuild(){
WIKI.(`(SEARCH/POSTGRES)RebuildingIndex...`)
awaitWIKI.models.knex('pagesVector').truncate()
awaitWIKI.models.knex('pagesWords').truncate()
awaitpipeline(
WIKI.models.knex.column('path','localeCode','title','description','render').select().from('pages').where({
isPublished:true,
isPrivate:false
}).stream(),
newstream.Transform({
objectMode:true,
transform:async(page,enc,cb)={
constcontent=WIKI.models.pages.cleanHTML(page.render)
awaitWIKI.models.knex.raw(`
INSERTINTO"pagesVector"(path,locale,title,description,"tokens",content)VALUES(
,,,,(setweight(to_tsvector('${this.config.dictLanguage}',),'A')||setweight(to_tsvector('${this.config.dictLanguage}',),'B')||setweight(to_tsvector('${this.config.dictLanguage}',),'C')),
`,[page.path,page.localeCode,page.title,page.description,page.title,page.description,content,content])
cb()
awaitWIKI.models.knex.raw(`
INSERTINTO"pagesWords"(word)
SELECTwordFROMts_stat(
'SELECTto_tsvector(''simple'',"title")||to_tsvector(''simple'',"description")||to
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 热力管网工程档案管理规范
- 实验室工程监理实施细则
- 旅居康养服务项目投资估算报告
- 流域畜禽养殖污染治理实施方案
- 认知症照护中心建设与运营方案
- 社区公共卫生服务设施配置方案
- 小学课间活动优化实施方案
- 施工用电巡检培训
- 孕产妇围产期健康管理工作手册
- 有毒液体储罐风险评估报告
- 国家保安员资格考试培训教材
- 全国行业职业技能竞赛-全国乡村建设工匠(水电安装工)技能竞赛理论题库(附答案)
- 浴池办卫生许可管理制度
- 教育培训机构预付费制度
- 2025年中职电气技术应用(直流电路分析)试题及答案
- 北京市朝阳区2025-2026学年高三上学期期中质量检测数学试卷(解析版)
- 营地营运方案
- 仓库6S管理制度仓库现场6S执行标准仓库6S检查表
- 高校教研室年度工作总结范文
- 牧业财务管理办法
- T/JSSES 39-2024水体中抗生素环境风险评估技术指南
评论
0/150
提交评论