outline 介绍
Outline 是一个开源的Wiki 知识库和团队协作文档管理工具,美观、实时协作、功能丰富且兼容 Markdown,设计用于帮助团队和组织有效地创建、共享和管理文档。
Outline 具有简单易用的界面和强大的功能,可以替代传统的文档管理系统,如 Google Docs 或 Confluence。Outline 提供了一种结构化的方式来组织信息,使团队成员可以快速访问和协作内容。
主要特性:
- ✏️ 直观的编辑体验:一个极快的编辑器,支持 Markdown、斜杠命令、交互式嵌入等等
- 👥 多人协作:与团队成员实时协作处理文档。评论和话题让对话井井有条。
- 🔍 强大的搜索和问答功能:立即在您的工作空间中进行搜索,并提出有关文档的问题以获得直接的 AI 答案。
- 💬 与 Slack 集成:无需离开聊天即可搜索、共享文档并提出问题。文档更新时向频道发布通知。
- 🌐 公开分享:通过链接公开共享文档,或与团队私下共享文档。使用您自己的品牌颜色、徽标和域名。**
其他特性:
- ⚡ 速度极快:Outline 速度很快,真的很快。努力确保毫秒级响应时间 - 文档立即加载、搜索快速、用户界面导航快捷。
- 🤝 协作性:Outline 经过精心设计,功能强大、实时且易于使用。阅读和编写文档应该是一件令人愉快的事情。
- 🌙 深色模式:对于夜猫子来说,我们已经为您提供了帮助。 Outline 具有漂亮的深色模式,既养眼又美观。
- 🔒 安全和权限:通过读写权限、用户组、来宾用户、公共共享等管理知识库。
- 🛠️ 20+ 集成:简单集成到您日常使用的工具中,例如 Slack、Figma、Loom 等。找不到您需要的集成?还有一个开放的 API 。
- 🌍 用您的语言:Outline 支持 RTL,包括 17 种语言的翻译,包括 🇫🇷 法语、🇪🇸 西班牙语、🇩🇪 德语、🇰🇷 韩语和 🇨🇳 中文。
- 🚀 建于公共场所:Outline 会定期更新新功能和修复,请查看我们的公共变更日志以了解进展情况!
- 🛠️ 开源:Outline 的源代码是公开的,开发是公开完成的。更喜欢托管在您自己的基础设施上?没问题。
- 🖌️ 可定制:自定义域允许您拥有 docs.yourteam.com 。带有您自己的品牌和颜色的白色标签。
官方网站:https://www.getoutline.com/
项目地址:https://github.com/outline/outline
outline 部署规划
我们将采用分步部署的方式,依次通过docker-compose安装traefik、minio、keyclock及outline组件,主要涉及docker-compose .env
环境变量文件的定义及docker-compose.yaml
文件的创建。
部署前置要求:
- 准备一台云服务器,例如 阿里云ECS服务器 或轻量服务器,本部署示例购买1台阿里云ECS服务器(包年包月购买3年具有2.6折的最低优惠),服务器配置为2核CPU/4G内存/100G磁盘,绑定公网IP,带宽按流量计费;
- 准备操作系统,本示例使用
Ubuntu 22.04 LTS
操作系统; - 准备可用域名,并配置好域名解析,本示例使用阿里云域名;
- -SSL证书,由
traefik
通过Let's Encrypt
自动申请免费证书; - 主机上已安装
docker
和docker compose
。
整体部署架构图:
部署服务组件清单:
我们采用 Traefik
作为反向代理和负载均衡器,MinIO
作为对象存储,Keycloak
作为身份和访问管理工具,并且利用 PostgreSQL
和 Redis
作为数据库和缓存,来支撑 Outline
的运行。
名称 | 说明 | 版本 |
---|---|---|
Traefik | 现代的反向代理和负载均衡器,用于管理和路由 HTTP 请求,提供 SSL 终止和路径路由功能。 | v3.1 |
MinIO | 高性能的对象存储服务,兼容 AWS S3 API,Outline 用于存储文件和附件。 | RELEASE.2024-08-26T15-33-07Z |
Keycloak | 开源的身份和访问管理工具,用于提供单点登录和身份认证功能,支持 OAuth2 和 SAML 协议。 | 25.0.4 |
Outline | 开源的知识库和文档管理工具,用于团队协作、文档编辑和知识共享。 | 0.78.0 |
Redis | 高性能的内存数据存储,用于缓存和会话管理,提高应用性能。 | 7.4.0 |
PostgreSQL | 开源的关系型数据库管理系统,用于存储 Outline 的持久化数据。 | 16.2 |
Outline文件存储选择
Outline文件存储支持以下两种形式,本示例采用minio S3存储
:
Local file system
:如果希望将文件上传存储在运行 Outline 的同一服务器上,则可以使用本地文件系统存储选项来实现。AWS S3
:由于AWS S3是对象存储的标准,因此不一定使用云上的AWS S3存储,例如可以通过自建minio来实现,完整的支持列表见下表。
Outline 可以与绝大多数 S3 兼容的 API 一起使用,因为使用的是可用 API 接口的一个非常小的子集,以下内容已经过社区成员的测试。
Service | Compatible |
---|---|
Amazon S3 | ✅ |
Minio | ✅ |
DigitalOcean Object Storage | ✅ |
Alibaba Cloud / Aliyun OSS | ✅ (discussion) |
Scaleway | ✅ (discussion) |
Cloudflare R2 | ❌ (discussion) |
Backblaze | ❌ (discussion) |
OVH Object Storage | ❌ |
身份验证提供程序选择
Outline 可以配置为接受各种 SSO、OIDC 和 SAML 身份验证选项,具体取决于所使用的版本,本部署示例使用支持OIDC的keycloak
。
- Microsoft / Azure
- Slack
- OIDC
- Discord
- GitLab
- Email magic link
- SAML
- Okta
- OneLogin
注:Outline不支持邮箱+密码认证
前置配置工作
1. 配置域名解析
域名配置清单:
域名地址 | 解析A记录值 | 说明 |
---|---|---|
https://outline.example.com | 120.79.11.68 | 用于访问 Outline 应用程序的主域名,提供知识库和文档管理功能,供团队成员进行协作和编辑。 |
https://keycloak.example.com | 120.79.11.68 | 用于访问 Keycloak 身份认证服务的域名,提供用户身份验证、授权管理和单点登录服务。 |
https://minio.example.com | 120.79.11.68 | 用于Outline访问 MinIO 对象存储服务的域名,存储 Outline 中的文件、文档和附件,支持 S3 兼容 API 操作。 |
https://console.minio.example.com | 120.79.11.68 | 用于访问 MinIO 管理控制台的域名,提供对 MinIO 对象存储服务的管理界面,包括用户和权限管理功能。 |
说明:
- 基本域名为
example.com
,前缀可自定义,请将example.com
域名更改为您自己的域名; - 示例公网IP地址
120.79.11.68
为Outline云主机的公网IP地址。
域名解析A记录示例:
2. 配置安全组规则
安全组规则放通清单:
端口号 | 协议 | 用途说明 |
---|---|---|
80 | TCP | Traefik 代理的 HTTP 端口,用于处理未加密的 Web 流量。 |
443 | TCP | Traefik 代理的 HTTPS 端口,用于处理加密的 Web 流量(SSL/TLS)。 |
安全组配置示例
3. 创建目录结构
创建组件安装目录
mkdir -p /data/{traefik,minio,keycloak,outline}
确认目录结构
root@ecs01:~# apt install -y tree
root@ubuntu:~# tree /data/
/data/
├── keycloak
├── minio
├── outline
└── traefik
4 directories, 0 files
4. 创建docker网络
手动创建docker网络
docker network create traefik-network
docker network create outline-network
docker network create keycloak-network
docker network create minio-network
确认docker网络创建成功
root@ecs01:~# docker network ls
NETWORK ID NAME DRIVER SCOPE
55509fbd4813 bridge bridge local
00b8d117c799 host host local
b1c5d0f0cd6d keycloak-network bridge local
3af149dcc797 minio-network bridge local
a1e5cf67efab none null local
f0f830bcea64 outline-network bridge local
ba512776a14a traefik-network bridge local
Traefik 部署
使用以下方式生成traefikadmin
密码,替换TRAEFIK_BASIC_AUTH
中的值。
root@ecs01:~# apt install -y apache2-utils
root@ecs01:~# echo $(htpasswd -nbB traefikadmin "mypassword") | sed -e s/\\$/\\$\\$/g
traefikadmin:$$2y$$05$$AKU6mkLPs1EB4t3Hh5QdB.nUJgLYFqZySUpFVyJw4XSFeG.dPXveS
创建环境变量文件
cd /data/traefik
cat >/data/traefik/.env<<EOF
# Traefik Variables
TRAEFIK_IMAGE_TAG=traefik:v3.1
TRAEFIK_LOG_LEVEL=WARN
TRAEFIK_ACME_EMAIL=example@outlook.com
TRAEFIK_HOSTNAME=traefik.example.com
# Basic Authentication for Traefik Dashboard
# Username: traefikadmin
# Passwords must be encoded using MD5, SHA1, or BCrypt https://hostingcanada.org/htpasswd-generator/
TRAEFIK_BASIC_AUTH=traefikadmin:$$2y$$05$$AKU6csLPs1EB4t3Hh5QdF.nUJgLYFqasdfwerqweJw4XSFeG.dPXveS
EOF
参数说明
TRAEFIK_IMAGE_TAG=traefik:v3.1
: 指定 Traefik 镜像的版本标签。TRAEFIK_LOG_LEVEL=WARN
: 设置 Traefik 的日志级别(DEBUG, INFO, WARN, ERROR)。TRAEFIK_ACME_EMAIL=example@outlook.com
: 用于 ACME 协议的电子邮件地址,管理 SSL 证书。TRAEFIK_HOSTNAME=traefik.example.com
: 指定 Traefik 仪表板的访问域名,替换为您自己的域名。TRAEFIK_BASIC_AUTH
: 用于保护 Traefik 仪表板的基本认证信息,格式为用户名:加密后的密码
。
创建docker-compose文件
cat >/data/traefik/docker-compose.yaml<<'EOF'
name: traefik
networks:
traefik-network:
external: true
volumes:
traefik-certificates:
services:
traefik:
image: ${TRAEFIK_IMAGE_TAG}
command:
- "--log.level=${TRAEFIK_LOG_LEVEL}"
- "--accesslog=true"
- "--api.dashboard=true"
- "--api.insecure=true"
# Docker Provider
- "--providers.docker=true"
- "--providers.docker.endpoint=unix:///var/run/docker.sock"
- "--providers.docker.exposedByDefault=false"
# health check
- "--ping=true"
- "--ping.entrypoint=ping"
- "--entryPoints.ping.address=:8082"
# letsencrypt
- "--entryPoints.websecure.address=:443"
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
- "--certificatesresolvers.letsencrypt.acme.email=${TRAEFIK_ACME_EMAIL}"
- "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/acme.json"
# dashboard-redirect-https
- "--entryPoints.web.address=:80"
- "--entrypoints.web.http.redirections.entryPoint.to=websecure"
- "--entrypoints.web.http.redirections.entryPoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
# prometheus
- "--metrics.prometheus=true"
- "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0"
- "--global.checkNewVersion=true"
- "--global.sendAnonymousUsage=false"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- traefik-certificates:/etc/traefik/acme
networks:
- traefik-network
ports:
- "80:80"
- "443:443"
healthcheck:
test: ["CMD", "wget", "http://localhost:8082/ping","--spider"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
labels:
- "traefik.enable=true"
# dashboard with https
- "traefik.http.routers.dashboard.rule=Host(`${TRAEFIK_HOSTNAME}`)"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.services.dashboard.loadbalancer.server.port=8080"
- "traefik.http.routers.dashboard.tls=true"
- "traefik.http.routers.dashboard.tls.certresolver=letsencrypt"
- "traefik.http.services.dashboard.loadbalancer.passhostheader=true"
# basic auth
- "traefik.http.routers.dashboard.middlewares=authtraefik"
- "traefik.http.middlewares.authtraefik.basicauth.users=${TRAEFIK_BASIC_AUTH}"
# service-redirect-https
- "traefik.http.routers.http-catchall.rule=HostRegexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
restart: unless-stopped
EOF
启动traefik容器
docker compose up -d
确认容器运行状态
root@ecs01:/data/traefik# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
traefik-traefik-1 traefik:v3.1 "/entrypoint.sh --lo…" traefik 3 minutes ago Up 2 minutes (healthy) 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp
root@ecs01:/data/traefik#
浏览器访问traefik dashboard: https://traefik.example.com
,输入traefik登录账号traefikadmin及设置的密码。
正常登录后如下:
Minio部署
创建环境变量文件
cd /data/minio
cat >/data/minio/.env<<EOF
# Minio Variables
MINIO_IMAGE_TAG=minio/minio:RELEASE.2024-08-26T15-33-07Z
MINIO_ADMIN=minioadmin
MINIO_ADMIN_PASSWORD=minioadmin
# MiniIO API hostname
MINIO_HOSTNAME=minio.example.com
MINIO_CONSOLE_URL=https://console.minio.example.com
MINIO_CONSOLE_HOSTNAME=console.minio.example.com
# Minio for Outline
OUTLINE_MINIO_BUCKET_NAME=outline
OUTLINE_MINIO_USER=bucket.outline
OUTLINE_MINIO_USER_PASSWORD=outlinepass
EOF
参数说明
-
MINIO_IMAGE_TAG
: 指定 MinIO 镜像的版本标签,表示使用的 MinIO 版本。 -
MINIO_ADMIN=minioadmin
: MinIO 的管理员用户名,示例值为minioadmin
。 -
MINIO_ADMIN_PASSWORD=minioadmin
: MinIO 的管理员密码,示例值为minioadmin
。 -
MINIO_HOSTNAME=minio.example.com
: 指定 MinIO 服务的主机名,用于通过域名访问 MinIO API,替换为您自己的域名。 -
MINIO_CONSOLE_URL=https://console.minio.example.com
: 指定 MinIO 控制台的完整 URL,用于访问 MinIO Web 管理界面,替换为您自己的域名。 -
MINIO_CONSOLE_HOSTNAME=console.minio.example.com
: 指定 MinIO 控制台的主机名,用于配置 Traefik 代理的路由或域名解析,替换为您自己的域名。 -
OUTLINE_MINIO_BUCKET_NAME=outline
:指定 Outline 在 MinIO 中使用的存储桶名称。这个存储桶用于存储 Outline 的文件、文档和附件,是一个用于组织和管理所有 Outline 相关数据的存储容器。 -
OUTLINE_MINIO_USER=bucket.outline
:设置访问 MinIO 存储桶的用户名。通常以“bucket.”作为前缀,表示该用户对OUTLINE_MINIO_BUCKET_NAME
中指定的存储桶具有特定的访问权限。 -
OUTLINE_MINIO_USER_PASSWORD=outlinepass
:这是与 MinIO 用户 (bucket.outline
) 关联的密码,用于认证和授权访问 Outline 指定的 MinIO 存储桶。
创建docker-compose文件
cat >/data/minio/docker-compose.yaml<<'EOF'
name: minio
networks:
minio-network:
external: true
traefik-network:
external: true
volumes:
minio-data:
services:
minio:
image: ${MINIO_IMAGE_TAG}
command: minio server /data --console-address ":9001"
volumes:
- minio-data:/data
environment:
MINIO_ROOT_USER: ${MINIO_ADMIN}
MINIO_ROOT_PASSWORD: ${MINIO_ADMIN_PASSWORD}
MINIO_BROWSER_REDIRECT_URL: ${MINIO_CONSOLE_URL}
networks:
- minio-network
- traefik-network
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
labels:
- "traefik.enable=true"
# Minio API
- "traefik.http.routers.minio-api.rule=Host(`${MINIO_HOSTNAME}`)"
- "traefik.http.routers.minio-api.service=minio-api"
- "traefik.http.routers.minio-api.entrypoints=websecure"
- "traefik.http.services.minio-api.loadbalancer.server.port=9000"
- "traefik.http.routers.minio-api.tls=true"
- "traefik.http.routers.minio-api.tls.certresolver=letsencrypt"
- "traefik.http.services.minio-api.loadbalancer.passhostheader=true"
- "traefik.http.routers.minio-api.middlewares=compresstraefik"
# Minio Console
- "traefik.http.routers.minio-console.rule=Host(`${MINIO_CONSOLE_HOSTNAME}`)"
- "traefik.http.routers.minio-console.service=minio-console"
- "traefik.http.routers.minio-console.entrypoints=websecure"
- "traefik.http.services.minio-console.loadbalancer.server.port=9001"
- "traefik.http.routers.minio-console.tls=true"
- "traefik.http.routers.minio-console.tls.certresolver=letsencrypt"
- "traefik.http.services.minio-console.loadbalancer.passhostheader=true"
- "traefik.http.routers.minio-console.middlewares=compresstraefik"
- "traefik.http.middlewares.compresstraefik.compress=true"
- "traefik.docker.network=traefik-network"
restart: unless-stopped
EOF
启动minio容器
docker compose up -d
确认容器运行状态
root@ecs01:/data/minio# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
minio-minio-1 minio/minio:RELEASE.2024-08-26T15-33-07Z "/usr/bin/docker-ent…" minio 4 hours ago Up 4 hours (healthy) 9000/tcp
浏览器访问minio console控制台: https://console.minio.example.com
,输入minio登录账号minioadmin
及设置的密码。
登录后示例如下:
Minio配置
安装minio客户端,可以从官网下载或从容器取出客户端文件
root@ecs01:/data/minio# docker cp minio-minio-1:/usr/bin/mc /usr/local/bin/
为outline创建存储桶及minio用户
定义minio配置变量,在当前shell执行以下命令
cd /data/minio
source .env
配置 minio 本地访问
mc config host add minio https://${MINIO_HOSTNAME} ${MINIO_ADMIN} ${MINIO_ADMIN_PASSWORD}
为outline创建单独的minio存储桶及用户
mc mb minio/${OUTLINE_MINIO_BUCKET_NAME}
mc admin user add minio ${OUTLINE_MINIO_USER} ${OUTLINE_MINIO_USER_PASSWORD}
为outline创建单独的minio访问策略
mkdir -p /data/minio/outline_policys
cd /data/minio/outline_policys
cat > outline-policy.json <<-EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetBucketLocation",
"s3:ListBucket",
"s3:ListBucketMultipartUploads"
],
"Resource": [
"arn:aws:s3:::${OUTLINE_MINIO_BUCKET_NAME}"
]
},
{
"Action": [
"s3:AbortMultipartUpload",
"s3:DeleteObject",
"s3:GetObject",
"s3:ListMultipartUploadParts",
"s3:PutObject",
"s3:GetObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::${OUTLINE_MINIO_BUCKET_NAME}/*"
],
"Sid": ""
}
]
}
EOF
cat > outline-public.json <<-EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": [
"*"
]
},
"Action": [
"s3:GetObject",
"s3:ListMultipartUploadParts"
],
"Resource": [
"arn:aws:s3:::${OUTLINE_MINIO_BUCKET_NAME}/public/*"
]
}
]
}
EOF
应用策略
mc anonymous set-json outline-public.json minio/${OUTLINE_MINIO_BUCKET_NAME}
mc admin policy create minio OutlineAppFullAccess outline-policy.json
mc admin policy attach minio OutlineAppFullAccess --user=${OUTLINE_MINIO_USER}
确认存储桶已创建
keycloak部署
创建环境变量文件
cd /data/keycloak
cat >/data/keycloak/.env<<EOF
# Keycloak Variables
KEYCLOAK_POSTGRES_IMAGE_TAG=docker.io/library/postgres:16.2
KEYCLOAK_IMAGE_TAG=docker.io/keycloak/keycloak:25.0.4
KEYCLOAK_DB_NAME=keycloakdb
KEYCLOAK_DB_USER=keycloakdbuser
KEYCLOAK_DB_PASSWORD=keycloakdbpass
KEYCLOAK_ADMIN_USERNAME=keycloakadmin
KEYCLOAK_ADMIN_PASSWORD=keycloakadminpass
KEYCLOAK_HOSTNAME=keycloak.example.com
EOF
参数说明
KEYCLOAK_POSTGRES_IMAGE_TAG=docker.io/library/postgres:16.2
: 指定用于 Keycloak 数据库的 PostgreSQL 镜像版本标签。KEYCLOAK_IMAGE_TAG=docker.io/keycloak/keycloak:25.0.4
: 指定 Keycloak 服务的镜像版本标签。KEYCLOAK_DB_NAME=keycloakdb
: Keycloak 使用的数据库名称。KEYCLOAK_DB_USER=keycloakdbuser
: 连接 Keycloak 数据库的用户名。KEYCLOAK_DB_PASSWORD=keycloakdbpass
: 连接 Keycloak 数据库的用户密码。KEYCLOAK_ADMIN_USERNAME=keycloakadmin
: Keycloak 管理员账户的用户名,用于登录 Keycloak 管理控制台。KEYCLOAK_ADMIN_PASSWORD=keycloakadminpass
: Keycloak 管理员账户的密码,用于登录 Keycloak 管理控制台。KEYCLOAK_HOSTNAME=keycloak.example.com
: 指定 Keycloak 服务的主机名,用于通过域名访问 Keycloak,替换为您自己的域名。
创建docker-compose文件
cat >/data/keycloak/docker-compose.yaml<<'EOF'
name: 'keyclock'
networks:
keycloak-network:
external: true
traefik-network:
external: true
volumes:
keycloak-postgres:
services:
postgres-keycloak:
image: ${KEYCLOAK_POSTGRES_IMAGE_TAG}
volumes:
- keycloak-postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${KEYCLOAK_DB_NAME}
POSTGRES_USER: ${KEYCLOAK_DB_USER}
POSTGRES_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
networks:
- keycloak-network
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "${KEYCLOAK_DB_NAME}", "-U", "${KEYCLOAK_DB_USER}" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
keycloak:
image: ${KEYCLOAK_IMAGE_TAG}
command: start-dev
environment:
KC_DB: postgres
KC_DB_URL_HOST: postgres-keycloak
KC_DB_URL_DATABASE: ${KEYCLOAK_DB_NAME}
KC_DB_USERNAME: ${KEYCLOAK_DB_USER}
KC_DB_PASSWORD: ${KEYCLOAK_DB_PASSWORD}
KC_DB_SCHEMA: public
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN_USERNAME}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_HEALTH_ENABLED: 'true'
KC_HOSTNAME: ${KEYCLOAK_HOSTNAME}
KC_HTTP_ENABLED: 'true'
KC_PROXY_HEADERS: 'xforwarded'
PROXY_ADDRESS_FORWARDING: 'true'
networks:
- keycloak-network
- traefik-network
healthcheck:
test:
- "CMD-SHELL"
- |
exec 3<>/dev/tcp/localhost/9000 &&
echo -e 'GET /health/ready HTTP/1.1\r\nHost: localhost\r\nConnection: close\r\n\r\n' >&3 &&
cat <&3 | tee /tmp/healthcheck.log | grep -q '200 OK'
interval: 10s
timeout: 5s
retries: 3
start_period: 90s
labels:
- "traefik.enable=true"
- "traefik.http.routers.keycloak.rule=Host(`${KEYCLOAK_HOSTNAME}`)"
- "traefik.http.routers.keycloak.service=keycloak"
- "traefik.http.routers.keycloak.entrypoints=websecure"
- "traefik.http.services.keycloak.loadbalancer.server.port=8080"
- "traefik.http.routers.keycloak.tls=true"
- "traefik.http.routers.keycloak.tls.certresolver=letsencrypt"
- "traefik.http.services.keycloak.loadbalancer.passhostheader=true"
- "traefik.http.routers.keycloak.middlewares=compresstraefik"
- "traefik.http.middlewares.compresstraefik.compress=true"
- "traefik.docker.network=traefik-network"
restart: unless-stopped
depends_on:
postgres-keycloak:
condition: service_healthy
EOF
启动keycloak容器
docker compose up -d
确认容器运行状态
root@ecs01:/data/keycloak# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
keyclock-keycloak-1 docker.io/keycloak/keycloak:25.0.4 "/opt/keycloak/bin/k…" keycloak 4 hours ago Up 4 hours (healthy) 8080/tcp, 8443/tcp, 9000/tcp
keyclock-postgres-keycloak-1 docker.io/library/postgres:16.2 "docker-entrypoint.s…" postgres-keycloak 4 hours ago Up 4 hours (healthy) 5432/tcp
root@ecs01:/data/keycloak#
keycloak配置
浏览器访问keycloak控制台: https://keycloak.example.com
,输入keycloak登录账号keycloakadmin
及设置的密码。
登录后下拉选择Create realm,新建realm
指定Realm name名称为outline
接下来,在“Manage”部分中选择“Clients”,然后单击“Create client”按钮。
在“Client type”字段中,选择“OpenID Connect”。
在“Client ID”字段中,输入“outline”(小写),然后单击“下一步”按钮。
接下来,您需要启用“Client authentication”并在“Authentication flow”部分中选择“Standard flow”。应禁用所有其他值。
单击“下一步”按钮。
设置登录选项
# Root URL
https://outline.example.com/
# Home URL
https://outline.example.com/
# Valid redirect URIs
https://outline.example.com/
请注意,outline.example.com
是我服务的域名。因此,需要指定你自己的域名,该域名指向安装了 Traefik 服务的服务器的 IP 地址,这会将请求重定向到 Outline。
单击“保存”按钮。
导航到“Credentials”选项卡并复制“Client Secret”字段的内容。
后续将“客户端密钥”字段的复制内容粘贴到outline .env
文件中的OUTLINE_OIDC_CLIENT_SECRET
变量中。
Bz8GjPI9E7QqAZcGoQRaktEsW155V5u2
现在让我们创建一个能够使用 Keycloak 登录 Outline 的用户。
在“Manage”部分中选择“Users”,然后单击“Create new user”按钮。
在下一步中,您需要指定:用户名、电子邮件地址、名字、姓氏和密码。
请注意,如果您提供电子邮件地址,用户不仅可以使用用户名还可以使用电子邮件登录 Outline。
单击“创建”按钮。
接下来,您需要为新用户设置密码。
转到“Credentials”选项卡,然后单击“Set password”按钮。
输入强密码并单击“保存”按钮。
单击“保存密码”按钮确认为用户分配新密码。新密码已成功设置。后续,您可以启动 Outline 及其附带服务,并使用之前创建的用户登录 Outline。
outline部署
创建环境变量文件
cd /data/outline
cat >/data/outline/.env<<EOF
# Outline Variables
OUTLINE_POSTGRES_IMAGE_TAG=postgres:16.2
OUTLINE_IMAGE_TAG=outlinewiki/outline:0.78.0
OUTLINE_REDIS_IMAGE_TAG=redis:7.4.0
OUTLINE_DB_NAME=outlinedb
OUTLINE_DB_USER=outlinedbuser
OUTLINE_DB_PASSWORD=outlinedbpass
OUTLINE_URL=https://outline.example.com
OUTLINE_HOSTNAME=outline.example.com
# MiniIO Variables
OUTLINE_MINIO_USER=bucket.outline
OUTLINE_MINIO_USER_PASSWORD=outlinepass
OUTLINE_MINIO_BUCKET_NAME=outline
OUTLINE_AWS_S3_UPLOAD_BUCKET_URL=https://minio.example.com/outline
OUTLINE_FILE_STORAGE_UPLOAD_MAX_SIZE=104857600000
OUTLINE_FILE_STORAGE_IMPORT_MAX_SIZE=104857600000
# Secret key
# It can be generated with the command:
# openssl rand -hex 32
OUTLINE_SECRET_KEY=00703eaa1fedbb92c903c439feac11e4268c76f6eecc097ba6cc714431b5cb08
# Utils secret
# It can be generated with the command:
# openssl rand -hex 32
OUTLINE_UTILS_SECRET=7eee2571836979c3724f9900a55b1291aa3cdfbc32c42056aacfaaa5b9978649
# Keycloak realm secret
# You need to specify it before starting the Outline container
# It can be found in your realm manage section. Clients/YourClient/Credentials.
OUTLINE_OIDC_CLIENT_SECRET=Bz8GjPI9E7QqAZcGoQRaktEsW155V5u2
# Authentication endpoints
# It can be found in your realm configure section. Realm Settings/General/Endpoints.
OUTLINE_OIDC_AUTH_URI=https://keycloak.example.com/realms/outline/protocol/openid-connect/auth
# It can be found in your realm configure section. Realm Settings/General/Endpoints.
OUTLINE_OIDC_TOKEN_URI=https://keycloak.example.com/realms/outline/protocol/openid-connect/token
# It can be found in your realm configure section. Realm Settings/General/Endpoints.
OUTLINE_OIDC_USERINFO_URI=https://keycloak.example.com/realms/outline/protocol/openid-connect/userinfo
OUTLINE_OIDC_DISPLAY_NAME=Keycloak
OUTLINE_OIDC_USERNAME_CLAIM=email
OUTLINE_OIDC_SCOPES=openid profile email
EOF
outline参数说明:
OUTLINE_POSTGRES_IMAGE_TAG=postgres:16.2
: 指定用于 Outline 数据库的 PostgreSQL 镜像版本标签。OUTLINE_IMAGE_TAG=outlinewiki/outline:0.78.0
: 指定 Outline 应用的镜像版本标签。OUTLINE_REDIS_IMAGE_TAG=redis:7.4.0
: 指定用于 Outline 缓存的 Redis 镜像版本标签。OUTLINE_DB_NAME=outlinedb
: Outline 使用的数据库名称。OUTLINE_DB_USER=outlinedbuser
: 连接 Outline 数据库的用户名。OUTLINE_DB_PASSWORD=outlinedbpass
: 连接 Outline 数据库的用户密码。OUTLINE_URL=https://outline.example.com
: Outline 应用的公共 URL,用于访问应用。OUTLINE_HOSTNAME=outline.example.com
: 指定 Outline 服务的主机名,用于通过域名访问 Outline。
MinIO 相关参数
OUTLINE_MINIO_USER=bucket.outline
: 用于连接 MinIO 服务的 Outline 用户名,示例格式为bucket.<bucketname>
。OUTLINE_MINIO_USER_PASSWORD=outlinepass
: 用于连接 MinIO 服务的 Outline 用户密码。OUTLINE_MINIO_BUCKET_NAME=outline
: MinIO 中用于存储 Outline 文件的存储桶名称。OUTLINE_AWS_S3_UPLOAD_BUCKET_URL=https://minio.example.com/outline
: Outline 使用的 MinIO 存储桶的完整 URL。OUTLINE_FILE_STORAGE_UPLOAD_MAX_SIZE=104857600
: Outline 允许的最大文件上传大小100M,以字节为单位。OUTLINE_FILE_STORAGE_IMPORT_MAX_SIZE=104857600
: Outline 允许的最大导入文件大小100M,以字节为单位。
安全密钥相关参数
OUTLINE_SECRET_KEY=
: Outline 的应用程序密钥,用于加密数据。OUTLINE_UTILS_SECRET=
: 用于实用工具(Utils)的秘密密钥。
Keycloak 相关参数
OUTLINE_OIDC_CLIENT_SECRET=
: 用于 Keycloak OIDC(OpenID Connect)客户端的秘密密钥。OUTLINE_OIDC_AUTH_URI=
: Keycloak 身份验证端点的 URI。OUTLINE_OIDC_TOKEN_URI=
: Keycloak 令牌端点的 URI。OUTLINE_OIDC_USERINFO_URI=
: Keycloak 用户信息端点的 URI。OUTLINE_OIDC_DISPLAY_NAME=Keycloak
: 用于在 Outline 中显示的 OIDC 提供程序名称。OUTLINE_OIDC_USERNAME_CLAIM=email
: 指定用于用户名的 OIDC 声明,通常是电子邮件。OUTLINE_OIDC_SCOPES=openid profile email
: 指定 OIDC 请求的权限范围,包括基本信息和电子邮件。
创建docker-compose文件
cat >/data/outline/docker-compose.yaml<<'EOF'
name: 'outline'
networks:
outline-network:
external: true
traefik-network:
external: true
volumes:
outline-postgres:
services:
postgres-outline:
image: ${OUTLINE_POSTGRES_IMAGE_TAG}
volumes:
- outline-postgres:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${OUTLINE_DB_NAME}
POSTGRES_USER: ${OUTLINE_DB_USER}
POSTGRES_PASSWORD: ${OUTLINE_DB_PASSWORD}
networks:
- outline-network
healthcheck:
test: [ "CMD", "pg_isready", "-q", "-d", "${OUTLINE_DB_NAME}", "-U", "${OUTLINE_DB_USER}" ]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
redis:
image: ${OUTLINE_REDIS_IMAGE_TAG}
networks:
- outline-network
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 3
start_period: 60s
restart: unless-stopped
outline:
image: ${OUTLINE_IMAGE_TAG}
environment:
DATABASE_URL: postgres://${OUTLINE_DB_USER}:${OUTLINE_DB_PASSWORD}@postgres-outline:5432/${OUTLINE_DB_NAME}?sslmode=disable&connect_timeout=10
PGSSLMODE: disable
REDIS_URL: redis://redis:6379
URL: ${OUTLINE_URL}
PORT: 3000
SECRET_KEY: ${OUTLINE_SECRET_KEY}
UTILS_SECRET: ${OUTLINE_UTILS_SECRET}
FORCE_HTTPS: 'false'
ENABLE_UPDATES: 'true'
AWS_REGION: eu-east-1
AWS_ACCESS_KEY_ID: ${OUTLINE_MINIO_USER}
AWS_SECRET_ACCESS_KEY: ${OUTLINE_MINIO_USER_PASSWORD}
AWS_S3_UPLOAD_BUCKET_URL: ${OUTLINE_AWS_S3_UPLOAD_BUCKET_URL}
AWS_S3_ACCELERATE_URL: ${OUTLINE_AWS_S3_UPLOAD_BUCKET_URL}
AWS_S3_UPLOAD_BUCKET_NAME: ${OUTLINE_MINIO_BUCKET_NAME}
AWS_S3_FORCE_PATH_STYLE: 'false'
AWS_S3_ACL: private
FILE_STORAGE_UPLOAD_MAX_SIZE: ${OUTLINE_FILE_STORAGE_UPLOAD_MAX_SIZE}
FILE_STORAGE_IMPORT_MAX_SIZE: ${OUTLINE_FILE_STORAGE_IMPORT_MAX_SIZE}
OIDC_CLIENT_ID: outline
OIDC_CLIENT_SECRET: ${OUTLINE_OIDC_CLIENT_SECRET}
OIDC_AUTH_URI: ${OUTLINE_OIDC_AUTH_URI}
OIDC_TOKEN_URI: ${OUTLINE_OIDC_TOKEN_URI}
OIDC_USERINFO_URI: ${OUTLINE_OIDC_USERINFO_URI}
OIDC_DISPLAY_NAME: ${OUTLINE_OIDC_DISPLAY_NAME}
OIDC_USERNAME_CLAIM: ${OUTLINE_OIDC_USERNAME_CLAIM}
OIDC_SCOPES: ${OUTLINE_OIDC_SCOPES}
networks:
- outline-network
- traefik-network
healthcheck:
test: timeout 10s bash -c ':> /dev/tcp/127.0.0.1/3000' || exit 1
interval: 10s
timeout: 5s
retries: 3
start_period: 90s
labels:
- "traefik.enable=true"
- "traefik.http.routers.outline.rule=Host(`${OUTLINE_HOSTNAME}`)"
- "traefik.http.routers.outline.service=outline"
- "traefik.http.routers.outline.entrypoints=websecure"
- "traefik.http.services.outline.loadbalancer.server.port=3000"
- "traefik.http.routers.outline.tls=true"
- "traefik.http.routers.outline.tls.certresolver=letsencrypt"
- "traefik.http.services.outline.loadbalancer.passhostheader=true"
- "traefik.http.routers.outline.middlewares=compresstraefik"
- "traefik.http.middlewares.compresstraefik.compress=true"
- "traefik.docker.network=traefik-network"
restart: unless-stopped
depends_on:
postgres-outline:
condition: service_healthy
redis:
condition: service_healthy
EOF
启动outline容器
docker compose up -d
确认容器运行状态
root@ecs01:/data/outline# docker compose ps
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
outline-outline-1 outlinewiki/outline:0.78.0 "docker-entrypoint.s…" outline 4 hours ago Up 4 hours (healthy) 3000/tcp
outline-postgres-outline-1 postgres:16.2 "docker-entrypoint.s…" postgres-outline 4 hours ago Up 4 hours (healthy) 5432/tcp
outline-redis-1 redis:7.4.0 "docker-entrypoint.s…" redis 4 hours ago Up 4 hours (healthy) 6379/tcp
root@ecs01:/data/outline#
outline访问
浏览器访问outline控制台: https://outline.example.com
,输入outline登录账号admin
及设置的密码。
首先会跳转到keycloak进行认证:
Outline 登录后如下
新建文档集及文档
查看对象存储中上传的数据:
至此,我们在一台云服务器上,通过docker-compose方式完成了outline的部署。
参考:
https://www.heyvaldemar.com/install-outline-and-keycloak-using-docker-compose/
https://github.com/heyvaldemar/outline-keycloak-traefik-letsencrypt-docker-compose