<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
wiki.js 是優秀的開源 Wiki 系統,相較於 xwiki ,功能目前性上比 xwiki 不夠完善,但也在不斷進步。 Wiki 寫作、分享、許可權管理功能還是有的,勝在 UI 設計很漂亮,能滿足小團隊的基本知識管理需求。
以下工作是在 KubeSphere 3.2.1 + Helm 3 已經部署好的情況下進行的。
部署 KuberSphere 的方法官網有很詳細的檔案介紹,這裡不再贅敘。 kubesphere.com.cn/docs/instal…
我們使用 OpenEBS 作為儲存,OpenEBS 預設安裝的 Local StorageSlass 在 Pod 銷燬後自動刪除,不適合用於我的 MySQL 儲存,我們在 Local StorageClass 基礎上稍作修改,建立新的 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,並在安裝 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: |- 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 使用者的密碼自行準備,明文儲存。
我們使用 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 服務了。
我們的 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
apiVersion: v1 kind: Service metadata: name: postgres-prod spec: selector: app: postgres-prod ports: - protocol: TCP port: 5432 targetPort: tcp-5432
測試略
我們使用 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 語句,完成資料庫和使用者的建立、授權。
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 的密碼自行修改。
採用 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
# wikijs-svc.yaml apiVersion: v1 kind: Service metadata: name: wikijs spec: selector: app: wikijs ports: - protocol: TCP port: 3000 targetPort: http-3000
# 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 域名需要自行設定。
$ kubectl apply -f wikijs-deploy.yaml $ kubectl apply -f wikijs-svc.yaml $ kubectl apply -f wikijs-ing.yaml
wiki.js 的全文檢索支援基於 PostgreSQL 的檢索,也支援 Elasticsearch 等,相對來說, PostgreSQL 比較輕量級,本專案中,我們使用 PostgreSQL 的全文檢索。
但是,因為 PostgreSQL 不支援中文分詞,需要額外安裝外掛並設定啟用中文分詞,下面描述了為 wiki.js 啟動基於 PostgreSQL 資料庫中文分詞的全文檢索。
通過資料庫管理工具登入有超管許可權的 PostgreSQL 使用者,臨時授予 wiki.js 使用者臨時超管許可權,便於啟動中文分詞功能。
ALTER USER wikijs WITH SUPERUSER;
使用資料庫管理工具登入 PostgreSQL 資料庫的 wikijs 使用者,執行以下命令,啟動資料庫的中文分詞功能。
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; -- 忽略標點影響 ALTER ROLE wikijs SET zhparser.punctuation_ignore = ON; -- 短詞複合 ALTER ROLE wikijs SET zhparser.multi_short = ON; -- 測試一下 select ts_debug('chinese_zh', '青春是最美好的年歲,青春是最燦爛的日子。每一個人的青春都無比寶貴,寶貴的青春只有與奮鬥為伴才最閃光、最出彩。');
登入 PostgreSQL 資料庫 wikijs 使用者,取消 wikijs 使用者的超管許可權。
ALTER USER wikijs WITH NOSUPERUSER;
# 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.`) } }
wiki.js 的基於 PostgreSQL 的全文檢索引擎設定位於 /wiki/server/modules/search/postgres ,我們將前面設定的 ConfigMap 載入到這個目錄。
# 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
$ kubectl apply -f zh-parse.yaml $ kubectl apply -f wikijs-zh.yaml
本文介紹的 wiki.js 部署方式支援中文全文檢索的支援,整合了 PostgreSQL 和 zhparser 中文分詞外掛。
相對於標準的 wiki.js 安裝部署過程,主要做了以下設定:
以上就是KubeSphere中部署Wiki系統wiki.js並啟用中文全文檢索的詳細內容,更多關於KubeSphere部署wiki.js並啟用的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45