本文档为技术人员提供在内网环境中部署 HedgeDoc 服务器的完整方案,支持 百人跨项目协作 。
📋 部署环境要求
1. 硬件要求
组件
最低配置
推荐配置 (百人)
说明
CPU
2核
4核 (Intel/AMD)
需支持虚拟化
内存
4GB
8-16GB DDR4
建议ECC内存
存储
100GB HDD
200GB SSD
数据盘建议RAID1
网络
千兆网卡
千兆网卡
内网专用
2. 软件环境
软件
版本要求
安装方式
备注
操作系统
CentOS 7.9+ / Ubuntu 20.04 LTS
ISO镜像安装
最小化安装
Docker
20.10.0+
官方仓库
配置国内镜像源
Docker Compose
2.0.0+
GitHub Releases
独立二进制文件
Git
2.25.0+
yum/apt安装
用于版本管理
🚀 部署步骤
阶段一:服务器初始化
1.1 系统配置
bash
复制代码
# 1. 更新系统
sudo yum update -y # CentOS
# 或
sudo apt update && sudo apt upgrade -y # Ubuntu
# 2. 设置主机名
sudo hostnamectl set-hostname hedgedoc-server
echo "192.168.10.100 hedgedoc-server" | sudo tee -a /etc/hosts
# 3. 关闭防火墙(内网环境可关闭)
sudo systemctl stop firewalld
sudo systemctl disable firewalld
# 或配置防火墙规则
sudo firewall-cmd --permanent --add-port=80/tcp
sudo firewall-cmd --permanent --add-port=443/tcp
sudo firewall-cmd --reload
# 4. 禁用SELinux
sudo setenforce 0
sudo sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
# 5. 优化系统参数
cat >> /etc/sysctl.conf << EOF
# HedgeDoc性能优化
vm.swappiness = 10
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.ip_local_port_range = 1024 65000
fs.file-max = 1000000
EOF
sudo sysctl -p
1.2 Docker环境部署
bash
复制代码
# 1. 安装Docker(CentOS示例)
sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-ce-cli containerd.io
# 2. 配置Docker镜像加速(内网可配置私有仓库)
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json << EOF
{
"registry-mirrors": [
"https://docker.mirrors.ustc.edu.cn",
"https://hub-mirror.c.163.com"
],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
},
"data-root": "/data/docker",
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
# 3. 启动Docker
sudo systemctl start docker
sudo systemctl enable docker
# 4. 安装Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.20.0/docker-compose-$(uname -s)-$(uname -m)" \
-o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
阶段二:HedgeDoc服务部署
2.1 创建部署目录结构
bash
复制代码
# 创建主目录
mkdir -p /opt/hedgedoc
cd /opt/hedgedoc
# 创建目录结构
mkdir -p {data/{postgres,redis},uploads,logs/{app,nginx},backup,config,ssl}
# 设置权限
chmod 755 /opt/hedgedoc/uploads
chown -R 1000:1000 /opt/hedgedoc/uploads # HedgeDoc容器用户
2.2 生成SSL证书(内网自签名)
bash
复制代码
# 生成私钥和证书
cd /opt/hedgedoc/ssl
# 生成CA私钥
openssl genrsa -out ca.key 4096
# 生成CA证书
openssl req -new -x509 -days 3650 -key ca.key -out ca.crt \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Company/CN=Internal CA"
# 生成服务器私钥
openssl genrsa -out hedgedoc.key 2048
# 生成证书签名请求
openssl req -new -key hedgedoc.key -out hedgedoc.csr \
-subj "/C=CN/ST=Beijing/L=Beijing/O=Company/CN=docs.internal.com" \
-reqexts SAN \
-config <(cat /etc/ssl/openssl.cnf \
<(printf "\n[SAN]\nsubjectAltName=DNS:docs.internal.com,DNS:*.internal.com"))
# 使用CA签名
openssl x509 -req -days 3650 -in hedgedoc.csr -CA ca.crt -CAkey ca.key \
-CAcreateserial -out hedgedoc.crt -extensions SAN \
-extfile <(cat /etc/ssl/openssl.cnf \
<(printf "\n[SAN]\nsubjectAltName=DNS:docs.internal.com,DNS:*.internal.com"))
# 验证证书
openssl verify -CAfile ca.crt hedgedoc.crt
2.3 创建docker-compose.yml
yaml
复制代码
# /opt/hedgedoc/docker-compose.yml
version: '3.8'
services:
# PostgreSQL数据库
database:
image: postgres:15-alpine
container_name: hedgedoc-postgres
restart: always
environment:
POSTGRES_USER: hedgedoc
POSTGRES_PASSWORD: ${DB_PASSWORD:-ChangeMe123!}
POSTGRES_DB: hedgedoc
POSTGRES_INITDB_ARGS: "--encoding=UTF8 --locale=C"
volumes:
- ./data/postgres:/var/lib/postgresql/data
- ./config/postgresql.conf:/etc/postgresql/postgresql.conf:ro
command: >
postgres
-c config_file=/etc/postgresql/postgresql.conf
-c shared_buffers=2GB
-c effective_cache_size=6GB
networks:
- hedgedoc-net
healthcheck:
test: ["CMD-SHELL", "pg_isready -U hedgedoc"]
interval: 30s
timeout: 10s
retries: 3
# Redis缓存
redis:
image: redis:7-alpine
container_name: hedgedoc-redis
restart: always
command: >
redis-server
--appendonly yes
--maxmemory 2gb
--maxmemory-policy allkeys-lru
--save 900 1
--save 300 10
--save 60 10000
volumes:
- ./data/redis:/data
- ./config/redis.conf:/usr/local/etc/redis/redis.conf:ro
networks:
- hedgedoc-net
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 30s
timeout: 10s
retries: 3
# HedgeDoc主应用
app:
image: quay.io/hedgedoc/hedgedoc:2.2.0
container_name: hedgedoc-app
restart: always
depends_on:
database:
condition: service_healthy
redis:
condition: service_healthy
environment:
# 基础配置
CMD_DOMAIN: docs.internal.com
CMD_PORT: 3000
CMD_PROTOCOL_USESSL: "true"
CMD_URL_ADDPORT: "false"
# 数据库配置
CMD_DB_URL: postgres://hedgedoc:${DB_PASSWORD:-ChangeMe123!}@database:5432/hedgedoc
CMD_DB_POOL_MIN: 5
CMD_DB_POOL_MAX: 50
# Redis配置
CMD_REDIS_URL: redis://redis:6379
CMD_CACHE_TTL: 7200
# 会话和Cookie
CMD_SESSION_SECRET: ${SESSION_SECRET:-ChangeMe456!}
CMD_SESSION_LIFETIME: 604800000
CMD_COOKIE_POLICY: "lax"
CMD_COOKIE_DOMAIN: ".internal.com"
# 安全配置
CMD_ALLOW_ANONYMOUS: "false"
CMD_ALLOW_EMAIL_REGISTER: "false"
CMD_ALLOW_FREEURL: "true"
CMD_DEFAULT_PERMISSION: "limited"
CMD_REQUIRE_VERIFIED_EMAIL: "false"
# 钉钉集成
CMD_OAUTH2_PROVIDERS: "dingtalk"
CMD_OAUTH2_DINGTALK_CLIENT_ID: ${DINGTALK_CLIENT_ID}
CMD_OAUTH2_DINGTALK_CLIENT_SECRET: ${DINGTALK_CLIENT_SECRET}
CMD_OAUTH2_DINGTALK_BASE_URL: https://oapi.dingtalk.com
# 文件上传
CMD_UPLOAD_TYPE: filesystem
CMD_UPLOAD_PATH: /hedgedoc/uploads
CMD_UPLOAD_MAX_FILESIZE: 52428800
# 性能配置
CMD_RATE_LIMIT_API_ENABLE: "true"
CMD_RATE_LIMIT_API_MAX: 200
CMD_RATE_LIMIT_API_WINDOWMS: 600000
# 日志配置
CMD_LOG_LEVEL: "info"
CMD_LOG_TRANSPORTS: "console,file"
CMD_LOG_FILE: "/hedgedoc/logs/app.log"
volumes:
- ./uploads:/hedgedoc/uploads
- ./logs/app:/hedgedoc/logs
- ./ssl/hedgedoc.crt:/hedgedoc/ssl.crt:ro
- ./ssl/hedgedoc.key:/hedgedoc/ssl.key:ro
networks:
- hedgedoc-net
healthcheck:
test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "https://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
# Nginx反向代理
nginx:
image: nginx:1.23-alpine
container_name: hedgedoc-nginx
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./config/nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl/hedgedoc.crt:/etc/nginx/ssl/hedgedoc.crt:ro
- ./ssl/hedgedoc.key:/etc/nginx/ssl/hedgedoc.key:ro
- ./logs/nginx:/var/log/nginx
depends_on:
- app
networks:
- hedgedoc-net
healthcheck:
test: ["CMD", "nginx", "-t"]
interval: 60s
timeout: 10s
retries: 3
networks:
hedgedoc-net:
driver: bridge
ipam:
driver: default
config:
- subnet: 172.22.0.0/24
2.4 创建Nginx配置
nginx
复制代码
# /opt/hedgedoc/config/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
multi_accept on;
use epoll;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'rt=$request_time uct="$upstream_connect_time"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;
client_max_body_size 100M;
# 上游服务器
upstream hedgedoc_backend {
server app:3000;
keepalive 32;
}
# 重定向HTTP到HTTPS
server {
listen 80;
server_name docs.internal.com;
return 301 https://$server_name$request_uri;
}
# HTTPS服务器
server {
listen 443 ssl http2;
server_name docs.internal.com;
ssl_certificate /etc/nginx/ssl/hedgedoc.crt;
ssl_certificate_key /etc/nginx/ssl/hedgedoc.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# 安全头
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 性能优化
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 $scheme;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 600s;
proxy_connect_timeout 75s;
# 健康检查
location /health {
proxy_pass https://hedgedoc_backend/health;
access_log off;
allow 192.168.10.0/24;
deny all;
}
# 静态资源缓存
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
proxy_pass https://hedgedoc_backend;
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
}
# 主应用
location / {
proxy_pass https://hedgedoc_backend;
# 限制请求频率
limit_req zone=api burst=30 nodelay;
limit_req_status 429;
}
}
# 请求限制
limit_req_zone $binary_remote_addr zone=api:20m rate=20r/s;
}
2.5 创建环境变量文件
bash
复制代码
# /opt/hedgedoc/.env
# 生成随机密码
DB_PASSWORD=$(openssl rand -base64 32 | tr -dc 'A-Za-z0-9' | head -c 32)
SESSION_SECRET=$(openssl rand -base64 64)
cat > /opt/hedgedoc/.env << EOF
# 数据库密码
DB_PASSWORD=${DB_PASSWORD}
# 会话密钥
SESSION_SECRET=${SESSION_SECRET}
# 钉钉应用信息(需提前在钉钉后台创建)
DINGTALK_CLIENT_ID=dingxxxxxxxxxxxxxxx
DINGTALK_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 内网域名
CMD_DOMAIN=docs.internal.com
# 内网IP段
CMD_TRUST_PROXY=192.168.10.0/24
# 可选:内网邮箱配置
# CMD_EMAIL=true
# CMD_EMAIL_HOST=smtp.internal.com
# CMD_EMAIL_PORT=25
# CMD_EMAIL_FROM=noreply@internal.com
EOF
# 保护环境变量文件
chmod 600 /opt/hedgedoc/.env
阶段三:启动与初始化
3.1 启动服务
bash
复制代码
cd /opt/hedgedoc
# 拉取镜像
docker-compose pull
# 启动服务
docker-compose up -d
# 查看启动状态
docker-compose ps
docker-compose logs -f --tail=100 app
# 检查服务健康
curl -k https://docs.internal.com/health
3.2 数据库初始化
bash
复制代码
# 等待数据库启动
sleep 30
# 创建数据库索引(优化查询性能)
docker-compose exec database psql -U hedgedoc hedgedoc << EOF
-- 创建性能优化索引
CREATE INDEX IF NOT EXISTS idx_notes_ownerid ON notes("ownerId");
CREATE INDEX IF NOT EXISTS idx_notes_alias ON notes(alias);
CREATE INDEX IF NOT EXISTS idx_notes_updatedat ON notes("updatedAt");
CREATE INDEX IF NOT EXISTS idx_revisions_noteid ON revisions("noteId");
CREATE INDEX IF NOT EXISTS idx_revisions_createdat ON revisions("createdAt");
CREATE INDEX IF NOT EXISTS idx_tags_noteid ON tags("noteId");
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
-- 查看表空间使用
SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename))
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
EOF
🔧 运维管理
1. 日常维护脚本
bash
复制代码
#!/bin/bash
# /opt/hedgedoc/scripts/maintenance.sh
set -e
echo "=== HedgeDoc 日常维护 $(date) ==="
# 1. 检查容器状态
echo "检查容器状态..."
docker-compose ps
# 2. 查看日志大小
echo "日志文件大小..."
du -sh /opt/hedgedoc/logs/*/
# 3. 数据库维护
echo "数据库维护..."
docker-compose exec database psql -U hedgedoc hedgedoc << EOF
VACUUM ANALYZE;
EOF
# 4. 清理Docker资源
echo "清理Docker资源..."
docker system prune -f --filter "until=24h"
# 5. 备份检查
echo "检查备份..."
ls -lh /opt/hedgedoc/backup/ | tail -5
echo "维护完成"
2. 备份脚本
bash
复制代码
#!/bin/bash
# /opt/hedgedoc/scripts/backup.sh
BACKUP_DIR="/opt/hedgedoc/backup/$(date +%Y%m%d_%H%M%S)"
mkdir -p $BACKUP_DIR
echo "开始备份: $(date)" | tee $BACKUP_DIR/backup.log
# 1. 备份数据库
echo "备份数据库..." | tee -a $BACKUP_DIR/backup.log
docker-compose exec -T database pg_dump -U hedgedoc --clean --create hedgedoc > $BACKUP_DIR/hedgedoc.sql
# 2. 备份上传文件
echo "备份上传文件..." | tee -a $BACKUP_DIR/backup.log
rsync -av --delete /opt/hedgedoc/uploads/ $BACKUP_DIR/uploads/ >> $BACKUP_DIR/backup.log 2>&1
# 3. 备份配置文件
echo "备份配置文件..." | tee -a $BACKUP_DIR/backup.log
cp /opt/hedgedoc/docker-compose.yml $BACKUP_DIR/
cp /opt/hedgedoc/.env $BACKUP_DIR/
cp -r /opt/hedgedoc/config $BACKUP_DIR/
cp -r /opt/hedgedoc/ssl $BACKUP_DIR/
# 4. 备份Redis数据
echo "备份Redis数据..." | tee -a $BACKUP_DIR/backup.log
docker-compose exec redis redis-cli SAVE
docker cp hedgedoc-redis:/data/dump.rdb $BACKUP_DIR/redis_dump.rdb
# 5. 压缩备份
echo "压缩备份..." | tee -a $BACKUP_DIR/backup.log
cd /opt/hedgedoc/backup
tar -czf hedgedoc_backup_$(date +%Y%m%d_%H%M%S).tar.gz -C $BACKUP_DIR .
# 6. 清理旧备份(保留30天)
find /opt/hedgedoc/backup -name "hedgedoc_backup_*.tar.gz" -mtime +30 -delete
# 7. 记录备份信息
echo "备份统计:" | tee -a $BACKUP_DIR/backup.log
du -sh $BACKUP_DIR | tee -a $BACKUP_DIR/backup.log
ls -lh /opt/hedgedoc/backup/hedgedoc_backup_*.tar.gz | tail -1 | tee -a $BACKUP_DIR/backup.log
echo "备份完成: $(date)" | tee -a $BACKUP_DIR/backup.log
3. 监控脚本
bash
复制代码
#!/bin/bash
# /opt/hedgedoc/scripts/monitor.sh
# 监控指标输出到Prometheus格式(可选)
METRICS_FILE="/tmp/hedgedoc_metrics.prom"
# 容器状态
CONTAINER_COUNT=$(docker-compose ps -q | wc -l)
echo "hedgedoc_containers_total $CONTAINER_COUNT" > $METRICS_FILE
# 服务健康
HEALTH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" https://docs.internal.com/health || echo "0")
echo "hedgedoc_health_status $HEALTH_STATUS" >> $METRICS_FILE
# 内存使用
MEM_USAGE=$(docker stats --no-stream --format "{{.MemUsage}}" hedgedoc-app | cut -d '/' -f 1 | tr -d 'MiB' | tr -d ' ')
echo "hedgedoc_memory_usage_bytes $MEM_USAGE" >> $METRICS_FILE
# 数据库连接
DB_CONNECTIONS=$(docker-compose exec database psql -U hedgedoc -t -c "SELECT count(*) FROM pg_stat_activity WHERE datname='hedgedoc';" 2>/dev/null || echo "0")
echo "hedgedoc_database_connections $DB_CONNECTIONS" >> $METRICS_FILE
# 用户活跃度
ACTIVE_USERS=$(docker-compose exec database psql -U hedgedoc -t -c "SELECT COUNT(DISTINCT user_id) FROM revisions WHERE \"updatedAt\" > NOW() - INTERVAL '1 hour';" 2>/dev/null || echo "0")
echo "hedgedoc_active_users $ACTIVE_USERS" >> $METRICS_FILE
🚨 故障排查
常见问题及解决方案
问题现象
可能原因
解决方案
服务无法启动
端口冲突
`netstat -tlnp
数据库连接失败
密码错误/网络问题
检查 .env 文件,验证网络连通性
钉钉登录失败
应用配置错误
检查钉钉应用回调地址和密钥
上传文件失败
权限问题
chown -R 1000:1000 /opt/hedgedoc/uploads
内存占用过高
内存泄漏/配置不当
调整JVM参数,重启服务
SSL证书错误
证书格式/路径错误
验证证书路径和权限
诊断命令
bash
复制代码
# 查看所有容器日志
docker-compose logs --tail=100
# 查看特定服务日志
docker-compose logs -f app
# 进入容器调试
docker-compose exec app sh
# 检查网络连通性
docker-compose exec app ping database
# 检查数据库连接
docker-compose exec database psql -U hedgedoc -c "\l"
# 性能监控
docker stats --no-stream
📊 性能优化建议
1. 数据库优化
sql
复制代码
-- 定期执行(每月)
VACUUM FULL ANALYZE;
REINDEX TABLE notes, revisions, users;
-- 调整参数(根据实际负载)
ALTER SYSTEM SET shared_buffers = '4GB';
ALTER SYSTEM SET work_mem = '64MB';
ALTER SYSTEM SET effective_cache_size = '12GB';
2. 应用层优化
yaml
复制代码
# 在docker-compose.yml中调整
app:
deploy:
resources:
limits:
cpus: '4'
memory: 8G
reservations:
cpus: '2'
memory: 4G
environment:
NODE_OPTIONS: "--max-old-space-size=6144"
CMD_DB_POOL_MAX: 100
CMD_CACHE_TTL: 14400
3. Nginx优化
nginx
复制代码
# 增加缓冲区
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
# 启用Gzip
gzip on;
gzip_min_length 1k;
gzip_comp_level 6;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml text/javascript;
🔄 更新与升级
升级流程
bash
复制代码
# 1. 备份当前系统
cd /opt/hedgedoc
./scripts/backup.sh
# 2. 拉取新版本镜像
docker-compose pull
# 3. 停止服务
docker-compose down
# 4. 启动新版本
docker-compose up -d
# 5. 验证升级
docker-compose logs --tail=100 app
curl -k https://docs.internal.com/health
🎯 部署检查清单
部署完成后检查项
📞 技术支持
内部联系方式
运维团队 :运维组内线 1001
钉钉支持 :钉钉应用管理员
应用开发 :开发团队负责人
外部资源
注意 :本文档为内网部署专用方案,所有配置均针对内部网络环境优化。如需映射到外网,需额外配置防火墙、安全组和DDOS防护。