之前在 一篇文章 中提到过docker部署minecraft服务器,后来发现并不好用,因为portainer免费版并没有很好的控制台管理方式,没有用户组之类的精确权限控制,同时也没有好用的文件管理方案,最终选择使用code-server来管理,在容器中安装tmux用于后台运行并随时打开控制台,同时还有vscode的好用文件管理

Dockerfile
# ubuntu 作为基础镜像。
FROM ubuntu:24.04
# HTTP 代理
ENV HTTP_PROXY="http://172.17.0.1:7890"
# HTTPS 代理
ENV HTTPS_PROXY="http://172.17.0.1:7890"
# 针对 code-server/npm/git 等的全局代理(通常是小写的)
ENV http_proxy="http://172.17.0.1:7890"
ENV https_proxy="http://172.17.0.1:7890"
ENV NO_PROXY="localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
# 时区
ENV TZ="Asia/Shanghai"
# 配置 APT 清华源
COPY aliyun-ubuntu.sources /etc/apt/sources.list.d/
RUN apt-get update
# 安装依赖
RUN DEBIAN_FRONTEND=noninteractive \
apt-get install -y --no-install-recommends \
curl \
gosu \
tmux \
ca-certificates \
iputils-ping \
wget \
zip \
unzip \
locales \
&& rm -rf /var/lib/apt/lists/*
# 设置编码
RUN locale-gen en_US.UTF-8 && update-locale LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8
# code-server
RUN curl -fsSL https://code-server.dev/install.sh | sh
COPY start.sh /
RUN chmod +x /start.sh
RUN useradd --shell /bin/bash -u 1001 -m mc
WORKDIR /home/mc
# 暴露端口
EXPOSE 8080
EXPOSE 25565
CMD ["/start.sh"]
start.sh
#!/bin/bash
set -e
# 检查环境变量是否已设置
if [ -z "$SERVER_NAME" ]; then
echo "Error: SERVER_NAME environment variable is not set correctly."
exit 1
fi
# 修正文件归属
chown -R mc /home/mc
# ===============================================================
# 信号捕获函数:用于优雅关闭 MC Server
# ===============================================================
graceful_shutdown() {
echo "Caught signal. Performing graceful shutdown..."
# 检查 tmux session 是否存在
if tmux has-session -t mc 2>/dev/null; then
echo "Sending 'stop' command to Minecraft server via tmux..."
# 使用 tmux send-keys 向 'mc' 会话发送 'stop' 命令和 Enter 键
# -t mc: 指定目标会话
# C-m: 相当于按下 Enter 键
tmux send-keys -t mc 'stop' C-m
# 等待 MC Server 进程退出。mc-server-runner 会处理 Java 的关闭
# 我们可以等待 tmux session 消失,表示 mc-server-runner 已退出
TIMEOUT=60
COUNT=0
while tmux has-session -t mc 2>/dev/null && [ $COUNT -lt $TIMEOUT ]; do
echo "Waiting for Minecraft server to stop... (Max $TIMEOUT seconds)"
sleep 1
COUNT=$((COUNT + 1))
done
if [ $COUNT -eq $TIMEOUT ]; then
echo "WARNING: Minecraft server did not stop gracefully within $TIMEOUT seconds. Killing tmux session."
tmux kill-session -t mc 2>/dev/null
else
echo "Minecraft server stopped successfully."
fi
else
echo "Minecraft server tmux session not found or already stopped."
fi
# 停止 code-server (exec 后的 code-server 已经是主进程,收到信号后会自动退出)
# 我们这里不需要手动杀死 code-server,因为 Tini 会转发信号给它。
# 退出脚本,允许 Tini 干净地清理进程
exit 0
}
# 捕获 SIGINT (Ctrl+C) 和 SIGTERM (Docker Stop) 信号
trap 'graceful_shutdown' SIGINT SIGTERM
echo Starting Server...
cd /home/mc/$SERVER_NAME
gosu mc tmux new -d -s $SERVER_NAME
gosu mc tmux send-keys -t $SERVER_NAME:0 "$START_CMD" C-m
echo Starting Code...
gosu mc sed -i "s#^password: .*#password: $CODE_PASSWORD#" /home/mc/.config/code-server/config.yaml
gosu mc code-server --bind-addr 0.0.0.0:8080 /home/mc/$SERVER_NAME &
CODE_SERVER_PID=$!
echo "code-server started with PID $CODE_SERVER_PID."
# 等待 code-server 进程或收到的信号。
# 这一行是保持 start.sh 脚本存活的关键,以监听信号。
wait $CODE_SERVER_PID
# 如果 code-server 意外退出,则执行优雅关闭流程
graceful_shutdown
aliyun-ubuntu.sources
Types: deb
URIs: http://mirrors.aliyun.com/ubuntu
Suites: noble noble-updates noble-backports
Components: main restricted universe multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
docker-compose.yml 使用示例(此处的镜像是本地构建的)
services:
velocity:
image: minecraft-universal:1.5
container_name: velocity
hostname: velocity
environment:
SERVER_NAME: "velocity"
START_CMD: "./start.sh"
CODE_PASSWORD: "<your-password>"
ports:
- "25565:25565"
volumes:
- /opt/java:/java:ro
- /opt/mc-velocity:/home/mc
restart: unless-stopped
networks:
- mc
- web
使用时需要先在映射的目录下创建目录,目录名字和环境变量中的SERVER_NAME一致,并在目录下添加 start.sh 以及其他服务器文件,对应compose中定义的启动指令