Docker 部署 Outline 团队知识库(对接 OpenLDAP + Dex)

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 树结构来填,用以下命令查看:

bash 复制代码
ldapsearch -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