【教程】使用Docker开服,优雅地隔离不同版本JDK

前言

对于各位腐竹来说,JDK作为Minecraft开服的前置想必都不陌生。但Minecraft 1.17开始对高版本JDK的需求也为一些需要多版本切换的腐竹产生了一些困扰。本文将简述一种基于AdoptJDK on Docker进行开服并借以实现JDK环境隔离的方法。

观前提示

为了便于叙述,本文将默认读者已经完成下列要求:

服务器需求

  • 已经做好基本配置(用户创建、密码修改、软件源配置等)
  • 已经安装Docker(必需)与docker-compose(可选)
  • 防火墙已经放通必要端口

前置知识

  • 基本的Linux操作(文件操作等)
  • 对Minecraft Java Edition服务器架设有一定了解(至少得知道该用什么版本JDK吧)

一些Docker概念解释

这里是一些Docker基本概念的必要解释。

以下内容引用自Docker — 从入门到实践

镜像/Image

Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。

容器/Container

镜像容器 的关系,就像是面向对象程序设计中的 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。因此容器可以拥有自己的 root 文件系统、自己的网络配置、自己的进程空间,甚至自己的用户 ID 空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。这种特性使得容器封装的应用比直接在宿主运行更加安全。

数据卷/Volume

数据卷 是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响镜像
  • 数据卷默认会一直存在,即使容器被删除

快速上手(直接创建 & 管理容器)

文件目录准备

选择一个文件夹作为服务器的工作目录,本文约定为/data/mcserver,并确认所使用的用户有对该目录rwx权限。

开服端准备

将你选择的开服端上传并解压,将所有文件放置在/data/mcserver目录下,同样确认权限。

本文约定服务端核心jar文件名为server-core.jar

拉取镜像(可选)

在任意目录下执行以下命令拉取将要使用的Docker镜像(需要用户处于docker用户组中)

# Java 8
docker pull eclipse-temurin:8-jdk-alpine

# Java 17
docker pull eclipse-temurin:17-jdk-alpine

# 如果还需要安装其他Java版本请自行替换上述命令冒号后的版本tag
# 可用的tag可以在https://hub.docker.com/_/eclipse-temurin/tags查询

当然,如果这里没有拉取镜像之后也是会自动拉取的,所以这一步是可以直接省略的。

创建容器

这里我们输入以下命令创建容器:

docker run -d -it --name mcserver --restart=always --mount type=bind,source=/data/mcserver,target=/server -p 25565:25565 eclipse-temurin:8-jdk-alpine java -jar /server/server.jar -Xmx2048M nogui

执行之后如果没有报错那么你的服务器就已经开好啦!

解释

如果看不懂上面的命令,下面我们来解释一下这条命令到底做了什么。

# 下面的命令经过重排,仅为方便阅读,不能在实际Shell中执行,望周知
docker run \
-d \
-it \
--name mcserver \
--restart=always \
--mount type=bind,source=/data/mcserver,target=/server \
-p 25565:25565 \
eclipse-temurin:8-jdk-alpine \
java -jar /server/server.jar -Xmx2048M nogui

下面我们来逐行分析上面的命令。

  1. docker run是基于镜像创建容器的指令。
  2. -d表示容器将在后台运行,而不会在前台输出内容。
  3. -it表示开启容器的STDIN输入流,并且为容器分配一个tty,这将允许稍后我们进入容器对Minecraft Server的控制台操作。
  4. --name mcserver表示将容器命名为mcserver,方便稍后的管理。这里的名字可以自由修改,如果没有命名,Docker将为容器随机分配一个名字(但不好记忆)
  5. --restart=always向Docker声明了容器的重启策略,always代表将在容器没有开启时自动重启容器,直到用户手动将容器结束。相对应地,这可能导致/stop指令无法正常将服务器关闭(关闭后会马上被Docker拉起),其他重启策略请参见文章结尾。
  6. --mount type=bind,source=/data/mcserver,target=/server表示在容器开启时将把主机内的/data/mcserver映射到容器中的/server,其他使用方式请自行查阅Docker文档。
  7. -p 25565:25565表示将容器的25565端口映射到主机的25565端口,具体数值请根据实际情况修改,参数格式为-p 主机端口:容器端口
  8. eclipse-temurin:8-jdk-alpine指示了创建容器时使用的镜像名称,格式为镜像名称:tag名称,在这里镜像名称就是eclipse-temurin,tag名称为8-jdk-alpine,根据AdoptJDK官方的规则,这个tag名字代表使用AdoptJDK的镜像,版本为8,包含JDK,镜像内的系统为Alpine Linux(一个极度精简,主要用于制作Docker镜像的Linux发行版)
  9. java -jar /server/server.jar -Xmx2048M nogui就是Minecraft开服的启动命令,在此不作解释。

管理容器

那么现在服务器已经运行起来了,腐竹常用的管理服务器操作又如何进行呢?

启动/停止容器

docker container start mcserver # 启动名为mcserver的容器(需要已经创建该容器)
docker container stop mcserver # 停止名为mcserver的容器

查看工作状态

docker ps # 查看正在运行的容器列表
docker container list # 同上
docker container list --all # 列出所有存在的容器(包括已停止的)

docker logs mcserver # 查看名为mcserver容器的日志(容器内运行程序输出到STDOUT与STDERR的内容)

进入控制台

docker attach mcserver # 进入名为mcserver的容器,此时等价于直接操作容器(类似使用Screen),退出容器使用Ctrl+C会导致java被结束,正确退出方法为先按Ctrl+P再按Ctrl+Q,这样类似于Screen的Ctrl+A - d

删除容器

docker rm mcserver # 删除容器mcserver(需要先结束容器)
docker rm mcserver --force # 强行删除容器mcserver(即使正在运行)

升级服务端?

不需要更新Java

直接停止容器,对服务端进行更新,再启动容器即可。

需要更新Java

停止容器后记下原容器的启动参数,删除原容器,创建一个新的容器,在创建时将镜像版本替换为你需要的版本。

使用Docker Compose(进阶)

Docker Compose是Docker官方推出的一个Docker容器编排管理工具,可以简化容器的部署和管理。

准备工作

基本与直接使用Docker中【文件目录准备】与【开服端准备】相同,但将服务端放置在/data/mcserver/server目录下,并检查权限。

编写docker-compose.yml

Docker Compose依赖配置文件docker-compose.yml工作,该文件遵循YAML规范。文件内容如下:

version: "3"

services:
  server:
    image: eclipse-temurin:8-jdk-alpine # 指定镜像名称和镜像版本
    ports:
      - "25565:25565" # 将容器25565端口映射到主机25565端口
    restart: always # 设置重启策略为always
    stdin_open: true # 打开容器STDIN,同 -i
    tty: true # 给容器分配tty,同 -t
    volumes:
      - /data/mcserver/server/:/server # 将主机/data/mcserver/server目录映射到容器内/server
    command: java -jar /server/server.jar -Xmx2048M nogui # Minecraft Server启动命令

编写完成后保存为/data/mcserver/docker-compose.yml

启动容器

这一步非常简单,只需要在/data/mcserver目录下执行如下命令即可:

docker compose up -d # 使用当前目录下的docker-compose.yml启动容器,并且在后台运行

这里Docker Compose创建的容器将被设置为诸如mcserver_server_1的名称(格式:当前目录名_service名(docker-compose.yml中设置的名称)_数字编号

管理容器

使用Docker Compose管理容器的操作略有不同。

以下命令均在/data/mcserver目录中执行。

启动/停止容器

docker compose up -d # 使用当前目录下的docker-compose.yml启动容器,并且在后台运行
docker compose down # 将使用当前目录下的docker-compose.yml容器停止并删除

查看工作状态

docker compose ps # 查看正在运行的容器列表
docker compose logs # 查看容器的日志(容器内运行程序输出到STDOUT与STDERR的内容)

进入控制台

docker attach mcserver_server_1 # 进入名为mcserver_server_1的容器,此时等价于直接操作容器(类似使用Screen),退出容器使用Ctrl+C会导致java被结束,正确退出方法为先按Ctrl+P再按Ctrl+Q,这样类似于Screen的Ctrl+A - d

升级服务端?

不需要更新Java

直接停止容器,对服务端进行更新,再启动容器即可。

需要更新Java

停止容器,修改docker-compose.yml文件中的镜像版本,重新启动容器。

后记

这篇文章主要记录其实是今年寒假时候我开服和同学联机打MC过程中的一点经验,希望能够帮到有需要的腐竹。

Docker的功能非常强大,本文无法一一讲解,只能以实用主义态度讲解需要用到的Docker特性,在实际开服过程中各位腐竹可以自行探索,研究更好的方法,欢迎在评论区里讨论。

其实在写作本文时Docker Hub上已经有了优秀的Minecraft开服镜像itzg/minecraft-server,但为了能更好地使用来自MCBBS等社区中各位大佬制作的优秀整合包这里我选择了使用官方AdoptJDK镜像直接开服,本质上这并没有完全利用Docker的优点,只是将Docker当作了一个虚拟环境在使用,严格来说违背了Docker的精神。但反正服和我只要有一个能跑就行了我暂时还没有想出通用的、更好的利用方法。

不过我有一个不成熟的想法,在开设小游戏服务器的时候可以将对局服务器的服务端制作为镜像放在Docker中并利用Docker Swarm等集群功能实现动态扩展,结合BungeeCord服务端的API实现自动注册,但这个方法未经过实践,且我本人并不熟悉BungeeCord和Docker集群,故将其放在这里仅作为一个思路供参考。

附:Docker Restart参数的重启策略

重启策略效果
no不自动重启容器(默认)
on-faliure[:max-retries]当且仅当容器中的程序异常结束(返回值不为0时)重启容器,如果设置了max-retries参数则最多重试max-retries
unless-stopped不管容器中的程序是正常结束还是异常结束都重启容器,直到用户手动停止容器或Docker本身被关闭
always不管容器中的程序是正常结束还是异常结束都重启容器,直到用户手动停止容器。即使Docker本身被关闭,在Docker启动后也会拉起容器
翻译自官方文档

常用on-failurealways参数。

参考

  1. Docker Documentation
  2. Docker – 从入门到实践
  3. AdoptJDK Docker 镜像介绍页面

鸣谢

  • 紫光(Bilibili: @紫光light UID:1690218019):对本文的初版进行了试阅读并提供了反馈
暂无评论

发送评论 编辑评论


				
上一篇