Tag

Docker

docker+mc的运维管理方案

之前在 一篇文章 中提到过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中定义的启动指令

minecraft docker运行

今天研究了一下docker中运行minecraft,原因是希望在不给ssh的情况下允许别人进入服务器后台,因为已经部署了portainer,所以希望可以直接通过portainer操作后台

构建镜像

首先需要一个镜像来运行服务端

一开始我选择了Alpine作为底包,然后发现这个包实在是太干净了,甚至用的都不是glibc,下好的预编译的jdk没法跑,于是换了ubuntu

然后希望通过不同的目录来区分各个不同的子服,所以用环境变量+启动脚本动态选择工作目录

Dockerfile

# ubuntu 作为基础镜像。
FROM ubuntu:24.04

ENV TINI_VERSION=v0.19.0
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
RUN chmod +x /tini

# 设置环境变量的默认值,这些值会被 docker-compose.yml 中的配置覆盖。
ENV SERVER_NAME=minecraft_server
ENV START_CMD="/java/bin/java -Xms1G -Xmx1G -jar server.jar --nogui"

# 设置容器内的通用工作目录。
WORKDIR /${SERVER_NAME}

# 暴露 Minecraft 服务器默认的 TCP 和 UDP 端口。
EXPOSE 25565

# 将启动脚本复制到镜像中。
COPY start.sh /start.sh

# 授权启动脚本可执行权限。
RUN chmod +x /start.sh

# 设置入口点。tini 确保当容器收到停止信号时,能优雅地关闭 Java 进程。
ENTRYPOINT ["/tini", "--"]

# 定义默认的启动命令。
CMD ["/start.sh"]

start.sh

#!/bin/bash

# 检查环境变量是否已设置
if [ -z "$SERVER_NAME" ]; then
    echo "Error: SERVER_NAME environment variable is not set correctly."
    exit 1
fi

cd /$SERVER_NAME
echo "starting..."
exec $START_CMD
# 构建指令
docker build -t minecraft-universal:1.0 .

docker compose

services:
  sc:
    image: minecraft-universal:1.2
    container_name: sc # 容器名字
    # 允许attach
    stdin_open: true
    tty: true
    ports:
      - "35565:25565" # 游戏端口映射,可以修改为其他端口
    environment:
      # 服务器名
      SERVER_NAME: "sc"
      START_CMD: "/java/bin/java -jar fabric-server-mc.1.21.8-loader.0.17.2-launcher.1.1.0.jar nogui"
    volumes:
      # 挂载宿主机的 Java 目录到容器中的 /java
      - /usr/local/jdk/21:/java
      # 挂载宿主机的服务器目录到容器中
      - ./sc:/sc
    restart: unless-stopped

使用如上配置之后可以在attach后正常和服务端控制台交互

重点是 stdin_open: truetty: true

docker 部署 nexus

docker 安装 nexus

#!/bin/bash

docker run -d --user root -p 8081:8081 -v /opt/nexus-data:/nexus-data --name nexus3 sonatype/nexus3:latest

数据目录映射到主机的 /opt/nexus-data,容器的/nexus-data

配置签名

本来不知道有这步的,但是偶然点进了system status界面,看到一个红叉和Nexus was not configured with an encryption key and is using the Default key.

签名配置是一个json文件,默认没有生成(起码我没找到),格式如下

{
  "active": "key-id",
  "keys": [
    {
      "id": "key-id",
      "key": "base64 key"
    }
  ]
}

这个key可以通过 openssl rand -base64 16 生成

有两种方式指定配置文件路径,一种是使用环境变量,另一种是nexus配置文件(/opt/nexus-data/etc/nexus.properties),我使用的是nexus配置文件,在配置文件添加一行

nexus.secrets.file=/nexus-data/secrets.json

关闭central

因为我的nexus搭建在家里云,本身上行带宽就不高,不希望跑太多流量

取消勾选 repository > centralonline

安全

禁用admin用户

因为admin用户名是默认的管理员账号,所以禁用之,然后创建子管理用户,给管理角色,并保存

创建ci用户并分配权限

准备后续使用ci推送构件到nexus,所以专门分配一个ci用户

给了如下权限

  • nx-repository-view-maven2-maven-central-add
  • nx-repository-view-maven2-maven-central-browse
  • nx-repository-view-maven2-maven-central-edit
  • nx-repository-view-maven2-maven-central-read
  • nx-repository-view-maven2-maven-public-add
  • nx-repository-view-maven2-maven-public-browse
  • nx-repository-view-maven2-maven-public-edit
  • nx-repository-view-maven2-maven-public-read
  • nx-repository-view-maven2-maven-releases-add
  • nx-repository-view-maven2-maven-releases-browse
  • nx-repository-view-maven2-maven-releases-edit
  • nx-repository-view-maven2-maven-releases-read
  • nx-repository-view-maven2-maven-snapshots-add
  • nx-repository-view-maven2-maven-snapshots-browse
  • nx-repository-view-maven2-maven-snapshots-edit
  • nx-repository-view-maven2-maven-snapshots-read

尝试推送

招了一个gradle项目,配置publish

publishing {
    repositories {
        maven {
            name = "snapshot"
            url = uri("http://localhost:8081/repository/maven-snapshots/")
            isAllowInsecureProtocol = true
            credentials {
                username = "homo"
                password = "114514"
            }
        }
    }
}

注意此处推送的是snapshot,需要在项目version后添加后缀-SNAPSHOT,否则推送响应400

总结

还没有使用所以没有总结(咕咕咕

安装docker

2025-04-01ubuntu-24.04.1 上安装 docker

参考自 https://www.sysgeek.cn/install-docker-ubuntu/

安装前置

sudo apt update
sudo apt install apt-transport-https curl

添加gpg密钥

这一步遇到问题,可能失败,失败后会在 /etc/apt/keyrings/docker.gpg 写一个空文件,需要删除后重试

curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

添加仓库

虽然格式和apt自带的不一致,但是能用

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

更新软件包列表

sudo apt update

安装

这一步下载很慢,选择给apt设置代理(http_proxy环境变量居然无效) 创建文件 /etc/apt/apt.conf.d/proxy.conf 并写入

Acquire::http::Proxy "http://192.168.1.1:7890/";
Acquire::https::Proxy "http://192.168.1.1:7890/";
# 安装
sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

检查docker状态

sudo systemctl is-active docker

hello world

下不动哈哈哈
https://cloud.tencent.com/developer/article/2485043 找到了可用的两个镜像源

编辑文件 /etc/docker/daemon.json

{
  "registry-mirrors": [
    "https://docker.xuanyuan.me",
    "https://docker.1ms.run"
  ]
}

配置完成后

sudo systemctl daemon-reload
sudo systemctl restart docker