私有化部署 Outline

这是一款适用于个人或团队场景使用的开源文档/Wiki软件,Outline。

我在这两篇文章(https://soulteary.com/2021/09/05/opensource-documentation-wiki-software-outline-part-1.htmlhttps://soulteary.com/2021/09/11/opensource-documentation-wiki-software-outline-part-2.html)看到了这个工具,打算试一试,结合那篇文章中罗列的信息,加上我自己的理解,基本上可以把这款软件的特点罗列如下:

  • 能够将数据完全自托管管理,不涉及私有格式,而且可以随时导出成开源格式(例如 PDF、Markdown)
  • Markdown 语法、所见即所得,可以直接上传附件和图片,也支持代码片段、数学公式
  • 类 Notion,允许插入富文本内容、卡片式渲染
  • 个人使用和管理文档,并在需要时可以邀请用户协同编辑、分享文档
  • 层级嵌套,方便分类和整理
  • 历史版本记录,并在文档被改动/编辑后有邮件提醒

缺点也不少:

  • 不支持本地登录,只支持 OAuth 登录
  • 不支持本地存储,只能使用 AWS S3 或者兼容 S3 协议的存储,例如 Minio
  • 从文档中删除图片,未必能清理后端存储中的文件
  • 没有评论功能,权限管理的层级不够丰富
  • 很多设置项不能在网页端修改,只能重启 docker-compose
  • 极度简陋的自托管支持,只能靠社区成员的零碎的讨论来解决问题

官方提供的 docker-compose 安装教程(https://docs.getoutline.com/s/hosting/doc/docker-7pfeLP5a8t)非常简陋,有很多细节都没有解释,所以并不是开箱即用的。根据官方提供的模板,做了很多尝试,我终于搭建起来了。我把缺少的细节都记录一下。

我的 docker-compose 采用 env_file 读取环境参数,然后把环境参数都写在 .env 文件里面,这样我就也可以在 docker-compose.yml 里面用环境变量,方便些。

我把 https-portal 删掉了,我打算直接开放 outline 的 3000 端口,之后用 NGINX 转发;Redis、Postgres 都只用容器内网络通讯,不开放端口;Minio 我开放了 9000 端口,也绑定了一个控制台,因为这样也可以方便后面管理(虽然理论上它也只需要容器内通讯)。

outline:
    image: ${DOCKER_OUTLINE_IMAGE_NAME}
    container_name: outline
    env_file: ./.env
    ports:
      - "9303:3000"
    restart: always
    networks:
      - outline
    extra_hosts:
      - "${DOCKER_OUTLINE_HOSTNAME}:0.0.0.0"
    depends_on:
      - postgres
      - redis
      - storage
  image: ${DOCKER_REDIS_IMAGE_NAME}
    env_file: ./.env
    volumes:
      - ./redis.conf:/redis.conf
    container_name: ${DOCKER_REDIS_HOSTNAME}
    restart: always
    networks:
      - outline
    command: ["redis-server", "/redis.conf"]
postgres:
    image: ${DOCKER_POSTGRES_IMAGE_NAME}
    env_file: ./.env
    volumes:
      - ./database-data:/var/lib/postgresql/data
    container_name: ${DOCKER_POSTGRES_HOST}
    restart: always
    networks:
      - outline
  image: ${DOCKER_MINIO_IMAGE_NAME}
    container_name: ${OUTLINE_MINIO}
    env_file: ./.env
    ports:
      - "${DOCKER_OUTLINE_MINIO_PORT}:9000"
      - "${DOCKER_OUTLINE_MINIO_ADMIN_PORT}:9001"
    command: "minio server /data --console-address 0.0.0.0:${DOCKER_OUTLINE_MINIO_ADMIN_PORT}"
    restart: always
    extra_hosts:
      - "${DOCKER_MINIO_HOSTNAME}:0.0.0.0"
      - "${DOCKER_MINIO_ADMIN_DOMAIN}:0.0.0.0"
    volumes:
      - ./storage-data:/data
    networks:
       - outline
networks:
  outline:
    external: true

.env 文件保留了环境变量,根据自己的情况修改。

所有的 IMAGE_NAME 我都用了此刻(2023-03-23)最新的。

DOCKER_OUTLINE_IMAGE_NAME=outlinewiki/outline:0.68.1
DOCKER_POSTGRES_IMAGE_NAME=postgres:15.2
DOCKER_REDIS_IMAGE_NAME=redis:7.2-rc1
DOCKER_MINIO_IMAGE_NAME=minio/minio:RELEASE.2023-03-22T06-36-24Z

Outline 相关的参数如下:

DOCKER_OUTLINE_HOSTNAME=outline.example.com
OUTLINE_URL=https://${DOCKER_OUTLINE_HOSTNAME}
URL=${OUTLINE_URL}
PORT=3000

Postgres 和 Redis 的参数没有特殊的地方,只要注意容器内地址通讯即可,例如:

DATABASE_URL=postgres://${DOCKER_POSTGRES_USER}:${DOCKER_POSTGRES_PASS}@${DOCKER_POSTGRES_HOST}:5432/${DOCKER_POSTGRES_DBNAME}

当然也要注意把改过名字的参数类型映射回 docker 的环境变量会用的名字,以及我 disable 了 PostgreSQL 的 SSL。

POSTGRES_USER=${DOCKER_POSTGRES_USER}
PGSSLMODE=disable

Outline 不支持本地存储,他只开放了 AWS S3 存储,但是也可以使用兼容 S3 协议的其他存储(比如 Minio)。

Minio 是一个兼容 S3 协议的存储,简单说就是启动了一个服务之后,它把 S3 处理请求解析后,把文件存放到本地。docker-compose 中我们启动了这个 Minio 的 docker 镜像,并把 /data 目录挂载到了本地持久存储。

初始化 Minio 的时候,我提供了 MINIO_ROOT_USERMINIO_ROOT_PASSWORD,这两个后面会当作 S3 的 Access ID 和 Secret Key 来使用。它们都只由小写 a-z 和数字组成,前者 16 位,后者 64 位,我使用 https://onlinerandomtools.com/generate-random-string 生成。

因为是自己的 Minio,所以 MINIO_REGION_NAME 就可以随便写了,这个后面会当作 S3 的 Region 来使用。

DOCKER_MINIO_HOSTNAME=minio.example.com
DOCKER_MINIO_ADMIN_DOMAIN=minio-admin.example.com

为了管理方便,docker-compose 还启动了 9001 管理界面,这里我们做一下重定向。

MINIO_BROWSER=on
MINIO_BROWSER_REDIRECT_URL=https://${DOCKER_MINIO_ADMIN_DOMAIN}
DOCKER_OUTLINE_MINIO_PORT=9000
DOCKER_OUTLINE_MINIO_ADMIN_PORT=9001

后面大部分参数都可以按照官方示例(https://github.com/outline/outline/blob/main/.env.sample)中的说明来操作,比如使用 openssl rand -hex 32 来生成 SECRET KEY 等等。

AWS 存储就使用 Minio。

AWS_ACCESS_KEY_ID=${MINIO_ROOT_USER}
AWS_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
AWS_REGION=${MINIO_REGION_NAME}
AWS_S3_UPLOAD_BUCKET_URL=https://${DOCKER_MINIO_HOSTNAME}
AWS_S3_UPLOAD_BUCKET_NAME=outline
AWS_S3_UPLOAD_MAX_SIZE=26214400
AWS_S3_FORCE_PATH_STYLE=true
AWS_S3_ACL=private

登录是个大麻烦,Outline 不支持本地用户登录,它目前只支持 Slack、Azure、Google 以及 OIDC,所以需要去各平台生成 OAuth Token,还挺麻烦的。好在 GitLab 支持标准 OIDC 协议,而我有一个私有部署的 GitLab 实例,就直接接入了。

首先填写通用的信息,如下:

OIDC_AUTH_URI=https://gitlab.example.com/oauth/authorize
OIDC_TOKEN_URI=https://gitlab.example.com/oauth/token
OIDC_USERINFO_URI=https://gitlab.example.com/oauth/userinfo
OIDC_USERNAME_CLAIM=username
OIDC_DISPLAY_NAME=GitLab
OIDC_SCOPES=openid email

接着去管理中心 - 应用 - 实例 OAuth 应用程序 (https://gitlab.example.com/admin/applications),新建一个应用。

回调 URI 写 https://outline.example.com/auth/oidc.callback,范围勾选 openidemail,视情况选择是否可信和是否私密。

3leymx.png

点击保存应用之后,把 CLIENT_IDCLIENT_SECRET 填写到 .env 文件中。

还有剩下一些杂项,根据情况修改。

我把 FORCE_HTTPS 改成了 falseDEFAULT_LANGUAGE 改成了 zh_CN。因为我不打算使用 Slack,所以我还把 Slack 的默认数据都删掉了。我启用了 SMTP,我用的是 mailgun 的服务,所以修改了 TLS_CIPHERS 以支持 587 TLS。

SMTP_TLS_CIPHERS=TLSv1.2
SMTP_SECURE=false

接下去还要修改 NGINX 配置。

首先是 Outline。

Outline 的核心是 proxy_pass 的时候要加上一些 Header。

location / {
    proxy_pass        http://localhost:9303;

    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
    proxy_set_header Host $host;
    proxy_set_header Access-Control-Allow-Origin "*";
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header Host $host;
    proxy_set_header Host $http_host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_redirect off;
}

如果遇到跨域的问题还要加 Allow-Origin。

add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,OPTIONS,PUT;
add_header Access-Control-Allow-Headers Origin,X-Requested-With,Content-Type,Accept,Authorization;

if ($request_method = 'OPTIONS') {
  return 204;
}

接着是 Minio。

Minio 直接转发就可以,不要带 Allow-Origin,也不要带 proxy_set_header,不然可能会出现奇怪的 CORS 错误(因为 Minio 有默认的 Allow * 的配置),或者可能出现管理界面 400 错误。

# minio-admin.example.com
location / {
    proxy_pass        http://localhost:9001;
}
# minio.example.com
location / {
    proxy_pass        http://localhost:9000;
}

接下来要新建一个 Minio 存储桶。

这一步也可以使用命令行完成,例如运行一个 minio/mc 的客户端,使用 /usr/bin/mc mb 来创建一个桶,并设置访问权限。

我访问了 minio.example.com,这会被自动重定向到 minio-admin.example.com,我使用刚才上面的 Access ID 和 Secret Key 作为用户名和密码登录,然后选择左侧 Buckets,新建一个存储桶,名称就用 docker-compose 或者 .env 文件中设置的。上例是 outline。

5781as.png
42qvvo.png

回到 docker-compose 和 .env 的目录,启动 docker-compose up -d

首次运行还需要创建数据库以及执行迁移。

docker-compose run --rm outline yarn db:create --env=production-ssl-disabled
docker-compose run --rm outline yarn db:migrate --env=production-ssl-disabled

运行之后可能还要根据自己的情况执行下面这个命令。这个命令是解决在内存不足的情况下后台保存可能会失败的问题。这个值是在主机级别,而不是容器级别。Redis 推荐 1 的原因是他们的后台保存机制(https://redis.io/topics/faq#background-saving-fails-with-a-fork-error-under-linux-even-if-i-have-a-lot-of-free-ram)。

sysctl vm.overcommit_memory=1

此时回到 outline.example.com 应该一切正常了。

57kzfm.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容