🤖 Minecraft 伺服器自動化系統

Discord 機器人 × Azure VM × Python 自動化 × 智能監控

完整教學:從零開始建立企業級自動化系統

🎯 系統架構概覽

完整自動化流程

Discord 指令
Python 機器人
Azure API
VM 控制
Minecraft 伺服器

🎮 遊戲體驗

Better MC (BMC4) 模組包
320+ 精選模組
20 人同時在線

☁️ 雲端基礎

Azure Standard_B4s
4 vCPUs, 16GB RAM
智能成本控制

🤖 自動控制

Discord 機器人整合
一鍵啟動/停止
即時狀態監控

📊 智能監控

自動閒置偵測
資源使用監控
自動備份系統

🚀 2025 年最新更新:
  • Python 3.12 支援與 async/await 優化
  • Azure Container Apps 整合選項
  • Terraform 基礎設施即代碼
  • 進階監控與告警系統
  • 多伺服器管理支援

💰 成本分析與優化

服務項目 規格 每月成本 (USD) 優化策略
Azure VM (Standard_B4s) 4 vCPUs, 16GB RAM $60-80 自動開關機
Premium SSD 64GB $8-12 定期清理
網路流量 出站流量 $3-8 CDN 優化
備份存儲 自動備份 $2-5 生命週期管理
總計 完整系統 $73-105 預計節省 40-60%
💡 成本優化技巧:
透過智能自動化,平均可節省 40-60% 的雲端成本。系統會在無玩家時自動關閉 VM,並在需要時快速重新啟動。

🤖 Discord 機器人設定

1

建立 Discord 應用程式

前往 Discord Developer Portal 並建立新的應用程式

# 1. 訪問 https://discord.com/developers/applications # 2. 點擊 "New Application" # 3. 設定應用程式名稱:MinecraftBot # 4. 進入 "Bot" 頁面 # 5. 點擊 "Add Bot" # 6. 複製 Token(稍後會用到)
2

設定機器人權限

配置必要的權限以確保機器人正常運作

必要權限:
  • ✅ Send Messages
  • ✅ Read Message History
  • ✅ Use Slash Commands
  • ✅ Embed Links
  • ✅ Attach Files
3

現代化 Python 機器人代碼 (2025)

import discord from discord.ext import commands, tasks import asyncio import aiohttp import json import os from datetime import datetime, timedelta from azure.identity import DefaultAzureCredential from azure.mgmt.compute import ComputeManagementClient import logging # 設定日誌記錄 logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) class MinecraftBot(commands.Bot): def __init__(self): intents = discord.Intents.default() intents.message_content = True super().__init__(command_prefix='!', intents=intents) # Azure 設定 self.subscription_id = os.getenv('AZURE_SUBSCRIPTION_ID') self.resource_group = os.getenv('AZURE_RESOURCE_GROUP') self.vm_name = os.getenv('AZURE_VM_NAME') # 初始化 Azure 客戶端 self.credential = DefaultAzureCredential() self.compute_client = ComputeManagementClient( self.credential, self.subscription_id ) # 狀態追蹤 self.server_status = { 'is_running': False, 'last_checked': None, 'players_online': 0, 'startup_time': None } async def on_ready(self): logger.info(f'{self.user} 已成功連接到 Discord!') await self.sync_commands() self.monitor_server.start() async def sync_commands(self): """同步斜線指令""" try: synced = await self.tree.sync() logger.info(f'同步了 {len(synced)} 個指令') except Exception as e: logger.error(f'指令同步失敗: {e}') @tasks.loop(minutes=5) async def monitor_server(self): """定期監控伺服器狀態""" try: vm_status = await self.get_vm_status() await self.update_server_status(vm_status) # 自動關機邏輯(無玩家 30 分鐘後) if (self.server_status['is_running'] and self.server_status['players_online'] == 0 and self.server_status['last_checked'] and datetime.now() - self.server_status['last_checked'] > timedelta(minutes=30)): await self.auto_shutdown() except Exception as e: logger.error(f'監控錯誤: {e}') async def get_vm_status(self): """獲取 Azure VM 狀態""" try: vm = self.compute_client.virtual_machines.get( self.resource_group, self.vm_name, expand='instanceView' ) # 檢查 VM 電源狀態 power_state = None for status in vm.instance_view.statuses: if status.code.startswith('PowerState'): power_state = status.code.split('/')[-1] break return { 'power_state': power_state, 'vm_size': vm.hardware_profile.vm_size, 'location': vm.location } except Exception as e: logger.error(f'獲取 VM 狀態失敗: {e}') return None async def start_vm(self): """啟動 Azure VM""" try: logger.info('正在啟動 Azure VM...') operation = self.compute_client.virtual_machines.begin_start( self.resource_group, self.vm_name ) await asyncio.get_event_loop().run_in_executor(None, operation.wait) logger.info('Azure VM 啟動完成') return True except Exception as e: logger.error(f'VM 啟動失敗: {e}') return False async def stop_vm(self): """停止 Azure VM""" try: logger.info('正在停止 Azure VM...') operation = self.compute_client.virtual_machines.begin_deallocate( self.resource_group, self.vm_name ) await asyncio.get_event_loop().run_in_executor(None, operation.wait) logger.info('Azure VM 停止完成') return True except Exception as e: logger.error(f'VM 停止失敗: {e}') return False async def get_minecraft_status(self, server_ip: str, port: int = 25565): """檢查 Minecraft 伺服器狀態""" try: async with aiohttp.ClientSession() as session: url = f'https://api.mcsrvstat.us/2/{server_ip}:{port}' async with session.get(url) as response: if response.status == 200: data = await response.json() return { 'online': data.get('online', False), 'players': data.get('players', {}), 'version': data.get('version', 'Unknown'), 'motd': data.get('motd', {}).get('clean', [''])[0] if data.get('motd') else 'N/A' } except Exception as e: logger.error(f'獲取 Minecraft 狀態失敗: {e}') return None # 斜線指令定義 bot = MinecraftBot() @bot.tree.command(name="start-server", description="啟動 Minecraft 伺服器") async def start_server(interaction: discord.Interaction): await interaction.response.defer() embed = discord.Embed( title="🚀 正在啟動 Minecraft 伺服器", color=discord.Color.orange(), timestamp=datetime.now() ) embed.add_field(name="狀態", value="正在啟動 Azure VM...", inline=False) embed.add_field(name="預計時間", value="2-3 分鐘", inline=True) embed.set_footer(text="自動化系統") await interaction.followup.send(embed=embed) # 啟動 VM success = await bot.start_vm() if success: # 等待 Minecraft 伺服器啟動 await asyncio.sleep(90) # 給予啟動時間 embed = discord.Embed( title="✅ Minecraft 伺服器已啟動", color=discord.Color.green(), timestamp=datetime.now() ) embed.add_field(name="伺服器 IP", value="mc.lueklake.me", inline=True) embed.add_field(name="版本", value="1.20.1 (BMC4)", inline=True) embed.add_field(name="狀態", value="🟢 線上", inline=True) embed.set_footer(text="準備好開始遊戲!") await interaction.followup.send(embed=embed) else: embed = discord.Embed( title="❌ 啟動失敗", description="請聯繫管理員或稍後重試", color=discord.Color.red(), timestamp=datetime.now() ) await interaction.followup.send(embed=embed) @bot.tree.command(name="stop-server", description="停止 Minecraft 伺服器") async def stop_server(interaction: discord.Interaction): await interaction.response.defer() embed = discord.Embed( title="🛑 正在停止 Minecraft 伺服器", description="正在安全關閉伺服器並停止 Azure VM...", color=discord.Color.orange(), timestamp=datetime.now() ) await interaction.followup.send(embed=embed) success = await bot.stop_vm() if success: embed = discord.Embed( title="✅ 伺服器已安全停止", description="Azure VM 已停止,節省雲端費用", color=discord.Color.green(), timestamp=datetime.now() ) embed.set_footer(text="使用 /start-server 重新啟動") await interaction.followup.send(embed=embed) @bot.tree.command(name="server-status", description="查看伺服器詳細狀態") async def server_status(interaction: discord.Interaction): await interaction.response.defer() # 獲取 VM 狀態 vm_status = await bot.get_vm_status() mc_status = await bot.get_minecraft_status("mc.lueklake.me") embed = discord.Embed( title="📊 Minecraft 伺服器狀態", color=discord.Color.blue(), timestamp=datetime.now() ) if vm_status: vm_state = "🟢 運行中" if vm_status['power_state'] == 'running' else "🔴 已停止" embed.add_field(name="Azure VM", value=vm_state, inline=True) embed.add_field(name="VM 規格", value=vm_status['vm_size'], inline=True) embed.add_field(name="資料中心", value=vm_status['location'], inline=True) if mc_status and mc_status['online']: players = mc_status['players'] player_info = f"{players.get('online', 0)}/{players.get('max', 20)}" embed.add_field(name="遊戲狀態", value="🟢 線上", inline=True) embed.add_field(name="玩家數量", value=player_info, inline=True) embed.add_field(name="版本", value=mc_status['version'], inline=True) if players.get('list'): player_names = ', '.join([p['name'] for p in players['list'][:5]]) if len(players['list']) > 5: player_names += f" (+{len(players['list'])-5} 更多)" embed.add_field(name="在線玩家", value=player_names, inline=False) else: embed.add_field(name="遊戲狀態", value="🔴 離線", inline=True) embed.set_footer(text="每 5 分鐘自動更新") await interaction.followup.send(embed=embed) # 啟動機器人 if __name__ == "__main__": token = os.getenv('DISCORD_BOT_TOKEN') if not token: logger.error('請設定 DISCORD_BOT_TOKEN 環境變數') exit(1) bot.run(token)
⚠️ 安全性注意事項:
• 將 Token 和 Azure 憑證存放在環境變數中
• 使用 Azure Managed Identity 進行身份驗證
• 定期輪換 Discord Bot Token
• 限制機器人權限範圍

☁️ Azure 基礎設施設定

1

使用 Terraform 建立基礎設施

現代化的基礎設施即代碼方法

# main.tf terraform { required_version = ">= 1.0" required_providers { azurerm = { source = "hashicorp/azurerm" version = "~> 3.0" } } } provider "azurerm" { features {} } # 資源群組 resource "azurerm_resource_group" "minecraft" { name = "minecraft-automation-rg" location = "UK South" tags = { Environment = "Production" Project = "MinecraftAutomation" CostCenter = "Gaming" } } # 虛擬網路 resource "azurerm_virtual_network" "minecraft" { name = "minecraft-vnet" address_space = ["10.0.0.0/16"] location = azurerm_resource_group.minecraft.location resource_group_name = azurerm_resource_group.minecraft.name } resource "azurerm_subnet" "minecraft" { name = "minecraft-subnet" resource_group_name = azurerm_resource_group.minecraft.name virtual_network_name = azurerm_virtual_network.minecraft.name address_prefixes = ["10.0.1.0/24"] } # 網路安全群組 resource "azurerm_network_security_group" "minecraft" { name = "minecraft-nsg" location = azurerm_resource_group.minecraft.location resource_group_name = azurerm_resource_group.minecraft.name security_rule { name = "SSH" priority = 1001 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "22" source_address_prefix = "*" destination_address_prefix = "*" } security_rule { name = "Minecraft" priority = 1002 direction = "Inbound" access = "Allow" protocol = "Tcp" source_port_range = "*" destination_port_range = "25565" source_address_prefix = "*" destination_address_prefix = "*" } } # 公用 IP resource "azurerm_public_ip" "minecraft" { name = "minecraft-pip" resource_group_name = azurerm_resource_group.minecraft.name location = azurerm_resource_group.minecraft.location allocation_method = "Static" domain_name_label = "minecraft-${random_string.suffix.result}" } resource "random_string" "suffix" { length = 8 special = false upper = false } # 網路介面 resource "azurerm_network_interface" "minecraft" { name = "minecraft-nic" location = azurerm_resource_group.minecraft.location resource_group_name = azurerm_resource_group.minecraft.name ip_configuration { name = "internal" subnet_id = azurerm_subnet.minecraft.id private_ip_address_allocation = "Dynamic" public_ip_address_id = azurerm_public_ip.minecraft.id } } # 關聯網路安全群組 resource "azurerm_network_interface_security_group_association" "minecraft" { network_interface_id = azurerm_network_interface.minecraft.id network_security_group_id = azurerm_network_security_group.minecraft.id } # 受管身份 resource "azurerm_user_assigned_identity" "minecraft" { name = "minecraft-identity" resource_group_name = azurerm_resource_group.minecraft.name location = azurerm_resource_group.minecraft.location } # 虛擬機器 resource "azurerm_linux_virtual_machine" "minecraft" { name = "minecraft-vm" resource_group_name = azurerm_resource_group.minecraft.name location = azurerm_resource_group.minecraft.location size = "Standard_B4s" admin_username = "azureuser" # 禁用密碼驗證,使用 SSH 金鑰 disable_password_authentication = true network_interface_ids = [ azurerm_network_interface.minecraft.id, ] admin_ssh_key { username = "azureuser" public_key = file("~/.ssh/id_rsa.pub") # 請替換為您的公鑰路徑 } os_disk { caching = "ReadWrite" storage_account_type = "Premium_LRS" disk_size_gb = 64 } source_image_reference { publisher = "Canonical" offer = "0001-com-ubuntu-server-jammy" sku = "22_04-lts-gen2" version = "latest" } identity { type = "UserAssigned" identity_ids = [azurerm_user_assigned_identity.minecraft.id] } # 自動關機設定 boot_diagnostics {} tags = { Environment = "Production" AutoShutdown = "Enabled" } } # 自動關機設定 resource "azurerm_dev_test_global_vm_shutdown_schedule" "minecraft" { virtual_machine_id = azurerm_linux_virtual_machine.minecraft.id location = azurerm_resource_group.minecraft.location enabled = true daily_recurrence_time = "0200" # 每日凌晨 2 點自動關機 timezone = "GMT Standard Time" notification_settings { enabled = false } } # 輸出重要資訊 output "public_ip_address" { value = azurerm_public_ip.minecraft.ip_address } output "ssh_connection" { value = "ssh azureuser@${azurerm_public_ip.minecraft.ip_address}" } output "minecraft_server_address" { value = "${azurerm_public_ip.minecraft.ip_address}:25565" }
2

部署基礎設施

# 初始化 Terraform terraform init # 規劃部署 terraform plan -out=minecraft.tfplan # 執行部署 terraform apply minecraft.tfplan # 獲取輸出資訊 terraform output
3

設定 Azure 權限

為機器人建立 Service Principal 並分配必要權限

# 建立 Service Principal az ad sp create-for-rbac --name "MinecraftBotSP" \ --role "Virtual Machine Contributor" \ --scopes "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/minecraft-automation-rg" # 記錄輸出的資訊: # { # "appId": "應用程式 ID", # "displayName": "MinecraftBotSP", # "password": "密碼", # "tenant": "租戶 ID" # } # 設定環境變數(在機器人執行環境中) export AZURE_CLIENT_ID="應用程式 ID" export AZURE_CLIENT_SECRET="密碼" export AZURE_TENANT_ID="租戶 ID" export AZURE_SUBSCRIPTION_ID="您的訂閱 ID" export AZURE_RESOURCE_GROUP="minecraft-automation-rg" export AZURE_VM_NAME="minecraft-vm"

⚙️ 伺服器自動化腳本

1

Minecraft 伺服器自動安裝腳本

#!/bin/bash # minecraft-install.sh - Better MC (BMC4) 自動安裝腳本 set -e # 遇到錯誤時停止執行 # 顏色定義 RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # 日誌函數 log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1" } log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # 檢查是否為 root 用戶 if [[ $EUID -eq 0 ]]; then log_error "請不要使用 root 用戶執行此腳本" exit 1 fi # 更新系統 log_info "更新系統套件..." sudo apt update && sudo apt upgrade -y # 安裝必要套件 log_info "安裝必要套件..." sudo apt install -y wget curl unzip htop screen ufw fail2ban # 安裝 Java 17 (Better MC 建議版本) log_info "安裝 Java 17..." sudo apt install -y openjdk-17-jdk # 驗證 Java 安裝 java_version=$(java -version 2>&1 | head -n 1) log_success "Java 安裝完成: $java_version" # 設定防火牆 log_info "設定防火牆..." sudo ufw allow ssh sudo ufw allow 25565/tcp sudo ufw --force enable # 建立 Minecraft 目錄和使用者 log_info "建立 Minecraft 使用者和目錄..." sudo useradd -m -r -s /bin/bash minecraft || true sudo mkdir -p /opt/minecraft sudo chown minecraft:minecraft /opt/minecraft # 下載 Better MC (BMC4) 伺服器 log_info "下載 Better MC (BMC4) 伺服器檔案..." cd /opt/minecraft # 下載 Forge 伺服器 FORGE_VERSION="47.2.20" MINECRAFT_VERSION="1.20.1" FORGE_INSTALLER="forge-${MINECRAFT_VERSION}-${FORGE_VERSION}-installer.jar" sudo -u minecraft wget -O "$FORGE_INSTALLER" \ "https://maven.minecraftforge.net/net/minecraftforge/forge/${MINECRAFT_VERSION}-${FORGE_VERSION}/forge-${MINECRAFT_VERSION}-${FORGE_VERSION}-installer.jar" # 安裝 Forge 伺服器 log_info "安裝 Forge 伺服器..." sudo -u minecraft java -jar "$FORGE_INSTALLER" --installServer # 建立 Better MC 模組包目錄 sudo -u minecraft mkdir -p mods config scripts kubejs # 下載 Better MC 模組包(示例 - 實際需要從官方來源下載) log_info "下載 Better MC 模組包..." # 注意: 實際部署時需要從官方來源下載完整的模組包 # 這裡僅作為示例結構 # 建立伺服器配置 log_info "建立伺服器配置..." sudo -u minecraft cat > server.properties << EOF # Better MC (BMC4) 伺服器配置 enable-jmx-monitoring=false rcon.port=25575 gamemode=survival enable-command-block=false enable-query=false generator-settings={} enforce-secure-profile=true level-name=world motd=Better MC (BMC4) - Azure 自動化伺服器 query.port=25565 pvp=true generate-structures=true max-chained-neighbor-updates=1000000 difficulty=normal network-compression-threshold=256 max-tick-time=60000 require-resource-pack=false use-native-transport=true max-players=20 online-mode=true enable-status=true allow-flight=false initial-disabled-packs= broadcast-rcon-to-ops=true view-distance=10 server-ip= resource-pack-prompt= allow-nether=true server-port=25565 enable-rcon=false sync-chunk-writes=true op-permission-level=4 prevent-proxy-connections=false hide-online-players=false resource-pack= entity-broadcast-range-percentage=100 simulation-distance=10 rcon.password= player-idle-timeout=0 debug=false force-gamemode=false rate-limit=0 hardcore=false white-list=false broadcast-console-to-ops=true spawn-npcs=true spawn-animals=true function-permission-level=2 initial-enabled-packs=vanilla level-type=minecraft\:normal text-filtering-config= spawn-monsters=true enforce-whitelist=false spawn-protection=16 resource-pack-sha1= max-world-size=29999984 EOF # 接受 EULA sudo -u minecraft echo "eula=true" > eula.txt # 建立啟動腳本 log_info "建立伺服器啟動腳本..." sudo -u minecraft cat > start-server.sh << 'EOF' #!/bin/bash # Better MC (BMC4) 啟動腳本 # 設定 Java 記憶體 MEMORY="12G" # 設定 Java 啟動參數(針對 Forge 伺服器優化) JAVA_ARGS="-Xms${MEMORY} -Xmx${MEMORY} \ -XX:+UseG1GC \ -XX:+ParallelRefProcEnabled \ -XX:MaxGCPauseMillis=200 \ -XX:+UnlockExperimentalVMOptions \ -XX:+DisableExplicitGC \ -XX:-OmitStackTraceInFastThrow \ -XX:+AlwaysPreTouch \ -XX:G1NewSizePercent=30 \ -XX:G1MaxNewSizePercent=40 \ -XX:G1HeapRegionSize=8M \ -XX:G1ReservePercent=20 \ -XX:G1HeapWastePercent=5 \ -XX:G1MixedGCCountTarget=8 \ -XX:+UseStringDeduplication \ -XX:G1MixedGCLiveThresholdPercent=90 \ -XX:G1RSetUpdatingPauseTimePercent=5 \ -XX:SurvivorRatio=32 \ -Dfile.encoding=UTF-8 \ -Duser.timezone=Asia/Taipei \ --add-modules=jdk.incubator.vector" # 尋找 Forge 伺服器 JAR 檔案 SERVER_JAR=$(find . -name "forge-*-shim.jar" | head -n 1) if [ -z "$SERVER_JAR" ]; then echo "錯誤: 找不到 Forge 伺服器檔案" exit 1 fi echo "正在啟動 Better MC (BMC4) 伺服器..." echo "伺服器檔案: $SERVER_JAR" echo "記憶體設定: $MEMORY" echo "啟動時間: $(date)" # 啟動伺服器 java $JAVA_ARGS -jar "$SERVER_JAR" nogui echo "伺服器已停止: $(date)" EOF # 設定執行權限 sudo chmod +x /opt/minecraft/start-server.sh # 建立 systemd 服務 log_info "建立 systemd 服務..." sudo cat > /etc/systemd/system/minecraft.service << EOF [Unit] Description=Better MC (BMC4) Minecraft Server After=network.target [Service] Type=simple User=minecraft Group=minecraft WorkingDirectory=/opt/minecraft ExecStart=/opt/minecraft/start-server.sh Restart=on-failure RestartSec=30 StandardInput=null # 資源限制 LimitNOFILE=4096 LimitNPROC=4096 [Install] WantedBy=multi-user.target EOF # 重新載入 systemd 並啟用服務 sudo systemctl daemon-reload sudo systemctl enable minecraft # 建立備份腳本 log_info "建立自動備份腳本..." sudo cat > /opt/minecraft/backup.sh << 'EOF' #!/bin/bash # Minecraft 自動備份腳本 BACKUP_DIR="/opt/minecraft/backups" WORLD_DIR="/opt/minecraft/world" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_NAME="world_backup_$DATE" # 建立備份目錄 mkdir -p "$BACKUP_DIR" # 執行備份 if [ -d "$WORLD_DIR" ]; then echo "正在備份世界檔案..." tar -czf "$BACKUP_DIR/$BACKUP_NAME.tar.gz" -C /opt/minecraft world echo "備份完成: $BACKUP_DIR/$BACKUP_NAME.tar.gz" # 刪除 7 天前的備份 find "$BACKUP_DIR" -name "world_backup_*.tar.gz" -mtime +7 -delete echo "已清理舊備份檔案" else echo "警告: 世界目錄不存在" fi EOF sudo chmod +x /opt/minecraft/backup.sh sudo chown minecraft:minecraft /opt/minecraft/backup.sh # 設定備份 cron job log_info "設定自動備份..." (sudo -u minecraft crontab -l 2>/dev/null; echo "0 */6 * * * /opt/minecraft/backup.sh") | sudo -u minecraft crontab - # 安裝完成 log_success "Better MC (BMC4) 伺服器安裝完成!" log_info "使用以下指令管理伺服器:" echo " 啟動: sudo systemctl start minecraft" echo " 停止: sudo systemctl stop minecraft" echo " 狀態: sudo systemctl status minecraft" echo " 日誌: sudo journalctl -u minecraft -f" echo "" log_info "伺服器位置: /opt/minecraft" log_info "首次啟動會需要一些時間來載入所有模組" log_warning "請確保已從官方來源下載完整的 Better MC 模組包"
2

智能監控腳本

#!/bin/bash # minecraft-monitor.sh - 智能監控與自動管理 MINECRAFT_DIR="/opt/minecraft" LOG_FILE="/var/log/minecraft-monitor.log" WEBHOOK_URL="YOUR_DISCORD_WEBHOOK_URL" # 日誌函數 log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "$LOG_FILE" } # 發送 Discord 通知 send_discord_notification() { local message="$1" local color="$2" # 16711680 (紅色), 65280 (綠色), 16776960 (黃色) curl -H "Content-Type: application/json" \ -X POST \ -d "{ \"embeds\": [{ \"title\": \"Minecraft 伺服器監控\", \"description\": \"$message\", \"color\": $color, \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)\" }] }" \ "$WEBHOOK_URL" } # 檢查伺服器狀態 check_server_status() { if systemctl is-active --quiet minecraft; then return 0 # 運行中 else return 1 # 未運行 fi } # 獲取在線玩家數 get_player_count() { if [ -f "$MINECRAFT_DIR/logs/latest.log" ]; then # 從日誌檔案中解析玩家數量 players=$(tail -100 "$MINECRAFT_DIR/logs/latest.log" | grep -o "players online" | wc -l) echo "$players" else echo "0" fi } # 檢查系統資源 check_system_resources() { cpu_usage=$(top -bn1 | grep "Cpu(s)" | awk '{print $2}' | sed 's/%us,//') memory_usage=$(free | grep Mem | awk '{printf("%.1f"), $3/$2 * 100.0}') disk_usage=$(df /opt/minecraft | tail -1 | awk '{print $5}' | sed 's/%//') log "系統資源 - CPU: ${cpu_usage}%, 記憶體: ${memory_usage}%, 磁碟: ${disk_usage}%" # 檢查資源警告 if (( $(echo "$memory_usage > 90" | bc -l) )); then send_discord_notification "⚠️ 記憶體使用率過高: ${memory_usage}%" 16776960 fi if (( disk_usage > 85 )); then send_discord_notification "⚠️ 磁碟使用率過高: ${disk_usage}%" 16776960 fi } # 自動重啟檢查 auto_restart_check() { if ! check_server_status; then log "偵測到伺服器未運行,嘗試自動重啟..." systemctl start minecraft sleep 30 if check_server_status; then log "伺服器自動重啟成功" send_discord_notification "✅ 伺服器已自動重啟" 65280 else log "伺服器自動重啟失敗" send_discord_notification "❌ 伺服器自動重啟失敗,需要手動檢查" 16711680 fi fi } # 自動備份檢查 auto_backup_check() { if check_server_status; then player_count=$(get_player_count) # 如果沒有玩家在線,執行備份 if [ "$player_count" -eq 0 ]; then log "執行自動備份(無玩家在線)" /opt/minecraft/backup.sh fi fi } # 閒置關機檢查 idle_shutdown_check() { if check_server_status; then player_count=$(get_player_count) # 檢查上次活動時間 last_activity_file="/tmp/minecraft_last_activity" current_time=$(date +%s) if [ "$player_count" -eq 0 ]; then if [ -f "$last_activity_file" ]; then last_activity=$(cat "$last_activity_file") idle_time=$((current_time - last_activity)) # 30 分鐘 = 1800 秒 if [ "$idle_time" -gt 1800 ]; then log "伺服器閒置超過 30 分鐘,準備關機" send_discord_notification "🛑 伺服器閒置超過 30 分鐘,正在關機以節省成本" 16776960 # 停止 Minecraft 服務 systemctl stop minecraft # 停止 Azure VM(如果在 Azure 環境中) if command -v az &> /dev/null; then az vm deallocate --resource-group minecraft-automation-rg --name minecraft-vm fi rm -f "$last_activity_file" fi else echo "$current_time" > "$last_activity_file" fi else # 有玩家在線,更新活動時間 echo "$current_time" > "$last_activity_file" fi fi } # 主監控循環 main() { log "=== Minecraft 監控腳本啟動 ===" while true; do check_system_resources auto_restart_check auto_backup_check idle_shutdown_check # 每 5 分鐘檢查一次 sleep 300 done } # 處理終止信號 trap 'log "監控腳本停止"; exit 0' SIGTERM SIGINT # 執行主程式 main
3

設定自動啟動

# 建立監控服務 sudo cat > /etc/systemd/system/minecraft-monitor.service << EOF [Unit] Description=Minecraft Server Monitor After=network.target minecraft.service [Service] Type=simple User=root ExecStart=/opt/minecraft/minecraft-monitor.sh Restart=always RestartSec=10 [Install] WantedBy=multi-user.target EOF # 啟用並啟動監控服務 sudo systemctl daemon-reload sudo systemctl enable minecraft-monitor sudo systemctl start minecraft-monitor # 檢查服務狀態 sudo systemctl status minecraft-monitor

📈 進階監控與告警系統

1

Azure Monitor 整合

# azure-monitor-setup.sh # 設定 Azure Monitor 代理程式 # 安裝 Azure Monitor 代理程式 wget https://aka.ms/azcmagent -O ~/install_linux_azcmagent.sh sudo bash ~/install_linux_azcmagent.sh # 連接到 Azure Arc sudo azcmagent connect \ --resource-group "minecraft-automation-rg" \ --tenant-id "YOUR_TENANT_ID" \ --location "uksouth" \ --subscription-id "YOUR_SUBSCRIPTION_ID" \ --cloud "AzureCloud" \ --correlation-id "$(uuidgen)" # 安裝 Log Analytics 代理程式 wget -O Onboard_Linux.sh https://raw.githubusercontent.com/Microsoft/OMS-Agent-for-Linux/master/installer/scripts/onboard_agent.sh sudo bash Onboard_Linux.sh \ -w "YOUR_WORKSPACE_ID" \ -s "YOUR_WORKSPACE_KEY" \ -d opinsights.azure.com
2

自定義監控指標

# minecraft-metrics.py # 收集 Minecraft 特定指標並發送到 Azure Monitor import json import time import requests import psutil import re from datetime import datetime, timezone from azure.monitor.opentelemetry import configure_azure_monitor from opentelemetry import metrics from opentelemetry.metrics import set_meter_provider from opentelemetry.sdk.metrics import MeterProvider class MinecraftMetricsCollector: def __init__(self, connection_string): # 設定 Azure Monitor configure_azure_monitor(connection_string=connection_string) set_meter_provider(MeterProvider()) self.meter = metrics.get_meter("minecraft-server") # 建立指標 self.player_count_gauge = self.meter.create_gauge( name="minecraft_players_online", description="Number of players currently online", unit="players" ) self.tps_gauge = self.meter.create_gauge( name="minecraft_tps", description="Server ticks per second", unit="tps" ) self.memory_usage_gauge = self.meter.create_gauge( name="minecraft_memory_usage", description="Server memory usage percentage", unit="percent" ) self.cpu_usage_gauge = self.meter.create_gauge( name="minecraft_cpu_usage", description="Server CPU usage percentage", unit="percent" ) def get_minecraft_stats(self): """從 Minecraft 伺服器日誌中提取統計資料""" try: with open('/opt/minecraft/logs/latest.log', 'r') as f: lines = f.readlines() # 解析玩家數量 player_count = 0 tps = 20.0 for line in reversed(lines[-100:]): # 檢查最後 100 行 # 查找玩家加入/離開訊息 if 'joined the game' in line: player_count += 1 elif 'left the game' in line: player_count = max(0, player_count - 1) # 查找 TPS 資訊(如果有相關插件) tps_match = re.search(r'TPS.*?(\d+\.?\d*)', line) if tps_match: tps = float(tps_match.group(1)) break return { 'player_count': player_count, 'tps': tps, 'timestamp': datetime.now(timezone.utc) } except Exception as e: print(f"錯誤獲取 Minecraft 統計: {e}") return None def get_system_stats(self): """獲取系統資源統計""" return { 'cpu_usage': psutil.cpu_percent(interval=1), 'memory_usage': psutil.virtual_memory().percent, 'disk_usage': psutil.disk_usage('/opt/minecraft').percent, 'timestamp': datetime.now(timezone.utc) } def collect_and_send_metrics(self): """收集並發送指標到 Azure Monitor""" try: # 獲取 Minecraft 統計 mc_stats = self.get_minecraft_stats() if mc_stats: self.player_count_gauge.set(mc_stats['player_count']) self.tps_gauge.set(mc_stats['tps']) # 獲取系統統計 sys_stats = self.get_system_stats() self.cpu_usage_gauge.set(sys_stats['cpu_usage']) self.memory_usage_gauge.set(sys_stats['memory_usage']) print(f"指標已發送 - 玩家: {mc_stats['player_count'] if mc_stats else 'N/A'}, " f"CPU: {sys_stats['cpu_usage']:.1f}%, " f"記憶體: {sys_stats['memory_usage']:.1f}%") except Exception as e: print(f"發送指標時出錯: {e}") def run(self, interval=60): """持續收集指標""" print("Minecraft 指標收集器已啟動...") while True: self.collect_and_send_metrics() time.sleep(interval) if __name__ == "__main__": # 設定您的 Application Insights 連接字串 connection_string = "InstrumentationKey=YOUR_KEY;IngestionEndpoint=https://uksouth-1.in.applicationinsights.azure.com/" collector = MinecraftMetricsCollector(connection_string) collector.run(interval=30) # 每 30 秒收集一次指標
3

設定 Azure 告警規則

# 使用 Azure CLI 建立告警規則 # 高 CPU 使用率告警 az monitor metrics alert create \ --name "Minecraft-High-CPU" \ --resource-group "minecraft-automation-rg" \ --scopes "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/minecraft-automation-rg/providers/Microsoft.Compute/virtualMachines/minecraft-vm" \ --condition "avg Percentage CPU > 80" \ --window-size 5m \ --evaluation-frequency 1m \ --severity 2 \ --description "Minecraft 伺服器 CPU 使用率過高" # 高記憶體使用率告警 az monitor metrics alert create \ --name "Minecraft-High-Memory" \ --resource-group "minecraft-automation-rg" \ --scopes "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/minecraft-automation-rg/providers/Microsoft.Compute/virtualMachines/minecraft-vm" \ --condition "avg Available Memory Bytes < 1073741824" \ --window-size 5m \ --evaluation-frequency 1m \ --severity 2 \ --description "Minecraft 伺服器可用記憶體不足" # 伺服器離線告警 az monitor metrics alert create \ --name "Minecraft-Server-Down" \ --resource-group "minecraft-automation-rg" \ --scopes "/subscriptions/YOUR_SUBSCRIPTION_ID/resourceGroups/minecraft-automation-rg/providers/Microsoft.Compute/virtualMachines/minecraft-vm" \ --condition "avg VmAvailabilityMetric < 1" \ --window-size 5m \ --evaluation-frequency 1m \ --severity 1 \ --description "Minecraft 伺服器離線"

🚀 進階功能與最佳實踐

🐳 容器化部署

使用 Docker 和 Azure Container Apps 進行現代化部署

🔄 CI/CD 管道

GitHub Actions 自動化部署與更新

🌍 多區域部署

支援全球多個資料中心的負載分散

🔐 安全強化

SSL/TLS、防火牆、入侵偵測系統

1

Docker 容器化

# Dockerfile FROM openjdk:17-jdk-slim # 安裝必要套件 RUN apt-get update && apt-get install -y \ wget \ curl \ unzip \ && rm -rf /var/lib/apt/lists/* # 建立 minecraft 用戶 RUN useradd -m -s /bin/bash minecraft # 設定工作目錄 WORKDIR /opt/minecraft RUN chown minecraft:minecraft /opt/minecraft # 切換到 minecraft 用戶 USER minecraft # 複製伺服器檔案 COPY --chown=minecraft:minecraft server/ /opt/minecraft/ # 設定 Java 啟動參數 ENV JAVA_OPTS="-Xms12G -Xmx12G -XX:+UseG1GC -XX:+ParallelRefProcEnabled" # 暴露端口 EXPOSE 25565 # 啟動指令 CMD ["sh", "-c", "java $JAVA_OPTS -jar forge-1.20.1-47.2.20-shim.jar nogui"]
2

GitHub Actions CI/CD

# .github/workflows/deploy.yml name: Deploy Minecraft Server on: push: branches: [ main ] workflow_dispatch: env: AZURE_RESOURCE_GROUP: minecraft-automation-rg AZURE_VM_NAME: minecraft-vm jobs: deploy: runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 - name: Azure Login uses: azure/login@v2 with: creds: ${{ secrets.AZURE_CREDENTIALS }} - name: Check if VM is running id: vm-status run: | status=$(az vm get-instance-view \ --resource-group $AZURE_RESOURCE_GROUP \ --name $AZURE_VM_NAME \ --query "instanceView.statuses[?code=='PowerState/running'].displayStatus" \ --output tsv) echo "vm_running=$([[ -n "$status" ]] && echo true || echo false)" >> $GITHUB_OUTPUT - name: Start VM if stopped if: steps.vm-status.outputs.vm_running == 'false' run: | echo "Starting VM..." az vm start --resource-group $AZURE_RESOURCE_GROUP --name $AZURE_VM_NAME echo "Waiting for VM to be ready..." sleep 60 - name: Deploy updated server files run: | # 獲取 VM 公用 IP VM_IP=$(az vm list-ip-addresses \ --resource-group $AZURE_RESOURCE_GROUP \ --name $AZURE_VM_NAME \ --query "[0].virtualMachine.network.publicIpAddresses[0].ipAddress" \ --output tsv) echo "Deploying to VM: $VM_IP" # 使用 SSH 部署(需要預先設定 SSH 金鑰) ssh -o StrictHostKeyChecking=no azureuser@$VM_IP << 'EOF' # 停止 Minecraft 服務 sudo systemctl stop minecraft # 更新伺服器檔案 cd /opt/minecraft sudo -u minecraft git pull origin main # 重新啟動服務 sudo systemctl start minecraft echo "部署完成" EOF - name: Verify deployment run: | echo "驗證部署狀態..." sleep 30 # 這裡可以添加健康檢查邏輯 - name: Send notification if: always() run: | status="${{ job.status }}" if [ "$status" == "success" ]; then message="✅ Minecraft 伺服器部署成功" color="65280" else message="❌ Minecraft 伺服器部署失敗" color="16711680" fi curl -H "Content-Type: application/json" \ -X POST \ -d "{\"embeds\":[{\"title\":\"部署通知\",\"description\":\"$message\",\"color\":$color}]}" \ ${{ secrets.DISCORD_WEBHOOK }}
3

安全性最佳實踐

🔐 安全檢查清單:
  • ✅ 使用 SSH 金鑰而非密碼驗證
  • ✅ 設定防火牆只允許必要端口
  • ✅ 定期更新系統和軟體
  • ✅ 使用 Fail2Ban 防範暴力破解
  • ✅ 啟用 Azure Security Center
  • ✅ 設定網路安全群組規則
  • ✅ 使用 Managed Identity
  • ✅ 加密備份資料
# 安全強化腳本 #!/bin/bash # 設定 Fail2Ban sudo apt install fail2ban -y sudo systemctl enable fail2ban # 設定 SSH 安全 sudo sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config sudo sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config sudo systemctl restart ssh # 設定自動更新 sudo apt install unattended-upgrades -y sudo dpkg-reconfigure -plow unattended-upgrades # 設定防火牆 sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow ssh sudo ufw allow 25565/tcp sudo ufw --force enable echo "安全設定完成"

📚 故障排除與維護

常見問題解決

# 檢查服務狀態 sudo systemctl status minecraft # 查看詳細日誌 sudo journalctl -u minecraft -f # 檢查端口是否開放 sudo netstat -tlnp | grep 25565 # 檢查記憶體使用 free -h # 檢查磁碟空間 df -h # 測試網路連接 ping google.com # 檢查 Java 版本 java -version
⚠️ 緊急故障排除:
1. 伺服器無法啟動 → 檢查 Java 記憶體設定和磁碟空間
2. 玩家無法連接 → 檢查防火牆和網路安全群組
3. 效能問題 → 監控 CPU/記憶體使用,調整 JVM 參數
4. 資料遺失 → 從自動備份恢復世界檔案