Docker 部署 Outline 团队知识库(对接 OpenLDAP + Dex)
markdown
# Docker 部署 Outline 知识库完整指南
> 📌 本文记录了在 CentOS 7 环境下,使用 Docker 从零部署 Outline 团队知识库的完整过程,包括对接远程 OpenLDAP 认证(通过 Dex 桥接)以及踩坑记录。
>
> 环境信息:CentOS 7 | Docker 19.03 | 4C 15G | 部署时间 2026-07
O、架构总览
markdown
┌─────────────────────────────────┐
│ 用户浏览器 │
└────────────┬────────────────────┘
│
┌────────────▼────────────────────┐
│ Nginx(反向代理) │
│ :80 → :443 │
└────────────┬────────────────────┘
│
┌───────────────────────┼───────────────────────┐
│ │ │
┌────────▼────────┐ ┌──────────▼──────────┐ │
│ Outline │ │ Dex │ │
│ :3000 │◄──│ OIDC Bridge :5556 │ │
└────────┬────────┘ └──────────┬──────────┘ │
│ │ │
┌────────▼────────┐ ┌──────────▼──────────┐ ┌────────▼────────┐
│ PostgreSQL │ │ 远程 OpenLDAP │ │ Redis │
│ :5432 │ │ 192.168.8.119:389 │ │ :6379 │
└─────────────────┘ └─────────────────────┘ └─────────────────┘
css
**认证流程**:浏览器 → Outline → Dex → OpenLDAP → 验证通过 → 回调 Outline → 登录成功
一、环境准备
1.1 目录规划
/data/outlines/
├── docker-compose.yml # 服务编排
├── .env # Outline 环境变量
├── outline/ # Outline 源码(git clone)
│ ├── Dockerfile
│ └── server/config/database.js
├── dex/
│ └── config.yml # Dex 配置
└── data/ # 持久化数据
├── postgres/
├── redis/
└── outline/
1.2 安装 docker-compose
bash
> ⚠️ Docker 19.03 不支持 `docker compose`(V2),需安装带横杠的旧版
```bash
# 方式一:pip 安装
sudo yum install -y gcc python3-devel libffi-devel openssl-devel
pip3 install docker-compose==1.29.2
# 方式二:下载二进制(pip 依赖编译失败时用)
python3 -c "
import wget
wget.download('https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64', '/tmp/docker-compose')
"
sudo mv /tmp/docker-compose /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
验证:
bash
docker-compose --version
# docker-compose version 1.29.2, build xxxxxxx
1.3 配置 Docker 镜像加速
💡 国内网络必须配置,否则 Docker Hub 镜像拉不下来
bash
sudo mkdir -p /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"registry-mirrors": [
"https://docker.1ms.run",
"https://docker.xuanyuan.me",
"https://docker.m.daocloud.io"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
1.4 添加 Swap
💡 源码构建 Outline 时内存峰值较高,无 Swap 容易 OOM
bash
sudo dd if=/dev/zero of=/swapfile bs=1M count=4096
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h # 确认 Swap 有 4G
1.5 创建数据目录
bash
mkdir -p /data/outlines/data/{postgres,redis,outline}
二、克隆 Outline 源码
bash
cd /data/outlines
git clone https://github.com/outline/outline.git
三、编写配置文件
3.1 .env
bash
vim /data/outlines/.env
ini
# ===== 基础 =====
NODE_ENV=production
FORCE_HTTPS=false
SECRET_KEY=<openssl rand -hex 32 生成两次,第一次放这里>
UTILS_SECRET=<第二次放这里>
URL=https://192.168.0.208
PORT=3000
# ===== 数据库 =====
POSTGRES_PASSWORD=outline123
DATABASE_URL=postgres://outline:outline123@postgres:5432/outline?sslmode=disable
DATABASE_CONNECTION_POOL_MIN=2
DATABASE_CONNECTION_POOL_MAX=10
PGSSLMODE=disable
# ===== Redis =====
REDIS_URL=redis://redis:6379
# ===== 文件存储 =====
FILE_STORAGE=local
FILE_STORAGE_LOCAL_ROOT_DIR=/var/lib/outline/data
# ===== OIDC(Dex 桥接) =====
OIDC_CLIENT_ID=outline
OIDC_CLIENT_SECRET=outline-secret-change-me
OIDC_AUTH_URI=http://192.168.0.208:5556/dex/auth
OIDC_TOKEN_URI=http://192.168.0.208:5556/dex/token
OIDC_USERINFO_URI=http://192.168.0.208:5556/dex/userinfo
OIDC_LOGOUT_URI=http://192.168.0.208:5556/dex/logout
OIDC_DISPLAY_NAME=LDAP Login
OIDC_USERNAME_CLAIM=preferred_username
OIDC_SCOPES=openid profile email groups
⚠️ 生成密钥:
openssl rand -hex 32执行两次
3.2 dex/config.yml
bash
mkdir -p /data/outlines/dex
vim /data/outlines/dex/config.yml
yaml
issuer: http://192.168.0.208:5556/dex
storage:
type: sqlite3
config:
file: /tmp/dex.db
web:
http: 0.0.0.0:5556
staticClients:
- id: outline
name: Outline
secret: outline-secret-change-me
redirectURIs:
- http://192.168.0.208:3000/auth/oidc.callback
- https://192.168.0.208/auth/oidc.callback
connectors:
- type: ldap
id: ldap
name: LDAP Login
config:
host: 192.168.8.119:389
insecureNoSSL: true
insecureSkipVerify: true
bindDN: cn=admin,dc=youneng,dc=com
bindPW: <LDAP管理员密码>
userSearch:
baseDN: dc=youneng,dc=com
filter: "(objectClass=inetOrgPerson)"
username: uid
idAttr: uid
emailAttr: mail
nameAttr: cn
groupSearch:
baseDN: dc=youneng,dc=com
filter: "(objectClass=posixGroup)"
nameAttr: cn
userMatchers:
- userAttr: uid
groupAttr: memberUid
oauth2:
skipApprovalScreen: true
responseTypes: ["code", "token", "id_token"]
💡
baseDN必须根据你的实际 LDAP 树结构来填,用以下命令查看:
bashldapsearch -x -H ldap://192.168.8.119:389 \ -b "dc=youneng,dc=com" \ -D "cn=admin,dc=youneng,dc=com" \ -w <密码>
3.3 docker-compose.yml
bash
vim /data/outlines/docker-compose.yml
yaml
version: "3.8"
services:
postgres:
image: postgres:15
restart: unless-stopped
privileged: true
environment:
POSTGRES_USER: outline
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-outline123}
POSTGRES_DB: outline
volumes:
- /data/outlines/data/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U outline -d outline"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
redis:
image: redis:7-alpine
restart: unless-stopped
volumes:
- /data/outlines/data/redis:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- backend
dex:
image: dexidp/dex:latest
restart: unless-stopped
ports:
- "5556:5556"
volumes:
- ./dex/config.yml:/etc/dex/config.yml:ro
command: dex serve /etc/dex/config.yml
networks:
- backend
outline:
build:
context: ./outline
dockerfile: Dockerfile
restart: unless-stopped
privileged: true
env_file: .env
ports:
- "3000:3000"
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- /data/outlines/data/outline:/var/lib/outline/data
networks:
- backend
networks:
backend:
driver: bridge
四、修改源码(关键步骤)
4.1 修改 Dockerfile
🎯 解决两个问题:Node 版本兼容 + APT 构建报错
bash
vim /data/outlines/outline/Dockerfile
改动一 ------ Node 版本降级(第 6 步):
diff
- FROM node:26.3.0-slim AS runner
+ FROM node:20-slim AS runner
⚠️ Outline 要求 Node
>= 18 <= 20,22 和 26 都不行
改动二 ------ 删除 wget 安装步骤:
diff
- RUN rm -f /etc/apt/apt.conf.d/docker-clean \
- && apt-get update \
- && apt-get install -y wget \
- && rm -rf /var/lib/apt/lists/*
💡 wget 非必需,删掉可避免 Debian trixie 的 APT bug 和内存不足问题
4.2 修改 database.js
🎯 解决 PostgreSQL SSL 连接问题
bash
vim /data/outlines/outline/server/config/database.js
把 production 配置改为与 shared 一致:
javascript
const shared = {
use_env_variable: process.env.DATABASE_URL ? "DATABASE_URL" : undefined,
dialect: "postgres",
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT || 5432,
username: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD || undefined,
database: process.env.DATABASE_NAME,
};
module.exports = {
development: shared,
test: shared,
"production-ssl-disabled": shared,
production: shared, // ← 去掉原来的 dialectOptions.ssl
};
五、构建与启动
bash
cd /data/outlines
# 构建(首次约 10-20 分钟)
docker-compose build --no-cache outline
# 启动全部服务
docker-compose up -d
# 等待就绪
sleep 15
# 检查状态
docker-compose ps
预期输出:
bash
Name Command State Ports
----------------------------------------------------------------------------------
outlines_dex_1 dex serve /etc/dex/... Up 0.0.0.0:5556->5556/tcp
outlines_outline_1 docker-entrypoint.sh ... Up 0.0.0.0:3000->3000/tcp
outlines_postgres_1 docker-entrypoint.sh ... Up (healthy) 5432/tcp
outlines_redis_1 docker-entrypoint.sh ... Up (healthy) 6379/tcp
六、配置 Nginx 反向代理
🎯 Outline
production模式强制 HTTPS 跳转,必须用 Nginx + 证书
6.1 安装 Nginx
bash
sudo yum install -y epel-release nginx
6.2 生成自签名证书
bash
sudo mkdir -p /etc/nginx/ssl
sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 \
-keyout /etc/nginx/ssl/outline.key \
-out /etc/nginx/ssl/outline.crt \
-subj "/CN=192.168.0.208"
6.3 配置反向代理
bash
sudo vim /etc/nginx/conf.d/outline.conf
nginx
server {
listen 80;
server_name 192.168.0.208;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name 192.168.0.208;
ssl_certificate /etc/nginx/ssl/outline.crt;
ssl_certificate_key /etc/nginx/ssl/outline.key;
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_buffering off;
}
}
6.4 启动 Nginx
bash
sudo nginx -t
sudo systemctl start nginx
sudo systemctl enable nginx
七、验证与登录
7.1 服务连通性验证
bash
# Outline(HTTPS)
curl -I -k https://192.168.0.208
# 期望:HTTP/1.1 200 OK
# Dex OpenID 配置
curl -s http://192.168.0.208:5556/dex/.well-known/openid-configuration | grep issuer
# 期望:"issuer": "http://192.168.0.208:5556/dex"
# LDAP 连通性
ldapsearch -x -H ldap://192.168.8.119:389 \
-b "dc=youneng,dc=com" \
-D "cn=admin,dc=youneng,dc=com" \
-w <密码> "(objectClass=inetOrgPerson)" uid mail
# 期望:返回用户列表
7.2 浏览器登录
markdown
1. 访问 https://192.168.0.208(自签名证书会提示不安全,点「继续访问」)
2. 点击「Sign in with LDAP Login」
3. 输入 LDAP 用户名和密码
4. 授权后自动跳转 Outline,首次登录自动创建账户
7.3 切换中文界面
登录后:头像 → Settings → Language → 简体中文
或在 .env 中添加:
bash
DEFAULT_LANGUAGE=zh_CN
然后 docker-compose down && docker-compose up -d
八、踩坑记录
🔴 坑 1:Docker 版本过低
| 项目 | 内容 |
|---|---|
| 现象 | docker: 'compose' is not a docker command |
| 原因 | Docker 19.03 不支持 docker compose V2 |
| 解决 | 安装 docker-compose 1.29.2(带横杠的旧版) |
🔴 坑 2:Docker Hub 被墙
| 项目 | 内容 |
|---|---|
| 现象 | Get https://registry-1.docker.io/v2/: request canceled |
| 解决 | 配置 Docker 镜像加速器 |
🔴 坑 3:源码构建 APT 报错
| 项目 | 内容 |
|---|---|
| 现象 | E: Problem executing scripts APT::Update::Post-Invoke |
| 原因 | Debian trixie 的 APT 已知 bug |
| 解决 | Dockerfile 中删除 wget 安装步骤(非必需) |
🔴 坑 4:构建时内存不足
| 项目 | 内容 |
|---|---|
| 现象 | lzma error: Cannot allocate memory |
| 原因 | 无 Swap,构建峰值超出物理内存 |
| 解决 | 添加 4G Swap |
🔴 坑 5:PostgreSQL 写入失败
| 项目 | 内容 |
|---|---|
| 现象 | could not write to file "postmaster.pid": Operation not permitted |
| 原因 | Alpine 版 PostgreSQL 在 CentOS 上权限兼容问题 |
| 解决 | 使用 postgres:15(Debian 版)+ privileged: true + bind mount |
🔴 坑 6:Node.js 版本不兼容
| 项目 | 内容 |
|---|---|
| 现象 A | Node 26:SlowBuffer.prototype.equal: Cannot read properties of undefined |
| 现象 B | Node 22:The engine "node" is incompatible. Expected ">= 18 <= 20" |
| 解决 | Dockerfile 中 FROM node:20-slim |
🔴 坑 7:PostgreSQL SSL 连接被拒
| 项目 | 内容 |
|---|---|
| 现象 | The server does not support SSL connections |
| 原因 | server/config/database.js 中 production 环境默认开启 SSL |
| 解决 | 修改 database.js,去掉 dialectOptions.ssl;.env 加 PGSSLMODE=disable |
💡 注意:CLI(
yarn db:migrate)读的是database.json,应用读的是database.js。需两个都确认。
🔴 坑 8:HTTP 强制跳转 HTTPS
| 项目 | 内容 |
|---|---|
| 现象 | 访问 http://IP:3000 被 301 跳转到 https://IP |
| 原因 | Outline production 模式硬编码 HTTPS 跳转,FORCE_HTTPS=false 无效 |
| 解决 | Nginx 反向代理 + 自签名证书,从 HTTPS 访问 |
🔴 坑 9:Dex 容器名浏览器无法解析
| 项目 | 内容 |
|---|---|
| 现象 | 跳转到 http://dex:5556/dex/auth 后 DNS_PROBE_FINISHED_NXDOMAIN |
| 原因 | dex 是 Docker 内部容器名,浏览器在宿主机上无法解析 |
| 解决 | .env 和 dex/config.yml 中全部使用真实 IP 替代容器名 |
🔴 坑 10:LDAP 用户搜索不到
| 项目 | 内容 |
|---|---|
| 现象 | No such object |
| 原因 | baseDN 配置了不存在的 ou=People,实际 LDAP 树结构不同 |
| 解决 | 先 ldapsearch 查看实际结构,将 baseDN 设为根 dc=youneng,dc=com |
九、常用运维命令
bash
# ── 服务管理 ──────────────────────────────
docker-compose ps # 查看服务状态
docker-compose logs -f outline # 查看 Outline 日志
docker-compose logs -f dex # 查看 Dex 日志
docker-compose restart outline # 重启单个服务
docker-compose down && docker-compose up -d # 重建全部服务(修改 .env 后)
# ── 调试 ──────────────────────────────────
docker-compose exec outline sh # 进入 Outline 容器
docker-compose exec dex sh # 进入 Dex 容器
# ── LDAP 测试 ─────────────────────────────
ldapsearch -x -H ldap://192.168.8.119:389 \
-b "dc=youneng,dc=com" \
-D "cn=admin,dc=youneng,dc=com" \
-w <密码> "(objectClass=inetOrgPerson)" uid mail
# ── 清理 ──────────────────────────────────
docker-compose down -v # 停止并删除容器和 volume
docker system prune -f # 清理无用镜像和缓存
十、要点速查
| 配置项 | 关键值 | 说明 |
|---|---|---|
| Node 版本 | node:20-slim |
必须 >= 18 <= 20 |
| PostgreSQL 镜像 | postgres:15(Debian 版) |
Alpine 版在 CentOS 上有权限问题 |
| PostgreSQL SSL | 关闭 | 去掉 database.js 中的 dialectOptions.ssl |
| HTTPS | Nginx 反向代理 + 证书 | Outline 强制跳转 HTTPS,无法关闭 |
| Dex 地址 | 用真实 IP | .env 和 config.yml 中不能用容器名 |
| LDAP baseDN | 根据实际树结构 | 先 ldapsearch 查看再配置 |
| docker-compose 命令 | 带横杠 | docker-compose 而非 docker compose |
| 修改 .env 后 | docker-compose down && up -d |
restart 不会重新读取 .env |