初次接触使用 Docker 和尝试打包 Minecraft 的踩坑记录: Play Server 的打包
2021-02-07
使用 Overlayfs 来实现程序和数据分开
Docker 的 Volume 挂载在 Container 中的效果类似于 Linux 下的 mount ,故为了做到在基础上增加一个“写层”,需要在 Container 中使用 Overlayfs 。
此方法有限制,要求 upper 层所在的文件系统不能是另外一个 Overlayfs 的 upper,且该方法需要特权。
- Overlay filesystem
- How do I use OverlayFS?
- Example OverlayFS Usage
- Union fs inside docker container (aufs/overlay)
- Can I mount docker host directory as copy on write/overlay?
- Cannot mount overlay with upperdir on overlay | Ubuntu bugs
- Re: Using overlay on top of overlay | LKML
Overlayfs 中的 whiteout
Overlayfs 中的文件类型为 c 的文件是字符文件,即 whiteout,用于标识文件删除。
该文件可以被 tar 打包。
Overlayfs 挂载后需要重新进入
使用 overlayfs 挂载完目录后,若合并后的目录与源目录相同,需要重新 cd 到目录内,否则修改的会是原有的内容。
使用 gosu 来实现低权限用户运行程序
使用低权限用户运行程序,可以降低因为给予 --cap-add SYS_ADMIN 权限导致的宿主系统被攻破的风险。
使用 gosu 可以在 shell 脚本中降低权限到一般用户后执行,Docker 中的 redis 就是这么干的。而且 gosu 更像 sudo 而非 su ,使用起来更加自然一些。
不知道 gosu 是否可以让程序避免收不到 SIGTERM 的尴尬? SIGTERM not received by java process using 'docker stop' and the official java image
拙劣的 gosu 替代品
su www-data -s '/bin/bash' -p -c 'your command here'
关于 JAVA_OPTS
JAVA_OPTS 这个环境变量,原版 Java 是不会读取的。这个环境变量是是一些热门的 Java Web 框架所拓展支持的,故自己编写启动脚本的时候,需要手动将这个添加到命令行中。
解决程序无法收到 SIGTERM 信号的问题
- 使用 [ "" ] entrypoint 语法
- 在脚本中使用 exec
sigterm 与 forged minecraft
搭配有 forge 的 Minecraft 服务器并不能很好的处理 sigterm 信号,而该信号正是 Docker 发送的结束信号,有一个小模组,叫做 sigterm-catcher 可以很好的处理这个问题,它会将 sigterm 等信号转换为 stop 指令,优雅的关闭掉服务器。
模组变更情况
更新了 forge 。从 http://files.minecraftforge.net/maven/net/minecraftforge/forge/index_1.12.2.html 下载。
更新了 spongeforge ,但是止步在了 7.2.2 ,因为 7.2.3 版本会和 appliedenergistics2 出现冲突(卡在地图生成步骤) Crash with AE2 exist
这个冲突也比较有趣,更新 sponge 到最新稳定版后,若更新 forge 到最新版,或者不更新,服务器都能正常启动,若 forge 更新到 sponge 需要的最新版后,服务器就崩溃。
从 https://www.spongepowered.org/downloads/spongeforge/stable/1.12.2 下载。
forge 和 spongeforge 版本应该匹配。https://docs.spongepowered.org/stable/en-GB/server/getting-started/implementations/spongeforge.html#dedicated-servers
更新了 foamfix ,因为 forge 更新了,这个不更新,会出冲突(crash)。
更新了 phosphor ,0.2.7 版本好像可以不需要手动设置 sponge 中的光照设置了,但是由于该版本放在客户端内会导致崩溃,故升级到 0.2.6 版本(还是需要手动设置光照)。
更新了 randompatches ,并且按照作者推荐增加了 randomtweaks 。
更新了 LagGoggles,同时因为其新版本好像没有 spongeforege 版本,故换成了 forge 版本,同时因为依赖关系,增加了库 TickCentral 。
为了显示卡顿情况,可能客户端也需要这个模组。
切换服务器的认证方式从离线模式启动+进入服务器后认证,变为在线模式启动+劫持mojang认证接口。
故增加了 authlib-injector 。
故移除了 flexiblelogin OfflineServerUsernameWebRequestNetworkManager 。
移除了 PrismPlus,因为其功能已经并入主线 Console SPAM
增加了 signal-catcher (server only) ,以处理 sigterm 事件。
增加了 AromaBackup (server only),以备份存档,同时因为依赖关系,增加了库 Aroma1997Core 。
Sponge 模组 AntiAttackRL EpicBanItem griefprevention LuckPerms Nucleus Prism ServerListPlus 被更新。
一个不错的 Sponge 插件清单
离线服务器安全问题
离线服务器运行时,客户端与服务器的数据包是不经过加密的,也就是说密码是在公网透明的。
(不过讲道理 mc 服务器又不需要证书,mitm 太容易了)
正版登录与离线服务器
在本地测试可以正常进入上次的存档,并且无需正版登录(即UUID并非正版(好像正版ID进入离线服务器是不使用正版UUID的)),ic2 正常工作。
游戏数据迁移
data 目录是由 aliyun-cn-beijing 20191201 快照中差分出来的,在数据导入后,为了避免重复,可以删除。
打包成 Docker 的 overlayfs 格式。
cd server-migration/
mkdir -p /tmp/data-build/data/{upper,work}
chmod a+rwx /tmp/data-build/data/upper
cp -r data/* /tmp/data-build/data/upper
chown -R 1000:1000 /tmp/data-build/data/upper/*
tar -C /tmp/data-build -cvzf data-init.tgz data
rm -r /tmp/data-build
Play Server 打包运行备份命令
# 打包
docker build -t wonld-orbis-server .
# 运行(最低要求)(只要在 server.properties 里关掉了 maxtick,cpu 使用率没有特定要求)
# or --privileged
docker run --cap-add SYS_ADMIN -dit -m 1600m --memory-swap 1600m -e JAVA_OPTS='-Xmx900m' -e 'AUTH_SERVER=https://auth.orbis.wonld.com/api/yggdrasil' -p 25565:25565 -p 25565:25565/udp -v /play-server-data/data:/data --name mc wonld-orbis-server
# 运行(推荐)(1.2 倍启动时间,相对于无限制(非常好了))(全新启动后,docker stats 显示内存使用 1.7g 如果用 swap 的话,堆大小还能更大一些)
docker run --cap-add SYS_ADMIN -dit --cpus 2 -m 2g --memory-swap 2g -e JAVA_OPTS='-Xmx1200m' -e 'AUTH_SERVER=https://auth.orbis.wonld.com/api/yggdrasil' -p 25565:25565 -p 25565:25565/udp -v /play-server-data/data:/data --name mc wonld-orbis-server
# 启动和停止
docker stop yd
docker start yd
# 进入控制台(Ctrl-P Ctrl-Q to detach)
docker attach yd
# 服务器内有备份mod
部署到服务器
# 请先完成 auth server 的配置
# local
docker build -t wonld-orbis-server .
docker save wonld-orbis-server | gzip > image.tgz
# remote
mkdir -p /sharedfolders/A/Docker/wonld-orbis-server
chown -R 1000:1000 /sharedfolders/A/Docker/wonld-orbis-server
pushd /sharedfolders/A/Docker/wonld-orbis-server
# copy local:image.tgz data-init.tgz -> remote wonld-orbis-server/
tar xzvf data-init.tgz
docker load < image.tgz
# 使用了 swap 以尽可能的使用内存
docker run --cap-add SYS_ADMIN -dit --cpus 2 -m 2g --memory-swap 4g -e JAVA_OPTS='-Xmx1500m' -e 'AUTH_SERVER=http://192.168.29.3/api/yggdrasil' --network wonld-orbis-net --ip 192.168.29.2 -p 25565:25565 -p 25565:25565/udp -v /sharedfolders/A/Docker/wonld-orbis-server/data:/data --name mc wonld-orbis-server
配置
启用了 online mode :
server.properties:
online-mode=false => true
关闭了版本检查:
config/forge.cfg:
general.disableVersionCheck=false => true
version_checking.Global=true => false
config/AppliedEnergistics2/VersionChecker.cfg:
general.enabled=true => false
阿里云转发
每次重启系统后需要手动运行一次。
#!/bin/bash
echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A PREROUTING -p tcp --dport 25565 -j DNAT --to 192.168.3.1:25565
iptables -t nat -A POSTROUTING -d 192.168.3.1 -p tcp --dport 25565 -j MASQUERADE
iptables -t nat -A PREROUTING -p udp --dport 25565 -j DNAT --to 192.168.3.1:25565
iptables -t nat -A POSTROUTING -d 192.168.3.1 -p udp --dport 25565 -j MASQUERADE