CUPS 打印服务完全指南 / 第 10 章:Docker 容器化部署
第 10 章:Docker 容器化部署
容器化是现代应用部署的标准方式。本章将介绍如何使用 Docker 部署 CUPS 打印服务,包括镜像构建、网络配置、持久化存储和生产环境最佳实践。
10.1 Docker 与 CUPS 概述
10.1.1 为什么使用 Docker 部署 CUPS
| 优势 | 说明 |
|---|---|
| 环境隔离 | CUPS 与主机系统完全隔离 |
| 快速部署 | 一键启动打印服务 |
| 版本管理 | 轻松切换 CUPS 版本 |
| 可移植性 | 跨平台部署 |
| 资源控制 | 限制 CPU/内存使用 |
| 易维护 | 简化升级和回滚 |
10.1.2 容器化架构
┌─────────────────────────────────────────────────┐
│ Docker Host │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ CUPS 容器 │ │
│ │ ┌───────────────────────────────────┐ │ │
│ │ │ CUPS Server (cupsd) │ │ │
│ │ │ - Web 界面 (:631) │ │ │
│ │ │ - IPP 服务 │ │ │
│ │ │ - 过滤器系统 │ │ │
│ │ └───────────────────────────────────┘ │ │
│ │ ┌───────────────────────────────────┐ │ │
│ │ │ 卷挂载 │ │ │
│ │ │ - /etc/cups (配置) │ │ │
│ │ │ - /var/log/cups (日志) │ │ │
│ │ │ - /var/spool/cups (队列) │ │ │
│ │ └───────────────────────────────────┘ │ │
│ └──────────────────────────────────────────┘ │
│ │ │ │
│ │ :631 │ :631 │
│ ▼ ▼ │
│ ┌─────────┐ ┌─────────────┐ │
│ │客户端 │ │网络打印机 │ │
│ └─────────┘ └─────────────┘ │
└─────────────────────────────────────────────────┘
10.2 基础 CUPS Docker 镜像
10.2.1 Dockerfile 基础版本
# Dockerfile.cups
FROM ubuntu:22.04
# 设置环境变量
ENV DEBIAN_FRONTEND=noninteractive
ENV CUPS_ADMIN=admin
ENV CUPS_PASSWORD=admin
# 安装 CUPS 和依赖
RUN apt-get update && \
apt-get install -y \
cups \
cups-client \
cups-bsd \
cups-filters \
printer-driver-gutenprint \
printer-driver-hpcups \
hplip \
ghostscript \
wget \
&& rm -rf /var/lib/apt/lists/*
# 备份默认配置
RUN cp /etc/cups/cupsd.conf /etc/cups/cupsd.conf.default
# 配置 CUPS
RUN cupsctl --share-printers --remote-any
# 暴露端口
EXPOSE 631
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:631/ || exit 1
# 启动脚本
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
10.2.2 启动脚本
#!/bin/bash
# entrypoint.sh
set -e
# 创建 CUPS 管理员用户
if [ -n "$CUPS_ADMIN" ] && [ -n "$CUPS_PASSWORD" ]; then
echo "Creating CUPS admin user: $CUPS_ADMIN"
htdigest /etc/cups/passwd "CUPS" "$CUPS_ADMIN" << EOF
$CUPS_PASSWORD
$CUPS_PASSWORD
EOF
fi
# 配置 CUPS
cat > /etc/cups/cupsd.conf << EOF
LogLevel info
MaxLogSize 1m
Port 631
Listen /run/cups/cups.sock
# Web 管理界面
WebInterface Yes
# 访问控制
<Location />
Order allow,deny
Allow all
</Location>
<Location /admin>
AuthType Default
Require user @SYSTEM
Order allow,deny
Allow all
</Location>
<Location /admin/conf>
AuthType Default
Require user @SYSTEM
Order allow,deny
Allow all
</Location>
DefaultAuthType Basic
DefaultEncryption IfRequested
ServerAlias *
BrowseLocalProtocols dnssd
DefaultShared Yes
EOF
# 添加管理员到 lpadmin 组
usermod -aG lpadmin root
# 添加环境变量中定义的打印机
if [ -n "$PRINTER_URI" ] && [ -n "$PRINTER_NAME" ]; then
echo "Adding printer: $PRINTER_NAME"
lpadmin -p "$PRINTER_NAME" -E \
-v "$PRINTER_URI" \
-m "${PRINTER_MODEL:-raw}" \
-D "${PRINTER_DESC:-Docker Printer}" \
-L "${PRINTER_LOC:-Docker Container}"
fi
# 启动 CUPS
echo "Starting CUPS..."
exec /usr/sbin/cupsd -f
10.2.3 构建和运行
# 构建镜像
docker build -t cups-server -f Dockerfile.cups .
# 运行容器(基本配置)
docker run -d \
--name cups \
-p 631:631 \
cups-server
# 运行容器(完整配置)
docker run -d \
--name cups \
-p 631:631 \
-v cups-config:/etc/cups \
-v cups-logs:/var/log/cups \
-v cups-spool:/var/spool/cups \
-e CUPS_ADMIN=admin \
-e CUPS_PASSWORD=securepass123 \
cups-server
# 访问 Web 界面
# http://localhost:631
# 用户名: admin
# 密码: securepass123
10.3 高级 Dockerfile
10.3.1 生产级 Dockerfile
# Dockerfile.cups.production
FROM ubuntu:22.04 AS builder
# 安装构建依赖
RUN apt-get update && \
apt-get install -y \
build-essential \
libcups2-dev \
libavahi-client-dev \
libdbus-1-dev \
&& rm -rf /var/lib/apt/lists/*
FROM ubuntu:22.04
# 设置元数据
LABEL maintainer="[email protected]"
LABEL version="1.0"
LABEL description="CUPS Print Server"
# 环境变量
ENV DEBIAN_FRONTEND=noninteractive
ENV CUPS_ADMIN=admin
ENV CUPS_PASSWORD=""
ENV TZ=Asia/Shanghai
# 安装运行时依赖
RUN apt-get update && \
apt-get install -y --no-install-recommends \
cups \
cups-client \
cups-bsd \
cups-filters \
printer-driver-gutenprint \
printer-driver-hpcups \
hplip \
ghostscript \
avahi-daemon \
avahi-utils \
dbus \
curl \
wget \
ca-certificates \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /var/run/dbus
# 配置时区
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \
echo $TZ > /etc/timezone
# 创建用户
RUN groupadd -r cups && \
useradd -r -g cups -s /bin/false cups-admin
# 暴露端口
EXPOSE 631
EXPOSE 5353/udp
# 卷
VOLUME ["/etc/cups", "/var/log/cups", "/var/spool/cups"]
# 健康检查
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -sf http://localhost:631/ > /dev/null || exit 1
# 复制配置和脚本
COPY cupsd.conf /etc/cups/cupsd.conf
COPY entrypoint.sh /entrypoint.sh
COPY healthcheck.sh /healthcheck.sh
RUN chmod +x /entrypoint.sh /healthcheck.sh
# 运行入口
ENTRYPOINT ["/entrypoint.sh"]
CMD ["/usr/sbin/cupsd", "-f"]
10.3.2 多阶段构建
# 多阶段构建减少镜像大小
FROM ubuntu:22.04 AS builder
RUN apt-get update && \
apt-get install -y \
cups \
cups-client \
cups-filters \
&& rm -rf /var/lib/apt/lists/*
# 清理不需要的文件
RUN apt-get clean && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
FROM scratch
COPY --from=builder / /
COPY entrypoint.sh /entrypoint.sh
EXPOSE 631
ENTRYPOINT ["/entrypoint.sh"]
10.4 Docker Compose 部署
10.4.1 docker-compose.yml
# docker-compose.yml
version: '3.8'
services:
cups:
build:
context: .
dockerfile: Dockerfile.cups
container_name: cups-server
hostname: cups-server
restart: unless-stopped
ports:
- "631:631"
- "5353:5353/udp"
volumes:
- cups-config:/etc/cups
- cups-logs:/var/log/cups
- cups-spool:/var/spool/cups
- cups-cache:/var/cache/cups
environment:
- CUPS_ADMIN=admin
- CUPS_PASSWORD=${CUPS_PASSWORD:-admin123}
- TZ=Asia/Shanghai
networks:
- cups-network
cap_add:
- NET_ADMIN
devices:
- /dev/bus/usb:/dev/bus/usb # USB 打印机支持
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:631/"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
# 可选: Avahi 服务发现
avahi:
image: alpine:latest
container_name: cups-avahi
restart: unless-stopped
network_mode: "service:cups"
volumes:
- cups-config:/etc/cups:ro
command: >
sh -c "
apk add --no-cache avahi dbus &&
dbus-daemon --system &&
avahi-daemon --no-drop-root
"
depends_on:
cups:
condition: service_healthy
# 可选: 日志收集
logspout:
image: gliderlabs/logspout:latest
container_name: cups-logspout
restart: unless-stopped
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: syslog://logs.example.com:514
depends_on:
- cups
volumes:
cups-config:
driver: local
cups-logs:
driver: local
cups-spool:
driver: local
cups-cache:
driver: local
networks:
cups-network:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
10.4.2 环境变量配置
# .env
CUPS_PASSWORD=your_secure_password
CUPS_ADMIN=admin
CUPS_LOGLEVEL=info
CUPS_SHARE=yes
CUPS_ENCRYPTION=ifRequested
10.4.3 Docker Compose 命令
# 启动服务
docker-compose up -d
# 查看日志
docker-compose logs -f cups
# 停止服务
docker-compose down
# 重启服务
docker-compose restart cups
# 进入容器
docker-compose exec cups bash
# 查看状态
docker-compose ps
# 更新镜像
docker-compose pull
docker-compose up -d
# 清理数据
docker-compose down -v
10.5 网络打印配置
10.5.1 网络模式
# docker-compose.yml
services:
cups:
# 网络模式选项
# 1. Bridge 模式(默认)
network_mode: bridge
ports:
- "631:631"
# 2. Host 模式(直接使用主机网络)
# network_mode: host
# 3. 自定义网络
networks:
- cups-network
10.5.2 容器间打印
# 多容器环境
services:
# CUPS 服务器
cups:
image: cups-server
ports:
- "631:631"
networks:
- print-network
# 应用服务器
app:
image: my-app
environment:
- CUPS_SERVER=cups
- PRINTER_URI=ipp://cups:631/printers/MyPrinter
networks:
- print-network
depends_on:
- cups
networks:
print-network:
driver: bridge
10.5.3 USB 打印机支持
# docker-compose.yml
services:
cups:
image: cups-server
devices:
# 映射 USB 设备
- /dev/bus/usb:/dev/bus/usb
# 或映射特定 USB 设备
# - /dev/usb/lp0:/dev/usb/lp0
privileged: true # 或使用 device_cgroup_rules
# device_cgroup_rules:
# - 'c 188:* rmw'
10.6 持久化存储
10.6.1 卷配置
# docker-compose.yml
services:
cups:
volumes:
# 命名卷(推荐)
- cups-config:/etc/cups
- cups-logs:/var/log/cups
- cups-spool:/var/spool/cups
# 绑定挂载(开发环境)
# - ./config/cupsd.conf:/etc/cups/cupsd.conf
# - ./logs:/var/log/cups
# NFS 挂载(生产环境)
# - type: volume
# source: cups-nfs
# target: /var/spool/cups
# volume:
# nocopy: true
volumes:
cups-config:
driver: local
driver_opts:
type: none
o: bind
device: /data/cups/config
cups-logs:
driver: local
cups-spool:
driver: local
# NFS 卷
# cups-nfs:
# driver: local
# driver_opts:
# type: nfs
# o: addr=nfs-server.example.com,rw
# device: ":/exports/cups"
10.6.2 备份与恢复
#!/bin/bash
# backup-cups.sh - CUPS 备份脚本
BACKUP_DIR="/backup/cups"
DATE=$(date +%Y%m%d_%H%M%S)
CONTAINER_NAME="cups-server"
# 创建备份目录
mkdir -p "$BACKUP_DIR"
# 备份配置
docker cp "$CONTAINER_NAME:/etc/cups" "$BACKUP_DIR/config_$DATE"
# 备份队列
docker cp "$CONTAINER_NAME:/var/spool/cups" "$BACKUP_DIR/spool_$DATE"
# 备份日志
docker cp "$CONTAINER_NAME:/var/log/cups" "$BACKUP_DIR/logs_$DATE"
# 压缩备份
tar -czf "$BACKUP_DIR/cups_backup_$DATE.tar.gz" \
"$BACKUP_DIR/config_$DATE" \
"$BACKUP_DIR/spool_$DATE" \
"$BACKUP_DIR/logs_$DATE"
# 清理临时文件
rm -rf "$BACKUP_DIR/config_$DATE" \
"$BACKUP_DIR/spool_$DATE" \
"$BACKUP_DIR/logs_$DATE"
echo "Backup completed: $BACKUP_DIR/cups_backup_$DATE.tar.gz"
#!/bin/bash
# restore-cups.sh - CUPS 恢复脚本
BACKUP_FILE="$1"
CONTAINER_NAME="cups-server"
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup_file.tar.gz>"
exit 1
fi
# 停止容器
docker stop "$CONTAINER_NAME"
# 解压备份
TEMP_DIR=$(mktemp -d)
tar -xzf "$BACKUP_FILE" -C "$TEMP_DIR"
# 恢复配置
docker cp "$TEMP_DIR"/config_* "$CONTAINER_NAME:/etc/cups"
# 恢复队列
docker cp "$TEMP_DIR"/spool_* "$CONTAINER_NAME:/var/spool/cups"
# 清理
rm -rf "$TEMP_DIR"
# 启动容器
docker start "$CONTAINER_NAME"
echo "Restore completed"
10.7 Docker Swarm 集群部署
10.7.1 Swarm 配置
# docker-stack.yml
version: '3.8'
services:
cups:
image: cups-server:latest
deploy:
replicas: 2
placement:
constraints:
- node.role == worker
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
ports:
- target: 631
published: 631
protocol: tcp
mode: ingress
volumes:
- cups-config:/etc/cups
- cups-logs:/var/log/cups
networks:
- cups-net
environment:
- CUPS_ADMIN=admin
- CUPS_PASSWORD_FILE=/run/secrets/cups_password
secrets:
- cups_password
# 负载均衡器
nginx:
image: nginx:alpine
deploy:
replicas: 1
placement:
constraints:
- node.role == manager
ports:
- "80:80"
- "443:443"
configs:
- source: nginx-config
target: /etc/nginx/nginx.conf
networks:
- cups-net
depends_on:
- cups
secrets:
cups_password:
external: true
configs:
nginx-config:
external: true
networks:
cups-net:
driver: overlay
volumes:
cups-config:
driver: local
cups-logs:
driver: local
10.7.2 部署 Swarm 集群
# 初始化 Swarm
docker swarm init
# 创建密码
echo "your_secure_password" | docker secret create cups_password -
# 创建 Nginx 配置
docker config create nginx-config nginx.conf
# 部署服务栈
docker stack deploy -d docker-stack.yml cups-cluster
# 查看服务状态
docker stack services cups-cluster
# 扩展服务
docker service scale cups-cluster_cups=3
# 查看日志
docker service logs cups-cluster_cups
10.8 Kubernetes 部署
10.8.1 K8s 配置文件
# cups-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cups-server
labels:
app: cups
spec:
replicas: 2
selector:
matchLabels:
app: cups
template:
metadata:
labels:
app: cups
spec:
containers:
- name: cups
image: cups-server:latest
ports:
- containerPort: 631
env:
- name: CUPS_ADMIN
value: "admin"
- name: CUPS_PASSWORD
valueFrom:
secretKeyRef:
name: cups-secret
key: password
volumeMounts:
- name: cups-config
mountPath: /etc/cups
- name: cups-logs
mountPath: /var/log/cups
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /
port: 631
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 631
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: cups-config
persistentVolumeClaim:
claimName: cups-config-pvc
- name: cups-logs
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: cups-service
spec:
selector:
app: cups
ports:
- protocol: TCP
port: 631
targetPort: 631
type: LoadBalancer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: cups-config-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
---
apiVersion: v1
kind: Secret
metadata:
name: cups-secret
type: Opaque
data:
password: YWRtaW4xMjM= # base64 编码的密码
# 部署到 Kubernetes
kubectl apply -f cups-deployment.yaml
# 查看状态
kubectl get pods -l app=cups
kubectl get services cups-service
# 查看日志
kubectl logs -l app=cups -f
# 扩展
kubectl scale deployment cups-server --replicas=3
10.9 容器管理脚本
10.9.1 管理脚本
#!/bin/bash
# cups-docker-manager.sh
# CUPS Docker 管理脚本
CONTAINER_NAME="${CUPS_CONTAINER:-cups-server}"
IMAGE_NAME="${CUPS_IMAGE:-cups-server}"
case "$1" in
start)
echo "Starting CUPS container..."
docker start "$CONTAINER_NAME"
;;
stop)
echo "Stopping CUPS container..."
docker stop "$CONTAINER_NAME"
;;
restart)
echo "Restarting CUPS container..."
docker restart "$CONTAINER_NAME"
;;
status)
echo "CUPS container status:"
docker ps -f name="$CONTAINER_NAME"
echo ""
echo "CUPS service status:"
docker exec "$CONTAINER_NAME" lpstat -r
;;
logs)
docker logs -f "$CONTAINER_NAME"
;;
add-printer)
if [ -z "$2" ] || [ -z "$3" ]; then
echo "Usage: $0 add-printer <name> <uri> [model]"
exit 1
fi
docker exec "$CONTAINER_NAME" lpadmin -p "$2" -E -v "$3" -m "${4:-raw}"
echo "Printer $2 added"
;;
remove-printer)
if [ -z "$2" ]; then
echo "Usage: $0 remove-printer <name>"
exit 1
fi
docker exec "$CONTAINER_NAME" lpadmin -x "$2"
echo "Printer $2 removed"
;;
list-printers)
docker exec "$CONTAINER_NAME" lpstat -p -d
;;
backup)
BACKUP_DIR="${2:-./backup}"
mkdir -p "$BACKUP_DIR"
docker cp "$CONTAINER_NAME:/etc/cups" "$BACKUP_DIR/cups-config"
echo "Backup saved to $BACKUP_DIR/cups-config"
;;
restore)
if [ -z "$2" ]; then
echo "Usage: $0 restore <backup_dir>"
exit 1
fi
docker cp "$2/cups-config" "$CONTAINER_NAME:/etc/cups"
docker restart "$CONTAINER_NAME"
echo "Restore completed"
;;
shell)
docker exec -it "$CONTAINER_NAME" bash
;;
*)
echo "Usage: $0 {start|stop|restart|status|logs|add-printer|remove-printer|list-printers|backup|restore|shell}"
exit 1
;;
esac
10.10 业务场景
10.10.1 场景一:开发环境快速部署
# 一键启动开发环境
docker run -d \
--name cups-dev \
-p 631:631 \
-e CUPS_ADMIN=admin \
-e CUPS_PASSWORD=admin \
-e PRINTER_NAME=PDF-Printer \
-e PRINTER_URI=cups-pdf:/ \
cups-server
# 或使用 Docker Compose
# docker-compose -f docker-compose.dev.yml up -d
10.10.2 场景二:生产环境高可用
# docker-compose.prod.yml
version: '3.8'
services:
cups-primary:
image: cups-server
deploy:
replicas: 1
placement:
constraints:
- node.labels.role == primary
environment:
- CUPS_PRIMARY=true
cups-replica:
image: cups-server
deploy:
replicas: 2
placement:
constraints:
- node.labels.role == replica
environment:
- CUPS_PRIMARY=false
- CUPS_PRIMARY_HOST=cups-primary
haproxy:
image: haproxy:alpine
ports:
- "631:631"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
deploy:
placement:
constraints:
- node.labels.role == edge
10.10.3 场景三:多租户打印服务
# 为每个租户创建独立的 CUPS 实例
services:
cups-tenant-a:
image: cups-server
container_name: cups-tenant-a
environment:
- CUPS_ADMIN=admin-a
- CUPS_PASSWORD=pass-a
networks:
- tenant-a-network
cups-tenant-b:
image: cups-server
container_name: cups-tenant-b
environment:
- CUPS_ADMIN=admin-b
- CUPS_PASSWORD=pass-b
networks:
- tenant-b-network
10.11 故障排查
10.11.1 常见问题
# 1. 容器无法启动
docker logs cups-server
# 2. 无法访问 Web 界面
docker exec cups-server curl -I http://localhost:631/
# 3. USB 打印机无法识别
docker exec cups-server lsusb
docker exec cups-server lpinfo -v
# 4. 网络打印机无法连接
docker exec cups-server ping 192.168.1.100
docker exec cups-server telnet 192.168.1.100 9100
# 5. 权限问题
docker exec cups-server ls -la /etc/cups/
docker exec cups-server ls -la /var/spool/cups/
10.11.2 调试技巧
# 进入容器调试
docker exec -it cups-server bash
# 查看 CUPS 配置
docker exec cups-server cat /etc/cups/cupsd.conf
# 查看 CUPS 状态
docker exec cups-server lpstat -t
# 查看 CUPS 日志
docker exec cups-server tail -f /var/log/cups/error_log
# 测试打印
docker exec cups-server echo "Test" | lp
# 查看容器资源使用
docker stats cups-server
10.12 扩展阅读
| 资源 | 链接 |
|---|---|
| Docker 官方文档 | https://docs.docker.com/ |
| Docker Compose 文档 | https://docs.docker.com/compose/ |
| Kubernetes 文档 | https://kubernetes.io/docs/ |
| Docker Hub CUPS | https://hub.docker.com/r/olbat/cupsd |
10.13 本章小结
| 主题 | 关键要点 |
|---|---|
| Dockerfile | 定义 CUPS 容器镜像的构建文件 |
| Docker Compose | 多容器编排,适合开发和简单生产环境 |
| 网络配置 | Bridge/Host/Overlay 多种网络模式 |
| 持久化 | 使用卷存储配置、日志和打印队列 |
| 集群部署 | Docker Swarm 或 Kubernetes 高可用 |
| 安全 | 使用 Secrets 管理敏感信息 |
下一章预告:我们将学习 CUPS 故障排查,包括常见问题诊断、日志分析和调试模式。
10.14 练习题
构建题:编写 Dockerfile 构建包含 HPLIP 驱动的 CUPS 镜像。
部署题:使用 Docker Compose 部署 CUPS 服务,并配置数据持久化。
网络题:配置 Docker 网络,使应用容器能够访问 CUPS 打印服务。
备份题:编写脚本自动备份 CUPS 容器的配置和打印队列。
集群题:设计一个使用 Docker Swarm 的 CUPS 高可用架构。