PaperMC 插件开发完全指南 / 第 15 章:Docker 开发环境
第 15 章:Docker 开发环境
使用 Docker 搭建一致的开发、测试和 CI/CD 环境。
15.1 Docker 在 Minecraft 开发中的价值
| 维度 | 传统方式 | Docker 方式 |
|---|
| 环境一致性 | 手动配置,容易不一致 | 镜像保证完全一致 |
| 多版本测试 | 安装多个 JDK/服务器 | 同时运行多个容器 |
| 清理环境 | 手动删除文件 | docker compose down -v |
| CI/CD | 需要配置环境 | 直接使用镜像 |
| 团队协作 | “在我电脑上能跑” | 统一的开发环境 |
15.2 基础 Dockerfile
Paper 服务器 Dockerfile
# Dockerfile
FROM eclipse-temurin:21-jre-alpine
# 安装必要工具
RUN apk add --no-cache curl bash
# 创建用户(非 root 运行更安全)
RUN addgroup -S minecraft && adduser -S minecraft -G minecraft
# 设置工作目录
WORKDIR /server
# 下载 Paper(构建时指定版本)
ARG PAPER_VERSION=1.21.4
ARG BUILD_NUMBER=123
RUN curl -o paper.jar \
"https://api.papermc.io/v2/projects/paper/versions/${PAPER_VERSION}/builds/${BUILD_NUMBER}/downloads/paper-${PAPER_VERSION}-${BUILD_NUMBER}.jar"
# 同意 EULA
RUN echo "eula=true" > eula.txt
# 默认配置
COPY server.properties /server/server.properties
# 设置权限
RUN chown -R minecraft:minecraft /server
# 切换到非 root 用户
USER minecraft
# 暴露端口
EXPOSE 25565
# JVM 参数
ENV JAVA_OPTS="-Xms512M -Xmax2G -XX:+UseG1GC"
# 启动命令
CMD ["bash", "-c", "java $JAVA_OPTS -jar paper.jar --nogui"]
15.3 Docker Compose 配置
开发服务器配置
# docker-compose.yml
version: '3.8'
services:
minecraft:
build: .
container_name: mc-dev-server
ports:
- "25565:25565"
volumes:
# 持久化服务器数据
- ./server-data:/server
# 挂载插件目录(热重载)
- ./target:/server/plugins:ro
# 挂载配置
- ./config/server.properties:/server/server.properties
environment:
- JAVA_OPTS=-Xms1G -Xmax2G -XX:+UseG1GC
restart: unless-stopped
stdin_open: true
tty: true
# 多服务器网络(可选)
proxy:
image: itzg/bungeecord:latest
container_name: mc-proxy
ports:
- "25577:25577"
volumes:
- ./proxy-data:/config
depends_on:
- minecraft
restart: unless-stopped
启动脚本
#!/bin/bash
# start-dev.sh
# 构建插件
mvn clean package -f pom.xml
# 复制插件到服务器
cp target/my-plugin-1.0.0.jar ./target/
# 启动服务器
docker compose up -d
# 查看日志
docker compose logs -f minecraft
15.4 开发工作流
热重载工作流
# docker-compose.dev.yml
version: '3.8'
services:
minecraft:
build:
context: .
dockerfile: Dockerfile
volumes:
# 挂载构建输出目录(文件变化自动同步)
- ./target:/server/plugins
# 挂载配置文件
- ./src/main/resources:/server/config
environment:
- JAVA_TOOL_OPTIONS=-XX:+UseG1GC
stdin_open: true
tty: true
自动重载脚本
#!/bin/bash
# watch-and-reload.sh
# 监视文件变化并重新构建
while true; do
inotifywait -e modify -r src/
echo "文件变化,重新构建..."
mvn clean package -q
echo "插件已更新,请在服务器中执行 /reload"
done
15.5 多版本测试
测试多个 Minecraft 版本
# docker-compose.test.yml
version: '3.8'
services:
mc-1.21.4:
image: itzg/minecraft-server:java21
container_name: mc-1.21.4
environment:
EULA: "TRUE"
TYPE: "PAPER"
VERSION: "1.21.4"
MEMORY: "1G"
ports:
- "25566:25565"
volumes:
- ./plugins-1.21:/data/plugins
mc-1.20.6:
image: itzg/minecraft-server:java21
container_name: mc-1.20.6
environment:
EULA: "TRUE"
TYPE: "PAPER"
VERSION: "1.20.6"
MEMORY: "1G"
ports:
- "25567:25565"
volumes:
- ./plugins-1.20:/data/plugins
15.6 CI/CD 集成
GitHub Actions 工作流
# .github/workflows/build-and-test.yml
name: Build and Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build with Maven
run: mvn clean package
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: my-plugin
path: target/*.jar
integration-test:
needs: build
runs-on: ubuntu-latest
services:
minecraft:
image: itzg/minecraft-server:java21
ports:
- 25565:25565
env:
EULA: "TRUE"
TYPE: "PAPER"
VERSION: "1.21.4"
MEMORY: "1G"
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: my-plugin
- name: Copy plugin to server
run: |
sleep 30 # 等待服务器启动
docker cp target/my-plugin-1.0.0.jar $(docker ps -q --filter "ancestor=itzg/minecraft-server"):/data/plugins/
- name: Run integration tests
run: |
# 运行集成测试脚本
python3 tests/integration/test_server.py
简化版 CI(Maven 构建)
# .github/workflows/build.yml
name: Build
on:
push:
branches: [main]
pull_request:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- run: mvn clean package
- uses: actions/upload-artifact@v4
with:
name: plugin-jar
path: target/*.jar
15.7 生产环境部署
生产 Dockerfile
FROM eclipse-temurin:21-jre-alpine
RUN apk add --no-cache curl
ARG PAPER_VERSION=1.21.4
ARG BUILD_NUMBER=123
WORKDIR /server
# 下载 Paper
RUN curl -o paper.jar \
"https://api.papermc.io/v2/projects/paper/versions/${PAPER_VERSION}/builds/${BUILD_NUMBER}/downloads/paper-${PAPER_VERSION}-${BUILD_NUMBER}.jar"
# 接受 EULA
RUN echo "eula=true" > eula.txt
# 复制服务器配置
COPY server.properties .
COPY spigot.yml .
COPY paper-global.yml ./config/
# 创建数据目录
RUN mkdir -p /data
# 使用卷挂载数据
VOLUME ["/data"]
# 暴露端口
EXPOSE 25565
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s \
CMD mc-health || exit 1
CMD ["java", "-Xms2G", "-Xmx4G", "-XX:+UseG1GC", "-jar", "paper.jar", "--nogui", "--plugins=/plugins"]
生产 docker-compose.yml
version: '3.8'
services:
minecraft:
build: .
container_name: mc-production
ports:
- "25565:25565"
volumes:
- mc-data:/data
- ./plugins:/plugins:ro
environment:
- TZ=Asia/Shanghai
deploy:
resources:
limits:
memory: 6G
cpus: '2.0'
restart: always
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
volumes:
mc-data:
15.8 备份策略
自动备份脚本
#!/bin/bash
# backup.sh
BACKUP_DIR="/backups"
SERVER_DIR="/var/lib/minecraft"
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="mc_backup_$DATE"
# 通知服务器正在备份
docker exec mc-production rcon-cli "say §e正在备份服务器..."
# 创建备份
mkdir -p $BACKUP_DIR
tar -czf "$BACKUP_DIR/$BACKUP_NAME.tar.gz" -C $SERVER_DIR .
# 保留最近 7 天的备份
find $BACKUP_DIR -name "mc_backup_*.tar.gz" -mtime +7 -delete
# 通知完成
docker exec mc-production rcon-cli "say §a备份完成!"
echo "备份完成: $BACKUP_NAME"
定时备份(crontab)
# crontab -e
# 每天凌晨 3 点备份
0 3 * * * /opt/minecraft/backup.sh >> /var/log/mc-backup.log 2>&1
15.9 RCON 管理
启用 RCON
# server.properties
enable-rcon=true
rcon.port=25575
rcon.password=your_secure_password
使用 RCON 发送命令
# 安装 mcrcon
# https://github.com/Tiiffi/mcrcon
# 连接
mcrcon -H localhost -P 25575 -p your_secure_password
# 执行命令
mcrcon -H localhost -P 25575 -p your_secure_password "say Hello World"
Docker 中使用 RCON
services:
minecraft:
# ...其他配置
ports:
- "25565:25565"
- "25575:25575" # RCON 端口
15.10 监控
Prometheus + Grafana 监控
# docker-compose.monitoring.yml
services:
minecraft:
# ...主配置
# Minecraft 导出器
mc-exporter:
image: samuelkadolph/minecraft-prometheus-exporter
environment:
- MINECRAFT_HOST=minecraft
- MINECRAFT_PORT=25565
ports:
- "9225:9225"
# Prometheus
prometheus:
image: prom/prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
# Grafana
grafana:
image: grafana/grafana
ports:
- "3000:3000"
15.11 常见问题排查
| 问题 | 原因 | 解决方案 |
|---|
| 容器无法启动 | 内存不足 | 增加 Docker 内存限制 |
| 插件未加载 | 路径挂载错误 | 检查卷映射 |
| 性能差 | 默认 JVM 参数不佳 | 自定义 JAVA_OPTS |
| 无法连接 | 端口未映射 | 检查 ports 配置 |
| 数据丢失 | 未使用持久化卷 | 使用 volumes 挂载 |
15.12 扩展阅读
15.13 本章小结
| 要点 | 内容 |
|---|
| Docker 优势 | 环境一致、易于部署、快速清理 |
| 基础镜像 | eclipse-temurin:21-jre-alpine |
| 持久化 | 使用 volumes 保存服务器数据 |
| CI/CD | GitHub Actions 自动构建和测试 |
| 生产部署 | 资源限制、健康检查、自动重启 |
| 备份 | 定时备份脚本 + 持久化存储 |
下一章: 第 16 章:测试 — 学习单元测试、MockBukkit 和集成测试。