第 11 章:Docker 部署
第 11 章:Docker 部署
学习在 Docker 中部署 Buku Web 服务,实现数据持久化、容器编排和自动化备份。
11.1 Docker 镜像
使用官方镜像
# 拉取官方镜像
docker pull jarun/buku
# 查看镜像信息
docker images jarun/buku
# REPOSITORY TAG IMAGE ID CREATED SIZE
# jarun/buku latest abc123def456 2 months ago 150MB
# 运行一次性命令
docker run --rm jarun/buku --version
构建自定义镜像
# Dockerfile
FROM python:3.11-slim
# 设置工作目录
WORKDIR /app
# 安装依赖
RUN pip3 install --no-cache-dir \
buku \
cryptography \
flask \
flask-admin
# 创建数据目录
RUN mkdir -p /root/.local/share/buku
# 暴露 Web 服务端口
EXPOSE 8080
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8080/api/bookmarks || exit 1
# 默认命令:启动 Web 服务
CMD ["buku", "--sr", "8080"]
# 构建镜像
docker build -t buku-custom .
# 运行容器
docker run -d \
--name buku \
-p 8080:8080 \
-v ~/buku_data:/root/.local/share/buku \
buku-custom
11.2 基本容器操作
运行容器
# 后台运行 Web 服务
docker run -d \
--name buku \
-p 8080:8080 \
-v ~/buku_data:/root/.local/share/buku \
jarun/buku --sr 8080
# 查看运行状态
docker ps | grep buku
# 查看日志
docker logs buku
docker logs -f buku # 实时跟踪
# 进入容器
docker exec -it buku /bin/bash
CLI 操作
# 添加书签
docker run --rm \
-v ~/buku_data:/root/.local/share/buku \
jarun/buku -a https://example.com "示例" ,test
# 列出书签
docker run --rm \
-v ~/buku_data:/root/.local/share/buku \
jarun/buku -p
# 搜索书签
docker run --rm \
-v ~/buku_data:/root/.local/share/buku \
jarun/buku -s python
# 导入书签
docker run --rm \
-v ~/buku_data:/root/.local/share/buku \
-v ~/Downloads:/imports \
jarun/buku -i /imports/bookmarks.html
容器管理
# 停止容器
docker stop buku
# 启动容器
docker start buku
# 重启容器
docker restart buku
# 删除容器
docker rm buku
# 强制删除运行中的容器
docker rm -f buku
11.3 数据持久化
Volume 挂载
# 使用 bind mount(推荐)
docker run -d \
--name buku \
-p 8080:8080 \
-v /home/user/buku_data:/root/.local/share/buku \
jarun/buku --sr 8080
# 使用 Docker volume
docker volume create buku_data
docker run -d \
--name buku \
-p 8080:8080 \
-v buku_data:/root/.local/share/buku \
jarun/buku --sr 8080
# 查看 volume
docker volume ls | grep buku
docker volume inspect buku_data
权限管理
# 确保宿主机目录权限正确
mkdir -p ~/buku_data
chmod 755 ~/buku_data
# 使用 --user 参数指定用户
docker run -d \
--name buku \
--user $(id -u):$(id -g) \
-p 8080:8080 \
-v ~/buku_data:/root/.local/share/buku \
jarun/buku --sr 8080
备份数据
# 备份容器数据
docker cp buku:/root/.local/share/buku/bookmarks.db ./bookmarks_backup.db
# 备份整个数据目录
tar czf buku_backup_$(date +%Y%m%d).tar.gz ~/buku_data/
# 从备份恢复
docker cp ./bookmarks_backup.db buku:/root/.local/share/buku/bookmarks.db
docker restart buku
11.4 Docker Compose
基本 Compose 配置
# docker-compose.yml
version: '3.8'
services:
buku:
image: jarun/buku
container_name: buku
restart: unless-stopped
ports:
- "8080:8080"
volumes:
- ./data:/root/.local/share/buku
command: ["buku", "--sr", "8080"]
# 启动服务
docker-compose up -d
# 查看状态
docker-compose ps
# 查看日志
docker-compose logs -f
# 停止服务
docker-compose down
完整 Compose 配置
# docker-compose.yml
version: '3.8'
services:
buku:
build:
context: .
dockerfile: Dockerfile
container_name: buku
restart: unless-stopped
ports:
- "${BUKU_PORT:-8080}:8080"
volumes:
- buku_data:/root/.local/share/buku
environment:
- TZ=Asia/Shanghai
- BUKU_SERVER_TOKEN=${BUKU_TOKEN}
networks:
- buku_net
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/bookmarks"]
interval: 30s
timeout: 3s
start_period: 5s
retries: 3
# 可选:Nginx 反向代理
nginx:
image: nginx:alpine
container_name: buku_nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- buku
networks:
- buku_net
volumes:
buku_data:
driver: local
networks:
buku_net:
driver: bridge
环境变量配置
# .env 文件
BUKU_PORT=8080
BUKU_TOKEN=my_secret_token
TZ=Asia/Shanghai
# docker-compose.yml 引用环境变量
services:
buku:
image: jarun/buku
ports:
- "${BUKU_PORT}:8080"
environment:
- TZ=${TZ}
11.5 生产环境部署
安全配置
# docker-compose.prod.yml
version: '3.8'
services:
buku:
image: jarun/buku
container_name: buku
restart: always
# 不直接暴露端口,通过 Nginx 反向代理
# ports:
# - "8080:8080"
volumes:
- buku_data:/root/.local/share/buku
networks:
- internal
command: ["buku", "--sr", "8080", "--sall", "token:${BUKU_TOKEN}"]
deploy:
resources:
limits:
cpus: '0.50'
memory: 256M
nginx:
image: nginx:alpine
container_name: buku_nginx
restart: always
ports:
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
- ./nginx/logs:/var/log/nginx
depends_on:
- buku
networks:
- internal
- external
deploy:
resources:
limits:
cpus: '0.25'
memory: 128M
volumes:
buku_data:
networks:
internal:
driver: bridge
external:
driver: bridge
Nginx 反向代理配置
# nginx/nginx.conf
events {
worker_connections 1024;
}
http {
upstream buku_backend {
server buku:8080;
}
# HTTPS 服务器
server {
listen 443 ssl http2;
server_name buku.example.com;
ssl_certificate /etc/nginx/certs/fullchain.pem;
ssl_certificate_key /etc/nginx/certs/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# 安全头
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 Strict-Transport-Security "max-age=31536000" always;
location / {
proxy_pass http://buku_backend;
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;
}
# API 速率限制
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://buku_backend;
}
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name buku.example.com;
return 301 https://$server_name$request_uri;
}
}
11.6 自动化备份
备份脚本
#!/bin/bash
# backup_buku_docker.sh - Docker Buku 自动备份
BACKUP_DIR="./backups"
DATE=$(date +%Y%m%d_%H%M%S)
CONTAINER_NAME="buku"
mkdir -p "$BACKUP_DIR"
# 方法一:从容器复制
docker cp "$CONTAINER_NAME":/root/.local/share/buku/bookmarks.db \
"$BACKUP_DIR/bookmarks_$DATE.db"
# 方法二:使用 docker exec
docker exec "$CONTAINER_NAME" \
sqlite3 /root/.local/share/buku/bookmarks.db ".backup /tmp/backup.db"
docker cp "$CONTAINER_NAME":/tmp/backup.db "$BACKUP_DIR/bookmarks_$DATE.db"
# 压缩备份
gzip "$BACKUP_DIR/bookmarks_$DATE.db"
# 清理 30 天前的备份
find "$BACKUP_DIR" -name "*.db.gz" -mtime +30 -delete
echo "备份完成: $BACKUP_DIR/bookmarks_$DATE.db.gz"
Cron 定时备份
# docker-compose.yml 中添加备份服务
services:
# ... 其他服务 ...
backup:
image: alpine
container_name: buku_backup
volumes:
- ./backup.sh:/backup.sh:ro
- ./backups:/backups
- /var/run/docker.sock:/var/run/docker.sock
entrypoint: /bin/sh
command: -c "echo '0 2 * * * /backup.sh' | crontab - && crond -f -l 8"
depends_on:
- buku
11.7 监控与日志
日志管理
# docker-compose.yml
services:
buku:
image: jarun/buku
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
健康检查
services:
buku:
image: jarun/buku
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/api/bookmarks"]
interval: 30s
timeout: 3s
start_period: 5s
retries: 3
监控脚本
#!/bin/bash
# monitor_buku.sh - 监控 Buku 容器状态
check_container() {
local status=$(docker inspect --format='{{.State.Status}}' buku 2>/dev/null)
local health=$(docker inspect --format='{{.State.Health.Status}}' buku 2>/dev/null)
echo "容器状态: $status"
echo "健康状态: $health"
if [ "$status" != "running" ]; then
echo "⚠️ 容器未运行,正在重启..."
docker start buku
fi
if [ "$health" = "unhealthy" ]; then
echo "⚠️ 容器不健康,查看日志..."
docker logs --tail 20 buku
fi
}
check_container
11.8 常见问题
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 数据丢失 | 未挂载 volume | 确保正确挂载数据目录 |
| 权限错误 | 容器用户不匹配 | 使用 --user 参数 |
| 端口冲突 | 端口被占用 | 更换端口或停止冲突服务 |
| 启动失败 | 数据库损坏 | 从备份恢复数据库 |
| 性能问题 | 资源限制过严 | 调整 CPU/内存限制 |
| 网络问题 | 容器网络配置错误 | 检查 Docker 网络设置 |
11.9 本章小结
| 要点 | 说明 |
|---|---|
| 官方镜像 | jarun/buku |
| 数据目录 | /root/.local/share/buku |
| 持久化 | 使用 Volume 或 bind mount |
| Compose | 推荐使用 docker-compose 管理 |
| 备份 | 定期备份 bookmarks.db |
| 安全 | 使用 Token 认证 + HTTPS |
扩展阅读
下一章:第 12 章:最佳实践 — 学习 Buku 的最佳实践,包括工作流设计、标签策略和同步方案。