KubeSphere中部署Wiki系統(tǒng)wiki.js并啟用中文全文檢索
背景
wiki.js 是優(yōu)秀的開源 Wiki 系統(tǒng),相較于 xwiki ,功能目前性上比 xwiki 不夠完善,但也在不斷進(jìn)步。 Wiki 寫作、分享、權(quán)限管理功能還是有的,勝在 UI 設(shè)計(jì)很漂亮,能滿足小團(tuán)隊(duì)的基本知識(shí)管理需求。
以下工作是在 KubeSphere 3.2.1 + Helm 3 已經(jīng)部署好的情況下進(jìn)行的。
部署 KuberSphere 的方法官網(wǎng)有很詳細(xì)的文檔介紹,這里不再贅敘。 kubesphere.com.cn/docs/instal…
準(zhǔn)備 storageclass
我們使用 OpenEBS 作為存儲(chǔ),OpenEBS 默認(rèn)安裝的 Local StorageSlass 在 Pod 銷毀后自動(dòng)刪除,不適合用于我的 MySQL 存儲(chǔ),我們?cè)?Local StorageClass 基礎(chǔ)上稍作修改,創(chuàng)建新的 StorageClass,允許 Pod 銷毀后,PV 內(nèi)容繼續(xù)保留,手動(dòng)決定怎么處理。
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 數(shù)據(jù)庫
我們團(tuán)隊(duì)其他項(xiàng)目中也需要使用 PostgreSQL, 為了提高 PostgreSQL 數(shù)據(jù)庫的利用率和統(tǒng)一管理,我們獨(dú)立部署 PostgreSQL,并在安裝 wiki.js 時(shí),配置為使用外部數(shù)據(jù)庫。
準(zhǔn)備用戶名密碼配置
我們使用 Secret 保存 PostgreSQL 用戶密碼等敏感信息。
kind: Secret apiVersion: v1 metadata: name: postgres-prod data: POSTGRES_PASSWORD: xxxx type: Opaque
以上 POSTGRES_PASSWORD 自行準(zhǔn)備,為 base64 編碼的數(shù)據(jù)。
準(zhǔn)備數(shù)據(jù)庫初始化腳本
使用 ConfigMap 保存數(shù)據(jù)庫初始化腳本,在 數(shù)據(jù)庫創(chuàng)建時(shí),將 ConfigMap 中的數(shù)據(jù)庫初始化腳本掛載到 /docker-entrypoint-initdb.d, 容器初始化時(shí)會(huì)自動(dòng)執(zhí)行該腳本。
apiVersion: v1
kind: ConfigMap
metadata:
name: wikijs-postgres-init
data:
init.sql: |-
CREATE DATABASE wikijs;
CREATE USER wikijs with password 'xxxx';
GRANT CONNECT ON DATABASE wikijs to wikijs;
GRANT USAGE ON SCHEMA public TO wikijs;
GRANT SELECT,update,INSERT,delete ON ALL TABLES IN SCHEMA public TO wikijs;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO wikijs;
以上 wikijs 用戶的密碼自行準(zhǔn)備,明文保存。
準(zhǔn)備存儲(chǔ)
我們使用 KubeSphere 默認(rèn)安裝的 OpenEBS 來提供存儲(chǔ)服務(wù)??梢酝ㄟ^創(chuàng)建 PVC 來提供持久化存儲(chǔ)。
這里聲明一個(gè) 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 數(shù)據(jù)庫
在前面的步驟準(zhǔn)備好各種配置信息和存儲(chǔ)后,就可以開始部署 PostgreSQL 服務(wù)了。
我們的 Kubernetes 沒有配置存儲(chǔ)陣列,使用的是 OpenEBS 作為存儲(chǔ),采用 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
創(chuàng)建供其他 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
準(zhǔn)備用戶名密碼配置
我們使用 Secret 保存 wiki.js 用于連接數(shù)據(jù)庫的用戶名密碼等敏感信息。
apiVersion: v1 kind: Secret metadata: name: wikijs data: DB_USER: d2lraWpz DB_PASS: xxxx type: Opaque
以上 DB_PASS 自行準(zhǔn)備,為 base64 編碼的數(shù)據(jù)。
準(zhǔn)備數(shù)據(jù)庫連接配置
我們使用 ConfigMap 保存 wiki.js 的數(shù)據(jù)庫連接信息。
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"
創(chuàng)建數(shù)據(jù)庫用戶和數(shù)據(jù)庫
如果 PostgreSQL 數(shù)據(jù)庫里沒有創(chuàng)建 wikijs 用戶和數(shù)據(jù) ,需要手工完成一下工作:
通過『數(shù)據(jù)庫工具』連接 PostgreSQL 數(shù)據(jù)庫,執(zhí)行一下 SQL 語句,完成數(shù)據(jù)庫和用戶的創(chuàng)建、授權(quán)。
CREATE DATABASE wikijs; CREATE USER wikijs with password 'xxxx'; GRANT CONNECT ON DATABASE wikijs to wikijs; GRANT USAGE ON SCHEMA public TO wikijs; GRANT SELECT,update,INSERT,delete ON ALL TABLES IN SCHEMA public TO wikijs; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO wikijs;
以上 wikijs 的密碼自行修改。
準(zhǔn)備 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
創(chuàng)建集群內(nèi)訪問 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
創(chuàng)建集群外訪問的 Ingress
# wikijs-ing.yaml
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: wikijs
spec:
ingressClassName: nginx
rules:
- host: wiki.xxxx.cn
http:
paths:
- path: /
pathType: ImplementationSpecific
backend:
service:
name: wikijs
port:
number: 3000
以上 host 域名需要自行配置。
執(zhí)行部署
$ kubectl apply -f wikijs-deploy.yaml $ kubectl apply -f wikijs-svc.yaml $ kubectl apply -f wikijs-ing.yaml
配置 wiki.js 支持中文全文檢索
wiki.js 的全文檢索支持基于 PostgreSQL 的檢索,也支持 Elasticsearch 等,相對(duì)來說, PostgreSQL 比較輕量級(jí),本項(xiàng)目中,我們使用 PostgreSQL 的全文檢索。
但是,因?yàn)?PostgreSQL 不支持中文分詞,需要額外安裝插件并配置啟用中文分詞,下面描述了為 wiki.js 啟動(dòng)基于 PostgreSQL 數(shù)據(jù)庫中文分詞的全文檢索。
授予 wikijs 用戶臨時(shí)超管權(quán)限
通過數(shù)據(jù)庫管理工具登錄有超管權(quán)限的 PostgreSQL 用戶,臨時(shí)授予 wiki.js 用戶臨時(shí)超管權(quán)限,便于啟動(dòng)中文分詞功能。
ALTER USER wikijs WITH SUPERUSER;
啟用數(shù)據(jù)庫的中文分詞能力
使用數(shù)據(jù)庫管理工具登錄 PostgreSQL 數(shù)據(jù)庫的 wikijs 用戶,執(zhí)行以下命令,啟動(dòng)數(shù)據(jù)庫的中文分詞功能。
CREATE EXTENSION pg_trgm;
CREATE EXTENSION zhparser;
CREATE TEXT SEARCH CONFIGURATION pg_catalog.chinese_zh (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION chinese_zh ADD MAPPING FOR n,v,a,i,e,l WITH simple;
-- 忽略標(biāo)點(diǎn)影響
ALTER ROLE wikijs SET zhparser.punctuation_ignore = ON;
-- 短詞復(fù)合
ALTER ROLE wikijs SET zhparser.multi_short = ON;
-- 測試一下
select ts_debug('chinese_zh', '青春是最美好的年歲,青春是最燦爛的日子。每一個(gè)人的青春都無比寶貴,寶貴的青春只有與奮斗為伴才最閃光、最出彩。');
取消 wikijs 用戶的臨時(shí)超管權(quán)限
登錄 PostgreSQL 數(shù)據(jù)庫 wikijs 用戶,取消 wikijs 用戶的超管權(quán)限。
ALTER USER wikijs WITH NOSUPERUSER;
創(chuàng)建支持中文分詞的配置 ConfigMap
# zh-parse.yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: wikijs-zhparser
data:
definition.yml: |-
key: postgres
title: Database - PostgreSQL
description: Advanced PostgreSQL-based search engine.
author: requarks.io
logo: https://static.requarks.io/logo/postgresql.svg
website: https://www.requarks.io/
isAvailable: true
props:
dictLanguage:
type: String
title: Dictionary Language
hint: Language to use when creating and querying text search vectors.
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: |-
const tsquery = require('pg-tsquery')()
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)
/* global WIKI */
module.exports = {
async activate() {
if (WIKI.config.db.type !== 'postgres') {
throw new WIKI.Error.SearchActivationFailed('Must use PostgreSQL database to activate this engine!')
}
},
async deactivate() {
WIKI.logger.info(`(SEARCH/POSTGRES) Dropping index tables...`)
await WIKI.models.knex.schema.dropTable('pagesWords')
await WIKI.models.knex.schema.dropTable('pagesVector')
WIKI.logger.info(`(SEARCH/POSTGRES) Index tables have been dropped.`)
},
/**
* INIT
*/
async init() {
WIKI.logger.info(`(SEARCH/POSTGRES) Initializing...`)
// -> Create Search Index
const indexExists = await WIKI.models.knex.schema.hasTable('pagesVector')
if (!indexExists) {
WIKI.logger.info(`(SEARCH/POSTGRES) Creating Pages Vector table...`)
await WIKI.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')
})
}
// -> Create Words Index
const wordsExists = await WIKI.models.knex.schema.hasTable('pagesWords')
if (!wordsExists) {
WIKI.logger.info(`(SEARCH/POSTGRES) Creating Words Suggestion Index...`)
await WIKI.models.knex.raw(`
CREATE TABLE "pagesWords" AS SELECT word FROM ts_stat(
'SELECT to_tsvector(''simple'', "title") || to_tsvector(''simple'', "description") || to_tsvector(''simple'', "content") FROM "pagesVector"'
)`)
await WIKI.models.knex.raw('CREATE EXTENSION IF NOT EXISTS pg_trgm')
await WIKI.models.knex.raw(`CREATE INDEX "pageWords_idx" ON "pagesWords" USING GIN (word gin_trgm_ops)`)
}
WIKI.logger.info(`(SEARCH/POSTGRES) Initialization completed.`)
},
/**
* QUERY
*
* @param {String} q Query
* @param {Object} opts Additional options
*/
async query(q, opts) {
try {
let suggestions = []
let qry = `
SELECT id, path, locale, title, description
FROM "pagesVector", to_tsquery(?,?) query
WHERE (query @@ "tokens" OR path ILIKE ?)
`
let qryEnd = `ORDER BY ts_rank(tokens, query) DESC`
let qryParams = [this.config.dictLanguage, tsquery(q), `%${q.toLowerCase()}%`]
if (opts.locale) {
qry = `${qry} AND locale = ?`
qryParams.push(opts.locale)
}
if (opts.path) {
qry = `${qry} AND path ILIKE ?`
qryParams.push(`%${opts.path}`)
}
const results = await WIKI.models.knex.raw(`
${qry}
${qryEnd}
`, qryParams)
if (results.rows.length < 5) {
const suggestResults = await WIKI.models.knex.raw(`SELECT word, word <-> ? AS rank FROM "pagesWords" WHERE similarity(word, ?) > 0.2 ORDER BY rank LIMIT 5;`, [q, q])
suggestions = suggestResults.rows.map(r => r.word)
}
return {
results: results.rows,
suggestions,
totalHits: results.rows.length
}
} catch (err) {
WIKI.logger.warn('Search Engine Error:')
WIKI.logger.warn(err)
}
},
/**
* CREATE
*
* @param {Object} page Page to create
*/
async created(page) {
await WIKI.models.knex.raw(`
INSERT INTO "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} page Page to update
*/
async updated(page) {
await WIKI.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'))
WHERE path = ? AND locale = ?
`, [page.title, page.description, page.title, page.description, page.safeContent, page.path, page.localeCode])
},
/**
* DELETE
*
* @param {Object} page Page to delete
*/
async deleted(page) {
await WIKI.models.knex('pagesVector').where({
locale: page.localeCode,
path: page.path
}).del().limit(1)
},
/**
* RENAME
*
* @param {Object} page Page to rename
*/
async renamed(page) {
await WIKI.models.knex('pagesVector').where({
locale: page.localeCode,
path: page.path
}).update({
locale: page.destinationLocaleCode,
path: page.destinationPath
})
},
/**
* REBUILD INDEX
*/
async rebuild() {
WIKI.logger.info(`(SEARCH/POSTGRES) Rebuilding Index...`)
await WIKI.models.knex('pagesVector').truncate()
await WIKI.models.knex('pagesWords').truncate()
await pipeline(
WIKI.models.knex.column('path', 'localeCode', 'title', 'description', 'render').select().from('pages').where({
isPublished: true,
isPrivate: false
}).stream(),
new stream.Transform({
objectMode: true,
transform: async (page, enc, cb) => {
const content = WIKI.models.pages.cleanHTML(page.render)
await WIKI.models.knex.raw(`
INSERT INTO "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()
}
})
)
await WIKI.models.knex.raw(`
INSERT INTO "pagesWords" (word)
SELECT word FROM ts_stat(
'SELECT to_tsvector(''simple'', "title") || to_tsvector(''simple'', "description") || to_tsvector(''simple'', "content") FROM "pagesVector"'
)
`)
WIKI.logger.info(`(SEARCH/POSTGRES) Index rebuilt successfully.`)
}
}
更新 wikijs 的 Deployment
wiki.js 的基于 PostgreSQL 的全文檢索引擎配置位于 /wiki/server/modules/search/postgres ,我們將前面配置的 ConfigMap 加載到這個(gè)目錄。
# wikijs-zh.yaml
kind: Deployment
apiVersion: apps/v1
metadata:
name: wikijs
labels:
app: wikijs
spec:
replicas: 1
selector:
matchLabels:
app: wikijs
template:
metadata:
labels:
app: wikijs
spec:
volumes:
- name: volume-dysh4f
configMap:
name: wikijs-zhparser
defaultMode: 420
containers:
- name: wikijs
image: 'requarks/wiki:2'
ports:
- name: http-3000
containerPort: 3000
protocol: TCP
envFrom:
- secretRef:
name: wikijs
- configMapRef:
name: wikijs
volumeMounts:
- name: volume-dysh4f
readOnly: true
mountPath: /wiki/server/modules/search/postgres
配置 wiki.js ,啟用基于 PostgreSQL 的全文檢索
- 重新 apply 新的 Delployment 文件后
$ kubectl apply -f zh-parse.yaml $ kubectl apply -f wikijs-zh.yaml
- 打開 wiki.js 管理
- 點(diǎn)擊搜索引擎
- 選擇 Database - PostgreSQL
- 在 Dictionary Language 的下拉菜單里選擇 chinese_zh。
- 點(diǎn)擊應(yīng)用,并重建索引。
- 完成配置。
總結(jié)
本文介紹的 wiki.js 部署方式支持中文全文檢索的支持,集成了 PostgreSQL 和 zhparser 中文分詞插件。
相對(duì)于標(biāo)準(zhǔn)的 wiki.js 安裝部署過程,主要做了以下配置:
- PostgreSQL 鏡像采用了 abcfy2/zhparser:12-alpine ,這個(gè)鏡像自帶 zhparser 中文分詞插件。
- wiki.js 鏡像外掛了 ConfigMap ,用于修改原 Docker 鏡像里關(guān)于 PostgreSQL 搜索引擎配置的信息,以支持 chinese_zh 選項(xiàng)。
以上就是KubeSphere中部署Wiki系統(tǒng)wiki.js并啟用中文全文檢索的詳細(xì)內(nèi)容,更多關(guān)于KubeSphere部署wiki.js并啟用的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Rainbond網(wǎng)絡(luò)治理插件ServiceMesh官方文檔說明
這篇文章主要為大家介紹了Rainbond網(wǎng)絡(luò)治理插件ServiceMesh官方文檔說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
k8s Job 執(zhí)行一次性以及批處理任務(wù)使用場景案例
這篇文章主要為大家介紹了k8s Job 執(zhí)行一次性以及批處理任務(wù)使用場景案例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
Spark實(shí)現(xiàn)K-Means算法代碼示例
這篇文章主要介紹了Spark實(shí)現(xiàn)K-Means算法代碼示例,簡單介紹了K-Means算法及其原理,然后通過具體實(shí)例向大家展示了用spark實(shí)現(xiàn)K-Means算法,需要的朋友可以參考下。2017-10-10
kubernetes(k8s)安裝metrics-server實(shí)現(xiàn)資源使用情況監(jiān)控方式詳解
這篇文章主要介紹了kubernetes(k8s)安裝metrics-server實(shí)現(xiàn)資源使用情況監(jiān)控,包括Metrics?Server下載方式,?k8s集群安裝部署metrics的問題,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-04-04
K8s學(xué)習(xí)之Pod的定義及詳細(xì)資源調(diào)用案例
Kubernetes將所有內(nèi)容抽象為資源,通過操作資源管理集群,核心單元是Pod,通過控制器管理Pod,資源管理分為命令式對(duì)象管理、命令式對(duì)象配置和聲明式對(duì)象配置,各有適用場景,需要的朋友可以參考下2024-09-09
kubelet為cadvisor添加namespace/pod/container標(biāo)簽示例詳解
這篇文章主要為大家介紹了kubelet為cadvisor添加namespace/pod/container標(biāo)簽示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-09-09

